Get Debugging Better!

Get Debugging Better!

By Jonathan Wakely

Overload, 23(127):18-19, June 2015


The GNU debugger has several useful features you may not know. Jonathan Wakely shows us how to save time and pain with some simple tricks.

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

[TUI] https://sourceware.org/gdb/onlinedocs/gdb/TUI.html






Your Privacy

By clicking "Accept Non-Essential Cookies" you agree ACCU can store non-essential cookies on your device and disclose information in accordance with our Privacy Policy and Cookie Policy.

Current Setting: Non-Essential Cookies REJECTED


By clicking "Include Third Party Content" you agree ACCU can forward your IP address to third-party sites (such as YouTube) to enhance the information presented on this site, and that third-party sites may store cookies on your device.

Current Setting: Third Party Content EXCLUDED



Settings can be changed at any time from the Cookie Policy page.