Getting a stack backtrace in C

Published

I wanted to be able to show some useful debugging information when my C program hits an unrecoverable error (or an error it's not worth trying to recover from in this case, like out of memory). There's no way to do it with what's in standard C, but there is a function for it in glibc called backtrace(3).

Here's a super simple function I wrote to do it, something I call after writing an error message to stderr:

void abort_with_backtrace (void) {
    void *buffer[10];
    int size = backtrace(buffer, 10);
    backtrace_symbols_fd(buffer, size, STDERR_FILENO);
    abort();
}

That gives me up to ten levels of call stack, something like this when compiled with debugging symbols:

./foo[0x41a346]
./foo(foo_chkstruct+0xc2)[0x41a426]
./foo[0x4121f4]
./foo[0x4122ea]
./foo(foo_munge_ast+0x18)[0x413153]
./foo(main+0x139)[0x4052d7]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x4e5a830]
./foo(_start+0x29)[0x404be9]

Aside from compiling with the -g option to get debugging symbols, you also have to link with the -rdynamic option for some reason.

Even then, as you can see above, it's not able to give you the names or location of functions for all the stack frames. In particular it only shows a hex address for static functions. You can usually recover that information after the fact using the addr2line program from binutils, but it's a bit awkward:

$ addr2line -e foo -p -f 0x41a346
abort_with_backtrace at /home/geoff/work/foo/csrc/util.c:35

Hmmm, this whole article may just indicate that I shouldn't be trying to write a compiler in C.