 |
Chapter 14. Tools for
Programmers
Many judge a computer system by the tools it offers its
programmers. Unix systems have won the contest by many people's
standards, having developed a very rich set over the years. Leading
the parade is the GNU debugger, gdb. In this
chapter, we take a close look at this invaluable utility and a number
of other auxiliary tools C programmers will find useful.
Even if you are not a programmer, you should consider using the
Revision Control System (RCS). It provides one of the most reassuring
protections a computer user could ask for--backups for everything you
do to a file. If you delete a file by accident, or decide that
everything you did for the past week was a mistake and should be
ripped out, RCS can recover any version you
want. If you are working on a larger project that involves either a
large number of developers or a large number of directories,
Concurrent Versioning System
(CVS) might be more suitable for you. It is
based on RCS, but provides some additional
features.
14.1. Debugging with gdb
Are you one of those programmers who scoff at the very idea
of using a debugger to trace through code? Is it your philosophy that
if the code is too complex for even the programmer to understand,
then the programmer deserves no mercy when it comes to bugs? Do you step through
your code, mentally, using a magnifying glass and a toothpick?
More often than not, are bugs usually caused by a single-character omission,
such as using the = operator when you mean +=?
Then perhaps you should meet gdb--the GNU debugger. Whether or not
you know it, gdb is your friend. It can locate obscure and
difficult-to-find bugs that result in core dumps, memory leaks, and
erratic behavior (both for the program and the programmer). Sometimes
even the most harmless-looking glitches in your code can cause everything
to go haywire, and without the aid of a debugger like gdb, finding
these problems can be nearly impossible--especially for programs longer than
a few hundred lines.
In this section, we'll introduce you to the most useful features of
gdb by way of examples.
There's a book on gdb, too--the Free Software Foundation's
Debugging with GDB.
gdb is capable of either debugging programs as they run, or
examining the cause for a program crash with a core dump. Programs
debugged at runtime with gdb can either be executed from within
gdb itself or can be run separately; that is,
gdb can attach itself
to an already running process to examine it.
First, we'll discuss how to debug programs running within gdb and
then move on to attaching to running processes and examining core dumps.
14.1.1. Tracing a Program
Our first example is a program called trymh that detects edges in
a grayscale image.
trymh takes as input an image file, does some
calculations on the data, and spits out another image file. Unfortunately,
it crashes whenever it is invoked, as so:
papaya$ trymh < image00.pgm > image00.pbm
Segmentation fault (core dumped)
Now, using gdb we could analyze the resulting core file, but
for this example, we'll show how to trace the program as
it runs, instead. [51]
[51]The sample programs in this section are
not programs you're likely to run into anywhere; they were
thrown together by the authors for the purpose of demonstration.
Before we use gdb to trace through the executable trymh,
we need to ensure that the executable has been compiled with debugging
code (see the section "Section 13.1.6, "Enabling Debugging Code""
in Chapter 13, "Programming
Languages").
To do so, we should compile trymh using the -g switch with
gcc.
Note that enabling optimization (-O) with debug code (-g)
is not illegal but not recommended. The problem is that gcc
is too smart for its own good. For example, if you have two identical
lines of code in two different places in a function,
gdb may unexpectedly jump to the second occurrence of the line,
instead of the first, as expected. This is because gcc combined
the two lines into a single line of machine code used in both instances.
Some of the automatic optimizations performed by gcc can be
confusing when using a debugger. To turn off all optimization
(even optimizations performed without specifying -O), use the
-O0 (that's dash-oh-zero) option with gcc.
Now we can fire up gdb to see what the problem might be:
papaya$ gdb trymh
GDB is free software and you are welcome to 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.
GDB 4.16 (i486-unknown-linux --target i486-linux),
Copyright 1996 Free Software Foundation, Inc.
(gdb)
Now gdb is waiting for a command. (The command help
displays information on the available commands.) The first thing we
want to do is start running the program so that we can observe its behavior.
However, if we immediately use the run command, the program
simply executes until it exits or crashes.
First, we need to set a
breakpoint somewhere in the program. A breakpoint is just a location
in the program where gdb should stop and allow us to control
execution of the program.
For the sake of simplicity, let's set a breakpoint on the first line of
actual code, so that the program stops just as it begins to execute.
The list command displays several lines of code (an
amount that is variable) at a time:
(gdb) list
12 main() {
13
14 FloatImage inimage;
15 FloatImage outimage;
16 BinaryImage binimage;
17 int i,j;
18
19 inimage = (FloatImage)imLoadF(IMAGE_FLOAT,stdin);
20 outimage = laplacian_float(inimage);
21
(gdb) break 19
Breakpoint 1 at 0x289c: file trymh.c, line 19.
(gdb)
A breakpoint is now set at line 19 in the current source file.
You can set many breakpoints in the program; breakpoints may be conditional
(that is, triggered only when a certain expression is true), unconditional,
delayed, temporarily disabled, and so on. You may set breakpoints on
a particular line of code, a particular function, a set of functions,
and in a slew of other ways.
You may also set a
watchpoint, using the watch command, which is similar to a
breakpoint but is triggered whenever a certain event takes place--not
necessarily at a specific line of code within the program.
We'll talk more about breakpoints and watchpoints later in the chapter.
Next, we use the run command to start the program running.
run takes as arguments the same arguments you'd give
trymh on the command line; this can include shell
wildcards and
input/output redirection, as the command is passed to
/bin/sh for execution:
(gdb) run < image00.pgm > image00.pfm
Starting program: /amd/dusk/d/mdw/vis/src/trymh < image00.pgm >\
image00.pfm
Breakpoint 1, main () at trymh.c:19
19 inimage = (FloatImage)imLoadF(IMAGE_FLOAT,stdin);
(gdb)
As expected, the breakpoint is reached immediately at the first line
of code. We can now take over.
The most useful program-stepping commands are next and step.
Both commands execute the next line of code in the program, except
that step descends into any function calls in the program,
and next steps only to the next line of code in the same
function. next quietly executes any function calls that
it steps over but does not descend in their code for us to examine.
imLoadF is a function that loads an image from a disk file. We
know this function is not at fault (you'll have to trust us on that
one), so we wish to step over it using the next command:
(gdb) next
20 outimage = laplacian_float(inimage);
(gdb)
Here, we are interested in tracing into the suspicious-looking
laplacian_float function, so we use the step command:
(gdb) step
laplacian_float (fim=0x0) at laplacian.c:21
21 i = 20.0;
(gdb)
Let's use the list command to get some idea of where we are:
(gdb) list
16 FloatImage laplacian_float(FloatImage fim) {
17
18 FloatImage mask;
19 float i;
20
21 i = 20.0;
22 mask=(FloatImage)imNew(IMAGE_FLOAT,3,3);
23 imRef(mask,0,0) = imRef(mask,2,0) = imRef(mask,0,2) = 1.0;
24 imRef(mask,2,2) = 1.0; imRef(mask,1,0) = imRef(mask,0,1) = i/5;
25 imRef(mask,2,1) = imRef(mask,1,2) = i/5; imRef(mask,1,1) = -i;
(gdb) list
26
27 return convolveFloatWithFloat(fim,mask);
28 }
(gdb)
As you can see, using list multiple times just
displays more of the code. Because we don't want to
step manually through this code, and we're not
interested in the imNew function on line 22, let's
continue execution until line 27. For this, we use the
until command:
(gdb) until 27
laplacian_float (fim=0x0) at laplacian.c:27
27 return convolveFloatWithFloat(fim,mask);
(gdb)
Before we step into the convolveFloatWithFloat
function, let's be sure the two parameters, fim and
mask, are valid. The print
command examines the value of a variable:
(gdb) print mask
$1 = (struct {...} *) 0xe838
(gdb) print fim
$2 = (struct {...} *) 0x0
(gdb)
mask looks fine, but fim, the
input image, is null. Obviously,
laplacian_float was passed a null pointer
instead of a valid image. If you have been paying close attention, you
noticed this as we entered
laplacian_ float earlier.
Instead of stepping deeper into the program (as it's apparent that
something has already gone wrong), let's continue execution until the
current function returns. The finish command
accomplishes this:
(gdb) finish
Run till exit from #0 laplacian_float (fim=0x0) at laplacian.c:27
0x28c0 in main () at trymh.c:20
20 outimage = laplacian_float(inimage);
Value returned is $3 = (struct {...} *) 0x0
(gdb)
Now we're back in main. To determine the source
of the problem, let's examine the values of some variables:
(gdb) list
15 FloatImage outimage;
16 BinaryImage binimage;
17 int i,j;
18
19 inimage = (FloatImage)imLoadF(IMAGE_FLOAT,stdin);
20 outimage = laplacian_float(inimage);
21
22 binimage = marr_hildreth(outimage);
23 if (binimage == NULL) {
24 fprintf(stderr,"trymh: binimage returned NULL\n");
(gdb) print inimage
$6 = (struct {...} *) 0x0
(gdb)
The variable inimage, containing the input image
returned from imLoadF, is null. Passing a null
pointer into the image-manipulation routines certainly would cause a
core dump in this case. However, we know
imLoadF to be tried and true because it's in a
well-tested library, so what's the
problem?
As it turns out, our library function imLoadF returns NULL
on failure--if the input format is bad, for example. Because we never
checked the return
value of imLoadF before passing it along to laplacian_float,
the program goes haywire when inimage is assigned NULL.
To correct the problem, we simply insert code to cause the problem to
exit with an error message if imLoadF returns a null pointer.
To quit gdb, just use the command quit. Unless the program
has finished execution, gdb will complain that the program is still
running:
(gdb) quit
The program is running. Quit anyway (and kill it)? (y or n) y
papaya$
In the following sections
we examine some specific features provided by the debugger, given the
general picture just presented.
14.1.2. Examining a Core File
Do you hate it when a program crashes and spites you again by leaving
a 10 MB core file in your working directory, wasting much-needed
space? Don't be so quick to delete that core file; it can be very
helpful. A core file is just a dump of the memory image of a process
at the time of the crash. You can use the core file with
gdb to examine the state of your program (such as
the values of variables and data) and determine the cause for failure.
The core file is written to disk by the operating system whenever
certain failures occur. The most frequent reason for a crash and the
subsequent core dump is a memory violation--that is, trying to
read or write memory that your program does not have access to. For
example, attempting to write data into a null pointer can cause a
segmentation fault, which is essentially a fancy way to say, "you screwed up." Other errors that result in core
files are so-called "bus errors" and "floating-point
exceptions." Segmentation faults are a common error and occur
when you try to access (read from or write to) a memory address that
does not belong to your process's address space. This includes the
address 0, as often happens with uninitialized pointers. Bus errors
result in using incorrectly aligned data and are therefore rare on the
Intel architecture, which does not pose strong alignment conditions
like other architectures, such as SPARC. Floating-point exceptions
point to a severe
problem in a floating-point calculation like an overflow, but the most
usual case is a division by zero.
However, not all such memory errors will cause immediate crashes. For
example, you may overwrite memory in some way, but the program
continues to run, not knowing the difference between actual data and
instructions or garbage. Subtle memory violations can cause programs
to behave erratically. One of the authors once witnessed a bug that caused the
program to jump randomly around but without tracing it with
gdb, it still appeared to work normally. The only
evidence of a bug was that the program returned output that meant,
roughly, that two and two did not add up to four. Sure enough, the
bug was an attempt to write one too many characters into a block of
allocated memory. That single-byte error caused hours of grief.
You can prevent these kinds of memory problems (even the best
programmers make these mistakes!) using the Checker package, a set of
memory-management routines that replaces the commonly used
malloc() and
free() functions. We'll talk about
Checker in the section "Section 14.2.5, "Using Checker"."
However, if your program does cause a memory fault, it will crash and
dump core. Under Linux, core files are named, appropriately,
core. The core file appears
in the current working directory of the running process, which is
usually the working directory of the shell that started the program,
but on occasion, programs may change their own working directory.
Some shells provide facilities for controlling whether core files
are written. Under bash, for example, the default behavior is
not to write core files at all. In order to enable core file output, you
should use the command:
ulimit -c unlimited
probably in your .bashrc initialization file. You can specify a
maximum size for core files other than unlimited, but truncated
core files may not be of use when debugging applications.
Also, in order for a core file to be useful, the program must be
compiled with debugging code enabled, as described in the previous section.
Most binaries on your system will not contain debugging code, so the
core file will be of limited value.
Our example for using gdb with a core file is yet another
mythical program called cross. Like trymh in the previous
section, cross takes as input an image file, does some calculations
on it, and outputs another image file. However, when running cross,
we get a segmentation fault:
papaya$ cross < image30.pfm > image30.pbm
Segmentation fault (core dumped)
papaya$
To invoke gdb for use with a core file, you must specify not only the
core filename but also the name of the executable that goes along with
that core file. This is because the core file itself does not contain all
the information necessary for debugging:
papaya$ gdb cross core
GDB is free software and you are welcome to 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.
GDB 4.8, Copyright 1993 Free Software Foundation, Inc...
Core was generated by `cross'.
Program terminated with signal 11, Segmentation fault.
#0 0x2494 in crossings (image=0xc7c8) at cross.c:31
31 if ((image[i][j] >= 0) &&
(gdb)
gdb tells us that the core file terminated with signal 11. A
signal is a kind of message that is sent to a running program
from the kernel, the user, or the program itself.
Signals are generally used to terminate a program (and possibly cause it
to dump core). For example, when you type the interrupt character,
a signal is sent to the running program,
which will probably kill the program.
In this case, signal 11 was sent to the running cross process by
the kernel when cross attempted to read or write to memory that it
did not have access to. This signal caused cross to die and dump
core. gdb says that the illegal memory reference occurred on line
31 of the source file cross.c:
(gdb) list
26 xmax = imGetWidth(image)-1;
27 ymax = imGetHeight(image)-1;
28
29 for (j=1; j<xmax; j++) {
30 for (i=1; i<ymax; i++) {
31 if ((image[i][j] >= 0) &&
32 (image[i-1][j-1] < 0) ||
33 (image[i-1][j] < 0) ||
34 (image[i-1][j+1] < 0) ||
35 (image[i][j-1] < 0) ||
(gdb)
Here, we see several things. First of all, there is a loop across
the two index variables i and j, presumably in order to
do calculations on the input image. Line 31 is an attempt to reference
data from image[i][j], a two-dimensional array. When a program
dumps core while attempting to access data from an array, it's usually
a sign that one of the indices is out of bounds. Let's check them:
(gdb) print i
$1 = 1
(gdb) print j
$2 = 1194
(gdb) print xmax
$3 = 1551
(gdb) print ymax
$4 = 1194
(gdb)
Here we see the problem. The program was attempting to reference
element image[1][1194], however, the array extends only to
image[1550][1193] (remember that arrays in C are indexed
from 0 to max-1). In other words, we attempted to read the
1195th row of an image that only has 1194 rows.
If we look at lines 29 and 30, we see the problem: the
values xmax and ymax are reversed. The variable
j should range from 1 to ymax (because it is the
row index of the array), and i should range from 1 to xmax.
Fixing the two for loops on lines 29 and 30 corrects the problem.
Let's say that your program is crashing within a function that is
called from many different locations, and you want to determine where
the function was invoked from and what situation led up to the crash.
The backtrace command displays the call
stack of the program at the time of failure. If you are
like me and are too lazy to type backtrace all the
time, you will be delighted to hear that you can also use the shortcut
bt.
The call stack is the list of functions that led up to the current
one. For example, if the program starts in function main,
which calls function foo, which calls bamf, the call
stack looks like:
(gdb) backtrace
#0 0x1384 in bamf () at goop.c:31
#1 0x4280 in foo () at goop.c:48
#2 0x218 in main () at goop.c:116
(gdb)
As each function is called, it pushes certain data onto the stack, such
as saved registers, function arguments, local variables, and so forth.
Each function has a certain amount of space allocated on the stack for
its use. The chunk of memory on the stack for a particular function is
called a stack frame, and the call stack is the ordered
list of stack frames.
In the following example, we are looking at
a core file for an X-based animation program. Using backtrace
gives us:
(gdb) backtrace
#0 0x602b4982 in _end ()
#1 0xbffff934 in _end ()
#2 0x13c6 in stream_drawimage (wgt=0x38330000, sn=4)\
at stream_display.c:94
#3 0x1497 in stream_refresh_all () at stream_display.c:116
#4 0x49c in control_update_all () at control_init.c:73
#5 0x224 in play_timeout (Cannot access memory at address 0x602b7676.
(gdb)
This is a list of stack frames for the process. The most recently called
function is frame 0, which is the "function" _end in this
case. Here, we see that play_timeout called
control_update_all, which called stream_refresh_all, and
so on. Somehow, the program jumped to _end where it crashed.
However, _end is not a function; it is simply a label that
specifies the end of the process data segment. When a program branches
to an address such as _end, which is not a real function, it is
a sign that something must have caused the process to go haywire,
corrupting the call stack. (This is known in hacker jargon as "jumping
to hyperspace.") In fact, the error "Cannot access memory at
address 0x602b7676" is another indication that something bizarre has
occurred.
We can see, however, that the last "real" function called
was stream_drawimage, and we might guess that it is the source of
the problem. To examine the state of stream_drawimage, we need
to select its stack frame (frame number 2), using the frame command:
(gdb) frame 2
#2 0x13c6 in stream_drawimage (wgt=0x38330000, sn=4)\
at stream_display.c:94
94 XCopyArea(mydisplay,streams[sn].frames[currentframe],\
XtWindow(wgt),
(gdb) list
91
92 printf("CopyArea frame %d, sn %d, wid %d\n",currentframe,sn,wgt);
93
94 XCopyArea(mydisplay,streams[sn].frames[currentframe],\
XtWindow(wgt),
95 picGC,0,0,streams[sn].width,streams[sn].height,0,0);
(gdb)
Well, not knowing anything else about the program at hand, we can't
see anything
wrong here, unless the variable sn (being used as an index into
the array streams) is out of range. From the output of
frame, we see that
stream_drawimage was called with an sn parameter of 4.
(Function parameters are displayed in the output of backtrace,
as well as whenever we change frames.)
Let's move up another frame, to stream_refresh_all, to see how
stream_display was called. To do this, we use the up
command, which selects the stack frame above the current one:
(gdb) up
#3 0x1497 in stream_refresh_all () at stream_display.c:116
116 stream_drawimage(streams[i].drawbox,i);
(gdb) list
113 void stream_refresh_all(void) {
114 int i;
115 for (i=0; i<=numstreams; i++) {
116 stream_drawimage(streams[i].drawbox,i);
117
(gdb) print i
$2 = 4
(gdb) print numstreams
$3 = 4
(gdb)
Here, we see that the index variable i is looping from 0 to
numstreams, and indeed i here is 4, the second
parameter to stream_drawimage. However, numstreams
is also 4. What's going on?
The for loop on line 115 looks funny; it should read:
for (i=0; i<numstreams; i++) {
The error is in the use of the <= comparison operator. The
streams array is indexed from 0 to numstreams- 1, not
from 0 to numstreams. This simple off-by-one error caused the
program to go berserk.
As you can see, using gdb with a core dump allows you to browse
through the image of a crashed program to find bugs. Never again will
you delete those pesky core files, right?
14.1.3. Debugging a Running Program
gdb can also debug a program that is already
running, allowing you to interrupt it, examine it, and then return the
process to its regularly scheduled execution. This is very similar to
running a program from within gdb, and there are
only a few new commands to learn.
The attach command attaches gdb
to a running process. In order to use attach you
must also have access to the executable that corresponds to the
process.
For example, if you have started the program
pgmseq with process ID 254, you can start up
gdb with:
papaya$ gdb pgmseq
and once inside gdb, use the command:
(gdb) attach 254
Attaching program `/home/loomer/mdw/pgmseq/pgmseq', pid 254
_ _select (nd=4, in=0xbffff96c, out=0xbffff94c, ex=0xbffff92c, tv=0x0)
at _ _select.c:22
_ _select.c:22: No such file or directory.
(gdb)
The No such file or directory error is given
because gdb can't locate the source file for
_ _select. This is often the case with
system calls and library functions, and it's nothing to worry about.
You can also start gdb with the command:
papaya$ gdb pgmseq 254
Once gdb attaches to the running process, it
temporarily suspends the program and lets you take over, issuing
gdb commands. Or you can set a breakpoint or
watchpoint (with the break and
watch commands) and use
continue to cause the program to continue execution
until the breakpoint is triggered.
The
detach command detaches gdb from
the running process. You can then use attach
again, on another process, if necessary. If you find a bug, you can
detach the current process, make changes to the
source, recompile, and use the file command to load
the new executable into gdb. You can then start the
new version of the program and use the attach
command to debug it. All without leaving
gdb!
In fact, gdb allows you to debug three programs
concurrently: one running directly under gdb, one
tracing with a core file, and one running as an independent
process. The target command allows you to select
which one you wish to debug.
14.1.4. Changing and Examining Data
To examine the values of variables in your program, you can use the
print, x, and
ptype commands. The print
command is the most commonly used data inspection command; it
takes as
an argument an expression in the source language (usually C or C++)
and returns its value. For example:
(gdb) print mydisplay
$10 = (struct _XDisplay *) 0x9c800
(gdb)
This displays the value of the variable mydisplay, as well as
an indication of its type. Because this variable is a pointer, you can
examine its contents by dereferencing the pointer, as you would in C:
(gdb) print *mydisplay
$11 = {ext_data = 0x0, free_funcs = 0x99c20, fd = 5, lock = 0,
proto_major_version = 11, proto_minor_version = 0,
vendor = 0x9dff0 "XFree86", resource_base = 41943040,
error_vec = 0x0, cms = {defaultCCCs = 0xa3d80 "",\
clientCmaps = 0x991a0 "'",
perVisualIntensityMaps = 0x0}, conn_checker = 0, im_filters = 0x0}
(gdb)
mydisplay is an extensive structure used by X
programs; we have
abbreviated the output for your reading enjoyment.
print can print the value of just about any expression, including
C function calls (which it executes on the fly, within
the context of the running program):
(gdb) print getpid()
$11 = 138
(gdb)
Of course, not all functions may be called in this manner. Only those
functions that have been linked to the running program may be called.
If a function has not been linked to the program and you attempt
to call it, gdb will complain that there is no such symbol in the
current context.
More complicated expressions may be used as arguments to print as
well, including assignments to variables. For example:
(gdb) print mydisplay->vendor = "Linux"
$19 = 0x9de70 "Linux"
(gdb)
assigns the value of the vendor member of the mydisplay
structure the value "Linux" instead of "XFree86" (a useless
modification, but interesting nonetheless). In this way, you can
interactively change data in a running program to correct errant
behavior or test uncommon situations.
Note that after each print command, the value displayed is
assigned to one of the gdb convenience registers, which are
gdb internal variables that may be handy for you to use.
For example, to recall the value of mydisplay in
the previous example, we
need to merely print the value of $10:
(gdb) print $10
$21 = (struct _XDisplay *) 0x9c800
(gdb)
You may also use expressions, such as typecasts, with the
print command. Almost anything goes.
The ptype command gives you detailed (and often long-winded)
information about a variable's type or the definition of a struct or
typedef. To get a full definition for the struct _XDisplay
used by the mydisplay variable, we use:
(gdb) ptype mydisplay
type = struct _XDisplay {
struct _XExtData *ext_data;
struct _XFreeFuncs *free_funcs;
int fd;
int lock;
int proto_major_version;
struct _XIMFilter *im_filters;
} *
(gdb)
If you're interested in examining memory on a more fundamental level,
beyond the petty confines of defined types, you can use the x
command. x takes a memory address as an argument. If
you give it a variable, it uses the value of that variable
as the address.
x also takes a count and a type specification as an optional
argument. The count is the number of objects of the given type
to display. For example, x/100x 0x4200 displays 100 bytes of
data, represented in hexadecimal format, at the address 0x4200.
Use help x to get a description of the various output formats.
To examine the value of mydisplay->vendor, we
can use:
(gdb) x mydisplay->vendor
0x9de70 <_end+35376>: 76 'L'
(gdb) x/6c mydisplay->vendor
0x9de70 <_end+35376>: 76 'L' 105 'i' 110 'n' 117 'u' 120 'x' 0 '\000'
(gdb) x/s mydisplay->vendor
0x9de70 <_end+35376>: "Linux"
(gdb)
The first field of each line gives the absolute address of the data.
The second represents the address as some symbol (in this case, _end)
plus an offset in bytes. The remaining fields give the actual value of
memory at that address, first in decimal, then as an ASCII character.
As described earlier you can force x to print the data in other formats.
14.1.5. Getting Information
The info command provides information about the status of the
program being debugged. There are many subcommands under info;
use help info to see them all. For example,
info program
displays the execution status of the program:
(gdb) info program
Using the running image of child process 138.
Program stopped at 0x9e.
It stopped at breakpoint 1.
(gdb)
Another useful command is info locals, which
displays the names and values of all local variables in the
current function:
(gdb) info locals
inimage = (struct {...} *) 0x2000
outimage = (struct {...} *) 0x8000
(gdb)
This is a rather cursory description of the variables. The print
or x commands describe them further.
Similarly, info variables displays a list of all known
variables in the program, ordered by source file. Note that many of
the variables displayed will be from sources outside of your actual
program--for example, the names of variables used within the library
code. The values for these variables are not displayed,
because the list is culled more or less directly from the executable's
symbol table. Only those local variables in the current stack frame
and global (static) variables are actually accessible from gdb.
info address gives you information about exactly where
a certain variable is stored. For example:
(gdb) info address inimage
Symbol "inimage" is a local variable at frame offset -20.
(gdb)
By frame offset, gdb means that
inimage is stored
20 bytes below the top of the stack frame.
You can get information on the current frame using the info frame
command, as so:
(gdb) info frame
Stack level 0, frame at 0xbffffaa8:
eip = 0x9e in main (main.c:44); saved eip 0x34
source language c.
Arglist at 0xbffffaa8, args: argc=1, argv=0xbffffabc
Locals at 0xbffffaa8, Previous frame's sp is 0x0
Saved registers:
ebx at 0xbffffaa0, ebp at 0xbffffaa8, esi at 0xbffffaa4, eip at\
0xbffffaac
(gdb)
This kind of information is useful if you're debugging at the
assembly-language level with the disass,
nexti, and stepi commands (see
the section "Section 14.1.6.2, "Instruction-level debugging"").
14.1.6. Miscellaneous Features
We have barely scratched the surface about what gdb
can do. It is an amazing program with a lot of power; we have
introduced
you only to the most commonly used commands. In this section, we'll
look at other features of gdb and then send you on
your way.
If you're interested in learning more about gdb, we
encourage you to read the gdb manual page and the
Free Software Foundation manual. The manual is also available as an
online Info file. (Info files
may be read under Emacs, or using the info reader; see
the section "Section 9.2.3, "Tutorial and Online Help"" in
Chapter 9, "Editors, Text Tools,
Graphics,
and Printing", for details.)
14.1.6.1. Breakpoints and watchpoints
As promised, we're going to demonstrate further use of breakpoints and
watchpoints. Breakpoints are set with the break command;
similarly, watchpoints are set with the watch command. The only
difference between the two is that breakpoints must break at a particular
location in the program--on a certain line of code, for example--and
watchpoints may be triggered whenever a certain expression is true,
regardless of location within the program. Though powerful, watchpoints can
be horribly inefficient; any time the state of the program changes, all
watchpoints must be reevaluated.
When a breakpoint or watchpoint is triggered, gdb suspends the
program and returns control to you. Breakpoints and watchpoints allow you to run the program (using the run and continue
commands)
and stop only in certain situations, thus saving you the trouble of using
many next and step commands to walk through the program
manually.
There are many ways to set a breakpoint in the program. You can
specify a line number, as in break 20. Or, you can
specify a particular function, as in break
stream_unload. You can also specify a line number in another
source file, as in break foo.c:38. Use
help break to see the complete syntax.
Breakpoints may be conditional; that is, the breakpoint
triggers only when a certain expression is true. For example, using the
command:
break 184 if (status == 0)
sets a conditional breakpoint at line 184 in the current source file,
which triggers only when the variable status is
zero. The variable status must be either a global
variable or a local variable in the current stack frame. The
expression may be any valid expression in the source language that
gdb understands, identical to the expressions used
by the print command. You can change the
breakpoint condition (if it is conditional) using the
condition command.
Using the command info break gives you a list of all breakpoints
and watchpoints and their status. This allows you to delete or
disable breakpoints, using the commands clear, delete, or
disable. A disabled breakpoint is merely inactive, until you reenable it
(with the enable command); on the other hand, a breakpoint that
has been deleted is gone from the list of breakpoints for good. You can
also specify that a breakpoint be enabled once; meaning that once it is
triggered, it will be disabled again--or enabled once and then deleted.
To set a watchpoint, use the watch command, as in:
watch (numticks < 1024 && incoming != clear)
Watchpoint conditions may be any valid source expression, as with
conditional breakpoints.
14.1.6.2. Instruction-level debugging
gdb is capable of debugging on the processor-instruction level,
allowing you to watch the innards of your program with great scrutiny.
However, understanding what you see requires not only knowledge of
the processor architecture and assembly language, but also some
gist of how the operating system sets up process address space. For example,
it helps to understand the conventions used for setting up stack frames,
calling functions, passing parameters and return values, and so on.
Any book on protected-mode 80386/80486 programming can fill you in on
these details. But be warned: protected-mode programming on this processor
is quite different from real-mode programming (as is used in the MS-DOS
world). Be sure that you're reading about native protected-mode
386 programming, or else you might subject yourself to terminal confusion.
The primary gdb commands used for instruction-level debugging
are nexti, stepi, and disass. nexti is
equivalent to next, except that it steps to the next instruction,
not the next source line. Similarly, stepi is the instruction-level
analogue of step.
The disass command displays a disassembly of an address range that
you supply. This address range may be specified by literal address
or function name. For example, to display a disassembly of the function
play_timeout, use the command:
(gdb) disass play_timeout
Dump of assembler code for function play_timeout:
to 0x2ac:
0x21c <play_timeout>: pushl %ebp
0x21d <play_timeout+1>: movl %esp,%ebp
0x21f <play_timeout+3>: call 0x494 <control_update_all>
0x224 <play_timeout+8>: movl 0x952f4,%eax
0x229 <play_timeout+13>: decl %eax
0x22a <play_timeout+14>: cmpl %eax,0x9530c
0x230 <play_timeout+20>: jne 0x24c <play_timeout+48>
0x232 <play_timeout+22>: jmp 0x29c <play_timeout+128>
0x234 <play_timeout+24>: nop
0x235 <play_timeout+25>: nop
0x2a8 <play_timeout+140>: addb %al,(%eax)
0x2aa <play_timeout+142>: addb %al,(%eax)
(gdb)
This is equivalent to using the command disass
0x21c (where 0x21c is the literal address
of the beginning of play_timeout).
You can specify an optional second argument to disass, which will be
used as the address where disassembly should stop. Using
disass 0x21c 0x232 will only display the first seven lines of the
assembly listing in the previous example (the instruction starting with 0x232 itself
will not be displayed).
If you use nexti and stepi
often, you may wish to use the command:
display/i $pc
This causes the current instruction to be displayed after every
nexti or stepi command. display
specifies variables to watch or commands to execute after every stepping
command. $pc is a gdb internal register that corresponds
to the processor's program counter, pointing to the current instruction.
14.1.6.3. Using Emacs with gdb
Emacs (described in the section "Section 9.2, "The Emacs Editor"" in
Chapter 9, "Editors, Text Tools,
Graphics,
and Printing") provides a debugging mode that lets you run
gdb--or another debugger--within the integrated
program-tracing environment provided by
Emacs. This so-called "Grand Unified Debugger" library is very
powerful and allows
you to debug and edit your programs entirely within Emacs.
To start gdb under Emacs, use the Emacs command M-x gdb
and give the name of the executable to debug as the argument. A buffer
will be created for gdb, which is similar to using
gdb alone. You can then use core-file to load a core file
or attach to attach to a running process, if you wish.
Whenever you step to a new frame (e.g., when you first
trigger a breakpoint), gdb opens a separate window that
displays the source corresponding to the current stack frame. This
buffer may be used to edit the source text just as you normally would
with Emacs, but the current source line is highlighted with an arrow
(the characters =>). This allows you to watch the source in one
window, and execute gdb commands in the other.
Within the debugging window, there are several special key sequences
that can be used. They are fairly long, though, so it's not
clear that you'll find them more convenient than just entering
gdb commands directly. Some of the more common commands
include:
- C-x C-a C-s
The equivalent of a gdb step
command, updating the source window appropriately
- C-x C-a C-i
The equivalent of a stepi command
- C-x C-a C-n
The equivalent of a next command
- C-x C-a C-r
The equivalent of a continue command
- C-x C-a <
The equivalent of an up command
- C-x C-a >
The equivalent of a down command
If you do type in commands in the traditional manner, you can use
M-p to move backwards to previously issued commands and M-n to
move forward. You can also move around in the buffer using Emacs
commands for searching, cursor movement, and so on. All in all, using
gdb within Emacs is more convenient than using it from
the shell.
In addition, you may edit the source text in the gdb source buffer;
the prefix arrow will not be present in the source when it is saved.
Emacs is very easy to customize, and there are many extensions to this
gdb interface that you could write yourself. You could
define Emacs keys for other commonly used gdb commands or change
the behavior of the source window. (For example, you could highlight all
breakpoints in some fashion or provide keys to
disable or clear breakpoints.)
 |  |  | | 13.7. Other Languages |  | 14.2. Programming Tools |
Copyright © 2001 O'Reilly & Associates. All rights reserved.
|
 |
|