gdb
Tutorialgdb
debugger. This
tutorial assumes you already know how to program in C++. It also sort of assumes that you basically know what
debugging is and that you have used a debugger on another system.
Once the files are unzipped, you can compile and build the target executable file by running the make command in the directory. The name of the target executable file is main:
cs143@cs143:~$ ls main.cc Makefile cs143@cs143:~$ make g++ -ggdb -Wall -o main main.cc cs143@cs143:~$ ls main main.cc MakefileNote that when you run the make command, a new executable file main is created in the current directory.
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 which are specific to
gdb
. The Makefile for this tutorial uses the
-ggdb
switch.
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.
Go ahead and make the target program "main" for this tutorial by running make. Now run the target program "main":
cs143@cs143:~$ ./main Creating Node, 1 are in existence right now ... Destroying Node, 4 are in existence right now ... Segmentation fault cs143@cs143:~$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 near impossible to determine why the program crashed, much less how to fix the problem. We will now begin to debug this program.
main
) 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 main
we want to type gdb main
. Here is
what it looks like when I run it:
cs143@cs143:~$ gdb main GNU gdb 6.6-debian Copyright (C) 2006 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 "i486-linux-gnu"... Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1". (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.)
gdb
is now waitng for the user to type a command. 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:
(gdb) run Starting program: /home/cs143/main Creating Node, 1 are in existence right now ... Destroying Node, 4 are in existence right now ... 1 Program received signal SIGSEGV, Segmentation fault. 0x080489e2 in NodeWe can see that when the program crashed (i.e., when it received SIGSEV signal), the program was at line 30 of main.cc,::next (this=0x0) at main.cc:30 30 Node * next () const { return next_; } (gdb)
this
pointed to 0 (this=0x0).
But we also want to know who called this method and we would like to be able to
examine values in the calling methods.
The gdb command backtrace
provides this information. Issue
backtrace
to gdb, which gives the following output:
(gdb) backtrace #0 0x080489e2 in NodeThe output from backtrace shows you the sequence of function calls (together with their parameters) to arrive at the current point. So from the above output, we can see that we were called by::next (this=0x0) at main.cc:30 #1 0x08048cdb in LinkedList ::remove (this=0x804b008, item_to_remove=@0xbf9bbc20) at main.cc:79 #2 0x08048919 in main () at main.cc:122 (gdb)
LinkedList<int>::remove()
, which was, in turn,
called by main()
. We also see
that the parameter item_to_remove
to LinkedList<int>::remove()
is stored at address 0xbf9bbc20
(from item_to_remove=@0xbf9bbc20, where @ means that the variable is stored at the given address). It
may help us to understand our bug if we know the value of
item_to_remove
, so let us examine the value at this
address. This can be done using the
x
command using the address as a parameter. ("x" can be
thought of as being short for "examine".) Here is what happens when I
run the command:
(gdb) x 0xbf9bbc20 0xbf9bbc20: 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.
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 54 of main.cc. 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 0x8048b58: file main.cc, line 54. (gdb)So now Breakpoint 1 is set at main.cc, line 54 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 54. 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 54 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.
Now we run the program again up to the breakpoint, so that we can start
investigating what happens after the breakpoint:
(gdb) run The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/cs143/main Creating Node, 1 are in existence right now ... Destroying Node, 4 are in existence right now ... 1 Breakpoint 1, LinkedListAfter typing::remove (this=0x804b008, item_to_remove=@0xbfa624c0) at main.cc:54 54 Node *marker = head_; (gdb)
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.
step
command.
gdb
has the nice feature that when enter is pressed without
typing a command, the last command is automatically used, so once
we issue the step
command once, we can simply press enter
to proceed to the next line:
Breakpoint 1, LinkedListWe type::remove (this=0x804b008, item_to_remove=@0xbfa624c0) at main.cc:54 54 Node *marker = head_; (gdb) step 55 Node *temp = 0; // temp points to one behind as we iterate (gdb) 57 while (marker != 0) { (gdb) 58 if (marker->value() == item_to_remove) { (gdb) Node ::value (this=0x804b058) at main.cc:32 32 const T& value () const { return value_; } (gdb) LinkedList ::remove (this=0x804b008, item_to_remove=@0xbfa624c0) at main.cc:77 77 marker = 0; // reset the marker (gdb) 78 temp = marker; (gdb) 79 marker = marker->next(); (gdb) Node ::next (this=0x0) at main.cc:30 30 Node * next () const { return next_; } (gdb) Program received signal SIGSEGV, Segmentation fault. 0x080489e2 in Node ::next (this=0x0) at main.cc:30 30 Node * next () const { return next_; } (gdb)
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 77 marker is set to 0, but at line 79 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 77 from main.cc.
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
. Also, as always, feel free to ask questions on the
newsgroup or you can ask me during lab hours.
Page last modified: May 7, 2007