views:

1373

answers:

7

As a developer, how do you use gdb to track down bugs within your code? What techniques tricks do you use to make your life easier?

+2  A: 

In general you find something that isn't how it should be, and work backwards until you understand why.

The most obvious is the most useful: Setting a breakpoint on a function or line number and walking through the code line by line.

Another handy tip is to have show functions for all your structures/objects even if they are never used in your program, because you can run these functions from within gdb:

gdb> p show_my_struct(struct)

My custom display of Foo:
   ...

Watchpoints can be really handy too, but may slow down your program a lot. These break the flow when the value of a variable or address changes.:

gdb> watch foo
Watchpoint4: foo
gdb>
Andrew Johnson
Why not simply "p my_struct", instead of "p show_my_struct(my_struct)"?
sigjuice
Because you can decode the flags field, run validation code, etc. It's not simply a dump of the values of the structure, you instead can dump the meaning, including relevant objects that are pointed to. For example, if obj1 has a list of obj2s, then you could nest a show of all the obj2s in there.
Andrew Johnson
+3  A: 

Some hints:

  • use a graphical frontend (kdbg is quite good, ddd is at least better than command-line gdb, kdevelop has a nice gdb frontend but has some bgs, nemiver looks quite nice as well but is still in the works)
  • make sure to have debug symbols and source code for all important parts (your own code and also some system libs)
    • on RedHat, you can install the -debuginfo packages to make both symbols and source code magically appear in the debugger - really cool because you can looks into libc function calls etc.
    • on Debian/Ubuntu, you can install the -dbg packages to get symbols; installing appropriate source files for system packages seems to be difficult, though
  • I tend to add assert() and abort() calls in places that should not be reached, or in places that I want to study (some kind of heavy-weight breakpoint)
  • ideally the assert() or abort() calls should be wrapped in some method or macro that only enables them in Debug releases, or even better that only enables them if a certain env var is set
  • install a signal handler for SIGSEGV and SIGABRT; personally I check if a certain env var is set before installing the handlers; and in the handler I execute a hardcoded external command which usually lives somewhere in ~/.local/bin/; that command might then start kdbg and attach it to the crashing app. Voila, debugger pops up the moment your app does something bad.
  • If you use unit tests, you could similarly attach a debugger whenever a test case fails, to inspect the app then.
oliver
Actually asserts are normally removed when building without debugging (-g). From ASSERT(3): "If the macro NDEBUG was defined at the moment <assert.h> was last included, the macro assert() generates no code, and hence does nothing at all."
David Holm
I'm not sure I would call that "normally". I usually keep my asserts, and I also think the very fact that you have to use the NDEBUG macro (as opposed to, say, not defining a macro "DEBUG") hints that the designers of assert agreed that asserts usually should stay.
Thomas Padron-McCarthy
A: 

Use ddd, a visual front-end for gdb. It lets you do things easily with a few mouse clicks and visualise how the code works, plus in the debugger console you have an intercative gdb.

Rob Kam
+1  A: 

You can also use Geany.

codeguru
+1  A: 

One particularly useful feature of gdb is its ability to inspect the final state of a program that's crashed.

To inspect a crash dump (or core file, as it's more commonly called), start gdb as follows:

gdb <program-name> <core-file>

For example:

gdb a.out core

Upon running this command on a core file, gdb will tell you how the program terminated and display where in the program the error occurred:

Program terminated with signal 11, Segmentation fault.
#0  0x08048364 in foo () at foo.c:4
4         *x = 100;

In the example above, you can see that the program terminated with a segmentation fault while trying to assign a value to a pointer. By typing backtrace (or bt or where) at gdb's prompt, you can view the program's complete backtrace:

(gdb) backtrace
#0  0x08048364 in foo () at foo.c:4
#1  0x0804837f in main () at foo.c:9

At this point, you know that main() called foo() and foo() crashed on line 4 while trying to assign a value to *x. Many times, this provides enough information to allow you to fix the bug.

Emerick Rogul
+1  A: 

I do a lot of parallel-program dev, so I've found that using a simple wrapper in python/ruby that allows me to have gdb attached to all processes on all nodes and communicating back to me is extraordinarily helpful (I haven't found a better way if anyone knows of one, not to hijack the thread, though...)

I'm not sure how experienced the OP is, so:

The GDB docs are pretty nice and all encompassing. The first chapter is a good introduction to all the basics.

http://www.gnu.org/software/gdb/documentation/

Although not gdb, they are related: I've personally found that breaking complex lines down to aid in determining which statements are erroring helps.

Also, Valgrind (http://valgrind.org/) is really nice/usefull for tackling buffer-overflows and the like (I haven't had luck with gdb for doing this.

jimktrains
A: 

Basic but very useful - Use the text gui with the option -tui.

therefromhere