Table of Contents

Finding Segmentation Faults using GDB

It never fails: you play with increasingly complex memory manipulations and logic, and whoops, accidentally access something that isn't there. Due to the memory protection provided by your Operating System, you program gets zapped, and you may see a message to the likes of:

Segmentation fault (core dumped)

All this means is that you tried accessing memory that wasn't yours. This can be caused by overstepping the bounds of your array, or even trying to dereference a NULL (without having proper checks in place).

In sufficiently complex code, as is so often the case in data and discrete, finding the location of the segfault is sometimes a non-trivial undertaking. When your manual debugging efforts fall short (you should ALWAYS start with manual debugging), you can make use of the debugger (gdb) to aid you in discovering sources of things like Segmentation faults.

This helpance will guide you through using it for that purpose.

Compiling with debug support

The trick with using the debugger is that you need to compile your code with debug support. If compiling manually, adding the -g option to your gcc command-line is needed.

If there's a Makefile assisting you, often you'll want to do something like:

$ make clean; make debug

To clear everything out, and recompile all the files for debug support.

Running the program through gdb

Next, you run the program through gdb. This is done as follows:

$ gdb ./yourprogram

Provide any command line arguments

If there are needed or desired command-line arguments, you don't provide them at the previous step, but here, with the run command:

$ run arg1 arg2 arg3

Or, if the program runs without any arguments (such as a unit test), you just issue the run command:

$ run

Locating the Segmentation fault

The program should then run as usual, encounter the Segmentation fault, and terminate.

However, through running it in gdb, when the program terminates, you are still in gdb, and useful runtime information is presented to you.

For example:

Program received signal SIGSEGV, Segmentation fault.
0x0000000000402017 in getpos (myList=0x6c1190, given=0x0) at pos.c:26
26                      if((given -> right == NULL) &&

In this example, the Segmentation fault occurred on THAT line (line 26, in the pos.c file within the getpos() function.

Maybe that is all the help you need. Clearly, in this instance, given is NULL, and lacking the proper checks, will hit this if() statement and attempt to dereference that NULL.

More information: the backtrace call stack

Sometimes, even if you know exactly where the problem occurs, you need more information. Specifically, HOW did we get to this point?

To do that, at the gdb prompt, we type in the bt command (shortcut for backtrace), and gdb happily tells us how we got here:

(gdb) bt
#0  0x0000000000402017 in getpos (myList=0x6c1190, given=0x0) at pos.c:26
#1  0x0000000000401ea2 in obtain (myList=0x6c1190, thatNode=0x7fffffffe088) at obtain.c:39
#2  0x000000000040195a in sortlist (myList=0x6c1190, mode=0) at sort.c:45
#3  0x0000000000401231 in main () at unit-sortlist.c:105
(gdb) 

It goes from most recent to least recent.

For instance, we see at the very top, our familiar line 26 in pos.c/getpos()

But HOW did we get here? Well, we got there via a call to the obtain() function, which took place on line 39. If we're unsure what the problem may be, we might want to also investigate (see what is going on, add in debug statements, etc.) obtain() in the code leading up to the getpos() function call on line 39 in that function.

That also only gives us part of the story: HOW was obtain() called? Well, it was called from the sortlist() function, in sort.c, on line 45. Another avenue to investigate (see what is going on, add in debug statements, etc.)

Finally, where was sortlist() called from? Turns out, from the main() of our program, on line 105. For a complex program (like a unit test), this could be quite useful information, as we can see the conditions leading up to the call to sortlist(), which may provide some of the most fundamental and useful clues for determining how a segfault ultimately occurred (is main() calling using parameters we haven't checked for, such as a NULL?)

This is an excellent way to discover where a Segmentation fault is occurring, and even do some initial investigation, to get a better context over what the conditions were that lead to the Segmentation fault.

(When done, the 'quit' command gets us out of gdb)