• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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