gdb is in the /opt/sfw/bin directory on atlas. To check that it is in your path,
type which gdb .
gdb can only use debugging symbols that are generated by
g++. For Sun CC users, there is the dbx debugger
which is very similar to gdb.
gdb is most effective when it is debugging a program that has
debugging symbols linked in to it. With g++, this is accomplished
using the -g command line argument.
For even more information, the -ggdb switch
can be used, which includes debugging symbols that are specific to
gdb. See the makefile for this tutorial.
Before a bug can be fixed, the source of the bug must be located. For example, with segmentation faults, it is useful to know on which line of code the seg fault is occuring. Once the line of code in question has been found, it is useful to know about the values in that method, who called the method, and why (specifically) the error is occuring. Using a debugger makes finding all of this information very simple.
Create a directory and save the Makefile, buggy.cpp, list.h, and node.h files to that directory. Use vi to look at the Makefile. You will see that the Makefile is set up to build an executable called "buggy" (the $@ is a parameter that represents the label on the stanza of the makefile that is executed). (Try changing "buggy:" to "fred:" in the Makefile. Then type "make". Look at the command line echoed to the screen. Notice the name of the executable that is created.).
Say more here about makefile ...
Now change the label back to "buggy:", type "make clean", and then type "make". (You might also type "rm fred" to get rid of the executable named fred.)
Now run the program by typing "buggy". The program will print out some messages, and then it will print that it has received a segmentation fault signal, resulting in a program crash. Given the information on the screen at this point, it is nearly impossible to determine why the program crashed, much less how to fix the problem.
We will now begin to debug this program.
buggy) and you
want to debug it. First you must launch the debugger. The debugger is called
gdb and you can tell it which file to debug at the shell prompt.
So to debug buggy we want to type gdb buggy. Here is
what it looks like when I run it:
atlas{eileen}83: gdb buggy
GNU gdb 5.0
Copyright 2000 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 "sparc-sun-solaris2.9"...
(gdb)
(Note: If you are using Emacs, you can run gdb from within Emacs by
typing M-x gdb. Then Emacs will split into two windows, where the second
window will show the source code with a cursor at the current instruction. I
haven't actually used gdb this way, but I have been told by a very reliable
source that this will work. :)
gdb is now waitng for the user to type a command.
One command that you might run is list. It will show the
contents of the source file that is currently (about to be) executing.
Here's what I get:
If I type6 7 int num_nodes = 0; 8 9 #include "node.h" 10 #include "list.h" 11 12 int main (int argc, char **argv) { 13 LinkedList<int> *list = new LinkedList<int> (); (gdb)
list again, it will show additional lines
of the file. If I just hit "enter", gdb again executes whatever I
last typed in, so it will show additional lines. If I keep doing
this, it will reach the end, and complain.
(gdb) 14 15 list->insert (1); 16 list->insert (2); 17 list->insert (3); 18 list->insert (4); 19 20 cout << "The fully created list is:" << endl; 21 list->print (); 22 23 cout << endl << "Now removing elements:" << endl; (gdb) 24 list->remove (4); 25 list->print (); 26 cout << endl; 27 28 list->remove (1); 29 list->print (); 30 cout << endl; 31 32 list->remove (2); 33 list->print (); (gdb) 34 cout << endl; 35 36 list->remove (3); 37 list->print (); 38 39 delete list; 40 41 return 0; 42 } 43 (gdb) 44 (gdb) Line number 45 out of range; buggy.cpp has 44 lines.
I can type list line_num to start the listing
over again, beginning at line_num.
We need to run the program so that the debugger can help us see what happens
when the program crashes. Type run at the (gdb) prompt.
Here is what happens when I run this command:
Starting program: /web/eileen/public_html/6950/GDB_tutorial/GDB_test/buggy
Constructing node, 1 currently exist
Constructing node, 2 currently exist
Constructing node, 3 currently exist
Constructing node, 4 currently exist
The fully created list is:
4
3
2
1
Now removing elements:
Constructing node, 5 currently exist
Destroying Node, 4 currently exist
4
3
2
1
Program received signal SIGSEGV, Segmentation fault.
Node <int >::next (this=0x0) at node.h:20
20 Node <T> * next () const { return next_; }
(gdb)
The program crashed so lets see what kind of information we can gather.
this points to 0, and we can see the line of code that was
executed. But we also want to know who (which method) called this method
and we would like to be able to examine values in the calling methods. So
at the gdb prompt,
we type where which gives me the following output (backtrace gives the same output)t:
(gdb) where
#0 Node <int>::next (this=0x0) at node.h:20
#1 0x11068 in LinkedList <int>::remove (this=0x217e0,
item_to_remove=@0xffbffac4) at list.h:40
#2 0x10b58 in main (argc=1, argv=0xffbffb54) at buggy.cpp:28
(gdb)
So in addition to what we knew about the current method and the local
variables, we can now also see what methods called this method and what their
parameters were. For example, we can see that we were called by
LinkedList<int>::remove () where the parameter
item_to_remove is at address 0xffbffac4. It
may help us to understand our bug if we know the value of
item_to_remove, so we want to see the value at the
address of item_to_remove. This can be done using the
x command using the address as a parameter("x" can be
thought of as being short for "examine memory".)
Here is what happens when I run the command:
c4 (gdb) x 0xffbffac4 0xffbffac4: 0x00000001 (gdb)
So the program is crashing while trying to run
LinkedList<int>::remove with a parameter of 1. We have now
narrowed the problem down to a specific function and a specific value for
the parameter.
This works, in general, but here we want to just run to a particular section of code and stop execution at that point so we can examine data at that location.
If you have ever used a debugger you are probably familiar with the concept
of breakpoints. Basically, a breakpoint is a line in the source code where
the debugger should break execution. In our example, we want to look at the
code in LinkedList<int>::remove () so we would want to set a
breakpoint at line 15 of list.h. Since you may not know the exact line
number, you can also tell the debugger which function to break in. Here is
what we want to type for our example:
(gdb) break LinkedList<int>::remove Breakpoint 1 at 0x10e9c: file list.h, line 15. (gdb)So now Breakpoint 1 is set at list.h, line 15 as desired. (The reason the breakpoint gets a number is so we can refer to the breakpoint later, for example if we want to delete it.) So when the program is run, it will return control to the debugger everytime it reaches line 15 in list.h. This may not be desirable if the method is called many times but only has problems with certain values that are passed. Conditional breakpoints can help us here. For our example, we know that the program crashes when
LinkedList<int>::remove() is called with a value of
1. So we might want to tell the debugger to only break at line 16 in list.h if
item_to_remove is equal to 1. This can be done by issuing
the following command:
(gdb) condition 1 item_to_remove==1 (gdb)This basically says "Only break at Breakpoint 1 if the value of
item_to_remove is 1." Now we can run the program and know that
the debugger will only break here when the specified condition is true.
step command.
gdb has the nice feature that when enter is pressed without
typing a command, the last command is automatically used. That way we can step
through by simply tapping the enter key after the first step
has been entered.
Here is what this looks like:
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /web/eileen/public_html/6950/Notes/GDB_tutorial/GDB_test/buggy
Constructing node, 1 currently exist
Constructing node, 2 currently exist
Constructing node, 3 currently exist
Constructing node, 4 currently exist
The fully created list is:
4
3
2
1
Now removing elements:
Constructing node, 5 currently exist
Destroying Node, 4 currently exist
4
3
2
1
Breakpoint 1, LinkedList<int>::remove (this=0x217e0,
item_to_remove=@0xffbffac4) at list.h:15
16 Node<T> *marker = head_;
(gdb) step
17 Node<T> *temp = 0; // temp points to one behind as we iterate
(gdb)
19 while (marker != 0) {
(gdb)
20 if (marker->value() == item_to_remove) {
(gdb)
Node<int>::value (this=0x21830) at node.h:22
22 const T& value () const { return value_; }
(gdb)
LinkedList <int>::remove (this=0x217e0, item_to_remove=@0xffbffac4) at list.h:38
39 marker = 0; // reset the marker
(gdb)
40 temp = marker;
(gdb)
41 marker = marker->next();
(gdb)
Node<int>::next (this=0x0) at node.h:20
20 Node <T>* next () const { return next_; }
(gdb)
Program received signal SIGSEGV, Segmentation fault.
Node <int>::next (this=0x0) at node.h:20
20 Node* next () const { return next_; }
(gdb)
After typing run, gdb asks us if we want to restart
the program, which we do. It then proceeds to run and breaks at the
desired location in the program. Then we type step and proceed
to hit enter to step through the program. Note that the debugger steps into
functions that are called. If you don't want to do this, you can use
next instead of step which otherwise has the same
behavior.
The error in the program is obvious. At line 39 marker is set to 0, but at line 41 a member of marker is accessed. Since the program can't access memory location 0, the seg fault occurs. In this example, nothing has to be done to marker and the error can be avoided by simply removing line 39 from list.h.
If you look at the output from running the program, you will see first of all that the program runs without crashing, but there is a memory leak somewhere in the program. (Hint: It is in the LinkedList<T>::remove() function. One of the cases for remove doesn't work properly.) It is left as an exercise to the reader to use the debugger in locating and fixing this bug. (I've always wanted to say that. ;)
gdb can be exited by typing quit.
gdb. For more information about gdb see the
gdb man page or take a look at a very long description of
gdb here.
Online help can be accessed by typing help while running
gdb.
Page last modified: Aug 31, 2009 Attribution: These materials based on Andrew Gilpin's tutorial for CS 342 at Wash U, St. Louis