bdb.py 21.4 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
Guido van Rossum's avatar
Guido van Rossum committed
6

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

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


13
class Bdb:
14 15 16 17 18 19 20
    """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.
    """

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

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

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

43 44 45 46 47 48 49 50 51 52 53
    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)
54 55 56 57 58 59
        if event == 'c_call':
            return self.trace_dispatch
        if event == 'c_exception':
            return self.trace_dispatch
        if event == 'c_return':
            return self.trace_dispatch
60
        print('bdb.Bdb.dispatch: unknown debugging event:', repr(event))
61
        return self.trace_dispatch
Tim Peters's avatar
Tim Peters committed
62

63 64 65 66 67
    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
68

69 70 71 72
    def dispatch_call(self, frame, arg):
        # XXX 'arg' is no longer used
        if self.botframe is None:
            # First call of dispatch since reset()
73
            self.botframe = frame.f_back # (CT) Note that this may also be None!
74 75 76 77 78 79 80
            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
81

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

92 93 94 95 96
    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
97

98 99 100
    # 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
101

102 103 104 105 106 107
    def is_skipped_module(self, module_name):
        for pattern in self.skip:
            if fnmatch.fnmatch(module_name, pattern):
                return True
        return False

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

    def break_here(self, frame):
        filename = self.canonic(frame.f_code.co_filename)
126
        if filename not in self.breaks:
127
            return False
128
        lineno = frame.f_lineno
129
        if lineno not in self.breaks[filename]:
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
133
            if lineno not in self.breaks[filename]:
134 135
                return False

136 137 138 139 140 141
        # 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))
142
            return True
143
        else:
144
            return False
Tim Peters's avatar
Tim Peters committed
145

146
    def do_clear(self, arg):
147
        raise NotImplementedError("subclass of bdb must implement do_clear()")
148

149
    def break_anywhere(self, frame):
150
        return self.canonic(frame.f_code.co_filename) in self.breaks
Tim Peters's avatar
Tim Peters committed
151

152 153
    # Derived classes should override the user_* methods
    # to gain control.
Tim Peters's avatar
Tim Peters committed
154

155 156 157 158
    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
159

160 161 162
    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
163

164 165 166
    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
167

168
    def user_exception(self, frame, exc_info):
169 170 171
        """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
172

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

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

184
    def set_until(self, frame, lineno=None):
185 186
        """Stop when the line with the line no greater than the current one is
        reached or when returning from current frame"""
187 188 189 190
        # the name "until" is borrowed from gdb
        if lineno is None:
            lineno = frame.f_lineno + 1
        self._set_stopinfo(frame, frame, lineno)
191

192 193
    def set_step(self):
        """Stop after one line of code."""
194 195 196 197 198 199 200 201
        # Issue #13183: pdb skips frames after hitting a breakpoint and running
        # step commands.
        # Restore the trace function in the caller (that may not have been set
        # for performance reasons) when returning from the current frame.
        if self.frame_returning:
            caller_frame = self.frame_returning.f_back
            if caller_frame and not caller_frame.f_trace:
                caller_frame.f_trace = self.trace_dispatch
202
        self._set_stopinfo(None, None)
Tim Peters's avatar
Tim Peters committed
203

204 205
    def set_next(self, frame):
        """Stop on the next line in or below the given frame."""
206
        self._set_stopinfo(frame, None)
Tim Peters's avatar
Tim Peters committed
207

208 209
    def set_return(self, frame):
        """Stop when returning from the given frame."""
210
        self._set_stopinfo(frame.f_back, frame)
Tim Peters's avatar
Tim Peters committed
211

212 213 214 215 216 217 218
    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
219 220 221 222 223 224 225 226 227 228
        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
229
        self._set_stopinfo(self.botframe, None, -1)
Georg Brandl's avatar
Georg Brandl committed
230
        if not self.breaks:
231 232
            # no breakpoints; run without debugger overhead
            sys.settrace(None)
233
            frame = sys._getframe().f_back
234 235 236
            while frame and frame is not self.botframe:
                del frame.f_trace
                frame = frame.f_back
Tim Peters's avatar
Tim Peters committed
237

238 239 240
    def set_quit(self):
        self.stopframe = self.botframe
        self.returnframe = None
241
        self.quitting = True
242
        sys.settrace(None)
Tim Peters's avatar
Tim Peters committed
243

244 245 246 247 248 249
    # 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
250

251
    def set_break(self, filename, lineno, temporary=False, cond=None,
252
                  funcname=None):
253 254 255 256
        filename = self.canonic(filename)
        import linecache # Import as late as possible
        line = linecache.getline(filename, lineno)
        if not line:
257 258 259
            return 'Line %s:%d does not exist' % (filename, lineno)
        list = self.breaks.setdefault(filename, [])
        if lineno not in list:
260
            list.append(lineno)
261
        bp = Breakpoint(filename, lineno, temporary, cond, funcname)
262

263 264 265 266 267 268
    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]

269 270
    def clear_break(self, filename, lineno):
        filename = self.canonic(filename)
271
        if filename not in self.breaks:
272 273
            return 'There are no breakpoints in %s' % filename
        if lineno not in self.breaks[filename]:
274
            return 'There is no breakpoint at %s:%d' % (filename, lineno)
275 276 277 278
        # 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()
279
        self._prune_breaks(filename, lineno)
Tim Peters's avatar
Tim Peters committed
280

281 282
    def clear_bpbynumber(self, arg):
        try:
283 284 285
            bp = self.get_bpbynumber(arg)
        except ValueError as err:
            return str(err)
286 287
        bp.deleteMe()
        self._prune_breaks(bp.file, bp.line)
288 289 290

    def clear_all_file_breaks(self, filename):
        filename = self.canonic(filename)
291
        if filename not in self.breaks:
292 293 294 295 296 297
            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
298

299 300 301 302 303 304 305
    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
306

307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
    def get_bpbynumber(self, arg):
        if not arg:
            raise ValueError('Breakpoint number expected')
        try:
            number = int(arg)
        except ValueError:
            raise ValueError('Non-numeric breakpoint number %s' % arg)
        try:
            bp = Breakpoint.bpbynumber[number]
        except IndexError:
            raise ValueError('Breakpoint number %d out of range' % number)
        if bp is None:
            raise ValueError('Breakpoint %d already deleted' % number)
        return bp

322 323
    def get_break(self, filename, lineno):
        filename = self.canonic(filename)
324
        return filename in self.breaks and \
325
            lineno in self.breaks[filename]
Tim Peters's avatar
Tim Peters committed
326

327 328
    def get_breaks(self, filename, lineno):
        filename = self.canonic(filename)
329
        return filename in self.breaks and \
330 331
            lineno in self.breaks[filename] and \
            Breakpoint.bplist[filename, lineno] or []
Tim Peters's avatar
Tim Peters committed
332

333 334
    def get_file_breaks(self, filename):
        filename = self.canonic(filename)
335
        if filename in self.breaks:
336 337 338
            return self.breaks[filename]
        else:
            return []
Tim Peters's avatar
Tim Peters committed
339

340 341
    def get_all_breaks(self):
        return self.breaks
Tim Peters's avatar
Tim Peters committed
342

343 344
    # 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
345

346 347 348 349 350 351 352 353 354 355 356 357 358 359
    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
Benjamin Peterson's avatar
Benjamin Peterson committed
360 361
        if f is None:
            i = max(0, len(stack) - 1)
362
        return stack, i
Tim Peters's avatar
Tim Peters committed
363

364
    def format_stack_entry(self, frame_lineno, lprefix=': '):
365
        import linecache, reprlib
366 367
        frame, lineno = frame_lineno
        filename = self.canonic(frame.f_code.co_filename)
368
        s = '%s(%r)' % (filename, lineno)
369
        if frame.f_code.co_name:
370
            s += frame.f_code.co_name
371
        else:
372
            s += "<lambda>"
373
        if '__args__' in frame.f_locals:
374 375 376 377
            args = frame.f_locals['__args__']
        else:
            args = None
        if args:
378
            s += reprlib.repr(args)
379
        else:
380
            s += '()'
381
        if '__return__' in frame.f_locals:
382
            rv = frame.f_locals['__return__']
383 384
            s += '->'
            s += reprlib.repr(rv)
385
        line = linecache.getline(filename, lineno, frame.f_globals)
386 387
        if line:
            s += lprefix + line.strip()
388
        return s
Tim Peters's avatar
Tim Peters committed
389

390 391 392
    # The following methods can be called by clients to use
    # a debugger to debug a statement or an expression.
    # Both can be given as a string, or a code object.
Tim Peters's avatar
Tim Peters committed
393

394 395 396 397 398 399 400
    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()
401 402
        if isinstance(cmd, str):
            cmd = compile(cmd, "<string>", "exec")
403 404
        sys.settrace(self.trace_dispatch)
        try:
405 406 407
            exec(cmd, globals, locals)
        except BdbQuit:
            pass
408
        finally:
409
            self.quitting = True
410
            sys.settrace(None)
Tim Peters's avatar
Tim Peters committed
411

412 413 414 415 416 417 418 419 420
    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)
        try:
421 422 423
            return eval(expr, globals, locals)
        except BdbQuit:
            pass
424
        finally:
425
            self.quitting = True
426 427 428 429 430 431 432 433
            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.

434
    def runcall(self, func, *args, **kwds):
435 436 437 438
        self.reset()
        sys.settrace(self.trace_dispatch)
        res = None
        try:
439 440 441
            res = func(*args, **kwds)
        except BdbQuit:
            pass
442
        finally:
443
            self.quitting = True
444 445
            sys.settrace(None)
        return res
Guido van Rossum's avatar
Guido van Rossum committed
446 447


448
def set_trace():
449
    Bdb().set_trace()
450

451 452

class Breakpoint:
453
    """Breakpoint class.
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474

    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()

475
    def __init__(self, file, line, temporary=False, cond=None, funcname=None):
476 477 478
        self.funcname = funcname
        # Needed if funcname is not None.
        self.func_first_executable_line = None
479 480 481 482
        self.file = file    # This better be in canonical form!
        self.line = line
        self.temporary = temporary
        self.cond = cond
483
        self.enabled = True
484 485 486
        self.ignore = 0
        self.hits = 0
        self.number = Breakpoint.next
487
        Breakpoint.next += 1
488 489
        # Build the two lists
        self.bpbynumber.append(self)
490
        if (file, line) in self.bplist:
491 492 493 494 495 496 497 498 499 500 501 502 503
            self.bplist[file, line].append(self)
        else:
            self.bplist[file, line] = [self]

    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):
504
        self.enabled = True
505 506

    def disable(self):
507
        self.enabled = False
508

509 510 511
    def bpprint(self, out=None):
        if out is None:
            out = sys.stdout
512 513 514
        print(self.bpformat(), file=out)

    def bpformat(self):
515
        if self.temporary:
Tim Peters's avatar
Tim Peters committed
516
            disp = 'del  '
517
        else:
Tim Peters's avatar
Tim Peters committed
518
            disp = 'keep '
519
        if self.enabled:
520
            disp = disp + 'yes  '
521
        else:
522
            disp = disp + 'no   '
523 524
        ret = '%-4dbreakpoint   %s at %s:%d' % (self.number, disp,
                                                self.file, self.line)
525
        if self.cond:
526
            ret += '\n\tstop only if %s' % (self.cond,)
527
        if self.ignore:
528 529 530 531 532 533 534 535
            ret += '\n\tignore next %d hits' % (self.ignore,)
        if self.hits:
            if self.hits > 1:
                ss = 's'
            else:
                ss = ''
            ret += '\n\tbreakpoint already hit %d time%s' % (self.hits, ss)
        return ret
536

537 538 539
    def __str__(self):
        return 'breakpoint %s at %s:%s' % (self.number, self.file, self.line)

540 541
# -----------end of Breakpoint class----------

542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
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

568 569 570
# 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):
571 572 573 574 575 576 577
    """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.

    """
578 579 580
    possibles = Breakpoint.bplist[file, line]
    for b in possibles:
        if not b.enabled:
581
            continue
582 583
        if not checkfuncname(b, frame):
            continue
584
        # Count every hit when bp is enabled
585
        b.hits += 1
586
        if not b.cond:
587
            # If unconditional, and ignoring go on to next, else break
588
            if b.ignore > 0:
589
                b.ignore -= 1
590 591
                continue
            else:
592 593
                # breakpoint and marker that it's ok to delete if temporary
                return (b, True)
594 595 596 597 598
        else:
            # Conditional bp.
            # Ignore count applies only to those bpt hits where the
            # condition evaluates to true.
            try:
599
                val = eval(b.cond, frame.f_globals, frame.f_locals)
600 601
                if val:
                    if b.ignore > 0:
602
                        b.ignore -= 1
603 604
                        # continue
                    else:
605
                        return (b, True)
606 607 608
                # else:
                #   continue
            except:
609 610 611 612
                # if eval fails, most conservative thing is to stop on
                # breakpoint regardless of ignore count.  Don't delete
                # temporary, as another hint to user.
                return (b, False)
613
    return (None, None)
614

615

Guido van Rossum's avatar
Guido van Rossum committed
616 617 618
# -------------------- testing --------------------

class Tdb(Bdb):
619 620 621
    def user_call(self, frame, args):
        name = frame.f_code.co_name
        if not name: name = '???'
622
        print('+++ call', name, args)
623
    def user_line(self, frame):
624
        import linecache
625 626 627
        name = frame.f_code.co_name
        if not name: name = '???'
        fn = self.canonic(frame.f_code.co_filename)
628
        line = linecache.getline(fn, frame.f_lineno, frame.f_globals)
629
        print('+++', fn, frame.f_lineno, name, ':', line.strip())
630
    def user_return(self, frame, retval):
631
        print('+++ return', retval)
632
    def user_exception(self, frame, exc_stuff):
633
        print('+++ exception', exc_stuff)
634
        self.set_continue()
Guido van Rossum's avatar
Guido van Rossum committed
635 636

def foo(n):
637
    print('foo(', n, ')')
638
    x = bar(n*10)
639
    print('bar returned', x)
Guido van Rossum's avatar
Guido van Rossum committed
640 641

def bar(a):
642
    print('bar(', a, ')')
643
    return a/2
Guido van Rossum's avatar
Guido van Rossum committed
644 645

def test():
646 647
    t = Tdb()
    t.run('import bdb; bdb.foo(10)')