The GNU Debugger (GDB) is a powerful tool, but if you’re used to working in an IDE then using a command-line debugger can be daunting and may seem to be lacking features you take for granted in an IDE. This article has some simple tips that might help you have a more pleasant debugging experience, and might inspire you to read the documentation [GDB] to see what other tricks are waiting to be discovered.
The first tip, and maybe the most important, is to make sure you’re using a recent version. Support for debugging C++ code got much better with GDB 7.0 and has continued to improve since then. If you’re using anything older than GDB 7.0 you should upgrade right away, and it’s probably worth upgrading anything older than a couple of releases (GDB 7.8.2 and 7.9 were both released in early 2015). If you can’t get a pre-built version for your OS then compiling GDB from source is very easy, just download the tarball, unpack it, run configure (setting your preferred install directory with --prefix=dir
) and run make.
One of the simplest GDB features, but one I always miss when using the venerable ‘dbx’ debugger on ‘proper’ UNIX machines, is the ability to use abbreviations for commands. Any unambiguous prefix for a command will run the full command, so instead of typing print foo
you only need p foo
and instead of break source.cc:325
just br source.cc:325
(and while not strictly a prefix of the full command, bt
will print a stack trace just like backtrace
).
You can also very easily create your own commands by defining them in your personal ~/.gdbinit file, which gets run by GDB on startup. I use the following to quit without being asked to confirm that I want to kill the process being debugged:
define qquit set confirm off quit end document qquit Quit without asking for confirmation. end
This allows me to use qquit
(or just qq
) to exit quickly. The document
section provides the documentation that will be printed if you type help qq
at the gdb prompt.
Sometimes stepping through C++ code can be very tedious if the program keeps stepping into tiny inline functions that don’t do anything interesting. This is very obvious in C++11 code that makes heavy use of std::move
and std::forward
, both functions that do nothing except cast a variable to the right kind of reference. The solution is to tell gdb not to bother stepping into uninteresting functions (or ones that get called a lot but which you know are not the source of the problem you’re debugging). Running the skip
command with no arguments will cause the current function to be skipped over next time it is reached, instead of stepping into it. You can also use skip FUNCTION
to cause a named function to be skipped instead of the current one, or skip file FILENAME
to skip whole files (as with most commands, run help skip
at the gdb prompt for more information). Unfortunately the skip
command treats every specialization of a function template as a separate function and there’s no way to skip over all specializations of say, std::move
, but if you skip each one as you step into it at least you know you won’t step into that particular specialization again. I define the following command in my .gdbinit to make this even easier:
define skipfin dont-repeat skip finish end document skipfin Return from the current function and skip over all future calls to it. end
This lets me use skipfin
to mark the current function to be skipped in future and to finish running it and return to the caller. The dont-repeat
line tells gdb that (unlike most built-in commands), hitting enter again after running skipfin
should not run skipfin
again, so that I don’t accidentally finish running the caller and mark that to be skipped as well!
Another useful entry in my .gdbinit is set history save
, which causes gdb to save your command history when exiting, so you can use the cursor keys to scroll through your history and easily create the same breakpoints or watchpoints as you used in an earlier debugging session.
The GDB feature that I most wish I’d known about sooner is the ‘TUI’ mode, which is activated by the -tui
command-line option, or can be turned on and off in an existing debugging session with Ctrl-X Ctrl-A [TUI]. This splits the terminal window horizontally, with the bottom pane showing the usual prompt where you type commands and get output, and the top pane showing the source code for the function being debugged, just like your IDE would. This gives you a much more immediate view of the code than using list
to print out chunks of it. One thing to be aware of is that the TUI mode changes the behaviour of the cursor keys, so they scroll up and down in the source code rather than through your command history. If you’re familiar with them from the terminal, you can still use readline key bindings (Ctrl-P, Ctrl-N etc.) to scroll through the command history.
After -tui
, the command-line option I most often use when starting gdb is --args
. This can be used to start debugging a program with a specific set of arguments, so instead of running gdb ./a.out
and then setting arguments for it with set args a b c
then running it with run
, you can start gdb as gdb --args ./a.out a b c
and then just run
. This is very useful when the program needs a long and complicated set of arguments, as you don’t need to find them and copy & paste them into gdb, just add gdb --args
before the usual command to run the program.
One of the most useful features of modern version of GDB is the embedded Python interpreter. This allows you to write pretty printers for your own types (or use the ones that come with GCC for printing standard library types such as containers). Defining pretty printers for the types in your system can be very useful, and although it’s not too complicated there isn’t room to explain here, however the embedded Python interpreter is also very useful for running simple one-liners without leaving gdb. For example if you have a time_t
variable containing a unix timestamp you can easily print it using Python’s datetime module:
(gdb) python import datetime (gdb) python print datetime.datetime.fromtimestamp(1425690208) 2015-03-07 01:03:28
If what you want to do isn’t suitable for a one-liner you can create multiple-line blocks of Python by entering just python
on a line on its own, and then end the block with end
. Many of gdb’s features are exposed via a python API (‘import gdb’) [Python] that lets you inspect variables, so you can examine their value, type, members etc.
None of these tips are groundbreaking, but I hope they give an idea of ways you can customise your debugging experience and define shortcuts to simplify the most repetitive tasks.
References
[GDB] https://sourceware.org/gdb/onlinedocs/gdb/index.html#Top
[Python] https://sourceware.org/gdb/onlinedocs/gdb/Python-API.html#Python-API
Overload Journal #127 - June 2015 + Programming Topics
Browse in : |
All
> Journals
> Overload
> o127
(7)
All > Topics > Programming (877) Any of these categories - All of these categories |