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