...

GDB Tutorial


The debugger for C/C++ is gdb, developed by GNU. This is a simple tutorial to get your started on debugging using gdb. For more detailed instruction and advanced features, please refer to various online resources, such as this, this, and that.

Getting started

We use the following example program foo.c:


1 #include <stdio.h>
2 #include <assert.h>
3
4 int sum_even(int end)
5 {
6    int sum = 0;
7    for (int i = 0; i != end; i+=2) {
8      sum +=i;
9    }
10    return sum;
11 }
12
13 int main()
14 {
15    int r1 = sum_even(10);
16    int r2 = sum_even(11);
17    assert(r1 == r2);
18 }

In order to use gdb, we need to compile the source code using the -g flag so that appropriate debug information will be included in the generated binary files.
$ gcc -c -g foo.c //compile foo.c into foo.o, -g flag will include debug information in foo.o
$ gcc -o foo foo.o //assemble foo.o into the binary executable foo.

Locating a suspicious point of execution

You can run the compiled program by typing ./foo. You'll notice that the foo program is buggy, and does not even terminate! To debug, type gdb foo. Under the gdb prompt, type r (short for run) to run the program. As expected, it does not terminate. Type Control-C to interrupt the program execution and bring up the gdb prompt. You'll see the following:

...
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from foo...
(gdb) r
Starting program: /home/jinyang/labs/foo
^C
Program received signal SIGINT, Interrupt.
0x0000555555555171 in sum_even (end=11) at foo.c:7
7	   for (int i = 0; i != end; i+=2) {
(gdb)

The above gdb message tells you that, when the program has been interrupted by your Ctrl-C signal, it is about to execute the line at foo.c:7.

You can type the command l foo.c:7 at the gdb prompt to print out the relevant lines of the C code (aka several lines starting from line 7 in foo.c). l is short for list.

Show the backtrace. It is usually not sufficient to know which function (or program location) is causing trouble, because many different functions could invoke the same function. For more information, we need to obtain the program's backtrace. A backtrace is a summary of how your program got where it is. It shows one line per frame (aka function invocation), for many frames, starting with the currently executing frame (frame zero), followed by its caller (frame one), and on up the stack. You can ask gdb to display backtrace by typing bt (short for backtrace).

(gdb) bt
#0  0x000055555555516e in sum_even (end=11) at foo.c:7
#1  0x000055555555519e in main () at foo.c:16

The above backtrace information says that the program is stopped at function sum_even foo.c:7, which is being called by main() at foo.c:16.

Displaying variable values

To debug further, I need to see the current values of various varibles. In our running example, when I see that my programing is being interrupted at foo.c:7, I am naturally wondering why the loop still has not terminated. What is the value of end? It should be 11, since I know the caller of sum_even is line sum_even(11) at foo.c:16, based on my previous backtrace command. Nevertheless, it still good to check that I am not mistaken. I can do this by typing p end (p is short for print).

Program received signal SIGINT, Interrupt.
0x0000555555555171 in sum_even (end=11) at foo.c:7
7	   for (int i = 0; i != end; i+=2) {
(gdb) p end
$1 = 11
Next, I want to know what is the current value of i? I do this by typing p i.
(gdb) p i
$2 = 1036418658

Wow, i's value is much larger than I expected. At this point in time, many of you would be able to identify the bug. Suppose I am not as smart and am still quite puzzled. What more can I do?

Stopping at a specific point and continuing execution

Now I want to investigate why the loop got out of hand. I plan to explicitly examine each time the loop body is executed. I can do this by asking gdb to stop the program execution at line foo.c:8, where the loop body sum += i is executed. I can do this by typing b foo.c:8 (b is short for break). Then I re-start the program by typing r, see below.

(gdb) b foo.c:8
Breakpoint 1 at 0x115b: file foo.c, line 8.
(gdb) r
Starting program: /home/jinyang/classes/cso/nyu-cso.github.io/labs/foo

Breakpoint 1, sum_even (end=10) at foo.c:8
8	     sum +=i;

I can print the value of i (and sum) at this point. I can see that i=0, suggesting that it's the first time the loop body is entered.

I want to let the program execute the next line. I can do so by typing n (n is short for next).

(gdb) n
7	   for (int i = 0; i != end; i+=2) {

To see the program execute line by line, I can keep typing n, or typing [Enter] which causes gdb to repeat the previous command. When such detailed following of the program's execution, I can surely identify my bug with some patience.

Note: gdb command c (c is short for continue) will continue executing the program until it hits the breakpoint again.

Another useful gdb command is s (s is short for step, aka step program until it reaches a different source line). This command is useful when, during line-by-line execution using n, you encounter a procedure invocation. If you are to use n, the entire procedure will be executed without interruption. If you want to step inside that procedure to continue line-by-line execution, you use s to do so.

gdb commands useful for investigating binaries

In Lab3 and certain other scenarios, you only have access to the binary executable and not its related source files. For this purpose, the gdb command disass, nexti, info registers, x commands are really useful. You can learn more about these commands here.

Summary: useful gdb commands to remember

We've seen how to use these most crucial gdb commands:

r (run)
bt (backtrace)
l (list, print lines from the relevant source file)
b (break) 
p (print) 
n (next) 
c (continue)
s (step: step program until it reaches a different source line)
x (examine memory: x/FMT address)
ni (next instruction)
disass (disassemble a procedure)
info registers (print out register values)
In general, you can type help to get the list of gdb commands. You can type help x to retrieve the information on command x or other commands.