Tip 9: gdb
The GNU Debugger (gdb) is extremely useful as a quick way to find problems in codes. I also like it because it is available on MANY platforms. If it is not on your current platform, it is probably easily available as a pre-built package. Here is a quick start guide to the most useful features. More is available in various tutorials and the man page. You can abbreviate almost any gdb command by typing just enough characters to provide a unique match. Tab completion also works.
Running gdb
gdb can be run in three ways:
1)
gdb program
(or gdb --args program -my -program -argument) will load your program and give a gdb prompt.
2)
gdb program core
will run your program using the state saved in a core dump.
3)
gdb program pid
will attach gdb to an already running program. (There are various ways to attach gdb over a network for embedded processors too...)
Once gdb is running, you have a prompt:
(gdb)
at which you can tell it to run:
(gdb) run
At any time, you can press Ctrl-C to interrupt and return to the debug prompt. Once there, you can single step into functions ("step" or just "s") or go to the next instruction ("next" or "n"). If you want to keep running, you can type "cont" or "c".
Investigating code
Also at the prompt, you may want to see where you are at in the program. To see the current code, you can type "list" or to see the current call stack you can type "backtrace" (or "bt").
If your program experiences a signal (segfault, bus error, fork, etc), it will return to the prompt. At this point, you want to investigate why the signal happened. The call stack is useful for this:
Program received signal SIGABRT, Aborted. 0x0000003977230265 in raise () from /lib64/libc.so.6 (gdb) bt #0 0x0000003977230265 in raise () from /lib64/libc.so.6 #1 0x0000003977231d10 in abort () from /lib64/libc.so.6 #2 0x00000039772296e6 in __assert_fail () from /lib64/libc.so.6 #3 0x0000000000453276 in my_function() from foo.C ... #11 0x000000000047a94e in main (argc=6, argv=0x7fff8732ad68) at main.C:53 (gdb)
At this point, go to the failing function by typing:
(gdb) up #1 0x0000003977231d10 in abort () from /lib64/libc.so.6 (gdb) #2 0x00000039772296e6 in __assert_fail () from /lib64/libc.so.6 (gdb) #3 0x0000000000453276 in my_function() from foo.C (gdb)
Note that pressing "enter" on a blank line will repeat the last command.
Querying Values
At any point, you can print a variable value with by using:
(gdb) print i $2 = 0
For structures or classes, it will show the nested values:
(gdb) print nodes[i] $4 = (node_t *&) @0x31d3900: 0x3123270 (gdb) print *nodes[i] $5 = {static node_id = 18, name = {static npos = 18446744073709551615, etc.
You can also access data structures:
(gdb) print nodes[i]->name $6 = {static npos = 18446744073709551615, _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0x3123258 "1"}}
which shows the char data in the string class is "1". The structures can get pretty nasty if there is a lot in them.
Breakpoints
You can set breakpoints in several ways:
1) By function
(gdb) break main Breakpoint 3 at 0x47a89d: file main.C, line 39.
2) By line number
(gdb) break main.C:39 Note: breakpoint 3 also set at pc 0x47a89d. Breakpoint 4 at 0x47a89d: file main.C, line 39.
To see your breakpoints, type:
(gdb) info b Num Type Disp Enb Address What 3 breakpoint keep y 0x000000000047a89d in main at main.C:39 4 breakpoint keep y 0x000000000047a89d in main at main.C:39
To remove a breakpoint, type:
(gdb) del b 3 (gdb) info b Num Type Disp Enb Address What 4 breakpoint keep y 0x000000000047a89d in main at main.C:39
Watches
You can also "watch" a value and break when it changes.
(gdb) watch i Hardware watchpoint 5: i
This is often useful for debugging corrupt stacks due to memory mismanagement.