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 nameddbgtstingdb, 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
dbgtstprogram:(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
wherecommand to determine where the program died. For thedbgtstprogram, 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. Thegetscall originates in theread_inputfunction, which in turn is called from themainfunction. -
Type the
listcommand 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.cfile, 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_inputis called. Then you list the lines around line 10 indbgtst.c(where theread_inputcall 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.