WSU logo


College of Engineering & CS
Wright State University
Dayton, Ohio 45435-0001

CEG 333: Introduction to Unix

Prabhaker Mateti

GDB


The GNU Debugger (GDB) is a free, high-quality system for examining the innards of a running program. Like GCC, it is a free alternative to commercial Unix debuggers, which share a similar interface.

To start the debugger and load a program, type gdb [--args] [PROGRAM], where PROGRAM is the compiled executable to be debugged. PROGRAM can be in the current directory or anywhere in the path. The program GDB is debugging can be specified later or changed with the file PROGRAM command.

If the --args option is given, GDB will pass any arguments after the program name to the program. Don't put program arguments on the command line without this. Otherwise, GDB will think those arguments are meant for it and get confused! (Arguments can also be given later, to run)

Note: GDB won't work properly with programs that were compiled without debugging symbols (extra information about the code). Be sure to use the -g compiler flag!

The standard Unix debugging interface is a command line, with it's own set of commands. These may be entered at the "(gdb)" prompt, and are not the same as those used by bash.

Important Debugging Commands:

Most commands can be abbreviated, frequently with just the first letter (for example "c" for "continue"). If an abbreviation could mean more than one command, GDB will ask which is meant.

Command Use
backtrace Show the call stack.
break PLACE Set a breakpoint at given line number or function name. Execution will stop at that point so the program's state may be examined. For example, break main to stop at the first line of the program, or break 289 to stop on line 289 every time the program is run.
cont Continue execution until the next breakpoint.
delete [NUMBER...] Remove a breakpoint, watchpoint, or auto-display.
disable [NUMBER...] Temporarily disable a breakpoint, watchpoint, or auto-display.
display EXPRESSION Automatically display the value of the given expression whenever execution stops. Use this to read the value
file PROGRAM (Re)load the program. This must be used if it's recompiled during a debugging session.
help With an argument (such as a command name), show help on that topic.
list [WHERE] Prints part of a program. WHERE can be a line number, a function, or FILE:LINENUM. Without arguments, prints the lines following the last listing, or with a "-" lists the lines before.
next Execute the next line, but don't step into functions.
print EXPRESSION Print the current value of the given expression once.
quit Quit GDB.
run [ARGUMENTS] Start the program's execution. Type any command-line arguments after it, in order. Warning: never give the name of the executable here, only its arguments.
step Execute the next line. If it is a function call, GDB will go through every line of the function.
watch EXPRESSION Set a watchpoint on the given variable or expression. Whenever the value of that expression changes, execution stops and the old and new values are displayed.

Examining Values (display and watch)

The difference between "display" and "watch" can be confusing at first, but it's very important important.

For example, say a programmer desires to show the current value of an expression every time the program passes through a certain point. "display" and a breakpoint should always be used for this case. "watch" will show the value only when it changes, and not necessarily in the important part of the program.

GDB evaluates expressions when the command is given. So when using "display", any variable names in the expression must be in scope. A breakpoint can be set to stop execution where the variables come into scope, after which display can be set.

An Example Debugging Session

Consider the following program, pow.cpp. It crashes after the line 12 printf. Why?

      #include <cstdio>
      #include <cstring>
      using namespace std;

      int pow(int x, int y) {
          return x * pow(x, y-1);
      }

      int main(int argc, char *argv[]) {
          int x = 3;
          printf("Line 12: x: %i\n", x);
          x = pow(x, 3);
          printf("Line 14: x: %i\n", x);
          return 0;
      }
    

Start the debugger with gdb pow, and run the program to catch the crash in action:

      (gdb) run
      Starting program: /tmp/gdb_eg/pow
      Line 12: x: 3

      Program received signal SIGSEGV, Segmentation fault.
      0x080484c8 in pow (x=3, y=-524111) at /tmp/gdb_eg/pow.cpp:7
      7               return x * pow(x, y-1);
    

If the bug is obvious, this alone might be enough information. If not, GDB can provide a list of all the recursive function calls that got the program to this point, in reverse order:

      (gdb) backtrace
      #0  0x080484c8 in pow (x=3, y=-524111) at /tmp/gdb_eg/pow.cpp:7
      #1  0x080484cd in pow (x=3, y=-524110) at /tmp/gdb_eg/pow.cpp:7
      #2  0x080484cd in pow (x=3, y=-524109) at /tmp/gdb_eg/pow.cpp:7
      #3  0x080484cd in pow (x=3, y=-524108) at /tmp/gdb_eg/pow.cpp:7
      #4  0x080484cd in pow (x=3, y=-524107) at /tmp/gdb_eg/pow.cpp:7
      #5  0x080484cd in pow (x=3, y=-524106) at /tmp/gdb_eg/pow.cpp:7
      #6  0x080484cd in pow (x=3, y=-524105) at /tmp/gdb_eg/pow.cpp:7
      #7  0x080484cd in pow (x=3, y=-524104) at /tmp/gdb_eg/pow.cpp:7
      #8  0x080484cd in pow (x=3, y=-524103) at /tmp/gdb_eg/pow.cpp:7
      #9  0x080484cd in pow (x=3, y=-524102) at /tmp/gdb_eg/pow.cpp:7
      #10 0x080484cd in pow (x=3, y=-524101) at /tmp/gdb_eg/pow.cpp:7
      #11 0x080484cd in pow (x=3, y=-524100) at /tmp/gdb_eg/pow.cpp:7
      #12 0x080484cd in pow (x=3, y=-524099) at /tmp/gdb_eg/pow.cpp:7
      #13 0x080484cd in pow (x=3, y=-524098) at /tmp/gdb_eg/pow.cpp:7
      #14 0x080484cd in pow (x=3, y=-524097) at /tmp/gdb_eg/pow.cpp:7
      #15 0x080484cd in pow (x=3, y=-524096) at /tmp/gdb_eg/pow.cpp:7
      #16 0x080484cd in pow (x=3, y=-524095) at /tmp/gdb_eg/pow.cpp:7
      #17 0x080484cd in pow (x=3, y=-524094) at /tmp/gdb_eg/pow.cpp:7
      #18 0x080484cd in pow (x=3, y=-524093) at /tmp/gdb_eg/pow.cpp:7
      #19 0x080484cd in pow (x=3, y=-524092) at /tmp/gdb_eg/pow.cpp:7
      #20 0x080484cd in pow (x=3, y=-524091) at /tmp/gdb_eg/pow.cpp:7
      #21 0x080484cd in pow (x=3, y=-524090) at /tmp/gdb_eg/pow.cpp:7
      #22 0x080484cd in pow (x=3, y=-524089) at /tmp/gdb_eg/pow.cpp:7
      ---Type <return> to continue, or q <return> to quit---q
      Quit
    

Oops. Why is it trying to raise x to a negative power? y is only supposed to be 3!

To examine the program as it goes wrong, set a breakpoint before the first call to pow() and re-run it, using "step" to go through line by line:

      (gdb) break 11
      Breakpoint 1 at 0x80484f7: file /tmp/gdb_eg/pow.cpp, line 11.
      (gdb) run
      The program being debugged has been started already.
      Start it from the beginning? (y or n) y
      Starting program: /tmp/gdb_eg/pow

      Breakpoint 1, main (argc=1, argv=0xbffff5e4) at /tmp/gdb_eg/pow.cpp:11
      11          printf("Line 12: x: %i\n", x);
      (gdb) step
      Line 12: x: 3
      12          x = pow(x, 3);
      (gdb) s
      pow (x=3, y=3) at /tmp/gdb_eg/pow.cpp:6
      6               return x * pow(x, y-1);
    

It might be helpful to set a new breakpoint here and a display on y, since that variable seems to be the source of the problem:

      (gdb) break 6
      Breakpoint 2 at 0x80484ba: file /tmp/gdb_eg/pow.cpp, line 6.
      (gdb) display y
      1: y = 3
      (gdb) c
      Continuing.

      Breakpoint 2, pow (x=3, y=2) at /tmp/gdb_eg/pow.cpp:6
      6               return x * pow(x, y-1);
      1: y = 2
      (gdb) c
      Continuing.

      Breakpoint 2, pow (x=3, y=1) at /tmp/gdb_eg/pow.cpp:6
      6               return x * pow(x, y-1);
      1: y = 1
      (gdb) c
      Continuing.

      Breakpoint 2, pow (x=3, y=0) at /tmp/gdb_eg/pow.cpp:6
      6               return x * pow(x, y-1);
      1: y = 0
      (gdb) c
      Continuing.

      Breakpoint 2, pow (x=3, y=-1) at /tmp/gdb_eg/pow.cpp:6
      6               return x * pow(x, y-1);
      1: y = -1
    

Oh. There's no end condition for the recursion! Going back to the source, the program can now be fixed:

      int pow(int x, int y) {
          if (y != 1) return x * pow(x, y-1);
          else return x;
      }
    

GDB can be used for a few more tests, just to make sure:

      (gdb) r
      Starting program: /tmp/gdb_eg/pow
      Line 12: x: 3
      Line 14: x: 27

      Program exited normally.
      (gdb) break 15
      Breakpoint 1 at 0x8048547: file /tmp/gdb_eg/pow.cpp, line 15.
      (gdb) r
      Starting program: /tmp/gdb_eg/pow
      Line 12: x: 3
      Line 14: x: 27

      Breakpoint 1, main (argc=1, argv=0xbffff5e4) at /tmp/gdb_eg/pow.cpp:15
      15           return 0;
      (gdb) set var x=pow(4,4)
      (gdb) print x
      $1 = 256
      (gdb) c
      Continuing.

      Program exited normally.
    

Note the use of an arbitrary function call to alter a variable.