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