Interactive Debugging in Python
Pages: 1, 2, 3, 4, 5, 6, 7
My next trick is to set a break point in f4() using the
(b)reak command, and continue to the break point using the
(c)ontinue command:
(Pdb) b f4
Breakpoint 1 at /home/jmjones/svn/articles/debugger
/simple_debugger_example.py:22
(Pdb) c
2
3
> /home/jmjones/svn/articles/debugger/simple_debugger_example.py(23)f4()
-> print some_arg
(Pdb) l
18 print some_arg
19 some_other_arg = some_arg + 1
20 return f4(some_other_arg)
21
22 B def f4(some_arg):
23 -> print some_arg
24 some_other_arg = some_arg + 1
25 return some_other_arg
26
27 if __name__ == "__main__":
28 pdb.runcall(f1, 1)
(Pdb)
The break command creates break points that the debugger will
stop at when it encounters them. The continue command tells the
debugger to keep executing code until it hits a break point or EOF.
I next issued a (w)here command:
(Pdb) where
/usr/local/python24/lib/python2.4/bdb.py(404)runcall()
-> res = func(*args, **kwds)
/home/jmjones/svn/articles/debugger/simple_debugger_example.py(10)f1()
-> return f2(some_other_arg)
/home/jmjones/svn/articles/debugger/simple_debugger_example.py(15)f2()
-> return f3(some_other_arg)
/home/jmjones/svn/articles/debugger/simple_debugger_example.py(20)f3()
-> return f4(some_other_arg)
> /home/jmjones/svn/articles/debugger/simple_debugger_example.py(23)f4()
-> print some_arg
(Pdb)
The where command prints out a stack trace, showing the call
tree from f1() to f4(). This can help you see how a
function is being called.
I then navigated up the stack trace with the (u)p command and
saw where I was with the list command:
(Pdb) u
> /home/jmjones/debugger/simple_debugger_example.py(20)f3()
-> return f4(some_other_arg)
(Pdb) u
> /home/jmjones/debugger/simple_debugger_example.py(15)f2()
-> return f3(some_other_arg)
(Pdb) u
> /home/jmjones/debugger/simple_debugger_example.py(10)f1()
-> return f2(some_other_arg)
(Pdb) u
> /usr/local/python24/lib/python2.4/bdb.py(404)runcall()
-> res = func(*args, **kwds)
(Pdb) u
*** Oldest frame
(Pdb) l
399 self.reset()
400 sys.settrace(self.trace_dispatch)
401 res = None
402 try:
403 try:
404 -> res = func(*args, **kwds)
405 except BdbQuit:
406 pass
407 finally:
408 self.quitting = 1
409 sys.settrace(None)
(Pdb)
The up command moves the debugger up a frame in the stack trace
to an older frame. In this example, it took me all the way up to the oldest
frame, which is one of the debugger modules, bdb.py. This is because
the debugger (part of which is in bdb.py) is actually executing this
example script.
I navigated down the stack trace a few frames with the (d)own
command, which is the opposite of the up command:
(Pdb) d
> /home/jmjones/svn/articles/debugger/simple_debugger_example.py(10)f1()
-> return f2(some_other_arg)
(Pdb) d
> /home/jmjones/svn/articles/debugger/simple_debugger_example.py(15)f2()
-> return f3(some_other_arg)
(Pdb) l
10 return f2(some_other_arg)
11
12 def f2(some_arg):
13 print some_arg
14 some_other_arg = some_arg + 1
15 -> return f3(some_other_arg)
16
17 def f3(some_arg):
18 print some_arg
19 some_other_arg = some_arg + 1
20 return f4(some_other_arg)
(Pdb)
I moved down two frames into f2(), but what does it really
mean to be in a different frame? As an illustration, I've
printed out a couple of variables in two different frames:
(Pdb) print some_arg
2
(Pdb) d
> /home/jmjones/svn/articles/debugger/simple_debugger_example.py(20)f3()
-> return f4(some_other_arg)
(Pdb) l
15 return f3(some_other_arg)
16
17 def f3(some_arg):
18 print some_arg
19 some_other_arg = some_arg + 1
20 -> return f4(some_other_arg)
21
22 B def f4(some_arg):
23 print some_arg
24 some_other_arg = some_arg + 1
25 return some_other_arg
(Pdb) print some_arg
3
(Pdb)
I went from f2() down to f3() and printed out
some_arg in both of them. I saw 2 from f2() and 3
from f3(). What would happen if I were to step
forward? Back up into f2() to see a clearer illustration:
(Pdb) u
> /home/jmjones/debugger/simple_debugger_example.py(15)f2()
-> return f3(some_other_arg)
(Pdb) l
10 return f2(some_other_arg)
11
12 def f2(some_arg):
13 print some_arg
14 some_other_arg = some_arg + 1
15 -> return f3(some_other_arg)
16
17 def f3(some_arg):
18 print some_arg
19 some_other_arg = some_arg + 1
20 return f4(some_other_arg)
(Pdb) s
4
> /home/jmjones/debugger/simple_debugger_example.py(24)f4()
-> some_other_arg = some_arg + 1
(Pdb) l
19 some_other_arg = some_arg + 1
20 return f4(some_other_arg)
21
22 B def f4(some_arg):
23 print some_arg
24 -> some_other_arg = some_arg + 1
25 return some_other_arg
26
27 if __name__ == "__main__":
28 pdb.runcall(f1, 1)
[EOF]
(Pdb)
If the debugger really were in f2(), stepping forward
should have taken it into f3() if it hadn't yet made the call to
f3(), or up into f1() if f3() had already
returned. Instead, it jumped to f4(). Navigating up or down a
stack trace provides access to that frame's local namespace, but that appears
to be about it.
Those are the majority of my most frequently used commands.