1.4 Debugging with GNU Debugger (GDB)

The debugger is the program that you use to figure out why your program isn't behaving the way you think it should. You'll be doing this a lot. [5] The GNU Debugger (GDB) is the debugger used by most Linux programmers. You can use GDB to step through your code, set breakpoints, and examine the value of local variables.

[5] . …unless your programs always work the first time.

1.4.1 Compiling with Debugging Information

To use GDB, you'll have to compile with debugging information enabled. Do this by adding the -g switch on the compilation command line. If you're using a Makefile as described previously, you can just set CFLAGS equal to -g when you run make, as shown here:

 
% make CFLAGS=-g 
gcc -g -c main.c 
g++ -g -c reciprocal.cpp 
g++ -g -o reciprocal main.o reciprocal.o 

When you compile with -g, the compiler includes extra information in the object files and executables. The debugger uses this information to figure out which addresses correspond to which lines in which source files, how to print out local variables, and so forth.

1.4.2 Running GDB

You can start up gdb by typing:

 
% gdb reciprocal 

When gdb starts up, you should see the GDB prompt:

 
(gdb) 

The first step is to run your program inside the debugger. Just enter the command run and any program arguments. Try running the program without any arguments, like this:

 
(gdb) run 
Starting program: reciprocal 
 
Program received signal SIGSEGV, Segmentation fault. 
__strtol_internal (nptr=0x0, endptr=0x0, base=10, group=0) 
at strtol.c:287 
287     strtol.c: No such file or directory. 
(gdb) 

The problem is that there is no error-checking code in main. The program expects one argument, but in this case the program was run with no arguments. The SIGSEGV message indicates a program crash. GDB knows that the actual crash happened in a function called __strtol_internal. That function is in the standard library, and the source isn't installed, which explains the "No such file or directory" message. You can see the stack by using the where command:

 
(gdb) where 
#0  __strtol_internal (nptr=0x0, endptr=0x0, base=10, group=0) 
    at strtol.c:287 
#1  0x40096fb6 in atoi (nptr=0x0) at ../stdlib/stdlib.h:251 
#2  0x804863e in main (argc=1, argv=0xbffff5e4) at main.c:8 

You can see from this display that main called the atoi function with a NULL pointer, which is the source of the trouble.

You can go up two levels in the stack until you reach main by using the up command:

 
(gdb) up 2 
#2  0x804863e in main (argc=1, argv=0xbffff5e4) at main.c:8 
8         i = atoi (argv[1]); 

Note that gdb is capable of finding the source for main.c, and it shows the line where the erroneous function call occurred. You can view the value of variables using the print command:

 
(gdb) print argv[1] 
$2 = 0x0 

That confirms that the problem is indeed a NULL pointer passed into atoi.

You can set a breakpoint by using the break command:

 
(gdb) break main 
Breakpoint 1 at 0x804862e: file main.c, line 8. 

This command sets a breakpoint on the first line of main. [6] Now try rerunning the program with an argument, like this:

[6] Some people have commented that saying break main is a little bit funny because usually you want to do this only when main is already broken.

 
(gdb) run 7 
Starting program: reciprocal 7 
 
Breakpoint 1, main (argc=2, argv=0xbffff5e4) at main.c:8 
8         i = atoi (argv[1]); 

You can see that the debugger has stopped at the breakpoint.

You can step over the call to atoi using the next command:

 
(gdb) next 
9         printf ("The reciprocal of %d is %g\n", i, reciprocal (i)); 

If you want to see what's going on inside reciprocal, use the step command like this:

 
(gdb) step 
reciprocal (i=7) at reciprocal.cpp:6 
6         assert (i != 0); 

You're now in the body of the reciprocal function.

You might find it more convenient to run gdb from within Emacs rather than using gdb directly from the command line. Use the command M-x gdb to start up gdb in an Emacs window. If you are stopped at a breakpoint, Emacs automatically pulls up the appropriate source file. It's easier to figure out what's going on when you're looking at the whole file rather than just one line of text.