$$ \newcommand{\tp}{\thinspace .} $$

This chapter is taken from book A Primer on Scientific Programming with Python by H. P. Langtangen, 4th edition, Springer, 2014.

Using a debugger

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.

Step 1

Go to the folder where the program Simpson.py resides.

Step 2

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.

Step 3

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.

Step 4

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.

Step 5

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:'

Step 6

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.

Step 7

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'>

Step 8

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.

Step 9

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

Step 10

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.