Debugging Under Unix: gdb Tutorial (2024)

Contents

  1. Introduction
    1. Who should read this?
    2. Source code
  2. Preparations
    1. Environment settings
    2. Debugging symbols
  3. Debugging
    1. When to use a debugger
    2. Loading a program
    3. Inspecting crashes
    4. Conditional breakpoints
    5. Stepping
  4. Further information
  5. Notes

Introduction

This tutorial was originally written for CS 342 at Washington University. It is stillmaintained by AndrewGilpin.

Who should read this?

This tutorial is written to help a programmer who is new to the Unixenvironment to get started with using the gdb debugger. Thistutorial assumes you already know how to program in C++ and you can compile andexecute programs. It also sort of assumes that you basically know whatdebugging is and that you have used a debugger on another system.

Source code

To help illustrate some of the debugging principles I will use a runningexample of a buggy program. As you progress through this tutorial, youwill use the debugger to locate and fix errors in the code. The code can bedownloaded here and a simple Makefile for the programcan be downloaded here.

The code is very simple and consists of two class definitions, a node and alinked list. There is also a simple driver to test the list. All of the codewas placed into a single file to make illustrating the process of debugginga little easier.

Preparations

Environment settings

gdb is in the gnu package on CEC machines. If you don'thave this package loaded then type pkgadd gnu at a shellprompt. If you can run g++, then you will be able to run gdb.

Debugging symbols

gdb can only use debugging symbols that are generated byg++. For Sun CC users, there is the dbx debuggerwhich is very similar to gdb.

gdb is most effective when it is debugging a program that hasdebugging symbols linked in to it. With g++, this is accomplishedusing the -g command line argument.For even more information, the -ggdb switchcan be used which includes debugging symbols which are specific togdb. The makefile for this tutorial uses the-ggdb switch.

Debugging

When to use a debugger

Debugging is something that can't be avoided. Every programmer will at onepoint in their programming career have to debug a section of code. Thereare many ways to go about debugging, from printing out messages to the screen,using a debugger, or just thinking about what the program is doing andmaking an educated guess as to what the problem is.

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 theseg fault is occuring. Once the line of code in question has been found, itis useful to know about the values in that method, who called the method, andwhy (specifically) the error is occuring. Using a debugger makes finding allof this information very simple.

Go ahead and make the program for this tutorial, and run the program. Theprogram will print out some messages, and then it will print that it hasreceived a segmentation fault signal, resulting in a program crash. Giventhe information on the screen at this point, it is near impossible to determinewhy the program crashed, much less how to fix the problem. We will nowbegin to debug this program.

Loading a program

So you now have an executable file (in this case main) and youwant to debug it. First you must launch the debugger. The debugger is calledgdb and you can tell it which file to debug at the shell prompt.So to debug main we want to type gdb main. Here iswhat it looks like when I run it:
agg1@sukhoi agg1/.www-docs/tutorial> gdb mainGNU gdb 4.18Copyright 1998 Free Software Foundation, Inc.GDB is free software, covered by the GNU General Public License, and you arewelcome 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.7"...(gdb)
(Note: If you are using Emacs, you can run gdb from within Emacs bytyping M-x gdb. Then Emacs will split into two windows, where the secondwindow will show the source code with a cursor at the current instruction. Ihaven't actually used gdb this way, but I have been told by a very reliablesource that this will work. :)

gdb is now waitng for the user to type a command. We need torun the program so that the debugger can help us see what happens whenthe program crashes. Type run at the (gdb) prompt.Here is what happens when I run this command:

(gdb) runStarting program: /home/cec/s/a/agg1/.www-docs/tutorial/main Creating Node, 1 are in existence right nowCreating Node, 2 are in existence right nowCreating Node, 3 are in existence right nowCreating Node, 4 are in existence right nowThe fully created list is:4321Now removing elements:Creating Node, 5 are in existence right nowDestroying Node, 4 are in existence right now4321Program received signal SIGSEGV, Segmentation fault.Node<int>::next (this=0x0) at main.cc:2828 Node<T>* next () const { return next_; }(gdb)
The program crashed so lets see what kind of information we can gather.

Inspecting crashes

So already we can see the that the program was at line 28 of main.cc, thatthis points to 0, and we can see the line of code that was executed. Butwe also want to know who called this method and we would like to be able toexamine values in the calling methods. So at the gdb prompt,we type backtrace which gives me the following output:
(gdb) backtrace#0 Node<int>::next (this=0x0) at main.cc:28#1 0x2a16c in LinkedList<int>::remove (this=0x40160, item_to_remove=@0xffbef014) at main.cc:77#2 0x1ad10 in main (argc=1, argv=0xffbef0a4) at main.cc:111(gdb)
So in addition to what we knew about the current method and the localvariables, we can now also see what methods called us and what theirparameters were. For example, we can see that we were called byLinkedList<int>::remove () where the parameteritem_to_remove is at address 0xffbef014. Itmay help us to understand our bug if we know the value ofitem_to_remove, so we want to see the value at theaddress of item_to_remove. This can be done using thex command using the address as a parameter. ("x" can bethought of as being short for "examine".) Here is what happens when Irun the command:
(gdb) x 0xffbef0140xffbef014:0x00000001(gdb)
So the program is crashing while trying to runLinkedList<int>::remove with a parameter of 1. We have nownarrowed the problem down to a specific function and a specific value forthe parameter.

Conditional breakpoints

Now that we know where and when the segfault is occuring, we want towatch what the program is doing right before it crashes. One way to do thisis to step through, one at a time, every statement of the program untilwe get to the point of execution where we want to see what is happening. Thisworks, but sometimes you may want to just run to a particular section of codeand stop execution at that point so you can examine data at that location.

If you have ever used a debugger you are probably familiar with the conceptof breakpoints. Basically, a breakpoint is a line in the source code wherethe debugger should break execution. In our example, we want to look at thecode in LinkedList<int>::remove () so we would want to set abreakpoint at line 52 of main.cc. Since you may not know the exact linenumber, you can also tell the debugger which function to break in. Here iswhat we want to type for our example:

(gdb) break LinkedList<int>::removeBreakpoint 1 at 0x29fa0: file main.cc, line 52.(gdb)
So now Breakpoint 1 is set at main.cc, line 52 as desired. (The reason thebreakpoint gets a number is so we can refer to the breakpoint later, forexample if we want to delete it.) So when the program is run, it will returncontrol to the debugger everytime it reaches line 52. This may not bedesirable if the method is called many times but only has problems withcertain values that are passed. Conditional breakpoints can help us here.For our example, we know that the programcrashes when LinkedList<int>::remove() is called with a value of1. So we might want to tell the debugger to only break at line 52 ifitem_to_remove is equal to 1. This can be done by issuingthe following command:
(gdb) condition 1 item_to_remove==1(gdb)
This basically says "Only break at Breakpoint 1 if the value ofitem_to_remove is 1." Now we can run the program and know thatthe debugger will only break here when the specified condition is true.

Stepping

Continuing with the example above, we have set a conditionalbreakpoint and now want togo through this method one line at a time and see if we can locate the sourceof the error. This is accomplished using the step command.gdb has the nice feature that when enter is pressed withouttyping a command, the last command is automatically used. That way we can stepthrough by simply tapping the enter key after the first stephas been entered. Here is what this looks like:
(gdb) runThe program being debugged has been started already.Start it from the beginning? (y or n) yStarting program: /home/cec/s/a/agg1/.www-docs/tutorial/main Creating Node, 1 are in existence right nowCreating Node, 2 are in existence right nowCreating Node, 3 are in existence right nowCreating Node, 4 are in existence right nowThe fully created list is:4321Now removing elements:Creating Node, 5 are in existence right nowDestroying Node, 4 are in existence right now4321Breakpoint 1, LinkedList<int>::remove (this=0x40160, item_to_remove=@0xffbef014) at main.cc:5252 Node<T> *marker = head_;(gdb) step53 Node<T> *temp = 0; // temp points to one behind as we iterate(gdb) 55 while (marker != 0) {(gdb) 56 if (marker->value() == item_to_remove) {(gdb) Node<int>::value (this=0x401b0) at main.cc:3030 const T& value () const { return value_; }(gdb) LinkedList<int>::remove (this=0x40160, item_to_remove=@0xffbef014) at main.cc:7575 marker = 0; // reset the marker(gdb) 76 temp = marker;(gdb) 77 marker = marker->next();(gdb) Node<int>::next (this=0x0) at main.cc:2828 Node<T>* next () const { return next_; }(gdb) Program received signal SIGSEGV, Segmentation fault.Node<int>::next (this=0x0) at main.cc:2828 Node<T>* next () const { return next_; }(gdb)
After typing run, gdb asks us if we want to restartthe program, which we do. It then proceeds to run and breaks at thedesired location in the program. Then we type step and proceedto hit enter to step through the program. Note that the debugger steps intofunctions that are called. If you don't want to do this, you can usenext instead of step which otherwise has the samebehavior.

The error in the program is obvious.At line 75 marker is set to 0, but at line 77 a member of marker is accessed.Since the program can't access memory location 0, the seg fault occurs. Inthis example, nothing has to be done to marker and the error can be avoidedby simply removing line 75 from main.cc.

If you look at the output from running the program, you will see first of allthat the program runs without crashing, but there is a memory leak somewherein the program. (Hint: It is in the LinkedList<T>::remove() function.One of the cases for remove doesn't work properly.) It isleft as an exercise to the reader to use the debugger in locating and fixingthis bug. (I've always wanted to say that. ;)

gdb can be exited by typing quit.

Further information

This document only coversthe bare minimum number of commands necessary to get started usinggdb. For more information about gdb see thegdb man page or take a look at a very long description ofgdb here.Online help can be accessed by typing help while runninggdb. Also, as always, feel free to ask questions on thenewsgroup or you can ask me during lab hours.

Notes

  • There is another bug in the source code for the linked list that is notmentioned in the above code. The bug does not show up for the sequenceof inserts and removes that are in the provided driver code, but for othersequences the bug shows up. For example, inserting 1, 2, 3, and 4, and thentrying to remove 2 will show the error. Special thanks to Linda Gu andXiaofeng Chen for locating this bug. The bug fix is pretty simple and isleft as an exercise.
  • Special thanks to Ximmbo da Jazz for providing valuable fixes forsome typos and erroneous output.
  • Special thanks to Raghuprasad Govindarao for discovering a brokenlink.
Please send comments, suggestions, and bug reports toAndrew Gilpin.

Page last modified: April 7, 2004

Debugging Under Unix: gdb Tutorial (2024)
Top Articles
Latest Posts
Article information

Author: Nathanial Hackett

Last Updated:

Views: 6246

Rating: 4.1 / 5 (52 voted)

Reviews: 83% of readers found this page helpful

Author information

Name: Nathanial Hackett

Birthday: 1997-10-09

Address: Apt. 935 264 Abshire Canyon, South Nerissachester, NM 01800

Phone: +9752624861224

Job: Forward Technology Assistant

Hobby: Listening to music, Shopping, Vacation, Baton twirling, Flower arranging, Blacksmithing, Do it yourself

Introduction: My name is Nathanial Hackett, I am a lovely, curious, smiling, lively, thoughtful, courageous, lively person who loves writing and wants to share my knowledge and understanding with you.