CSE 303, Autumn 2004 - gdb Exercises

Due: N/A

gdb is the GNU Debugger, and can be a useful tool for analyzing problems with your code. This page gives a simple set of exercises to help you get acquianted with the debugger and it's functionality. For more information consult the man page ('man gdb'), or view the online manual.

Setup

Login to attu. (The behavior of the (buggy) program used as an example can be slightly different under cygwin, and might not match what follows. gdb behaves mostly the same under the two systems, though. )

Create a directory, copy the test program into it, and cd into that directory:

$ mkdir ~/gdbTest
$ cd !$
$ cp /cse/courses/cse303/04au/gdbExample.c .
It appears that the default behavior on attu is to not create core dump files. Since we want them, turn them on:
$ ulimit -c unlimited

A First Pass

Take a brief look at the source file - it's a simple C program. Now, try executing the following commands:

$ gcc gdbExample.c
$ ./a.out
Segmentation fault (core dumped)

The program crashes. This is probably not a good sign - let's try taking a closer look at it:

$ gdb a.out
(gdb) core core.<tab>enter>

gdb loads up the core file, which is actually the state of the memory of our program at the time it crashed. We can ask gdb to tell us what functions were being called at the time of the crash using 'bt'

(gdb) bt
#0  0x080483cf in main ()

It looks like we crashed in main() - lets try to see where exactly this happened:

(gdb) list
No symbol table is loaded.  Use the "file" command.

This isn't very useful information. Why is gdb being so recalcitrant? We address that next. For now, just quit gdb and clean up:

(gdb) q
$ rm core.*

Enabling Debugging Information

gdb wasn't very cooperative so far because it doesn't have enough information - it doesn't have a way to relate the executable code (a.out) to the source that produced it (gdbExample.c). It turns out the way to relate those two is to embed debugging information in the executable. That's done using the -g switch when compiling:

$ gcc -ggdb gdbExample.c
$ ./a.out
Segmentation fault(core dumped)
$ gdb a.out
(gdb) core core.<tab><tab><enter>
   [elided lines...]
#0  0x080483cf in main (argc=1008, argv=0x3f1) at gdbExample.c:9
9                       x[i] = i;

Now this is better - gdb has actually located the exact line in our source file which caused the crash! What can we do with this? Let's see what the variables look like:

(gdb) print i
$1 = 1544
(gdb) print x[10]
$2 = 10
(gdb) print x[i]
Cannot access memory at address 0xc0000000
(We're not sure what that means, but it can't be good.)

What did the code nearby look like?

(gdb) list
4       int main(int argc, char* argv[]) {
5               int i, j, k;
6               int x[1000];
7
8               for(i = 0; i < 10000; ++i){
9                       x[i] = i;
10              }
11
12              printf("Enter integer: ");
13              scanf("%d", k);

Oops. The array x is only 1/10th the size we meant it to be. Fix it (by correcting its size to 10000). (Exit gdb.)

Executing Inside gdb

We're going to compile and run the corrected program, but this time we're going to run inside of gdb:

$ gcc -ggdb gdbExample.c
$ gdb a.out
   [elided lines...]

Now set a breakpoint, and start execution:

(gdb) break gdbExample.c:8
Breakpoint 1 at 0x80483b7: file gdbExample.c, line 8.
(gdb) run
Starting program: /homes/iws/zahorjan/test/a.out
Breakpoint 1, main (argc=1, argv=0xbfffdea4) at gdbExample.c:8
8               for(i = 0; i < 10000; ++i){
Execution has stopped at the line shown. We can single step:
(gdb) step
9                       x[i] = i;
(gdb) step
8               for(i = 0; i < 10000; ++i){
(gdb) step
9                       x[i] = i;
(gdb) step
8               for(i = 0; i < 10000; ++i){

Since this will take a while with a loop running 10000 times, let's continue on:

(gdb) break gdbExample.c:12
Breakpoint 2 at 0x80483dd: file gdbExample.c, line 12.
(gdb) continue
Continuing.

Breakpoint 2, main (argc=1, argv=0xbfffdea4) at gdbExample.c:12
12              printf("Enter integer: ");
Okay, you have the idea of how gdb works. Let's just continue program execution:
(gdb) continue
Continuing.
Enter integer in 0..9999: 9998

Program received signal SIGSEGV, Segmentation fault.
0x0804842d in tester (c=0xbfff59c0, k=1187688412) at gdbExample.c:19
19              printf("x[%d] = %d\n", k, c[k]);
(gdb) bt
#0  0x0804842d in tester (c=0xbfff59c0, k=1187688412) at gdbExample.c:19
#1  0x08048412 in main (argc=1, argv=0xbffff6a4) at gdbExample.c:15

Well, there's yet another bug. No problem, though, at this point you know enough about gdb to find it easily.

Exercises (don't turn these in)