Debugging with GDB
Last week during a training course for the IOI this year, one of my friends called Patrik, showed me some useful GDB tricks which I want to share here. Let’s start with a simple example program “perm.cpp”, which is intended for printing out all permutations of a given sequence S. Please note that this program will produce a (wanted) run-time error which we are going to debug next.
#include <iostream> #include <algorithm> #include <vector> #include <iterator> using namespace std; template <class T> void perm(vector<T> &S, int i=0) { if (static_cast<size_t>(i) >= S.size()) { copy(S.begin(), S.end()-1, ostream_iterator<T>(cout, " ")); cout << S.back() << endl; return; } for (size_t j = i; j < S.size(); j++) { swap(S[i], S[j]); perm(S, i); swap(S[i], S[j]); } } int main() { string seq("abcd"); vector<char> S(seq.begin(), seq.end()); perm(S); }
Now compile and run the program by typing “make perm && ./perm” for example. The program should abrupt shortly with the message “Segmentation fault”. In order to be able to debug the program, we must compile it with -g to generate debug information. The command “CPPFLAGS=’-g’ make perm && (./perm || gdb ./perm)” (which is probably worth an alias) will do that for us and whenever the program exits abnormally, it will start gdb automatically.
We can start the program in GDB by typing “run” (optionally followed by command line arguments or a redirection of the input stream). After the program has terminated again, we can examine the trace of the function calls by typing backtrace. As we can see immediately our perm() function is called far more often than 4! = 24 and we are probably dealing with an stack overflow.
So let’s examine the problem a bit deeper by setting a breakpoint at line 8, which is the beginning of our perm() function, by typing “break 8″. Next we set up some commands which should be executed automatically whenever the breakpoint is reached by typing “commands 1″ (1 is the ID associated with our breakpoint) followed by the following script:
p i c end
This script will print the value of the variable i each time and afterwards the program will continue again. The “end” is just needed to exit the input mode. As we can see (and probably have notices before), the value of i never changes because the increment is missing. The right line is of course “perm(S, i+1);”.
Some other useful commands are “info breakpoints” to get a list of all the breakpoints currently active and “step” or “next” to execute the program line by line. Another interesting feature of Linux are core-files which are written whenever a program terminates abnormally, however your distribution might have disabled them by default. Type “ulimit -c unlimited” to remove the restriction. With the help of the executable and the core file you can debug the program even when you are sitting in front of an other machine.
I hope this short introduction will help you to debug your future applications without all those nasty printf’s in every line, however GDB can do far more for you. For bigger applications dtrace and the Linux clone systemtap might also prove quite useful.
Update: I have looked a bit deeper at the manual and explored that compiling with “-ggdb3″ makes debugging a bit easier. But the kick-ass feature is probably C-x a and C-x s afterwards. Do you know any other tricks?

Leave a Reply