209

How does one determine where the mistake is in the code that causes a segmentation fault?

Can my compiler (gcc) show the location of the fault in the program?

Trilarion
  • 9,942
  • 9
  • 61
  • 98
  • 7
    No gcc/gdb cannot. You can find out _where_ the segfault occured, but the actual error could be at a totally different location. –  May 20 '10 at 18:51

6 Answers6

296

GCC can't do that but GDB (a debugger) sure can. Compile you program using the -g switch, like this:

gcc program.c -g

Then use gdb:

$ gdb ./a.out
(gdb) run
<segfault happens here>
(gdb) backtrace
<offending code is shown here>

Here is a nice tutorial to get you started with GDB.

Where the segfault occurs is generally only a clue as to where "the mistake which causes" it is in the code. The given location is not necessarily where the problem resides.

Trilarion
  • 9,942
  • 9
  • 61
  • 98
nc3b
  • 14,524
  • 5
  • 49
  • 63
70

Also, you can give valgrind a try: if you install valgrind and run

valgrind --leak-check=full <program>

then it will run your program and display stack traces for any segfaults, as well as any invalid memory reads or writes and memory leaks. It's really quite useful.

NAND
  • 655
  • 8
  • 22
jwkpiano1
  • 821
  • 5
  • 4
  • 4
    +1 , Valgrind is so much faster / easier to use to spot memory errors. On non-optimized builds with debugging symbols, it tells you _exactly_ where a segfault happened and why. – Tim Post May 21 '10 at 16:38
  • 1
    Sadly my segfault disappears when compiling with -g -O0 and combined with valgrind. – JohnMudd Jan 19 '18 at 22:30
  • 4
    `--leak-check=full` will not help to debug segfaults. It is useful only for debugging memory leaks. – ks1322 Sep 04 '18 at 09:49
  • @JohnMudd I have a segfault only appear about 1% of the input files tested, if you repeat the failed input it will not fail. My problem was caused by multithreading. So far I have not figured out the line of code causing this problem. I am using retry to cover up this problem for now. If use -g option, fault goes away! – Kemin Zhou Apr 14 '20 at 19:25
22

You could also use a core dump and then examine it with gdb. To get useful information you also need to compile with the -g flag.

Whenever you get the message:

 Segmentation fault (core dumped)

a core file is written into your current directory. And you can examine it with the command

 gdb your_program core_file

The file contains the state of the memory when the program crashed. A core dump can be useful during the deployment of your software.

Make sure your system doesn't set the core dump file size to zero. You can set it to unlimited with:

ulimit -c unlimited

Careful though! that core dumps can become huge.

Mathieu Rodic
  • 6,459
  • 2
  • 41
  • 49
Lucas
  • 13,039
  • 12
  • 58
  • 90
18

There are a number of tools available which help debugging segmentation faults and I would like to add my favorite tool to the list: Address Sanitizers (often abbreviated ASAN).

Modern¹ compilers come with the handy -fsanitize=address flag, adding some compile time and run time overhead which does more error checking.

According to the documentation these checks include catching segmentation faults by default. The advantage here is that you get a stack trace similar to gdb's output, but without running the program inside a debugger. An example:

int main() {
  volatile int *ptr = (int*)0;
  *ptr = 0;
}
$ gcc -g -fsanitize=address main.c
$ ./a.out
AddressSanitizer:DEADLYSIGNAL
=================================================================
==4848==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x5654348db1a0 bp 0x7ffc05e39240 sp 0x7ffc05e39230 T0)
==4848==The signal is caused by a WRITE memory access.
==4848==Hint: address points to the zero page.
    #0 0x5654348db19f in main /tmp/tmp.s3gwjqb8zT/main.c:3
    #1 0x7f0e5a052b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)
    #2 0x5654348db099 in _start (/tmp/tmp.s3gwjqb8zT/a.out+0x1099)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /tmp/tmp.s3gwjqb8zT/main.c:3 in main
==4848==ABORTING

The output is slightly more complicated than what gdb would output but there are upsides:

  • There is no need to reproduce the problem to receive a stack trace. Simply enabling the flag during development is enough.

  • ASANs catch a lot more than just segmentation faults. Many out of bounds accesses will be caught even if that memory area was accessible to the process.


¹ That is Clang 3.1+ and GCC 4.8+.

asynts
  • 1,725
  • 2
  • 20
  • 29
  • 1
    This is most helpful to me. I have a very subtle bug that happen randomly with a frequency about 1%. I process large number of input files with (16 major steps; each one done by a different C or C++ binary). One later step will trigger segmentation fault only randomly because of multi-threading. It is hard to debug. This option triggered the debug information output at least it gave me a start point for code review to find the location of the bug. – Kemin Zhou Apr 15 '20 at 03:15
4

All of the above answers are correct and recommended; this answer is intended only as a last-resort if none of the aforementioned approaches can be used.

If all else fails, you can always recompile your program with various temporary debug-print statements (e.g. fprintf(stderr, "CHECKPOINT REACHED @ %s:%i\n", __FILE__, __LINE__);) sprinkled throughout what you believe to be the relevant parts of your code. Then run the program, and observe what the was last debug-print printed just before the crash occurred -- you know your program got that far, so the crash must have happened after that point. Add or remove debug-prints, recompile, and run the test again, until you have narrowed it down to a single line of code. At that point you can fix the bug and remove all of the temporary debug-prints.

It's quite tedious, but it has the advantage of working just about anywhere -- the only times it might not is if you don't have access to stdout or stderr for some reason, or if the bug you are trying to fix is a race-condition whose behavior changes when the timing of the program changes (since the debug-prints will slow down the program and change its timing)

Jeremy Friesner
  • 63,706
  • 14
  • 113
  • 212
2

Lucas's answer about core dumps is good. In my .cshrc I have:

alias core 'ls -lt core; echo where | gdb -core=core -silent; echo "\n"'

to display the backtrace by entering 'core'. And the date stamp, to ensure I am looking at the right file :(.

Added: If there is a stack corruption bug, then the backtrace applied to the core dump is often garbage. In this case, running the program within gdb can give better results, as per the accepted answer (assuming the fault is easily reproducible). And also beware of multiple processes dumping core simultaneously; some OS's add the PID to the name of the core file.

Joseph Quinsey
  • 9,185
  • 10
  • 53
  • 75
  • 5
    and don't forget `ulimit -c unlimited` to enable core dumps in the first place. – James Morris May 20 '10 at 19:14
  • @James: Correct. Lucas already mentioned this. And for those of us who are still stuck in the csh, use 'limit'. And I've never been able to read the CYGWIN stackdumps (but I haven't tried for 2 or 3 years). – Joseph Quinsey May 20 '10 at 19:44