This chapter is taken from book A Primer on Scientific Programming with Python by H. P. Langtangen, 4th edition, Springer, 2014.
A debugger is a program that can help you find out what is going on in a computer program. You can stop the execution at any prescribed line number, print out variables, continue execution, stop again, execute statements one by one, and repeat such actions until you have tracked down abnormal behavior and found bugs.
Here we shall use the debugger to demonstrate the program flow of the code Simpson.py (which can integrate functions of one variable with the famous Simpson's rule). You are strongly encouraged to carry out the steps below on your computer to get a glimpse of what a debugger can do.
Go to the folder
where the program Simpson.py
resides.
If you use the Spyder Integrated Development Environment, choose Debug on the Run pull-down menu. If you run your programs in a plain terminal window, start IPython:
Terminal> ipython
Run the program Simpson.py
with the
debugger on (-d
):
In [1]: run -d Simpson.py
We now enter the debugger and get a prompt
ipdb>
After this prompt we can issue various debugger commands. The most important ones will be described as we go along.
Type continue
or just c
to go to the first line
in the file. Now you can see a printout of where we are in the
program:
1---> 1 def Simpson(f, a, b, n=500):
2 """
3 Return the approximation of the integral of f
Each program line is numbered and the arrow points to the next line to be executed. This is called the current line.
You can set a break point where you want the program to stop
so that you can examine variables and perhaps follow the execution
closely. We start by setting a break point in the application
function:
ipdb> break application
Breakpoint 2 at /home/.../src/funcif/Simpson.py:30
You can also say break X
, where X
is a line number in the
file.
Continue execution until the break point by writing continue
or
c
. Now the program stops at line 31 in the application
function:
ipdb> c
> /home/.../src/funcif/Simpson.py(31)application()
2 30 def application():
---> 31 from math import sin, pi
32 print 'Integral of 1.5*sin^3 from 0 to pi:'
Typing step
or just s
executes one statement at a time:
ipdb> s
> /home/.../src/funcif/Simpson.py(32)application()
31 from math import sin, pi
---> 32 print 'Integral of 1.5*sin^3 from 0 to pi:'
33 for n in 2, 6, 12, 100, 500:
ipdb> s
Integral of 1.5*sin^3 from 0 to pi:
> /home/.../src/funcif/Simpson.py(33)application()
32 print 'Integral of 1.5*sin^3 from 0 to pi:'
---> 33 for n in 2, 6, 12, 100, 500:
34 approx = Simpson(h, 0, pi, n)
Typing another s
reaches the call to Simpson
, and a new
s
steps into the function Simpson
:
ipdb> s
--Call--
> /home/.../src/funcif/Simpson.py(1)Simpson()
1---> 1 def Simpson(f, a, b, n=500):
2 """
3 Return the approximation of the integral of f
Type a few more s
to step ahead of the if
tests.
Examining the contents of variables is easy with the print
(or p
) command:
ipdb> print f, a, b, n
<function h at 0x898ef44> 0 3.14159265359 2
We can also check the type of the objects:
ipdb> whatis f
Function h
ipdb> whatis a
<type 'int'>
ipdb> whatis b
<type 'float'>
ipdb> whatis n
<type 'int'>
Set a new break point in the application
function so that we
can jump directly there without having to go manually through all the
statements in the Simpson
function. To see line numbers and
corresponding statements around some line with number X
, type
list X
. For example,
ipdb> list 32
27 def h(x):
28 return (3./2)*sin(x)**3
29
30 from math import sin, pi
31
2 32 def application():
33 print 'Integral of 1.5*sin^3 from 0 to pi:'
34 for n in 2, 6, 12, 100, 500:
35 approx = Simpson(h, 0, pi, n)
36 print 'n=%3d, approx=%18.15f, error=%9.2E' % \
37 (n, approx, 2-approx)
We set a line break at line 35:
ipdb> break 35
Breakpoint 3 at /home/.../src/funcif/Simpson.py:35
Typing c
continues execution up to the next break point, line 35.
The command next
or n
is like step
or s
in that the current line is executed, but the execution does not
step into functions, instead the function calls are just performed
and the program stops at the next line:
ipdb> n
> /home/.../src/funcif/Simpson.py(36)application()
3 35 approx = Simpson(h, 0, pi, n)
---> 36 print 'n=%3d, approx=%18.15f, error=%9.2E' % \
37 (n, approx, 2-approx)
ipdb> print approx, n
1.9891717005835792 6
The command disable X Y Z
disables break points with numbers
X
, Y
, and Z
, and so on.
To remove our three break points and continue
execution until the program naturally stops, we write
ipdb> disable 1 2 3
ipdb> c
n=100, approx= 1.999999902476350, error= 9.75E-08
n=500, approx= 1.999999999844138, error= 1.56E-10
In [2]:
At this point, I hope you realize that a debugger is a very handy tool for monitoring the program flow, checking variables, and thereby understanding why errors occur.