% mkdir gdb-stuff % cd gdb-stuff % cp /u/cs217/examples/gdb/* .Then, run emacs. This pops up a new window on your screen.
Type M-x compile and hit return twice. The example program will be compiled. Notice how everything happened automatically? Emacs ran a program called make which reads a file called Makefile, which tells it what files have to be compiled to make your program.
We'll talk about makefiles in more detail later on. For now, you can look at how the file is written and mutate it to serve your needs in other assignments.
The important thing, for debugging purposes, is the -g flag that's passed to the compiler. This instructs the compiler to annotate your code so the debugger can follow along as your program is running.
If you want the code to run as fast as possible, replace -g with -O, and the optimizer will work on your code (lcc will complain, but gcc will do the right thing).
Cool trick: If the compiler found a bug in your code, you could type C-x ` and you'd be taken straight to the offending line of source code. Cool, huh?
To quit the debugger, simply type quit at the (gdb) prompt. To make the gdb buffer go away, you type C-x k, as usual.
C programs begin in main(), so find it (it's near the bottom) and go down to the line which says LInsert(7);. Type C-x space. You'll notice that some stuff appeared in the GDB buffer. You just set a breakpoint at that line of code. When you first run the program, it will stop here.
Notice the arrow that appeared next to our breakpoint? The first two lines of the program ran, and it stopped before running LInsert(7).
Our next two commands let us watch the program as it progresses. They're called step and next, but you can type s or n as a shorthand. step will go into a function call, and next will go over a function call. First, try next. The arrow should have gone down one line. If you just hit the return key, it will repeat your last command. Do that, and the arrow should now be in front of LInsert(3).
Now, let's go wander down to see how things are working. This time, step into the call. The arrow should have jumped up to the beginning of the LInsert() function. Type next and hit return a couple times. We're going to be inserting the number ``3'' into a list which contains an empty node, ``2'', ``7'', and another empty node.
(gdb) p prevPtr $1 = (struct list *) 0x4100 (gdb) p curPtr $2 = (struct list *) 0x61f0Not very exciting, huh? All it did was give you the numerical addresses. This is a little more interesting.
(gdb) p *prevPtr $3 = {next = 0x61f0, data = -2147483648} (gdb) p *curPtr $4 = {next = 0x61e0, data = 2} (gdb) p *curPtr->next $5 = {next = 0x40f8, data = 7} (gdb) p *curPtr->next->next $6 = {next = 0x40f8, data = 2147483647} (gdb) p *curPtr->next->next->next $7 = {next = 0x40f8, data = 2147483647}You see those dollar signs? All those are values that you can reference again. Watch this:
(gdb) p $5->data $8 = 7 (gdb) p curPtr->data + $5->data $9 = 9Basically, you can type any C-expression after the print statement and it will tell you the answer. You can even call functions!
(gdb) p LPrint(curPtr) [2,7] $10 = void
Cool trick: It's always a good idea to leave functions like LPrint() around, even if you don't need them in your final program. The debugging print interface gets annoying sometimes, but you can always run some pretty-printing code you've got hanging around.
In the source window, go down the bottom, where it calls LSummarize(lhead.next). Set a breakpoint there, and type continue to gdb (or c for shorthand).
The LSummarize() procedure computes a list of partial sums of the list by calling itself recursively. step and hit return a bunch of times. You'll notice the arrow bouncing between the recursive call to LSummarize() and the top of the function. In the gdb window, you'll also notice it's printing out the argument being passed to the function as you're wandering down.
Type where. You should see something like this:
#0 LSummarize (lPtr=0x61f0) at llist.c:117 #1 0x2830 in LSummarize (lPtr=0x6210) at llist.c:119 #2 0x2830 in LSummarize (lPtr=0x6220) at llist.c:119 #3 0x2b30 in main (argc=1, argv=0xf7fff954) at llist.c:146This is the call stack of your program. You can type up and down to move around. Or, you can type frame 2 to go directly to that particular frame. When you're in a frame, you can print its local variables.
This example is pretty cheesy, but in a bigger program, this is an invaluable way to track down bugs. You generally discover a bug somewhere deep in a program, and it's often caused because somebody called your function with bogus arguments. By going upward, you can see who did it, and let them know about it.
Exercise: Try breaking the source code I gave you. See if you can get one of the assert() statements to fail, or see if you can get the program to get a segmentation fault or a bus error.