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