Running Linux, 5th Edition - Matthias Kalle Dalheimer [392]
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.[*]
Before we use gdb to trace through the executable trymh, we need to ensure that the executable has been compiled with debugging code (see "Enabling Debugging Code," earlier in this chapter). To do so, we should compile trymh using the -g switch with gcc.
Note that enabling optimization (-O) with debug code (-g) is legal but discouraged. 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
GNU gdb 6.3
Copyright 2004 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 "i586-suse-linux".
(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, or 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 running the program. run takes as arguments the same arguments you'd give trymh on the command line; these 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 directly to the next line of code in the same function. next quietly executes any