gdb Debugger
You may run into a bug or two in your programs. There are many techniques for finding
bugs, but a good debugger can make the job a lot easier. In most programs of any
significant size, it is not possible to track down all of the bugs in a program just by staring
at the source — you need to see clues in the runtime behavior of the program to find the
bug. It's worth investing time to learn to use debuggers well.
GDB
We recommend the GNU debugger gdb, since it basically stomps on dbx in every
possible area and works nicely with the gcc compiler. Other nice debugging
environments include ups and CodeCenter, but these are not as universally available as
gdb, and in the case of CodeCenter not as cheaply. While gdb does not have a flashy
graphical interface as do the others, it is a powerful tool that provides the knowledgeable
programmer with all of the information they could possibly want and then some.
This section does not come anywhere close to describing all of the features of gdb, but
will hit on the high points. There is on-line help for gdb which can be seen by using the
help command from within gdb. If you want more information try xinfo if you are
logged onto the console of a machine with an X display or use the info-browser mode
from within emacs.
Starting the debugger
As with make there are two different ways of invoking gdb. To start the debugger from
the shell just type...
gdb program
where program is the name of the target executable that you want to debug. If you do
not specify a target then gdb will start without a target and you will need to specify one
later before you can do anything useful.
As an alternative, from within emacs you can use the command [Esc]-x gdb which
will then prompt you for the name of the executable file. You cannot start an inferior gdb
session from within emacs without specifying a target. The emacs window will then split
between the gdb buffer and a separate buffer showing the current source line.
Running the debugger
Once started, the debugger will load your application and its symbol table (which
contains useful information about variable names, source code files, etc.). This symbol
9
table is the map produced by the -g compiler option that the debugger reads as it is
running your program.
The debugger is an interactive program. Once started, it will prompt you for commands.
The most common commands in the debugger are: setting breakpoints, single stepping,
continuing after a breakpoint, and examining the values of variables.
Running the Program
run Reset the program, run (or rerun) from the
beginning. You can supply command-line
arguments the same way you can supply commandline
arguments to your executable from the shell.
step Run next line of source and return to debugger. If a
subroutine call is encountered, follow into that
subroutine.
step count Run count lines of source.
next Similar to step, but doesn't step into subroutines.
finish Run until the current function/method returns.
return Make selected stack frame return to its caller.
jump address Continue program at specified line or address.
When a target executable is first selected (usually on startup) the current source file is set
to the file with the main function in it, and the current source line is the first executable
line of the this function.
As you run your program, it will always be executing some line of code in some source
file. When you pause the program (when the flow of control hits a “breakpoint” of by
typing Control-C to interrupt), the “current target file” is the source code file in which the
program was executing when you paused it. Likewise, the “current source line” is the line
of code in which the program was executing when you paused it.
Breakpoints
You can use breakpoints to pause your program at a certain point. Each breakpoint is
assigned an identifying number when you create it, and so that you can later refer to that
breakpoint should you need to manipulate it.
A breakpoint is set by using the command break specifying the location of the code
where you want the program to be stopped. This location can be specified in several
ways, such as with the file name and either a line number or a function name within that
file (a line needs to be a line of actual source code — comments and whitespace don't
count). If the file name is not specified the file is assumed to be the current target file, and
if no arguments are passed to break then the current source line will be the breakpoint.
gdb provides the following commands to manipulate breakpoints:
info break Prints a list of all breakpoints with numbers and
status.
10
break function Place a breakpoint at start of the specified function
break linenumber Prints a breakpoint at line, relative to current source
file.
break filename:linenumber Place a breakpoint at the specified line within the
specified source file.
You can also specify an if clause to create a conditional breakpoint:
break fn if expression Stop at the breakpoint, only if expression evaluates
to true. Expression is any valid C expression,
evaluated within current stack frame when hitting
the breakpoint.
disable breaknum
enable breaknum Disable/enable breakpoint identified by breaknum
delete breaknum Delete the breakpoint identified by breaknum
commands breaknum Specify commands to be executed when breaknum
is reached. The commands can be any list of C
statements or gdb commands. This can be useful to
fix code on-the-fly in the debugger without recompiling
(Woo Hoo!).
cont Continue a program that has been stopped.
For example, the commands...
break binky.c:120
break DoGoofyStuff
set a breakpoint on line 120 of the file binky.c and another on the first line of the function
DoGoofyStuff. When control reaches these locations, the program will stop and give
you a chance to look around in the debugger.
Gdb (and most other debuggers) provides mechanisms to determine the current state of
the program and how it got there. The things that we are usually interested in are (a)
where are we in the program? and (b) what are the values of the variables around us?
Examining the stack
To answer question (a) use the backtrace command to examine the run-time stack.
The run-time stack is like a trail of breadcrumbs in a program; each time a function call is
made, a crumb is dropped (an run-time stack frame is pushed). When a return from a
function occurs, the corresponding stack frame is popped and discarded. These stack
frames contain valuable information about the sequence of callers which brought us to the
current line, and what the parameters were for each call.
Gdb assigns numbers to stack frames counting from zero for the innermost (currently
executing) frame. At any time gdb identifies one frame as the “selected” frame. Variable
lookups are done with respect to the selected frame. When the program being debugged
stops (at a breakpoint), gdb selects the innermost frame. The commands below can be
used to select other frames by number or address.
11
backtrace Show stack frames, useful to find the calling
sequence that produced a crash.
frame framenumber Start examining the frame with framenumber. This
does not change the execution context, but allows
to examine variables for a different frame.
down Select and print stack frame called by this one. (The
metaphor here is that the stack grows down with
each function call.)
up Select and print stack frame that called this one.
info args Show the argument variables of current stack
frame.
info locals Show the local variables of current stack frame.
Examining source files
Another way to find our current location in the program and other useful information is to
examine the relevant source files. gdb provides the following commands:
list linenum Print ten lines centered around linenum in current
source file.
list function Print ten lines centered around beginning of
function (or method).
list Print ten more lines.
The list command will show the source lines with the current source line centered in
the range. (Using gdb from within emacs makes these command obsolete since it does
all of the current source stuff for you.)
Examining data
To answeer the question (b) “what are the values of the variables around us?” use the
following commands...
print expression Print value of expression. Expression is any valid C
expression, can include function calls and
arithmetic expressions, all evaluated within current
stack frame.
set variable = expression Assign value of variable to expression. You can
set any variable in the current scope. Variables
which begin with $ can be used as temporary
variables local to gdb.
display expression Print value of expression each time the program
stops. This can be useful to watch the change in a
variable as you step through code.
undisplay Cancels previous display requests.
12
In gdb, there are two different ways of displaying the value of a variable: a snapshot of
the variable’s current value and a persistent display for the entire life of the variable. The
print command will print the current value of a variable, and the display command
will make the debugger print the variable's value on every step for as long as the variable
exists. The desired variable is specified by using C syntax. For example...
print x.y[3]
will print the value of the fourth element of the array field named y of a structure variable
named x. The variables that are accessible are those of the currently selected function's
activation frame, plus all those whose scope is global or static to the current target file.
Both the print and display functions can be used to evaluate arbitrarily complicated
expressions, even those containing, function calls, but be warned that if a function has
side-effects a variety of unpleasant and unexpected situations can arise.
Shortcuts
Finally, there are some things that make using gdb a bit simpler. All of the commands
have short-cuts so that you don’t have to type the whole command name every time you
want to do something simple. A command short-cut is specified by typing just enough of
the command name so that it unambiguously refers to a command, or for the special
commands break, delete, run, continue, step, next and print you need only
use the first letter. Additionally, the last command you entered can be repeated by just
hitting the return key again. This is really useful for single stepping for a range while
watching variables change.
Miscellaneous
editmode mode Set editmode for gdb command line. Supported
values for mode are emacs, vi, dumb.
shell command Execute the rest of the line as a shell command.
history Print command history.
Debugging Strategies
Some people avoid using debuggers because they don't want to learn another tool. This is
a mistake. Invest the time to learn to use a debugger and all its features — it will make
you much more productive in tracking down problems.
Sometimes bugs result in program crashes (a.k.a. “core dumps”, “register dumps”, etc.)
that bring your program to a halt with a message like “Segmentation Violation” or the
like. If your program has such a crash, the debugger will intercept the signal sent by the
processor that indicates the error it found, and allow you to examine the state program.
Thus with almost no extra effort, the debugger can show you the state of the program at
the moment of the crash.
Often, a bug does not crash explicitly, but instead produces symptoms of internal
problems. In such a case, one technique is to put a breakpoint where the program is
misbehaving, and then look up the call stack to get some insight about the data and
control flow path that led to the bad state. Another technique is to set a breakpoint at
some point before the problems start and step forward towards the problems, examining
the state of the program along the way.