bdb.py 20.6 KB
Newer Older
1
"""Debugger basics"""
Guido van Rossum's avatar
Guido van Rossum committed
2

3
import fnmatch
Guido van Rossum's avatar
Guido van Rossum committed
4
import sys
5
import os
6
import types
Guido van Rossum's avatar
Guido van Rossum committed
7

8 9
__all__ = ["BdbQuit","Bdb","Breakpoint"]

10 11
class BdbQuit(Exception):
    """Exception to give up completely"""
Guido van Rossum's avatar
Guido van Rossum committed
12 13


14
class Bdb:
Tim Peters's avatar
Tim Peters committed
15

16 17 18 19 20 21 22
    """Generic Python debugger base class.

    This class takes care of details of the trace facility;
    a derived class should implement user interaction.
    The standard debugger class (pdb.Pdb) is an example.
    """

23 24
    def __init__(self, skip=None):
        self.skip = set(skip) if skip else None
25 26 27 28
        self.breaks = {}
        self.fncache = {}

    def canonic(self, filename):
29 30
        if filename == "<" + filename[1:-1] + ">":
            return filename
31 32 33
        canonic = self.fncache.get(filename)
        if not canonic:
            canonic = os.path.abspath(filename)
34
            canonic = os.path.normcase(canonic)
35 36
            self.fncache[filename] = canonic
        return canonic
Tim Peters's avatar
Tim Peters committed
37

38 39 40 41
    def reset(self):
        import linecache
        linecache.checkcache()
        self.botframe = None
42
        self._set_stopinfo(None, None)
Tim Peters's avatar
Tim Peters committed
43

44 45 46 47 48 49 50 51 52 53 54
    def trace_dispatch(self, frame, event, arg):
        if self.quitting:
            return # None
        if event == 'line':
            return self.dispatch_line(frame)
        if event == 'call':
            return self.dispatch_call(frame, arg)
        if event == 'return':
            return self.dispatch_return(frame, arg)
        if event == 'exception':
            return self.dispatch_exception(frame, arg)
55 56 57 58 59 60
        if event == 'c_call':
            return self.trace_dispatch
        if event == 'c_exception':
            return self.trace_dispatch
        if event == 'c_return':
            return self.trace_dispatch
61
        print 'bdb.Bdb.dispatch: unknown debugging event:', repr(event)
62
        return self.trace_dispatch
Tim Peters's avatar
Tim Peters committed
63

64 65 66 67 68
    def dispatch_line(self, frame):
        if self.stop_here(frame) or self.break_here(frame):
            self.user_line(frame)
            if self.quitting: raise BdbQuit
        return self.trace_dispatch
Tim Peters's avatar
Tim Peters committed
69

70 71 72 73
    def dispatch_call(self, frame, arg):
        # XXX 'arg' is no longer used
        if self.botframe is None:
            # First call of dispatch since reset()
74
            self.botframe = frame.f_back # (CT) Note that this may also be None!
75 76 77 78 79 80 81
            return self.trace_dispatch
        if not (self.stop_here(frame) or self.break_anywhere(frame)):
            # No need to trace this function
            return # None
        self.user_call(frame, arg)
        if self.quitting: raise BdbQuit
        return self.trace_dispatch
Tim Peters's avatar
Tim Peters committed
82

83 84 85 86
    def dispatch_return(self, frame, arg):
        if self.stop_here(frame) or frame == self.returnframe:
            self.user_return(frame, arg)
            if self.quitting: raise BdbQuit
87
        return self.trace_dispatch
Tim Peters's avatar
Tim Peters committed
88

89 90 91 92 93
    def dispatch_exception(self, frame, arg):
        if self.stop_here(frame):
            self.user_exception(frame, arg)
            if self.quitting: raise BdbQuit
        return self.trace_dispatch
Tim Peters's avatar
Tim Peters committed
94

95 96 97
    # Normally derived classes don't override the following
    # methods, but they may if they want to redefine the
    # definition of stopping and breakpoints.
Tim Peters's avatar
Tim Peters committed
98

99 100 101 102 103 104
    def is_skipped_module(self, module_name):
        for pattern in self.skip:
            if fnmatch.fnmatch(module_name, pattern):
                return True
        return False

105
    def stop_here(self, frame):
Neal Norwitz's avatar
Neal Norwitz committed
106 107
        # (CT) stopframe may now also be None, see dispatch_call.
        # (CT) the former test for None is therefore removed from here.
108 109 110
        if self.skip and \
               self.is_skipped_module(frame.f_globals.get('__name__')):
            return False
111
        if frame is self.stopframe:
112 113
            if self.stoplineno == -1:
                return False
114
            return frame.f_lineno >= self.stoplineno
115 116
        while frame is not None and frame is not self.stopframe:
            if frame is self.botframe:
117
                return True
118
            frame = frame.f_back
119
        return False
120 121 122

    def break_here(self, frame):
        filename = self.canonic(frame.f_code.co_filename)
123
        if not filename in self.breaks:
124
            return False
125 126
        lineno = frame.f_lineno
        if not lineno in self.breaks[filename]:
127 128 129 130 131 132
            # The line itself has no breakpoint, but maybe the line is the
            # first line of a function with breakpoint set by function name.
            lineno = frame.f_code.co_firstlineno
            if not lineno in self.breaks[filename]:
                return False

133 134 135 136 137 138
        # flag says ok to delete temp. bp
        (bp, flag) = effective(filename, lineno, frame)
        if bp:
            self.currentbp = bp.number
            if (flag and bp.temporary):
                self.do_clear(str(bp.number))
139
            return True
140
        else:
141
            return False
Tim Peters's avatar
Tim Peters committed
142

143 144 145
    def do_clear(self, arg):
        raise NotImplementedError, "subclass of bdb must implement do_clear()"

146
    def break_anywhere(self, frame):
147
        return self.canonic(frame.f_code.co_filename) in self.breaks
Tim Peters's avatar
Tim Peters committed
148

149 150
    # Derived classes should override the user_* methods
    # to gain control.
Tim Peters's avatar
Tim Peters committed
151

152 153 154 155
    def user_call(self, frame, argument_list):
        """This method is called when there is the remote possibility
        that we ever need to stop in this function."""
        pass
Tim Peters's avatar
Tim Peters committed
156

157 158 159
    def user_line(self, frame):
        """This method is called when we stop or break at this line."""
        pass
Tim Peters's avatar
Tim Peters committed
160

161 162 163
    def user_return(self, frame, return_value):
        """This method is called when a return trap is set here."""
        pass
Tim Peters's avatar
Tim Peters committed
164

165 166
    def user_exception(self, frame, exc_info):
        exc_type, exc_value, exc_traceback = exc_info
167 168 169
        """This method is called if an exception occurs,
        but only if we are to stop at or just below this level."""
        pass
Tim Peters's avatar
Tim Peters committed
170

171
    def _set_stopinfo(self, stopframe, returnframe, stoplineno=0):
172 173 174
        self.stopframe = stopframe
        self.returnframe = returnframe
        self.quitting = 0
175 176
        # stoplineno >= 0 means: stop at line >= the stoplineno
        # stoplineno -1 means: don't stop at all
177 178
        self.stoplineno = stoplineno

179 180
    # Derived classes and clients can call the following methods
    # to affect the stepping state.
Tim Peters's avatar
Tim Peters committed
181

182 183 184 185 186
    def set_until(self, frame): #the name "until" is borrowed from gdb
        """Stop when the line with the line no greater than the current one is
        reached or when returning from current frame"""
        self._set_stopinfo(frame, frame, frame.f_lineno+1)

187 188
    def set_step(self):
        """Stop after one line of code."""
189
        self._set_stopinfo(None, None)
Tim Peters's avatar
Tim Peters committed
190

191 192
    def set_next(self, frame):
        """Stop on the next line in or below the given frame."""
193
        self._set_stopinfo(frame, None)
Tim Peters's avatar
Tim Peters committed
194

195 196
    def set_return(self, frame):
        """Stop when returning from the given frame."""
197
        self._set_stopinfo(frame.f_back, frame)
Tim Peters's avatar
Tim Peters committed
198

199 200 201 202 203 204 205
    def set_trace(self, frame=None):
        """Start debugging from `frame`.

        If frame is not specified, debugging starts from caller's frame.
        """
        if frame is None:
            frame = sys._getframe().f_back
206 207 208 209 210 211 212 213 214 215
        self.reset()
        while frame:
            frame.f_trace = self.trace_dispatch
            self.botframe = frame
            frame = frame.f_back
        self.set_step()
        sys.settrace(self.trace_dispatch)

    def set_continue(self):
        # Don't stop except at breakpoints or when finished
216
        self._set_stopinfo(self.botframe, None, -1)
217 218 219
        if not self.breaks:
            # no breakpoints; run without debugger overhead
            sys.settrace(None)
220
            frame = sys._getframe().f_back
221 222 223
            while frame and frame is not self.botframe:
                del frame.f_trace
                frame = frame.f_back
Tim Peters's avatar
Tim Peters committed
224

225 226 227 228 229
    def set_quit(self):
        self.stopframe = self.botframe
        self.returnframe = None
        self.quitting = 1
        sys.settrace(None)
Tim Peters's avatar
Tim Peters committed
230

231 232 233 234 235 236
    # Derived classes and clients can call the following methods
    # to manipulate breakpoints.  These methods return an
    # error message is something went wrong, None if all is well.
    # Set_break prints out the breakpoint line and file:lineno.
    # Call self.get_*break*() to see the breakpoints or better
    # for bp in Breakpoint.bpbynumber: if bp: bp.bpprint().
Tim Peters's avatar
Tim Peters committed
237

238 239
    def set_break(self, filename, lineno, temporary=0, cond = None,
                  funcname=None):
240 241 242 243 244 245
        filename = self.canonic(filename)
        import linecache # Import as late as possible
        line = linecache.getline(filename, lineno)
        if not line:
            return 'Line %s:%d does not exist' % (filename,
                                   lineno)
246
        if not filename in self.breaks:
247 248 249 250
            self.breaks[filename] = []
        list = self.breaks[filename]
        if not lineno in list:
            list.append(lineno)
251
        bp = Breakpoint(filename, lineno, temporary, cond, funcname)
252

253 254 255 256 257 258
    def _prune_breaks(self, filename, lineno):
        if (filename, lineno) not in Breakpoint.bplist:
            self.breaks[filename].remove(lineno)
        if not self.breaks[filename]:
            del self.breaks[filename]

259 260
    def clear_break(self, filename, lineno):
        filename = self.canonic(filename)
261
        if not filename in self.breaks:
262 263 264 265 266 267 268 269
            return 'There are no breakpoints in %s' % filename
        if lineno not in self.breaks[filename]:
            return 'There is no breakpoint at %s:%d' % (filename,
                                    lineno)
        # If there's only one bp in the list for that file,line
        # pair, then remove the breaks entry
        for bp in Breakpoint.bplist[filename, lineno][:]:
            bp.deleteMe()
270
        self._prune_breaks(filename, lineno)
Tim Peters's avatar
Tim Peters committed
271

272 273 274 275 276 277 278 279 280 281 282
    def clear_bpbynumber(self, arg):
        try:
            number = int(arg)
        except:
            return 'Non-numeric breakpoint number (%s)' % arg
        try:
            bp = Breakpoint.bpbynumber[number]
        except IndexError:
            return 'Breakpoint number (%d) out of range' % number
        if not bp:
            return 'Breakpoint (%d) already deleted' % number
283 284
        bp.deleteMe()
        self._prune_breaks(bp.file, bp.line)
285 286 287

    def clear_all_file_breaks(self, filename):
        filename = self.canonic(filename)
288
        if not filename in self.breaks:
289 290 291 292 293 294
            return 'There are no breakpoints in %s' % filename
        for line in self.breaks[filename]:
            blist = Breakpoint.bplist[filename, line]
            for bp in blist:
                bp.deleteMe()
        del self.breaks[filename]
Tim Peters's avatar
Tim Peters committed
295

296 297 298 299 300 301 302
    def clear_all_breaks(self):
        if not self.breaks:
            return 'There are no breakpoints'
        for bp in Breakpoint.bpbynumber:
            if bp:
                bp.deleteMe()
        self.breaks = {}
Tim Peters's avatar
Tim Peters committed
303

304 305
    def get_break(self, filename, lineno):
        filename = self.canonic(filename)
306
        return filename in self.breaks and \
307
            lineno in self.breaks[filename]
Tim Peters's avatar
Tim Peters committed
308

309 310
    def get_breaks(self, filename, lineno):
        filename = self.canonic(filename)
311
        return filename in self.breaks and \
312 313
            lineno in self.breaks[filename] and \
            Breakpoint.bplist[filename, lineno] or []
Tim Peters's avatar
Tim Peters committed
314

315 316
    def get_file_breaks(self, filename):
        filename = self.canonic(filename)
317
        if filename in self.breaks:
318 319 320
            return self.breaks[filename]
        else:
            return []
Tim Peters's avatar
Tim Peters committed
321

322 323
    def get_all_breaks(self):
        return self.breaks
Tim Peters's avatar
Tim Peters committed
324

325 326
    # Derived classes and clients can call the following method
    # to get a data structure representing a stack trace.
Tim Peters's avatar
Tim Peters committed
327

328 329 330 331 332 333 334 335 336 337 338 339 340 341
    def get_stack(self, f, t):
        stack = []
        if t and t.tb_frame is f:
            t = t.tb_next
        while f is not None:
            stack.append((f, f.f_lineno))
            if f is self.botframe:
                break
            f = f.f_back
        stack.reverse()
        i = max(0, len(stack) - 1)
        while t is not None:
            stack.append((t.tb_frame, t.tb_lineno))
            t = t.tb_next
342 343
        if f is None:
            i = max(0, len(stack) - 1)
344
        return stack, i
Tim Peters's avatar
Tim Peters committed
345 346 347

    #

348
    def format_stack_entry(self, frame_lineno, lprefix=': '):
349
        import linecache, repr
350 351
        frame, lineno = frame_lineno
        filename = self.canonic(frame.f_code.co_filename)
352
        s = '%s(%r)' % (filename, lineno)
353 354 355 356
        if frame.f_code.co_name:
            s = s + frame.f_code.co_name
        else:
            s = s + "<lambda>"
357
        if '__args__' in frame.f_locals:
358 359 360 361
            args = frame.f_locals['__args__']
        else:
            args = None
        if args:
362
            s = s + repr.repr(args)
363 364
        else:
            s = s + '()'
365
        if '__return__' in frame.f_locals:
366 367
            rv = frame.f_locals['__return__']
            s = s + '->'
368
            s = s + repr.repr(rv)
369
        line = linecache.getline(filename, lineno, frame.f_globals)
370
        if line: s = s + lprefix + line.strip()
371
        return s
Tim Peters's avatar
Tim Peters committed
372

373 374
    # The following two methods can be called by clients to use
    # a debugger to debug a statement, given as a string.
Tim Peters's avatar
Tim Peters committed
375

376 377 378 379 380 381 382 383 384 385 386
    def run(self, cmd, globals=None, locals=None):
        if globals is None:
            import __main__
            globals = __main__.__dict__
        if locals is None:
            locals = globals
        self.reset()
        sys.settrace(self.trace_dispatch)
        if not isinstance(cmd, types.CodeType):
            cmd = cmd+'\n'
        try:
387 388 389
            exec cmd in globals, locals
        except BdbQuit:
            pass
390 391 392
        finally:
            self.quitting = 1
            sys.settrace(None)
Tim Peters's avatar
Tim Peters committed
393

394 395 396 397 398 399 400 401 402 403 404
    def runeval(self, expr, globals=None, locals=None):
        if globals is None:
            import __main__
            globals = __main__.__dict__
        if locals is None:
            locals = globals
        self.reset()
        sys.settrace(self.trace_dispatch)
        if not isinstance(expr, types.CodeType):
            expr = expr+'\n'
        try:
405 406 407
            return eval(expr, globals, locals)
        except BdbQuit:
            pass
408 409 410 411 412 413 414 415 416 417
        finally:
            self.quitting = 1
            sys.settrace(None)

    def runctx(self, cmd, globals, locals):
        # B/W compatibility
        self.run(cmd, globals, locals)

    # This method is more useful to debug a single function call.

418
    def runcall(self, func, *args, **kwds):
419 420 421 422
        self.reset()
        sys.settrace(self.trace_dispatch)
        res = None
        try:
423 424 425
            res = func(*args, **kwds)
        except BdbQuit:
            pass
426 427 428 429
        finally:
            self.quitting = 1
            sys.settrace(None)
        return res
Guido van Rossum's avatar
Guido van Rossum committed
430 431


432
def set_trace():
433
    Bdb().set_trace()
434

435 436 437

class Breakpoint:

438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
    """Breakpoint class

    Implements temporary breakpoints, ignore counts, disabling and
    (re)-enabling, and conditionals.

    Breakpoints are indexed by number through bpbynumber and by
    the file,line tuple using bplist.  The former points to a
    single instance of class Breakpoint.  The latter points to a
    list of such instances since there may be more than one
    breakpoint per line.

    """

    # XXX Keeping state in the class is a mistake -- this means
    # you cannot have more than one active Bdb instance.

    next = 1        # Next bp to be assigned
    bplist = {}     # indexed by (file, lineno) tuple
    bpbynumber = [None] # Each entry is None or an instance of Bpt
                # index 0 is unused, except for marking an
                # effective break .... see effective()

460 461 462 463
    def __init__(self, file, line, temporary=0, cond=None, funcname=None):
        self.funcname = funcname
        # Needed if funcname is not None.
        self.func_first_executable_line = None
464 465 466 467 468 469 470 471 472 473 474
        self.file = file    # This better be in canonical form!
        self.line = line
        self.temporary = temporary
        self.cond = cond
        self.enabled = 1
        self.ignore = 0
        self.hits = 0
        self.number = Breakpoint.next
        Breakpoint.next = Breakpoint.next + 1
        # Build the two lists
        self.bpbynumber.append(self)
475
        if (file, line) in self.bplist:
476 477 478 479
            self.bplist[file, line].append(self)
        else:
            self.bplist[file, line] = [self]

Tim Peters's avatar
Tim Peters committed
480

481 482 483 484 485 486 487 488 489 490 491 492 493 494
    def deleteMe(self):
        index = (self.file, self.line)
        self.bpbynumber[self.number] = None   # No longer in list
        self.bplist[index].remove(self)
        if not self.bplist[index]:
            # No more bp for this f:l combo
            del self.bplist[index]

    def enable(self):
        self.enabled = 1

    def disable(self):
        self.enabled = 0

495 496 497
    def bpprint(self, out=None):
        if out is None:
            out = sys.stdout
498
        if self.temporary:
Tim Peters's avatar
Tim Peters committed
499
            disp = 'del  '
500
        else:
Tim Peters's avatar
Tim Peters committed
501
            disp = 'keep '
502
        if self.enabled:
503
            disp = disp + 'yes  '
504
        else:
505
            disp = disp + 'no   '
506 507
        print >>out, '%-4dbreakpoint   %s at %s:%d' % (self.number, disp,
                                                       self.file, self.line)
508
        if self.cond:
509
            print >>out, '\tstop only if %s' % (self.cond,)
510
        if self.ignore:
511
            print >>out, '\tignore next %d hits' % (self.ignore)
512 513 514
        if (self.hits):
            if (self.hits > 1): ss = 's'
            else: ss = ''
515 516
            print >>out, ('\tbreakpoint already hit %d time%s' %
                          (self.hits, ss))
517 518 519

# -----------end of Breakpoint class----------

520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
def checkfuncname(b, frame):
    """Check whether we should break here because of `b.funcname`."""
    if not b.funcname:
        # Breakpoint was set via line number.
        if b.line != frame.f_lineno:
            # Breakpoint was set at a line with a def statement and the function
            # defined is called: don't break.
            return False
        return True

    # Breakpoint set via function name.

    if frame.f_code.co_name != b.funcname:
        # It's not a function call, but rather execution of def statement.
        return False

    # We are in the right frame.
    if not b.func_first_executable_line:
        # The function is entered for the 1st time.
        b.func_first_executable_line = frame.f_lineno

    if  b.func_first_executable_line != frame.f_lineno:
        # But we are not at the first line number: don't break.
        return False
    return True

546 547 548
# Determines if there is an effective (active) breakpoint at this
# line of code.  Returns breakpoint number or 0 if none
def effective(file, line, frame):
549 550 551 552 553 554 555 556 557 558 559 560
    """Determine which breakpoint for this file:line is to be acted upon.

    Called only if we know there is a bpt at this
    location.  Returns breakpoint that was triggered and a flag
    that indicates if it is ok to delete a temporary bp.

    """
    possibles = Breakpoint.bplist[file,line]
    for i in range(0, len(possibles)):
        b = possibles[i]
        if b.enabled == 0:
            continue
561 562
        if not checkfuncname(b, frame):
            continue
563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580
        # Count every hit when bp is enabled
        b.hits = b.hits + 1
        if not b.cond:
            # If unconditional, and ignoring,
            # go on to next, else break
            if b.ignore > 0:
                b.ignore = b.ignore -1
                continue
            else:
                # breakpoint and marker that's ok
                # to delete if temporary
                return (b,1)
        else:
            # Conditional bp.
            # Ignore count applies only to those bpt hits where the
            # condition evaluates to true.
            try:
                val = eval(b.cond, frame.f_globals,
Tim Peters's avatar
Tim Peters committed
581
                       frame.f_locals)
582 583 584 585 586 587 588 589 590 591 592
                if val:
                    if b.ignore > 0:
                        b.ignore = b.ignore -1
                        # continue
                    else:
                        return (b,1)
                # else:
                #   continue
            except:
                # if eval fails, most conservative
                # thing is to stop on breakpoint
Tim Peters's avatar
Tim Peters committed
593
                # regardless of ignore count.
594 595 596 597
                # Don't delete temporary,
                # as another hint to user.
                return (b,0)
    return (None, None)
598

Guido van Rossum's avatar
Guido van Rossum committed
599 600 601
# -------------------- testing --------------------

class Tdb(Bdb):
602 603 604 605 606
    def user_call(self, frame, args):
        name = frame.f_code.co_name
        if not name: name = '???'
        print '+++ call', name, args
    def user_line(self, frame):
607
        import linecache
608 609 610
        name = frame.f_code.co_name
        if not name: name = '???'
        fn = self.canonic(frame.f_code.co_filename)
611
        line = linecache.getline(fn, frame.f_lineno, frame.f_globals)
612
        print '+++', fn, frame.f_lineno, name, ':', line.strip()
613 614 615 616 617
    def user_return(self, frame, retval):
        print '+++ return', retval
    def user_exception(self, frame, exc_stuff):
        print '+++ exception', exc_stuff
        self.set_continue()
Guido van Rossum's avatar
Guido van Rossum committed
618 619

def foo(n):
620 621 622
    print 'foo(', n, ')'
    x = bar(n*10)
    print 'bar returned', x
Guido van Rossum's avatar
Guido van Rossum committed
623 624

def bar(a):
625 626
    print 'bar(', a, ')'
    return a/2
Guido van Rossum's avatar
Guido van Rossum committed
627 628

def test():
629 630
    t = Tdb()
    t.run('import bdb; bdb.foo(10)')
631 632

# end