Basics
In the following sample sessions, user input is red, comments are green,
while computer output is blue. The
sessions will use the program hello.c.
In order to use source-level debugging, your C program must be compiled
with the -g flag. This will instruct the compiler and linker to
leave symbol table information in the executable, which the debugger can
then use to associate addresses with source code lines and C data
structures.
The easiest way to do this is by setting the CFLAGS
environment. I highly recommend to put the line
export CFLAGS="-g -Wall"
into your .profile. There is, in general, no reason not
to use these flags.
You can then compile and run your programs normally:
% make hello
cc -Wall -g hello.c -o hello
hello.c:19: warning: return type of `main' is not `int'
% ./hello
In main
Entering divide
zsh: floating point exception ./hello
%
Post-Mortem Debugging
You can use the debugger either post-mortem, i.e., after a
program has crashed, or at run time. A program that crashes will produce
a core file, which is simply the process image written to a
file. The debugger can examine this image. Example:
% gdb hello core
GNU gdb 19990928
Copyright 1998 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 "i686-pc-linux-gnu"...
Core was generated by `./hello'.
Program terminated with signal 8, Floating point exception.
Reading symbols from /lib/libc.so.6...done.
Reading symbols from /lib/ld-linux.so.2...done.
#0 0x804843c in divide (i=1, j=0) at hello.c:8
8 k = i/j;
(gdb) p i print variable i
$1 = 1
(gdb) p j
$2 = 0
(gdb) bt "back trace"
(show execution stack)
#0 0x804843c in divide (i=1, j=0) at hello.c:8
#1 0x80484c9 in main () at hello.c:32
(gdb)
... and now we know what happened. What we do not necessarily know is
how this could happen. This is where run-time debugging is handy, as you
can trace the execution of the program.
Run-Time Debugging
Gdb allows you to turn a post-mortem debugging session into a
run-time debugging session, you can simply use the r ("run")
command. Alternatively, you can start up the debugger without requiring
a core file:
% gdb hello
GNU gdb 19990928
Copyright 1998 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 "i686-pc-linux-gnu"...
(gdb)
Here we can set breakpoints using the b command, where
execution will stop and control is given back to the
debugger. Breakpoints can be set on functions (stop at the entry to the
function), on function exit, or on particular line numbers.
We can use the commands n ("next") and s ("step")
to step through the program. The difference between the two is that
n will step over function calls, while s will
step into functions. The c ("continue") command simply
continues normal execution of the program, until it hits the next
breakpoint, or causes an exception.
The p command can show arbitrary C data structures.
Example:
(gdb) b main
Breakpoint 1 at 0x804846a: file hello.c, line 24.
(gdb) r
Starting program: /export/zuse/2/gernot/tmp/hello
Breakpoint 1, main () at hello.c:24
24 printf ("In main\n");
(gdb) n
In main
25 r.count = 5;
(gdb) p r
$1 = {count = 2147477592, next = 0x2aae2e68} r
isn't inilialised yet...
(gdb) n
26 p = malloc(sizeof(struct rec));
(gdb) p r
$2 = {count = 5, next = 0x2aae2e68}
(gdb) p p
$3 = (struct rec *) 0x8049658 neither is p
(gdb) n
27 p->count = 10;
(gdb) p p
$4 = (struct rec *) 0x8049678
(gdb) p *p
$5 = {count = 0, next = 0x0} now p is, but *p isn't
(gdb) n
28 p->next = &r;
(gdb) p *p
$6 = {count = 10, next = 0x0} still the same
(gdb) n
29 r.next = p;
(gdb) p *p
$7 = {count = 10, next = 0x7fffe838}
(gdb) p *p->next
$8 = {count = 5, next = 0x2aae2e68}
(gdb) p *p->next->next
$9 = {count = -762985847, next = 0x2c71274}
(gdb) n
30 i = 1;
(gdb) p *p->next->next
$10 = {count = 10, next = 0x7fffe838}
(gdb) n
31 j = 0;
(gdb) n
32 k = divide (i, j); /* Ouch! */
(gdb) s
divide (i=1, j=0) at hello.c:7
7 printf ("Entering divide\n");
(gdb) bt
#0 divide (i=1, j=0) at hello.c:7
#1 0x80484c9 in main () at hello.c:32
(gdb) n
Entering divide
8 k = i/j;
(gdb) n
Program received signal SIGFPE, Arithmetic exception.
0x804843c in divide (i=1, j=0) at hello.c:8
8 k = i/j;
(gdb) bt
#0 0x804843c in divide (i=1, j=0) at hello.c:8
#1 0x80484c9 in main () at hello.c:32
(gdb) ^DThe program is running. Exit anyway? (y or n) y
%
What next?
That should give you the basic idea. More details can be found from
man gdb
Also, ddd
gives you an nice point-and-click interface to gdb.
|