gdb is a powerful interactive debugger that is frequently used to debug programs com- piled with GNU’s gcc compiler. It allows you to run your program, stop at a predeter- mined location, display and/or set variables, and continue execution. It allows you to trace your program’s execution and even execute it one line at a time. gdb also has a facility for determining where core dumps occur. A core dump occurs due to some abnor-mal event, possibly division by zero or attempts to access past the end of an array. This results in the creation of a file named core that contains a snapshot of the contents of the process’s memory at the time it terminated.
Your C program must be compiled with the gcc compiler using the -g option to make full use of gdb’s features. The -g option causes the C compiler to add extra infor- mation to the output file, including variable and structure types, source filenames, and C statement to machine code mappings.
Program 18.4 shows a program that attempts to access elements past the end of an array.
Program 18.4 A Simple Program for Use with gdb
#include <stdio.h>
int main (void)
{
const int data[5] = {1, 2, 3, 4, 5};
int i, sum;
for (i = 0; i >= 0; ++i)
sum += data[i];
printf (“sum = %i\n”, sum);
return 0;
}
Here’s what happens when the program is run on a Mac OS X system from a terminal window (on other systems you might get a different message displayed when you run the program):
$ a.out
Segmentation fault
Use gdb to try to track down the error. This is certainly a contrived example; neverthe- less, it is illustrative.
First, make sure you compile the program with the –g option. Then, you can start up gdb on the executable file, which is a.out by default. This might result in lines of intro- ductory messages being displayed on your system:
$ gcc –g p18.4.c Recompile with debugging information for gdb
$ gdb a.out Start up gdb on the executable file
GNU gdb 5.3-20030128 (Apple version gdb-309) (Thu Dec 4 15:41:30 GMT 2003) Copyright 2003 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 “powerpc-apple-darwin”.
Reading symbols for shared libraries .. done
When gdb is ready to accept commands, it displays a (gdb) prompt. In our simple example, you’re simply telling it to run your program by entering the run command. This causes gdb to start execution of your program until it finishes or an abnormal event occurs:
(gdb) run
Starting program: /Users/stevekochan/MySrc/c/a.out
Reading symbols for shared libraries . done
Program received signal EXC_BAD_ACCESS, Could not access memory.
0x00001d7c in main () at p18-4.c:9
9 sum += data[i];
(gdb)
So your program receives an error (as it did before), but it still remains under the control of gdb. That’s the nice part because now you can see what it was doing when the error occurred as well as look at the values of variables at the time of the error.
As you can see from the previous display, the program tried to perform an invalid memory access at line 9 in your program. The actual line in question is automatically displayed from your source file. To gain a little context around that line, you can use the list command, which displays a window of 10 lines around the specified line (5 lines before and 4 lines after):
(gdb) list 9
4 {
5 const int data[5] = {1, 2, 3, 4, 5};
6 int i, sum;
7
8 for (i = 0; i >= 0; ++i)
9 sum += data[i];
10
11 printf (“sum = %i\n”, sum);
12
13 return 0;
(gdb)
You can look at variables by using the print command. Take a look at what the value of sum was at the time the program stopped with an error:
(gdb) print sum
$1 = -1089203864
This value for sum is obviously out of whack (and on your system might differ from what’s shown here). The $n notation is used by gdb to keep track of the values you’ve previously displayed, so you can easily reference them again later.
Take a look at what the index variable i is set to:
(gdb) print i
$2 = 232
Oops! That’s not good.You have only five elements in the array and you tried to access the 233rd element when the error occurred. On your system, the error might occur ear- lier or later. But eventually, you should get an error.
Before you exit from gdb, look at another variable. See how nicely gdb deals with variables like arrays and structures:
(gdb) print data Show the contents of the data array
$3 = {1, 2, 3, 4, 5}
(gdb) print data[0] Show the value of the first element
$4 = 1
You’ll see an example of a structure a little later. To complete this first example with gdb, you need to learn how to get out.You can do that with the quit command:
(gdb) quit
The program is running. Exit anyway? (y or n) y
$
Even though the program had an error, technically speaking, it was still active inside gdb; the error merely caused your program’s execution to be suspended, but not terminated. That’s the reason gdb asked for confirmation about quitting.
1. Working with Variables
gdb has two basic commands that allow you to work with variables in your program. One you’ve seen already is print. The other allows you to set the value of a variable. This is done with the set var command. The set command actually takes a number of different options, but var is the one you want to use to assign a value to a variable:
(gdb) set var i=5
(gdb) print i
$1 = 5
(gdb) set var i=i*2 You can write any valid expression
(gdb) print i
$2 = 10
(gdb) set var i=$1+20 You can use so-called “convenience variables”
(gdb) print i
$3 = 25
A variable must be accessible by the current function, and the process must be active, that is, running. gdb maintains an idea of a current line (like an editor), a current file (the source file of the program), and a current function. When gdb starts up without a core file, the current function is main, the current file is the one that contains main, and the current line is the first executable line in main; otherwise, the current line, file, and pro- cedure are set to the location where the program aborted.
If a local variable with the specified name doesn’t exist, gdb looks for an external variable of the same name. In the previous example, the function executing at the time the invalid access occurred was main, and i was a variable local to main.
A function can be specified as part of the variable name in the form function::variable to reference a variable local to a specific routine, for example,
(gdb) print main::i Display contents of i in main
$4 = 25
(gdb) set var main::i=0 Set value of i in main
Note that attempting to set a variable in an inactive function (that is, a function that is not either currently executing or waiting for another function to return to continue its own execution) is an error and results in the following message:
No symbol “var” in current context.
Global variables can be directly referenced as ‘file’::var. This forces gdb to access an external variable as defined in the file file and ignore any local variable of the same name in the current function.
Structure and union members can be accessed using standard C syntax. If datePtr is a pointer to a date structure, print datePtr->year prints the year member of the structure pointed to by datePtr.
Referencing a structure or union without a member causes the contents of the entire structure or union to be displayed.
You can force gdb to display a variable in a different format, for example hexadeci- mal, by following the print command with a / and a letter that specifies the format to use. Many gdb commands can be abbreviated with a single letter. In the following exam- ple, the abbreviation for the print command, which is p, is used:
(gdb) set var i=35 Set i to 35
(gdb) p /x i Display i in hexadecimal
$1 = 0x23
2. Source File Display
gdb provides several commands that give you access to the source files. This enables you to debug the program without having to reference a source listing or open your source files in other windows.
As mentioned earlier, gdb maintains an idea of what the current line and file are. You’ve seen how you can display the area around the current line with the list com- mand, which can be abbreviated as l. Each time you subsequently type the list com- mand (or more simply, just press the Enter or Return key), the next 10 lines from the file are displayed. This value of 10 is the default and can be set to any value by using the listsize command.
If you want to display a range of lines, you can specify the starting and ending line numbers, separated by a comma, as follows:
(gdb) list 10,15 List lines 10 through 15
Lines from a function can be listed by specifying the function’s name to the list com- mand:
(gdb) list foo Display lines for function foo
If the function is in another source file, gdb automatically switches to that file.You can find the name of the current source file being displayed with gdb by typing in the com- mand info source.
Typing a + after the list command causes the next 10 lines from the current file to be displayed, which is the same action that occurs if just list is typed. Typing a – causes the previous 10 lines to be displayed. Both the + and – options can also be followed by a number to specify a relative offset to be added or subtracted from the current line.
3. Controlling Program Execution
Displaying lines from a file doesn’t modify the way a program is executed.You must use other commands for that.You’ve seen two commands that control the execution of a program in gdb: run, which runs the program from the beginning, and quit, which ter- minates execution of the current program.
The run command can be followed by command-line arguments and/or redirection (< or >), and gdb handles them properly. Subsequent use of the run command without any arguments reuses the previous arguments and redirection.You can display the current arguments with the command show args.
3.1. Inserting Breakpoints
The break command can be used to set breakpoints in your program. A breakpoint is just as its name implies—a point in your program that, when reached during execution, caus- es the program to “break” or pause. The program’s execution is suspended, which allows you to do things such as look at variables and determine precisely what’s going on at the point.
A breakpoint can be set at any line in your program by simply specifying the line number to the command. If you specify a line number but no function or filename, the breakpoint is set on that line in the current file; if you specify a function, the breakpoint is set on the first executable line in that function.
(gdb) break 12 Set breakpoint on line 12
Breakpoint 1 at 0x1da4: file mod1.c, line 12.
(gdb) break main Set breakpoint at start of main
Breakpoint 2 at 0x1d6c: file mod1.c, line 3.
(gdb) break mod2.c:foo Breakpoint in function foo in file mod2.c
Breakpoint 3 at 0x1dd8: file mod2.c, line 4.
When a breakpoint is reached during program execution, gdb suspends execution of your program, returns control to you, and identifies the breakpoint and the line of your pro- gram at which it stopped.You can do anything you want at that point:You can display or set variables, set or unset breakpoints, and so on. To resume execution of the program, you can simply use the continue command, which can be abbreviated as simply c.
3.2. Single Stepping
Another useful command for controlling program execution is the step command, which can be abbreviated as s. This command single steps your program, meaning that one line of C code in your program is executed for each step command you enter. If you follow the step command with a number, then that many lines are executed. Note that a line might contain several C statements; however, gdb is line oriented, and exe- cutes all statements on a line as a single step. If a statement spans several lines, single step- ping the first line of the statement causes all the lines of the statement to be executed. You can single step your program at any time that a continue is appropriate (after a sig- nal or breakpoint).
If the statement contains a function call and you step, gdb takes you into the func- tion (provided it’s not a system library function; these are typically not entered). If you use the next command instead of step, gdb makes the function call and does not step you into it.
Try some of gdb’s features on Program 18.5, which otherwise serves no useful pur- pose.
Program 18.5 Working with gdb
#include <stdio.h>
#include <stdlib.h>
struct date {
int month;
int day;
int year;
};
struct date foo (struct date x)
{
++x.day;
return x;
}
int main (void)
{
struct date today = {10, 11, 2004};
int array[5] = {1, 2, 3, 4, 5};
struct date *newdate, foo ();
char *string = “test string”;
int i = 3;
newdate = (struct date *) malloc (sizeof (struct date));
newdate->month = 11;
newdate->day = 15;
newdate->year = 2004;
today = foo (today);
free (newdate);
return 0;
}
In the sample session for Program 18.5, your output might be slightly different, depend- ing on which version and on what system you are running gdb.
Program 18.5 gdb Session
$ gcc -g p18-5.c
$ gdb a.out
GNU gdb 5.3-20030128 (Apple version gdb-309) (Thu Dec 4 15:41:30 GMT 2003) Copyright 2003 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 “powerpc-apple-darwin”.
Reading symbols for shared libraries .. done
(gdb) list main
14
15 return x;
16 }
17
18 int main (void)
19 {
20 struct date today = {10, 11, 2004};
21 int array[5] = {1, 2, 3, 4, 5};
22 struct date *newdate, foo ();
23 char *string = “test string”;
(gdb) break main Set breakpoint in main
Breakpoint 1 at 0x1ce8: file p18-5.c, line 20.
(gdb) run Start program execution
Starting program: /Users/stevekochan/MySrc/c/a.out
Reading symbols for shared libraries . done
Breakpoint 1, main () at p18-5.c:20
20 struct date today = {10, 11, 2004};
(gdb) step Execute line 20
21 int array[5] = {1, 2, 3, 4, 5};
(gdb) print today
$1 = {
month = 10,
day = 11,
year = 2004
}
(gdb) print array This array hasn’t been initialized yet
$2 = {-1881069176, -1880816132, -1880815740, -1880816132, -1880846287}
(gdb) step Run another line
23 char *string = “test string”;
(gdb) print array Now try it
$3 = {1, 2, 3, 4, 5} That’s better
(gdb) list 23,28
23 char *string = “test string”;
24 int i = 3;
25
26 newdate = (struct date *) malloc (sizeof (struct date));
27 newdate->month = 11;
28 newdate->day = 15;
(gdb) step 5 Execute 5 lines
29 newdate->year = 2004;
(gdb) print string
$4 = 0x1fd4 “test string”
(gdb) print string[1]
$5 = 101 ‘e’
(gdb) print array[i] The program set i to 3
$6 = 3
(gdb) print newdate This is a pointer variable
$7 = (struct date *) 0x100140
(gdb) print newdate->month
$8 = 11
(gdb) print newdate->day + i Arbitrary C expression
$9 = 18
(gdb) print $7 Access previous value
$10 = (struct date *) 0x100140
(gdb) info locals Show the value of all local variables
today = {
month = 10,
day = 11,
year = 2004
}
array = {1, 2, 3, 4, 5}
newdate = (struct date *) 0x100140
string = 0x1fd4 “test string”
i = 3
(gdb) break foo Put a breakpoint at the start of foo
Breakpoint 2 at 0x1c98: file p18-5.c, line 13.
(gdb) continue Continue execution
Continuing.
Breakpoint 2, foo (x={month = 10, day = 11, year = 2004}) at p18-5.c:13
13 ++x.day; 0x8e in foo:25: {
(gdb) print today Display value of today
No symbol “today” in current context
(gdb) print main::today Display value of today from main
$11 = {
month = 10,
day = 11,
year = 2004
}
(gdb) step
15 return x;
(gdb) print x.day
$12 = 12
(gdb) continue
Continuing.
Program exited normally.
(gdb)
Note one feature of gdb: After a breakpoint is reached or after single stepping, it lists the line that will be executed next when you resume execution of your program, and not the last executed line. That’s why array was still not initialized the first time it was dis-
played. Single stepping one line caused it to be initialized. Also note that declarations that initialize automatic variables are considered executable lines (they actually do cause the compiler to produce executable code).
3.3. Listing and Deleting Breakpoints
Once set, breakpoints remain in a program until gdb exits or until you delete them.You can see all the breakpoints that you have set by using the info break command, as follows:
(gdb) info break
Num Type Disp Enb Address What
1 breakpoint keep y 0x00001c9c in main at p18-5.c:20
2 breakpoint keep y 0x00001c4c in foo at p18-5.c:13
You can delete a breakpoint at a particular line with the clear command followed by the line number.You can delete a breakpoint at the start of a function by specifying the function’s name to the clear command instead:
(gdb) clear 20 Remove breakpoint from line 20
Deleted breakpoint 1
(gdb) info break
Num Type Disp Enb Address What
2 breakpoint keep y 0x00001c4c in foo at p18-5.c:13
(gdb) clear foo Remove breakpoint on entry into foo
Deleted breakpoint 2
(gdb) info break
No breakpoints or watchpoints.
(gdb)
4. Getting a Stack Trace
Sometimes, you’ll want to know exactly where you are in terms of the hierarchy of function calls when a program gets interrupted. This is useful information when examin- ing a core file.You can take a look at the call stack by using the backtrace command, which can be abbreviated as bt. The following is an example use of Program 18.5.
(gdb) break foo
Breakpoint 1 at 0x1c4c: file p18-5.c, line 13. (gdb) run
Starting program: /Users/stevekochan/MySrc/c/a.out
Reading symbols for shared libraries . done
Breakpoint 1, foo (x={month = 10, day = 11, year = 2004}) at p18-5.c:13
13 ++x.day;
(gdb) bt Print stack trace
#0 foo (x={month = 10, day = 11, year = 2004}) at p18-5.c:13
#1 0x00001d48 in main () at p18-5.c:31 (gdb)
When the break is taken on entry to foo, the backtrace command is entered. The out- put shows two functions on the call stack: foo and main. As you can see, the arguments to the functions are also listed.Various commands (such as up, down, frame, and info args) that are not covered here allow you to work your way around in the stack so that you can more easily examine arguments passed to a particular function or work with its local variables.
5. Calling Functions and Setting Arrays and Structures
You can use function calls in gdb expressions as follows:
(gdb) print foo(*newdate) Call foo with date structure pointed to by newdate
$13 = {
month = 11,
day = 16,
year = 2004
}
(gdb)
Here, the function foo is as defined in Program 18.5.
You can assign values to an array or structure by listing them inside a set of curly braces, as follows:
(gdb) print array
$14 = {1, 2, 3, 4, 5}
(gdb) set var array = {100, 200}
(gdb) print array
$15 = {100, 200, 0, 0} Unspecified values set to zero
(gdb) print today
$16 = {
month = 10,
day = 11,
year = 2004
}
(gdb) set var today={8, 8, 2004}
(gdb) print today
$17 = {
month = 8,
day = 8,
year = 2004
} (gdb)
6. Getting Help with gdb Commands
You can use the built-in help command to get information about various commands or types of commands (called classes by gdb).
The command help, without any arguments, lists all the available classes:
(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.
Now, you can give the help command one of those listed classes, as follows:
(gdb) help breakpoints
Making program stop at certain points.
List of commands:
awatch — Set a watchpoint for an expression
break — Set breakpoint at specified line or function
catch — Set catchpoints to catch events
clear — Clear breakpoint at specified line or function
commands — Set commands to be executed when a breakpoint is hit
condition — Specify breakpoint number N to break only if COND is true
delete — Delete some breakpoints or auto-display expressions
disable — Disable some breakpoints enable — Enable some breakpoints
future-break — Set breakpoint at expression
hbreak — Set a hardware assisted breakpoint
ignore — Set ignore-count of breakpoint number N to COUNT
rbreak — Set a breakpoint for all functions matching REGEXP
rwatch — Set a read watchpoint for an expression
save-breakpoints — Save current breakpoint definitions as a script
set exception-catch-type-regexp –
Set a regexp to match against the exception type of a caughtobject
set exception-throw-type-regexp –
Set a regexp to match against the exception type of a thrownobject
show exception-catch-type-regexp –
Show a regexp to match against the exception type of a caughtobject
show exception-throw-type-regexp –
Show a regexp to match against the exception type of a thrownobject
tbreak — Set a temporary breakpoint
tcatch — Set temporary catchpoints to catch events
thbreak — Set a temporary hardware assisted breakpoint
watch — Set a watchpoint for an expression
Type “help” followed by command name for full documentation.
Command name abbreviations are allowed if unambiguous.
(gdb)
Alternatively, you can specify a command, such as one from the previous list:
(gdb_ help break
Set breakpoint at specified line or function.
Argument may be line number, function name, or “*” and an address.
If line number is specified, break at start of code for that line.
If function is specified, break at start of code for that function.
If an address is specified, break at that exact address.
With no arg, uses current execution address of selected stack frame.
This is useful for breaking on return to a stack frame.
Multiple breakpoints at one place are permitted, and useful if conditional.
break … if <cond> sets condition <cond> on the breakpoint as it is created.
Do “help breakpoints” for info on other commands dealing with breakpoints.
(gdb)
So, you can see that you have a lot of help information built right in to the gdb debug- ger. Be sure to take advantage of it!
7. Odds and Ends
Many other features are available with gdb that can’t be covered here for space reasons. These include the ability to
- Set temporary breakpoints that are automatically removed when they are reached.
- Enable and disable breakpoints without having to clear them.
- Dump memory locations in a specified format.
- Set a watchpoint that allows for your program’s execution to be stopped whenever the value of a specified expression changes (for example, when a variable changes its value).
- Specify a list of values to be displayed whenever the program stops.
- Set your own “convenience variables” by name.
Table 18.1 lists the gdb commands covered in this chapter. A leading bold character for a command name shows how the command can be abbreviated.
Source: Kochan Stephen G. (2004), Programming in C: A Complete Introduction to the C Programming Language, Sams; Subsequent edition.