gcc -g myprogram.c
gdb a.out
(gdb)
– all examples are from this prompt.r
r arg1 arg2
r < file1
help
h breakpoints
q
– Quit GDBStepping lets you trace the path of your program, and zero in on the code that is crashing or returning invalid input.
l
l 50
l myfunction
next
step
next
. If it’s a function, it will jump into the function, execute the first statement, then pause. Step is good for diving into the details of your code.finish
Breakpoints are one of the keys to debugging. They pause (break) a program when it reaches a certain location. You can examine and change variables, then resume execution. This is helpful when seeing why certain inputs fail, or testing inputs.
break 45
break myfunction
watch x == 3
continue
delete N
Viewing and changing variables at run-time is a huge part of debugging. Try giving functions invalid inputs or running other test cases to find the root of problems. Typically, you will view/set variables when the program is paused.
print x
set x = 3
set x = y
call myfunction()
call myotherfunction(x)
call strlen(mystring)
display x
undisplay x
The stack is a list of the current function calls – it shows you where you are in the program. A frame stores the details of a single function call, such as the arguments.
bt
Backtrace, aka print the current function stack to show where you are in the current program. If main
calls function a()
, which calls b()
, which calls c()
, the backtrace is
c <= current location
b
a
main
up
down
c
, you can move tob
or a
to examine local variables.return
A “core dump” is a snapshot of memory at the instant the program crashed, typically saved in a file called “core”. GDB can read the core dump and give you the line number of the crash, the arguments that were passed, and more. This is very helpful, but remember to compile with (-g) or the core dump will be difficult to debug.
gdb myprogram core
bt
Signals are messages thrown after certain events, such as a timer or error. GDB may pause when it encounters a signal; you may wish to ignore them instead.
handle [signalname] [action]
handle SIGUSR1 nostop
handle SIGUSR1 noprint
handle SIGUSR1 ignore
SIGUSR1
) when it occurs. There are varying levels of ignoring.The Emacs text editor integrates well with GDB. Debugging directly inside the editor is great because you can see an entire screen of code at a time. Use M-x gdb
to start a new window with GDB and learn more here.
printf
works well for tracing. But wrap printf
in alog
function for flexibility.Code speaks, so here it is. Use #define LOG_LEVEL LOG_WARN
to display warnings and above. Use #define LOG_LEVEL LOG_NONE
to turn off debugging.
#include <stdio.h>
#define LOG_NONE 0
#define LOG_ERROR 1
#define LOG_WARN 2
#define LOG_INFO 3
#define LOG_LEVEL LOG_WARN
// shows msg if allowed by LOG_LEVEL
int log(char *msg, int level){
if (LOG_LEVEL >= level){
printf("LOG %d: %s\n", level, msg);
// could also log to file
}
return 0;
}
int main(int argc, char** argv){
printf("Hi there!\n");
log("Really bad error!", LOG_ERROR);
log("Warning, not so serious.", LOG_WARN);
log("Just some info, not that important.", LOG_INFO);
return 0;
}
Spend the time to learn GDB (or another debugging tool)! I know, it’s like telling people to eat their vegetables, but it really is good for you – you’ll thank me lat