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.
The gdb debugger can debug C and C++ programs.
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 -g option with GCC. You can do this by defining the variable CFLAGS in the makefile as: CFLAGS= -g |
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 Red Hat 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 |
---|---|
break NUM |
Set a breakpoint at the specified line number (the debugger stops at breakpoints) |
bt |
Display a trace of all stack frames. (This command shows you the sequence of function calls so far.) |
clear clear FILENAME:NUM |
Delete the breakpoint at a specific line in a source file. For example, xdraw.c:8 clears the breakpoint at line 8 of file xdraw.c. |
Continue |
Continue running the program being debugged. (Use this command after the program has stopped due to a signal or breakpoint.) |
display EXPR |
Display value of expression (consisting of variables defined in the program) each time the program stops |
file FILE |
Load specified executable file for debugging |
help NAME |
Display help on the command named NAME |
info break |
Display a list of current breakpoints, including information on how many times each breakpoint has been reached |
info files |
Display detailed information about the file being debugged |
info func |
Display all function names |
info local |
Display information about local variables of current function |
info prog |
Display the execution status of the program being debugged |
info var |
Display all global and static variable names |
Kill |
End the program you are debugging |
List |
List a section of the source code |
Make |
Run the make utility to rebuild the executable without leaving gdb |
Next |
Advance one line of source code in the current function without stepping into other functions |
print EXPR |
Show the value of the expression EXPR |
Quit |
Quit gdb |
Run |
Start running the currently loaded executable |
set variable |
Set the value of the variable VAR to VALUE VAR=VALUE |
shell CMD |
Execute a Linux command CMD, without leaving gdb |
Step |
Advance one line in the current function, stepping into other functions, if any |
watch VAR |
Show the value of the variable named VAR whenever the value changes |
Where |
Display the call sequence. Use this command to locate where your program died. |
x/F ADDR |
Examine the contents of the memory location at address ADDR in the format specified by the letter F, which can be o (octal); x (hex); d (decimal); u (unsigned decimal); t (binary); f (float); a (address); i (instruction); c (char); or s (string). You can append a letter indicating the size of data type to the format letter. Size letters are b (byte); h (halfword, 2 bytes), w (word, 4 bytes); and g (giant, 8 bytes). Typically, ADDR is the name of a variable or pointer. |
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 named dbgtst in gdb, 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 the dbgtst 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. The gets call originates in the read_input function, which in turn is called from the main function.
Type the list command with the name of a file and line number to inspect the lines of suspect source code. In dbgtst, you might start with line 22 of dbgtst.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 in dbgtst.c (where the read_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.
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.