Introduction to Debugging

GNU Debugger - gdb

GDB Graphical Interfaces

  • gdbgui, starts fabulous GDB interface in local webserver
  • gdb dashboard, Python-based, gorgeous replacement of GDB command-line interface


Install gdb on the VM:

$ sudo apt-get update && sudo apt-get install gdb
# Confirm with <ENTER>


Using test suite as an example for debugging with gdb:

$ gdb ./testsuite.bin

This starts program testsuite.bin and halts execution at its entry point (usually the main function entry point).

First, let’s switch to a more useful view. In gdb, enter <CTRL-X> 2. This switches to a TUI (terminal user interface, a GUI in the terminal) consisting of three view sections: the source code at the top with assembly below, and the gdb command line at the bottom.

Or, write the content of this file to ~/.gdbinit for colored output and overall prettier gdb (incompatible with TUI apparently).

Searching for gdb dotfiles on github gives lots of other coder’s gdb tweaks like this one.

Now, enable pretty printing for displaying values:

(gdb) set print pretty on

You will find the integrated help system of gdb very useful:

(gdb) help
# display help for a command category like "running":
(gdb) help running
# display help for a specific command:
(gdb) help next

To debug a specific function like vector__push_back, add a break point at the function’s entry point using the gdb command break.

You can use auto-completion to choose from all functions named ’vector__’ like:

(gdb) break vector__<TAB><TAB> (select a function name) <ENTER>

To run the program, use the gdb command run. Execution will halt at the first break point.

(gdb) run

To continue execution in single operations step-by-step, use next or n:

(gdb) n

To repeat the last command, just hit <ENTER>.

Show the value of a variable with print (ensure that pretty-printing is enabled as described above):

(gdb) print *data

Memory Debugging: valgrind

Install valgrind on the VM:

$ sudo apt-get update && sudo apt-get install valgrind
# Confirm with <ENTER>

From the valgrind quick start tutorial:

Example program:

  #include <stdlib.h>

  void f(void)
     int* x = malloc(10 * sizeof(int));
     x[10] = 0;        // problem 1: heap block overrun
  }                    // problem 2: memory leak -- x not freed

  int main(void)
     return 0;

Using valgrind to check for memory leaks:

$ valgrind --leak-check=yes myprog [arg1 arg2 ...]

Output from memory check:

  ==19182== Invalid write of size 4
  ==19182==    at 0x804838F: f (example.c:6)
  ==19182==    by 0x80483AB: main (example.c:11)
  ==19182==  Address 0x1BA45050 is 0 bytes after a block of size 40 alloc'd
  ==19182==    at 0x1B8FF5CD: malloc (vg_replace_malloc.c:130)
  ==19182==    by 0x8048385: f (example.c:5)
  ==19182==    by 0x80483AB: main (example.c:11)