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.
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)
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.
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?