0001 """Debugger basics""" 0002 0003 import sys 0004 import os 0005 import types 0006 0007 __all__ = ["BdbQuit","Bdb","Breakpoint"] 0008 0009 class BdbQuit(Exception): 0010 """Exception to give up completely""" 0011 0012 0013 class Bdb: 0014 0015 """Generic Python debugger base class. 0016 0017 This class takes care of details of the trace facility; 0018 a derived class should implement user interaction. 0019 The standard debugger class (pdb.Pdb) is an example. 0020 """ 0021 0022 def __init__(self): 0023 self.breaks = {} 0024 self.fncache = {} 0025 0026 def canonic(self, filename): 0027 if filename == "<" + filename[1:-1] + ">": 0028 return filename 0029 canonic = self.fncache.get(filename) 0030 if not canonic: 0031 canonic = os.path.abspath(filename) 0032 canonic = os.path.normcase(canonic) 0033 self.fncache[filename] = canonic 0034 return canonic 0035 0036 def reset(self): 0037 import linecache 0038 linecache.checkcache() 0039 self.botframe = None 0040 self.stopframe = None 0041 self.returnframe = None 0042 self.quitting = 0 0043 0044 def trace_dispatch(self, frame, event, arg): 0045 if self.quitting: 0046 return # None 0047 if event == 'line': 0048 return self.dispatch_line(frame) 0049 if event == 'call': 0050 return self.dispatch_call(frame, arg) 0051 if event == 'return': 0052 return self.dispatch_return(frame, arg) 0053 if event == 'exception': 0054 return self.dispatch_exception(frame, arg) 0055 if event == 'c_call': 0056 return self.trace_dispatch 0057 if event == 'c_exception': 0058 return self.trace_dispatch 0059 if event == 'c_return': 0060 return self.trace_dispatch 0061 print 'bdb.Bdb.dispatch: unknown debugging event:', repr(event) 0062 return self.trace_dispatch 0063 0064 def dispatch_line(self, frame): 0065 if self.stop_here(frame) or self.break_here(frame): 0066 self.user_line(frame) 0067 if self.quitting: raise BdbQuit 0068 return self.trace_dispatch 0069 0070 def dispatch_call(self, frame, arg): 0071 # XXX 'arg' is no longer used 0072 if self.botframe is None: 0073 # First call of dispatch since reset() 0074 self.botframe = frame.f_back # (CT) Note that this may also be None! 0075 return self.trace_dispatch 0076 if not (self.stop_here(frame) or self.break_anywhere(frame)): 0077 # No need to trace this function 0078 return # None 0079 self.user_call(frame, arg) 0080 if self.quitting: raise BdbQuit 0081 return self.trace_dispatch 0082 0083 def dispatch_return(self, frame, arg): 0084 if self.stop_here(frame) or frame == self.returnframe: 0085 self.user_return(frame, arg) 0086 if self.quitting: raise BdbQuit 0087 return self.trace_dispatch 0088 0089 def dispatch_exception(self, frame, arg): 0090 if self.stop_here(frame): 0091 self.user_exception(frame, arg) 0092 if self.quitting: raise BdbQuit 0093 return self.trace_dispatch 0094 0095 # Normally derived classes don't override the following 0096 # methods, but they may if they want to redefine the 0097 # definition of stopping and breakpoints. 0098 0099 def stop_here(self, frame): 0100 # (CT) stopframe may now also be None, see dispatch_call. 0101 # (CT) the former test for None is therefore removed from here. 0102 if frame is self.stopframe: 0103 return True 0104 while frame is not None and frame is not self.stopframe: 0105 if frame is self.botframe: 0106 return True 0107 frame = frame.f_back 0108 return False 0109 0110 def break_here(self, frame): 0111 filename = self.canonic(frame.f_code.co_filename) 0112 if not filename in self.breaks: 0113 return False 0114 lineno = frame.f_lineno 0115 if not lineno in self.breaks[filename]: 0116 # The line itself has no breakpoint, but maybe the line is the 0117 # first line of a function with breakpoint set by function name. 0118 lineno = frame.f_code.co_firstlineno 0119 if not lineno in self.breaks[filename]: 0120 return False 0121 0122 # flag says ok to delete temp. bp 0123 (bp, flag) = effective(filename, lineno, frame) 0124 if bp: 0125 self.currentbp = bp.number 0126 if (flag and bp.temporary): 0127 self.do_clear(str(bp.number)) 0128 return True 0129 else: 0130 return False 0131 0132 def do_clear(self, arg): 0133 raise NotImplementedError, "subclass of bdb must implement do_clear()" 0134 0135 def break_anywhere(self, frame): 0136 return self.breaks.has_key( 0137 self.canonic(frame.f_code.co_filename)) 0138 0139 # Derived classes should override the user_* methods 0140 # to gain control. 0141 0142 def user_call(self, frame, argument_list): 0143 """This method is called when there is the remote possibility 0144 that we ever need to stop in this function.""" 0145 pass 0146 0147 def user_line(self, frame): 0148 """This method is called when we stop or break at this line.""" 0149 pass 0150 0151 def user_return(self, frame, return_value): 0152 """This method is called when a return trap is set here.""" 0153 pass 0154 0155 def user_exception(self, frame, (exc_type, exc_value, exc_traceback)): 0156 """This method is called if an exception occurs, 0157 but only if we are to stop at or just below this level.""" 0158 pass 0159 0160 # Derived classes and clients can call the following methods 0161 # to affect the stepping state. 0162 0163 def set_step(self): 0164 """Stop after one line of code.""" 0165 self.stopframe = None 0166 self.returnframe = None 0167 self.quitting = 0 0168 0169 def set_next(self, frame): 0170 """Stop on the next line in or below the given frame.""" 0171 self.stopframe = frame 0172 self.returnframe = None 0173 self.quitting = 0 0174 0175 def set_return(self, frame): 0176 """Stop when returning from the given frame.""" 0177 self.stopframe = frame.f_back 0178 self.returnframe = frame 0179 self.quitting = 0 0180 0181 def set_trace(self): 0182 """Start debugging from here.""" 0183 frame = sys._getframe().f_back 0184 self.reset() 0185 while frame: 0186 frame.f_trace = self.trace_dispatch 0187 self.botframe = frame 0188 frame = frame.f_back 0189 self.set_step() 0190 sys.settrace(self.trace_dispatch) 0191 0192 def set_continue(self): 0193 # Don't stop except at breakpoints or when finished 0194 self.stopframe = self.botframe 0195 self.returnframe = None 0196 self.quitting = 0 0197 if not self.breaks: 0198 # no breakpoints; run without debugger overhead 0199 sys.settrace(None) 0200 frame = sys._getframe().f_back 0201 while frame and frame is not self.botframe: 0202 del frame.f_trace 0203 frame = frame.f_back 0204 0205 def set_quit(self): 0206 self.stopframe = self.botframe 0207 self.returnframe = None 0208 self.quitting = 1 0209 sys.settrace(None) 0210 0211 # Derived classes and clients can call the following methods 0212 # to manipulate breakpoints. These methods return an 0213 # error message is something went wrong, None if all is well. 0214 # Set_break prints out the breakpoint line and file:lineno. 0215 # Call self.get_*break*() to see the breakpoints or better 0216 # for bp in Breakpoint.bpbynumber: if bp: bp.bpprint(). 0217 0218 def set_break(self, filename, lineno, temporary=0, cond = None, 0219 funcname=None): 0220 filename = self.canonic(filename) 0221 import linecache # Import as late as possible 0222 line = linecache.getline(filename, lineno) 0223 if not line: 0224 return 'Line %s:%d does not exist' % (filename, 0225 lineno) 0226 if not filename in self.breaks: 0227 self.breaks[filename] = [] 0228 list = self.breaks[filename] 0229 if not lineno in list: 0230 list.append(lineno) 0231 bp = Breakpoint(filename, lineno, temporary, cond, funcname) 0232 0233 def clear_break(self, filename, lineno): 0234 filename = self.canonic(filename) 0235 if not filename in self.breaks: 0236 return 'There are no breakpoints in %s' % filename 0237 if lineno not in self.breaks[filename]: 0238 return 'There is no breakpoint at %s:%d' % (filename, 0239 lineno) 0240 # If there's only one bp in the list for that file,line 0241 # pair, then remove the breaks entry 0242 for bp in Breakpoint.bplist[filename, lineno][:]: 0243 bp.deleteMe() 0244 if not Breakpoint.bplist.has_key((filename, lineno)): 0245 self.breaks[filename].remove(lineno) 0246 if not self.breaks[filename]: 0247 del self.breaks[filename] 0248 0249 def clear_bpbynumber(self, arg): 0250 try: 0251 number = int(arg) 0252 except: 0253 return 'Non-numeric breakpoint number (%s)' % arg 0254 try: 0255 bp = Breakpoint.bpbynumber[number] 0256 except IndexError: 0257 return 'Breakpoint number (%d) out of range' % number 0258 if not bp: 0259 return 'Breakpoint (%d) already deleted' % number 0260 self.clear_break(bp.file, bp.line) 0261 0262 def clear_all_file_breaks(self, filename): 0263 filename = self.canonic(filename) 0264 if not filename in self.breaks: 0265 return 'There are no breakpoints in %s' % filename 0266 for line in self.breaks[filename]: 0267 blist = Breakpoint.bplist[filename, line] 0268 for bp in blist: 0269 bp.deleteMe() 0270 del self.breaks[filename] 0271 0272 def clear_all_breaks(self): 0273 if not self.breaks: 0274 return 'There are no breakpoints' 0275 for bp in Breakpoint.bpbynumber: 0276 if bp: 0277 bp.deleteMe() 0278 self.breaks = {} 0279 0280 def get_break(self, filename, lineno): 0281 filename = self.canonic(filename) 0282 return filename in self.breaks and \ 0283 lineno in self.breaks[filename] 0284 0285 def get_breaks(self, filename, lineno): 0286 filename = self.canonic(filename) 0287 return filename in self.breaks and \ 0288 lineno in self.breaks[filename] and \ 0289 Breakpoint.bplist[filename, lineno] or [] 0290 0291 def get_file_breaks(self, filename): 0292 filename = self.canonic(filename) 0293 if filename in self.breaks: 0294 return self.breaks[filename] 0295 else: 0296 return [] 0297 0298 def get_all_breaks(self): 0299 return self.breaks 0300 0301 # Derived classes and clients can call the following method 0302 # to get a data structure representing a stack trace. 0303 0304 def get_stack(self, f, t): 0305 stack = [] 0306 if t and t.tb_frame is f: 0307 t = t.tb_next 0308 while f is not None: 0309 stack.append((f, f.f_lineno)) 0310 if f is self.botframe: 0311 break 0312 f = f.f_back 0313 stack.reverse() 0314 i = max(0, len(stack) - 1) 0315 while t is not None: 0316 stack.append((t.tb_frame, t.tb_lineno)) 0317 t = t.tb_next 0318 return stack, i 0319 0320 # 0321 0322 def format_stack_entry(self, frame_lineno, lprefix=': '): 0323 import linecache, repr 0324 frame, lineno = frame_lineno 0325 filename = self.canonic(frame.f_code.co_filename) 0326 s = '%s(%r)' % (filename, lineno) 0327 if frame.f_code.co_name: 0328 s = s + frame.f_code.co_name 0329 else: 0330 s = s + "<lambda>" 0331 if '__args__' in frame.f_locals: 0332 args = frame.f_locals['__args__'] 0333 else: 0334 args = None 0335 if args: 0336 s = s + repr.repr(args) 0337 else: 0338 s = s + '()' 0339 if '__return__' in frame.f_locals: 0340 rv = frame.f_locals['__return__'] 0341 s = s + '->' 0342 s = s + repr.repr(rv) 0343 line = linecache.getline(filename, lineno) 0344 if line: s = s + lprefix + line.strip() 0345 return s 0346 0347 # The following two methods can be called by clients to use 0348 # a debugger to debug a statement, given as a string. 0349 0350 def run(self, cmd, globals=None, locals=None): 0351 if globals is None: 0352 import __main__ 0353 globals = __main__.__dict__ 0354 if locals is None: 0355 locals = globals 0356 self.reset() 0357 sys.settrace(self.trace_dispatch) 0358 if not isinstance(cmd, types.CodeType): 0359 cmd = cmd+'\n' 0360 try: 0361 try: 0362 exec cmd in globals, locals 0363 except BdbQuit: 0364 pass 0365 finally: 0366 self.quitting = 1 0367 sys.settrace(None) 0368 0369 def runeval(self, expr, globals=None, locals=None): 0370 if globals is None: 0371 import __main__ 0372 globals = __main__.__dict__ 0373 if locals is None: 0374 locals = globals 0375 self.reset() 0376 sys.settrace(self.trace_dispatch) 0377 if not isinstance(expr, types.CodeType): 0378 expr = expr+'\n' 0379 try: 0380 try: 0381 return eval(expr, globals, locals) 0382 except BdbQuit: 0383 pass 0384 finally: 0385 self.quitting = 1 0386 sys.settrace(None) 0387 0388 def runctx(self, cmd, globals, locals): 0389 # B/W compatibility 0390 self.run(cmd, globals, locals) 0391 0392 # This method is more useful to debug a single function call. 0393 0394 def runcall(self, func, *args, **kwds): 0395 self.reset() 0396 sys.settrace(self.trace_dispatch) 0397 res = None 0398 try: 0399 try: 0400 res = func(*args, **kwds) 0401 except BdbQuit: 0402 pass 0403 finally: 0404 self.quitting = 1 0405 sys.settrace(None) 0406 return res 0407 0408 0409 def set_trace(): 0410 Bdb().set_trace() 0411 0412 0413 class Breakpoint: 0414 0415 """Breakpoint class 0416 0417 Implements temporary breakpoints, ignore counts, disabling and 0418 (re)-enabling, and conditionals. 0419 0420 Breakpoints are indexed by number through bpbynumber and by 0421 the file,line tuple using bplist. The former points to a 0422 single instance of class Breakpoint. The latter points to a 0423 list of such instances since there may be more than one 0424 breakpoint per line. 0425 0426 """ 0427 0428 # XXX Keeping state in the class is a mistake -- this means 0429 # you cannot have more than one active Bdb instance. 0430 0431 next = 1 # Next bp to be assigned 0432 bplist = {} # indexed by (file, lineno) tuple 0433 bpbynumber = [None] # Each entry is None or an instance of Bpt 0434 # index 0 is unused, except for marking an 0435 # effective break .... see effective() 0436 0437 def __init__(self, file, line, temporary=0, cond=None, funcname=None): 0438 self.funcname = funcname 0439 # Needed if funcname is not None. 0440 self.func_first_executable_line = None 0441 self.file = file # This better be in canonical form! 0442 self.line = line 0443 self.temporary = temporary 0444 self.cond = cond 0445 self.enabled = 1 0446 self.ignore = 0 0447 self.hits = 0 0448 self.number = Breakpoint.next 0449 Breakpoint.next = Breakpoint.next + 1 0450 # Build the two lists 0451 self.bpbynumber.append(self) 0452 if self.bplist.has_key((file, line)): 0453 self.bplist[file, line].append(self) 0454 else: 0455 self.bplist[file, line] = [self] 0456 0457 0458 def deleteMe(self): 0459 index = (self.file, self.line) 0460 self.bpbynumber[self.number] = None # No longer in list 0461 self.bplist[index].remove(self) 0462 if not self.bplist[index]: 0463 # No more bp for this f:l combo 0464 del self.bplist[index] 0465 0466 def enable(self): 0467 self.enabled = 1 0468 0469 def disable(self): 0470 self.enabled = 0 0471 0472 def bpprint(self): 0473 if self.temporary: 0474 disp = 'del ' 0475 else: 0476 disp = 'keep ' 0477 if self.enabled: 0478 disp = disp + 'yes' 0479 else: 0480 disp = disp + 'no ' 0481 print '%-4dbreakpoint %s at %s:%d' % (self.number, disp, 0482 self.file, self.line) 0483 if self.cond: 0484 print '\tstop only if %s' % (self.cond,) 0485 if self.ignore: 0486 print '\tignore next %d hits' % (self.ignore) 0487 if (self.hits): 0488 if (self.hits > 1): ss = 's' 0489 else: ss = '' 0490 print ('\tbreakpoint already hit %d time%s' % 0491 (self.hits, ss)) 0492 0493 # -----------end of Breakpoint class---------- 0494 0495 def checkfuncname(b, frame): 0496 """Check whether we should break here because of `b.funcname`.""" 0497 if not b.funcname: 0498 # Breakpoint was set via line number. 0499 if b.line != frame.f_lineno: 0500 # Breakpoint was set at a line with a def statement and the function 0501 # defined is called: don't break. 0502 return False 0503 return True 0504 0505 # Breakpoint set via function name. 0506 0507 if frame.f_code.co_name != b.funcname: 0508 # It's not a function call, but rather execution of def statement. 0509 return False 0510 0511 # We are in the right frame. 0512 if not b.func_first_executable_line: 0513 # The function is entered for the 1st time. 0514 b.func_first_executable_line = frame.f_lineno 0515 0516 if b.func_first_executable_line != frame.f_lineno: 0517 # But we are not at the first line number: don't break. 0518 return False 0519 return True 0520 0521 # Determines if there is an effective (active) breakpoint at this 0522 # line of code. Returns breakpoint number or 0 if none 0523 def effective(file, line, frame): 0524 """Determine which breakpoint for this file:line is to be acted upon. 0525 0526 Called only if we know there is a bpt at this 0527 location. Returns breakpoint that was triggered and a flag 0528 that indicates if it is ok to delete a temporary bp. 0529 0530 """ 0531 possibles = Breakpoint.bplist[file,line] 0532 for i in range(0, len(possibles)): 0533 b = possibles[i] 0534 if b.enabled == 0: 0535 continue 0536 if not checkfuncname(b, frame): 0537 continue 0538 # Count every hit when bp is enabled 0539 b.hits = b.hits + 1 0540 if not b.cond: 0541 # If unconditional, and ignoring, 0542 # go on to next, else break 0543 if b.ignore > 0: 0544 b.ignore = b.ignore -1 0545 continue 0546 else: 0547 # breakpoint and marker that's ok 0548 # to delete if temporary 0549 return (b,1) 0550 else: 0551 # Conditional bp. 0552 # Ignore count applies only to those bpt hits where the 0553 # condition evaluates to true. 0554 try: 0555 val = eval(b.cond, frame.f_globals, 0556 frame.f_locals) 0557 if val: 0558 if b.ignore > 0: 0559 b.ignore = b.ignore -1 0560 # continue 0561 else: 0562 return (b,1) 0563 # else: 0564 # continue 0565 except: 0566 # if eval fails, most conservative 0567 # thing is to stop on breakpoint 0568 # regardless of ignore count. 0569 # Don't delete temporary, 0570 # as another hint to user. 0571 return (b,0) 0572 return (None, None) 0573 0574 # -------------------- testing -------------------- 0575 0576 class Tdb(Bdb): 0577 def user_call(self, frame, args): 0578 name = frame.f_code.co_name 0579 if not name: name = '???' 0580 print '+++ call', name, args 0581 def user_line(self, frame): 0582 import linecache 0583 name = frame.f_code.co_name 0584 if not name: name = '???' 0585 fn = self.canonic(frame.f_code.co_filename) 0586 line = linecache.getline(fn, frame.f_lineno) 0587 print '+++', fn, frame.f_lineno, name, ':', line.strip() 0588 def user_return(self, frame, retval): 0589 print '+++ return', retval 0590 def user_exception(self, frame, exc_stuff): 0591 print '+++ exception', exc_stuff 0592 self.set_continue() 0593 0594 def foo(n): 0595 print 'foo(', n, ')' 0596 x = bar(n*10) 0597 print 'bar returned', x 0598 0599 def bar(a): 0600 print 'bar(', a, ')' 0601 return a/2 0602 0603 def test(): 0604 t = Tdb() 0605 t.run('import bdb; bdb.foo(10)') 0606 0607 # end 0608
Generated by PyXR 0.9.4