Home | History | Annotate | Download | only in Lib
      1 """Debugger basics"""
      2 
      3 import fnmatch
      4 import sys
      5 import os
      6 from inspect import CO_GENERATOR
      7 
      8 __all__ = ["BdbQuit", "Bdb", "Breakpoint"]
      9 
     10 class BdbQuit(Exception):
     11     """Exception to give up completely."""
     12 
     13 
     14 class Bdb:
     15     """Generic Python debugger base class.
     16 
     17     This class takes care of details of the trace facility;
     18     a derived class should implement user interaction.
     19     The standard debugger class (pdb.Pdb) is an example.
     20     """
     21 
     22     def __init__(self, skip=None):
     23         self.skip = set(skip) if skip else None
     24         self.breaks = {}
     25         self.fncache = {}
     26         self.frame_returning = None
     27 
     28     def canonic(self, filename):
     29         if filename == "<" + filename[1:-1] + ">":
     30             return filename
     31         canonic = self.fncache.get(filename)
     32         if not canonic:
     33             canonic = os.path.abspath(filename)
     34             canonic = os.path.normcase(canonic)
     35             self.fncache[filename] = canonic
     36         return canonic
     37 
     38     def reset(self):
     39         import linecache
     40         linecache.checkcache()
     41         self.botframe = None
     42         self._set_stopinfo(None, None)
     43 
     44     def trace_dispatch(self, frame, event, arg):
     45         if self.quitting:
     46             return # None
     47         if event == 'line':
     48             return self.dispatch_line(frame)
     49         if event == 'call':
     50             return self.dispatch_call(frame, arg)
     51         if event == 'return':
     52             return self.dispatch_return(frame, arg)
     53         if event == 'exception':
     54             return self.dispatch_exception(frame, arg)
     55         if event == 'c_call':
     56             return self.trace_dispatch
     57         if event == 'c_exception':
     58             return self.trace_dispatch
     59         if event == 'c_return':
     60             return self.trace_dispatch
     61         print('bdb.Bdb.dispatch: unknown debugging event:', repr(event))
     62         return self.trace_dispatch
     63 
     64     def dispatch_line(self, frame):
     65         if self.stop_here(frame) or self.break_here(frame):
     66             self.user_line(frame)
     67             if self.quitting: raise BdbQuit
     68         return self.trace_dispatch
     69 
     70     def dispatch_call(self, frame, arg):
     71         # XXX 'arg' is no longer used
     72         if self.botframe is None:
     73             # First call of dispatch since reset()
     74             self.botframe = frame.f_back # (CT) Note that this may also be None!
     75             return self.trace_dispatch
     76         if not (self.stop_here(frame) or self.break_anywhere(frame)):
     77             # No need to trace this function
     78             return # None
     79         # Ignore call events in generator except when stepping.
     80         if self.stopframe and frame.f_code.co_flags & CO_GENERATOR:
     81             return self.trace_dispatch
     82         self.user_call(frame, arg)
     83         if self.quitting: raise BdbQuit
     84         return self.trace_dispatch
     85 
     86     def dispatch_return(self, frame, arg):
     87         if self.stop_here(frame) or frame == self.returnframe:
     88             # Ignore return events in generator except when stepping.
     89             if self.stopframe and frame.f_code.co_flags & CO_GENERATOR:
     90                 return self.trace_dispatch
     91             try:
     92                 self.frame_returning = frame
     93                 self.user_return(frame, arg)
     94             finally:
     95                 self.frame_returning = None
     96             if self.quitting: raise BdbQuit
     97             # The user issued a 'next' or 'until' command.
     98             if self.stopframe is frame and self.stoplineno != -1:
     99                 self._set_stopinfo(None, None)
    100         return self.trace_dispatch
    101 
    102     def dispatch_exception(self, frame, arg):
    103         if self.stop_here(frame):
    104             # When stepping with next/until/return in a generator frame, skip
    105             # the internal StopIteration exception (with no traceback)
    106             # triggered by a subiterator run with the 'yield from' statement.
    107             if not (frame.f_code.co_flags & CO_GENERATOR
    108                     and arg[0] is StopIteration and arg[2] is None):
    109                 self.user_exception(frame, arg)
    110                 if self.quitting: raise BdbQuit
    111         # Stop at the StopIteration or GeneratorExit exception when the user
    112         # has set stopframe in a generator by issuing a return command, or a
    113         # next/until command at the last statement in the generator before the
    114         # exception.
    115         elif (self.stopframe and frame is not self.stopframe
    116                 and self.stopframe.f_code.co_flags & CO_GENERATOR
    117                 and arg[0] in (StopIteration, GeneratorExit)):
    118             self.user_exception(frame, arg)
    119             if self.quitting: raise BdbQuit
    120 
    121         return self.trace_dispatch
    122 
    123     # Normally derived classes don't override the following
    124     # methods, but they may if they want to redefine the
    125     # definition of stopping and breakpoints.
    126 
    127     def is_skipped_module(self, module_name):
    128         for pattern in self.skip:
    129             if fnmatch.fnmatch(module_name, pattern):
    130                 return True
    131         return False
    132 
    133     def stop_here(self, frame):
    134         # (CT) stopframe may now also be None, see dispatch_call.
    135         # (CT) the former test for None is therefore removed from here.
    136         if self.skip and \
    137                self.is_skipped_module(frame.f_globals.get('__name__')):
    138             return False
    139         if frame is self.stopframe:
    140             if self.stoplineno == -1:
    141                 return False
    142             return frame.f_lineno >= self.stoplineno
    143         if not self.stopframe:
    144             return True
    145         return False
    146 
    147     def break_here(self, frame):
    148         filename = self.canonic(frame.f_code.co_filename)
    149         if filename not in self.breaks:
    150             return False
    151         lineno = frame.f_lineno
    152         if lineno not in self.breaks[filename]:
    153             # The line itself has no breakpoint, but maybe the line is the
    154             # first line of a function with breakpoint set by function name.
    155             lineno = frame.f_code.co_firstlineno
    156             if lineno not in self.breaks[filename]:
    157                 return False
    158 
    159         # flag says ok to delete temp. bp
    160         (bp, flag) = effective(filename, lineno, frame)
    161         if bp:
    162             self.currentbp = bp.number
    163             if (flag and bp.temporary):
    164                 self.do_clear(str(bp.number))
    165             return True
    166         else:
    167             return False
    168 
    169     def do_clear(self, arg):
    170         raise NotImplementedError("subclass of bdb must implement do_clear()")
    171 
    172     def break_anywhere(self, frame):
    173         return self.canonic(frame.f_code.co_filename) in self.breaks
    174 
    175     # Derived classes should override the user_* methods
    176     # to gain control.
    177 
    178     def user_call(self, frame, argument_list):
    179         """This method is called when there is the remote possibility
    180         that we ever need to stop in this function."""
    181         pass
    182 
    183     def user_line(self, frame):
    184         """This method is called when we stop or break at this line."""
    185         pass
    186 
    187     def user_return(self, frame, return_value):
    188         """This method is called when a return trap is set here."""
    189         pass
    190 
    191     def user_exception(self, frame, exc_info):
    192         """This method is called if an exception occurs,
    193         but only if we are to stop at or just below this level."""
    194         pass
    195 
    196     def _set_stopinfo(self, stopframe, returnframe, stoplineno=0):
    197         self.stopframe = stopframe
    198         self.returnframe = returnframe
    199         self.quitting = False
    200         # stoplineno >= 0 means: stop at line >= the stoplineno
    201         # stoplineno -1 means: don't stop at all
    202         self.stoplineno = stoplineno
    203 
    204     # Derived classes and clients can call the following methods
    205     # to affect the stepping state.
    206 
    207     def set_until(self, frame, lineno=None):
    208         """Stop when the line with the line no greater than the current one is
    209         reached or when returning from current frame"""
    210         # the name "until" is borrowed from gdb
    211         if lineno is None:
    212             lineno = frame.f_lineno + 1
    213         self._set_stopinfo(frame, frame, lineno)
    214 
    215     def set_step(self):
    216         """Stop after one line of code."""
    217         # Issue #13183: pdb skips frames after hitting a breakpoint and running
    218         # step commands.
    219         # Restore the trace function in the caller (that may not have been set
    220         # for performance reasons) when returning from the current frame.
    221         if self.frame_returning:
    222             caller_frame = self.frame_returning.f_back
    223             if caller_frame and not caller_frame.f_trace:
    224                 caller_frame.f_trace = self.trace_dispatch
    225         self._set_stopinfo(None, None)
    226 
    227     def set_next(self, frame):
    228         """Stop on the next line in or below the given frame."""
    229         self._set_stopinfo(frame, None)
    230 
    231     def set_return(self, frame):
    232         """Stop when returning from the given frame."""
    233         if frame.f_code.co_flags & CO_GENERATOR:
    234             self._set_stopinfo(frame, None, -1)
    235         else:
    236             self._set_stopinfo(frame.f_back, frame)
    237 
    238     def set_trace(self, frame=None):
    239         """Start debugging from `frame`.
    240 
    241         If frame is not specified, debugging starts from caller's frame.
    242         """
    243         if frame is None:
    244             frame = sys._getframe().f_back
    245         self.reset()
    246         while frame:
    247             frame.f_trace = self.trace_dispatch
    248             self.botframe = frame
    249             frame = frame.f_back
    250         self.set_step()
    251         sys.settrace(self.trace_dispatch)
    252 
    253     def set_continue(self):
    254         # Don't stop except at breakpoints or when finished
    255         self._set_stopinfo(self.botframe, None, -1)
    256         if not self.breaks:
    257             # no breakpoints; run without debugger overhead
    258             sys.settrace(None)
    259             frame = sys._getframe().f_back
    260             while frame and frame is not self.botframe:
    261                 del frame.f_trace
    262                 frame = frame.f_back
    263 
    264     def set_quit(self):
    265         self.stopframe = self.botframe
    266         self.returnframe = None
    267         self.quitting = True
    268         sys.settrace(None)
    269 
    270     # Derived classes and clients can call the following methods
    271     # to manipulate breakpoints.  These methods return an
    272     # error message is something went wrong, None if all is well.
    273     # Set_break prints out the breakpoint line and file:lineno.
    274     # Call self.get_*break*() to see the breakpoints or better
    275     # for bp in Breakpoint.bpbynumber: if bp: bp.bpprint().
    276 
    277     def set_break(self, filename, lineno, temporary=False, cond=None,
    278                   funcname=None):
    279         filename = self.canonic(filename)
    280         import linecache # Import as late as possible
    281         line = linecache.getline(filename, lineno)
    282         if not line:
    283             return 'Line %s:%d does not exist' % (filename, lineno)
    284         list = self.breaks.setdefault(filename, [])
    285         if lineno not in list:
    286             list.append(lineno)
    287         bp = Breakpoint(filename, lineno, temporary, cond, funcname)
    288 
    289     def _prune_breaks(self, filename, lineno):
    290         if (filename, lineno) not in Breakpoint.bplist:
    291             self.breaks[filename].remove(lineno)
    292         if not self.breaks[filename]:
    293             del self.breaks[filename]
    294 
    295     def clear_break(self, filename, lineno):
    296         filename = self.canonic(filename)
    297         if filename not in self.breaks:
    298             return 'There are no breakpoints in %s' % filename
    299         if lineno not in self.breaks[filename]:
    300             return 'There is no breakpoint at %s:%d' % (filename, lineno)
    301         # If there's only one bp in the list for that file,line
    302         # pair, then remove the breaks entry
    303         for bp in Breakpoint.bplist[filename, lineno][:]:
    304             bp.deleteMe()
    305         self._prune_breaks(filename, lineno)
    306 
    307     def clear_bpbynumber(self, arg):
    308         try:
    309             bp = self.get_bpbynumber(arg)
    310         except ValueError as err:
    311             return str(err)
    312         bp.deleteMe()
    313         self._prune_breaks(bp.file, bp.line)
    314 
    315     def clear_all_file_breaks(self, filename):
    316         filename = self.canonic(filename)
    317         if filename not in self.breaks:
    318             return 'There are no breakpoints in %s' % filename
    319         for line in self.breaks[filename]:
    320             blist = Breakpoint.bplist[filename, line]
    321             for bp in blist:
    322                 bp.deleteMe()
    323         del self.breaks[filename]
    324 
    325     def clear_all_breaks(self):
    326         if not self.breaks:
    327             return 'There are no breakpoints'
    328         for bp in Breakpoint.bpbynumber:
    329             if bp:
    330                 bp.deleteMe()
    331         self.breaks = {}
    332 
    333     def get_bpbynumber(self, arg):
    334         if not arg:
    335             raise ValueError('Breakpoint number expected')
    336         try:
    337             number = int(arg)
    338         except ValueError:
    339             raise ValueError('Non-numeric breakpoint number %s' % arg)
    340         try:
    341             bp = Breakpoint.bpbynumber[number]
    342         except IndexError:
    343             raise ValueError('Breakpoint number %d out of range' % number)
    344         if bp is None:
    345             raise ValueError('Breakpoint %d already deleted' % number)
    346         return bp
    347 
    348     def get_break(self, filename, lineno):
    349         filename = self.canonic(filename)
    350         return filename in self.breaks and \
    351             lineno in self.breaks[filename]
    352 
    353     def get_breaks(self, filename, lineno):
    354         filename = self.canonic(filename)
    355         return filename in self.breaks and \
    356             lineno in self.breaks[filename] and \
    357             Breakpoint.bplist[filename, lineno] or []
    358 
    359     def get_file_breaks(self, filename):
    360         filename = self.canonic(filename)
    361         if filename in self.breaks:
    362             return self.breaks[filename]
    363         else:
    364             return []
    365 
    366     def get_all_breaks(self):
    367         return self.breaks
    368 
    369     # Derived classes and clients can call the following method
    370     # to get a data structure representing a stack trace.
    371 
    372     def get_stack(self, f, t):
    373         stack = []
    374         if t and t.tb_frame is f:
    375             t = t.tb_next
    376         while f is not None:
    377             stack.append((f, f.f_lineno))
    378             if f is self.botframe:
    379                 break
    380             f = f.f_back
    381         stack.reverse()
    382         i = max(0, len(stack) - 1)
    383         while t is not None:
    384             stack.append((t.tb_frame, t.tb_lineno))
    385             t = t.tb_next
    386         if f is None:
    387             i = max(0, len(stack) - 1)
    388         return stack, i
    389 
    390     def format_stack_entry(self, frame_lineno, lprefix=': '):
    391         import linecache, reprlib
    392         frame, lineno = frame_lineno
    393         filename = self.canonic(frame.f_code.co_filename)
    394         s = '%s(%r)' % (filename, lineno)
    395         if frame.f_code.co_name:
    396             s += frame.f_code.co_name
    397         else:
    398             s += "<lambda>"
    399         if '__args__' in frame.f_locals:
    400             args = frame.f_locals['__args__']
    401         else:
    402             args = None
    403         if args:
    404             s += reprlib.repr(args)
    405         else:
    406             s += '()'
    407         if '__return__' in frame.f_locals:
    408             rv = frame.f_locals['__return__']
    409             s += '->'
    410             s += reprlib.repr(rv)
    411         line = linecache.getline(filename, lineno, frame.f_globals)
    412         if line:
    413             s += lprefix + line.strip()
    414         return s
    415 
    416     # The following methods can be called by clients to use
    417     # a debugger to debug a statement or an expression.
    418     # Both can be given as a string, or a code object.
    419 
    420     def run(self, cmd, globals=None, locals=None):
    421         if globals is None:
    422             import __main__
    423             globals = __main__.__dict__
    424         if locals is None:
    425             locals = globals
    426         self.reset()
    427         if isinstance(cmd, str):
    428             cmd = compile(cmd, "<string>", "exec")
    429         sys.settrace(self.trace_dispatch)
    430         try:
    431             exec(cmd, globals, locals)
    432         except BdbQuit:
    433             pass
    434         finally:
    435             self.quitting = True
    436             sys.settrace(None)
    437 
    438     def runeval(self, expr, globals=None, locals=None):
    439         if globals is None:
    440             import __main__
    441             globals = __main__.__dict__
    442         if locals is None:
    443             locals = globals
    444         self.reset()
    445         sys.settrace(self.trace_dispatch)
    446         try:
    447             return eval(expr, globals, locals)
    448         except BdbQuit:
    449             pass
    450         finally:
    451             self.quitting = True
    452             sys.settrace(None)
    453 
    454     def runctx(self, cmd, globals, locals):
    455         # B/W compatibility
    456         self.run(cmd, globals, locals)
    457 
    458     # This method is more useful to debug a single function call.
    459 
    460     def runcall(self, func, *args, **kwds):
    461         self.reset()
    462         sys.settrace(self.trace_dispatch)
    463         res = None
    464         try:
    465             res = func(*args, **kwds)
    466         except BdbQuit:
    467             pass
    468         finally:
    469             self.quitting = True
    470             sys.settrace(None)
    471         return res
    472 
    473 
    474 def set_trace():
    475     Bdb().set_trace()
    476 
    477 
    478 class Breakpoint:
    479     """Breakpoint class.
    480 
    481     Implements temporary breakpoints, ignore counts, disabling and
    482     (re)-enabling, and conditionals.
    483 
    484     Breakpoints are indexed by number through bpbynumber and by
    485     the file,line tuple using bplist.  The former points to a
    486     single instance of class Breakpoint.  The latter points to a
    487     list of such instances since there may be more than one
    488     breakpoint per line.
    489 
    490     """
    491 
    492     # XXX Keeping state in the class is a mistake -- this means
    493     # you cannot have more than one active Bdb instance.
    494 
    495     next = 1        # Next bp to be assigned
    496     bplist = {}     # indexed by (file, lineno) tuple
    497     bpbynumber = [None] # Each entry is None or an instance of Bpt
    498                 # index 0 is unused, except for marking an
    499                 # effective break .... see effective()
    500 
    501     def __init__(self, file, line, temporary=False, cond=None, funcname=None):
    502         self.funcname = funcname
    503         # Needed if funcname is not None.
    504         self.func_first_executable_line = None
    505         self.file = file    # This better be in canonical form!
    506         self.line = line
    507         self.temporary = temporary
    508         self.cond = cond
    509         self.enabled = True
    510         self.ignore = 0
    511         self.hits = 0
    512         self.number = Breakpoint.next
    513         Breakpoint.next += 1
    514         # Build the two lists
    515         self.bpbynumber.append(self)
    516         if (file, line) in self.bplist:
    517             self.bplist[file, line].append(self)
    518         else:
    519             self.bplist[file, line] = [self]
    520 
    521     def deleteMe(self):
    522         index = (self.file, self.line)
    523         self.bpbynumber[self.number] = None   # No longer in list
    524         self.bplist[index].remove(self)
    525         if not self.bplist[index]:
    526             # No more bp for this f:l combo
    527             del self.bplist[index]
    528 
    529     def enable(self):
    530         self.enabled = True
    531 
    532     def disable(self):
    533         self.enabled = False
    534 
    535     def bpprint(self, out=None):
    536         if out is None:
    537             out = sys.stdout
    538         print(self.bpformat(), file=out)
    539 
    540     def bpformat(self):
    541         if self.temporary:
    542             disp = 'del  '
    543         else:
    544             disp = 'keep '
    545         if self.enabled:
    546             disp = disp + 'yes  '
    547         else:
    548             disp = disp + 'no   '
    549         ret = '%-4dbreakpoint   %s at %s:%d' % (self.number, disp,
    550                                                 self.file, self.line)
    551         if self.cond:
    552             ret += '\n\tstop only if %s' % (self.cond,)
    553         if self.ignore:
    554             ret += '\n\tignore next %d hits' % (self.ignore,)
    555         if self.hits:
    556             if self.hits > 1:
    557                 ss = 's'
    558             else:
    559                 ss = ''
    560             ret += '\n\tbreakpoint already hit %d time%s' % (self.hits, ss)
    561         return ret
    562 
    563     def __str__(self):
    564         return 'breakpoint %s at %s:%s' % (self.number, self.file, self.line)
    565 
    566 # -----------end of Breakpoint class----------
    567 
    568 def checkfuncname(b, frame):
    569     """Check whether we should break here because of `b.funcname`."""
    570     if not b.funcname:
    571         # Breakpoint was set via line number.
    572         if b.line != frame.f_lineno:
    573             # Breakpoint was set at a line with a def statement and the function
    574             # defined is called: don't break.
    575             return False
    576         return True
    577 
    578     # Breakpoint set via function name.
    579 
    580     if frame.f_code.co_name != b.funcname:
    581         # It's not a function call, but rather execution of def statement.
    582         return False
    583 
    584     # We are in the right frame.
    585     if not b.func_first_executable_line:
    586         # The function is entered for the 1st time.
    587         b.func_first_executable_line = frame.f_lineno
    588 
    589     if  b.func_first_executable_line != frame.f_lineno:
    590         # But we are not at the first line number: don't break.
    591         return False
    592     return True
    593 
    594 # Determines if there is an effective (active) breakpoint at this
    595 # line of code.  Returns breakpoint number or 0 if none
    596 def effective(file, line, frame):
    597     """Determine which breakpoint for this file:line is to be acted upon.
    598 
    599     Called only if we know there is a bpt at this
    600     location.  Returns breakpoint that was triggered and a flag
    601     that indicates if it is ok to delete a temporary bp.
    602 
    603     """
    604     possibles = Breakpoint.bplist[file, line]
    605     for b in possibles:
    606         if not b.enabled:
    607             continue
    608         if not checkfuncname(b, frame):
    609             continue
    610         # Count every hit when bp is enabled
    611         b.hits += 1
    612         if not b.cond:
    613             # If unconditional, and ignoring go on to next, else break
    614             if b.ignore > 0:
    615                 b.ignore -= 1
    616                 continue
    617             else:
    618                 # breakpoint and marker that it's ok to delete if temporary
    619                 return (b, True)
    620         else:
    621             # Conditional bp.
    622             # Ignore count applies only to those bpt hits where the
    623             # condition evaluates to true.
    624             try:
    625                 val = eval(b.cond, frame.f_globals, frame.f_locals)
    626                 if val:
    627                     if b.ignore > 0:
    628                         b.ignore -= 1
    629                         # continue
    630                     else:
    631                         return (b, True)
    632                 # else:
    633                 #   continue
    634             except:
    635                 # if eval fails, most conservative thing is to stop on
    636                 # breakpoint regardless of ignore count.  Don't delete
    637                 # temporary, as another hint to user.
    638                 return (b, False)
    639     return (None, None)
    640 
    641 
    642 # -------------------- testing --------------------
    643 
    644 class Tdb(Bdb):
    645     def user_call(self, frame, args):
    646         name = frame.f_code.co_name
    647         if not name: name = '???'
    648         print('+++ call', name, args)
    649     def user_line(self, frame):
    650         import linecache
    651         name = frame.f_code.co_name
    652         if not name: name = '???'
    653         fn = self.canonic(frame.f_code.co_filename)
    654         line = linecache.getline(fn, frame.f_lineno, frame.f_globals)
    655         print('+++', fn, frame.f_lineno, name, ':', line.strip())
    656     def user_return(self, frame, retval):
    657         print('+++ return', retval)
    658     def user_exception(self, frame, exc_stuff):
    659         print('+++ exception', exc_stuff)
    660         self.set_continue()
    661 
    662 def foo(n):
    663     print('foo(', n, ')')
    664     x = bar(n*10)
    665     print('bar returned', x)
    666 
    667 def bar(a):
    668     print('bar(', a, ')')
    669     return a/2
    670 
    671 def test():
    672     t = Tdb()
    673     t.run('import bdb; bdb.foo(10)')
    674