The GNU Debugger
The GNU Debugger
Although make
automates the process of building a program, that task is the least of your worries when a program does not work correctly or when a program suddenly quits with an error message. You need a debugger to find the cause of program errors. This book's companion CD-ROMs include gdb
-the versatile GNU debugger with a command-line interface.
Like any debugger, gdb
lets you perform typical debugging tasks, such as the following:
-
Set the breakpoint so that program execution stops at a specified line.
-
Watch the values of variables in the program.
-
Step through the program one line at a time.
-
Change variables in an attempt to fix errors.
Preparing a Program for Debugging
If you want to debug a program by using gdb
, you have to ensure that the compiler generates and places debugging information in the executable. The debugging information contains the names of variables in your program and the mapping of addresses in the executable file to lines of code in the source file. gdb needs this information to perform its functions, such as stopping after executing a specified line of source code.
Insider Insight |
To ensure that the executable is properly prepared for debugging, use the CFLAGS= -g |
Running gdb
The most common way to debug a program is to run gdb
by using the following command:
gdb progname
progname
is the name of the program's executable file. After it runs, gdb
displays the following message and prompts you for a command:
GNU gdb CentOS Linux (5.3post-0.20021129.7rh) Copyright 2002 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-redhat-linux-gnu". (gdb) help List of classes of commands: aliases -- Aliases of other commands breakpoints -- Making program stop at certain points data -- Examining data files -- Specifying and examining files internals -- Maintenance commands obscure -- Obscure features running -- Running the program stack -- Examining the stack status -- Status inquiries support -- Support facilities tracepoints -- Tracing of program execution without stopping the program user-defined -- User-defined commands Type "help" followed by a class name for a list of commands in that class. Type "help" followed by command name for full documentation. Command name abbreviations are allowed if unambiguous. (gdb) q
When you type help, gdb
displays the classes of gdb
commands. You can get further help on a specific class of commands or a specific command by following the instructions. To see the list of commands you use to run the program you are debugging, type help running at the gdb prompt.
To quit gdb
, type q, and press Enter.
gdb
has a large number of commands, but you need only a few to find the cause of an error quickly. Table 23-4 lists the commonly used gdb
commands.
Command |
Description |
---|---|
|
Set a breakpoint at the specified line number (the debugger stops at breakpoints) |
|
Display a trace of all stack frames. (This command shows you the sequence of function calls so far.) |
|
Delete the breakpoint at a specific line in a source file. For example, |
|
Continue running the program being debugged. (Use this command after the program has stopped due to a signal or breakpoint.) |
|
Display value of expression (consisting of variables defined in the program) each time the program stops |
Load specified executable file for debugging | |
|
Display help on the command named |
|
Display a list of current breakpoints, including information on how many times each breakpoint has been reached |
|
Display detailed information about the file being debugged |
|
Display all function names |
|
Display information about local variables of current function |
|
Display the execution status of the program being debugged |
|
Display all global and static variable names |
End the program you are debugging | |
|
List a section of the source code |
|
Run the |
|
Advance one line of source code in the current function without stepping into other functions |
|
Show the value of the expression |
|
Quit |
|
Start running the currently loaded executable |
|
Set the value of the variable |
|
Execute a Linux command |
|
Advance one line in the current function, stepping into other functions, if any |
|
Show the value of the variable named |
|
Display the call sequence. Use this command to locate where your program died. |
|
Examine the contents of the memory location at address |
Finding Bugs by Using gdb
To understand how you can find bugs by using gdb
, you need to see an example. The procedure is easiest to show with a simple example, so I start with a rather contrived program that contains a typical bug.
This is the contrived program, which I store in the file dbgtst.c
:
#include <stdio.h> static char buf[256]; void read_input(char *s); int main(void) { char *input = NULL; /* Just a pointer, no storage for string */ read_input(input); /* Process command. */ printf("You typed: %s\n", input); /* ... */ return 0; } void read_input(char *s) { printf("Command: "); gets(s); }
This program's main
function calls the read_input
function to get a line of input from the user. The read_input
function expects a character array in which it returns what the user types. In this example, however, main
calls read_input
with an uninitialized pointer-that's the bug in this simple program.
Build the program by using gcc
with the -g
option:
gcc -g -o dbgtst dbgtst.c
Ignore the warning message about the gets()
function being dangerous; we are trying to use the shortcoming of the gets()
function to show how gdb
can be used to track down errors.
To see the problem with this program, run it:
./dbgtst Command: test Segmentation fault
The program dies after displaying the Segmentation fault
message. For this small program, you can find the cause by examining the source code. In a real-world application, however, you may not immediately know what causes the error. That's when you use gdb
to find the cause of the problem.
To use gdb
to locate a bug, follow these steps:
-
Load the program under
gdb
. To load a program nameddbgtst
ingdb
, type the following:gdb dbgtst
-
At the (gdb) prompt, type run. When the program prompts for input, type some input text. The program should fail as it has previously. Here's what happens with the
dbgtst
program:(gdb) run Starting program: /home/naba/apps/dbgtst Command: test Program received signal SIGSEGV, Segmentation fault. 0x420632cc in gets () from /lib/tls/libc.so.6 (gdb)
-
Type the
where
command to determine where the program died. For thedbgtst
program, this command yields this output:(gdb) where #0 0x420632cc in gets () from /lib/tls/libc.so.6 #1 0x080483bc in read_input (s=0x0) at dbgtst.c:22 #2 0x0804837e in main () at dbgtst.c:10 #3 0x420154a0 in __libc_start_main () from /lib/tls/libc.so.6 (gdb)
The output shows the sequence of function calls. Function call #0-the most recent one-is to a C library function,
gets
. Thegets
call originates in theread_input
function, which in turn is called from themain
function. -
Type the
list
command with the name of a file and line number to inspect the lines of suspect source code. Indbgtst
, you might start with line 22 ofdbgtst.c
file, as follows:(gdb) list dbgtst.c:22 17 } 18 19 void read_input(char *s) 20 { 21 printf("Command: "); 22 gets(s); 23 } 24 (gdb)
After looking at this listing, if you have some experience writing C programs, you should be able to tell that the problem might be the way
read_input
is called. Then you list the lines around line 10 indbgtst.c
(where theread_input
call originates):(gdb) list dbgtst.c:10 5 6 int main(void) 7 { 8 char *input = NULL; /* Just a pointer, no storage for string */ 9 10 read_input(input); 11 12 /* Process command. */ 13 printf("You typed: %s\n", input); 14 (gdb)
At this point, you should be able to narrow the problem to the variable named
input
. That variable should be an array, not a NULL pointer.
Fixing Bugs in gdb
Sometimes you can try a bug fix directly in gdb
. For the example program in the preceding section, you can try this fix immediately after the program dies after displaying an error message. Because the example is contrived, I have an extra buffer named buf
defined in the dbgtst
program, as follows:
static char buf[256];
I can fix the problem of the uninitialized pointer by setting the variable input
to buf
. The following session with gdb
corrects the problem of the uninitialized pointer (this example picks up immediately after the program has run and died, due to the segmentation fault):
(gdb) file dbgtst A program is being debugged already. Kill it? (y or n) y Load new symbol table from "dbgtst"? (y or n) y Reading symbols from dbgtst... done. (gdb) list 1 #include <stdio.h> 2 3 static char buf[256]; 4 void read_input(char *s); 5 6 int main(void) 7 { 8 char *input = NULL; /* Just a pointer, no storage for string */ 9 10 read_input(input); (gdb) break 9 Breakpoint 1 at 0x8048373: file dbgtst.c, line 9. (gdb) run Starting program: /home/naba/apps/dbgtst Breakpoint 1, main () at dbgtst.c:10 10 read_input(input); (gdb) set var input=buf (gdb) cont Continuing. Command: test You typed: test Program exited normally. (gdb) q
As the previous listing shows, if I stop the program just before read_input
is called and set the variable named input
to buf
(which is a valid array of characters), the rest of the program runs fine.
After trying in gdb
a fix that works, you can make the necessary changes to the source files and can make the fix permanent.