GDB Debugger¶
The GNU debugger, gdb for short, is a command line tool that allows you to
diagnose coding erros. gdb is included in the gcc package.
Running your program within gdb you can interactively step line by line to do
some detective work about what might be wrong.
Prerequisite¶
To use gdb the program must compile without errors.
Compile with debugging¶
We need to compile a program with the compiler debugging flag -g. This
instructs the compiler to include the original source code into the executable.
For example, compile as
gcc -g -o [MY_PROGRAM] [MY_SOURCE].c
Run program with gdb¶
Launch the program through gdb
gdb [PATH_TO_EXECUTABLE]
Example source code to test gdb¶
ex_debug.c:
#include <stdio.h>
float otherFunction(float a) {
float b = 3.2*a;
return b;
}
float someFunction(float a) {
int b = 2*otherFunction(a);
return b;
}
int main(int argc, char **argv) {
float a = 1.2;
float b = someFunction(a);
printf("a=%f, b=%f\n", a, b);
return 0;
}
There is something wrong with this code. It should print out a=1.2, b=7.68.
Commands to control gdb¶
(gdb) run¶
Run the entire program
(gdb) run
Starting program: [PATH_TO_EXECUTABLE]
a=1.200000, b=7.000000
[Inferior 1 (process 2070606) exited normally]
(gdb) break¶
Set a breakpoint in a function and then start gdb running the program until the breakpoint.
(gdb) break [FUNCTION_NAME]
(gdb) run
main function
(gdb) break main
(gdb) run
Breakpoint 1 at 0x5555555551c1: file ex_debug.c, line 14.
The debugger stops execution at the first executable statement in the function
where the breakpoint is set. Thus, run only keeps going until the breakpoint:
(gdb) run
Starting program: [PATH_TO_EXECUTABLE]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, main (argc=1, argv=0x7fffffffe1d8) at ex_debug.c:14
14 float a = 1.2;
(gdb) list¶
You can see the next few lines of code by typing list.
For the example, the output is
(gdb) list
9 int b = 2*otherFunction(a);
10 return b;
11 }
12
13 int main(int argc, char **argv) {
14 float a = 1.2;
15 float b = someFunction(a);
16
17 printf("a=%f, b=%f\n", a, b);
18
(gdb) step¶
We can step through line by line as the code is executed using the step
command.
In the following example, typing step and pressing Enter makes the debugger
either processes the next line of code or step into the function if the
statement is a function call (with each time pressing Enter).
For the example, the output is
(gdb) step
15 float b = someFunction(a);
(gdb)
someFunction (a=1.20000005) at ex_debug.c:9
9 int b = 2*otherFunction(a);
(gdb)
otherFunction (a=1.20000005) at ex_debug.c:4
4 float b = 3.2*a;
(gdb)
5 return b;
(gdb)
6 }
(gdb). This repeats the
last command given to gdb.
(gdb) cont¶
When stopped at a breakpoint, type cont for the debugger to continue until the
next breakpoint.
For the example, continue after breaking at main
(gdb) cont
Continuing.
a=1.200000, b=7.000000
[Inferior 1 (process 2070606) exited normally]
For the example, suppose that something went wrong in the function
someFunction. Add another breakpoint at someFunction. When running the
program, gdb first stops in main (i.e., the first breakpoint). Type cont
and the debugger will continue until the second breakpoint.
(gdb) break otherFunction
Breakpoint 2 at 0x555555555156: file ex_debug.c, line 4.
(gdb) run
Starting program: [PATH_TO_EXECUTABLE]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, main (argc=1, argv=0x7fffffffe1d8) at ex_debug.c:14
14 float a = 1.2;
(gdb) cont
Continuing.
Breakpoint 2, otherFunction (a=1.20000005) at ex_debug.c:4
4 float b = 3.2*a;
(gdb) print¶
Print the values of variables. Use as
(gdb) print [VARIABLE_NAME]
Continuing the example,
(gdb) print a
$1 = 1.20000005
(gdb) cont
Continuing.
a=1.200000, b=7.000000
[Inferior 1 (process 2070738) exited normally]
(gdb) next¶
Similar to step command, but does not go into functions (also know as "step
over" command).
Continuing the example, the output is
(gdb) next
5 return b;
(gdb)
6 }
(gdb)
someFunction (a=1.20000005) at ex_debug.c:9
9 int b = 2*otherFunction(a);
(gdb)
10 return b;
(gdb)
11 }
(gdb)
main (argc=1, argv=0x7fffffffe1d8) at ex_debug.c:17
17 printf("a=%f, b=%f\n", a, b);
(gdb)
a=1.200000, b=7.000000
19 return 0;
(gdb)
20 }
(gdb) cont
Continuing.
[Inferior 1 (process 2070748) exited normally]
(gdb). This repeats the
last command given to gdb.
(gdb) where¶
See where we are in the call stack, this means see the nested sequence of function calls.
For the example, the output is
(gdb) run
Starting program: /home/jrudi/cmda3634-fa23/L09/debug
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, main (argc=1, argv=0x7fffffffe1d8) at ex_debug.c:14
14 float a = 1.2;
(gdb) where
#0 main (argc=1, argv=0x7fffffffe1d8) at ex_debug.c:14
(gdb) cont
Continuing.
Breakpoint 2, otherFunction (a=1.20000005) at ex_debug.c:4
4 float b = 3.2*a;
(gdb) where
#0 otherFunction (a=1.20000005) at ex_debug.c:4
#1 0x0000555555555198 in someFunction (a=1.20000005) at ex_debug.c:9
#2 0x00005555555551da in main (argc=1, argv=0x7fffffffe1d8) at ex_debug.c:15
main
function:
#0 otherFunction (a=1.20000005) at ex_debug.c:4
#1 0x0000555555555198 in someFunction (a=1.20000005) at ex_debug.c:9
#2 0x00005555555551da in main (argc=1, argv=0x7fffffffe1d8) at ex_debug.c:15
Finding the bug¶
Continuing the example, we advance the program to return from otherFunction.
(gdb) step
5 return b;
(gdb)
6 }
(gdb)
someFunction (a=1.20000005) at ex_debug.c:9
9 int b = 2*otherFunction(a);
(gdb) where
#0 someFunction (a=1.20000005) at ex_debug.c:9
#1 0x00005555555551da in main (argc=1, argv=0x7fffffffe1d8) at ex_debug.c:15
someFunction
(gdb) step
10 return b;
(gdb) print b
$2 = 7
otherFunction but turned into an integer inside someFunction. Looking
at the code for someFunction, we see that the b variable is actually an
int. Bug found!
(gdb) quit¶
Quit the debugger when finished.
Cheat Sheet for GDB¶
Note: One can just use the first letter of each command instead of the whole command.
run [PATH_TO_EXECUTABLE] # Start running
break [FUNCTION] # Add a breakpoint in [FUNCTION]
cont # Continue running after breakpoint
print [VARIABLE_NAME] # Print value of [VARIABLE_NAME]
next # Process next statement
step # Step into next function or process next step
where # Print call stack trace
list # List next few lines of code
up # Move up the call stack
down # Mode down the call stack
quit # Quit debugger