Planning to write a series of posts about debugging & trouble shooting tricks. And I’d like to make backtrace as a start. As a typical programmer(AKA nerd), I’d like to jump to the topic directly before run into blah-blah.
Everyone knows, bug is free, so it may be at everywhere, to check out your free gift(s) sending to your clients, you may consider backtrace.
It’s a great way for trouble shooting. It’ll be terribly useful when your program run into core dump.
And it’s really simple, all you need to do is:
1. Register SIGSEGV and use signal(SIGSEGV, &your_function); to catch the signal.
2. Put the code to the head of your main function.
Here I copied a code snippet for you:
#include <stdio.h> #include <stdlib.h> #include <stddef.h> #include <execinfo.h> #include <signal.h> void dump(int signo) { void *buffer[30] = {0}; size_t size; char **strings = NULL; size_t i = 0; size = backtrace(buffer, 30); fprintf(stdout, "Obtained %zd stack frames.nm\n", size); strings = backtrace_symbols(buffer, size); if (strings == NULL) { perror("backtrace_symbols."); exit(EXIT_FAILURE); } for (i = 0; i < size; i++) { fprintf(stdout, "%s\n", strings[i]); } free(strings); strings = NULL; exit(0); } void func_c() { //This is the "free gift" you send to your clients *((volatile char *)0x0) = 0x9999; } void func_b() { func_c(); } void func_a() { func_b(); } int main(int argc, const char *argv[]) { if (signal(SIGSEGV, dump) == SIG_ERR) perror("can't catch SIGSEGV"); func_a(); return 0; }
Save it with filename like backtrace1.c, and compile it with command:
gcc backtrace1.c -o test
gcc -g -rdynamic backtrace1.c -o test
Run the newly compiled program
./test
When your free gift shows up, and caused a segmentation fault(core dump), you’ll see something like this:
=================== call trace ====================== Obtained 7 stack frames.nm ./test(dump+0x45) [0x80487a9] [0x736400] ./test(func_b+0x8) [0x804886c] ./test(func_a+0x8) [0x8048876] ./test(main+0x33) [0x80488ab] /lib/libc.so.6(__libc_start_main+0xe6) [0xbbccc6] ./test() [0x80486d1]
Dont panic. Dont you like the lovely address numbers? no? Okay, a few more steps you’ll get the truth:
1. Use objdump to disassemble the executable file
objdump -d test > test.s
2. Check the assemble codes for the displayed addresss: 0x804886c
08048864 <func_b>: 8048864: 55 push %ebp 8048865: 89 e5 mov %esp,%ebp 8048867: e8 eb ff ff ff call 8048857 <func_c> 804886c: 5d pop %ebp 804886d: c3 ret
As you can see, the previous step was “call 8048857 <func_c>“, we can make the conclusion that it was <func_c> where went wrong, although address 804886c did not locate direct to the error code.
3. A more effecial tool than disassembling the objects: addr2line
You can use addr2line to convert it code line.
addr2line 0x804886C -e test -f -C
Truth revealed.