■ API
// 获取将backstrace信息,将地址存到buffer中。
// 参数size指定buffer的最大值,返回值则是backstrace的实际大小
int backtrace(void **buffer, int size)
// 根据buffer指定的地址,返回符号信息。参数size指定返回符号信息的大小
char ** backtrace_symbols(void *const *buffer, int size)
// 类似backtrace_symbols()函数,但是不需要malloc空间来存放符号信息,
// 而是将结果写到文件描述符fd所代表的文件中
void backtrace_symbols_fd(void *const *buffer, int size, int fd)
注意:
. 使用函数backtrace_symbols()或者backtrace_symbols_fd()时,需要用-rdynamic编译才能得到正确的符号名,否则只能得到偏移地址。
. 为了可以判断地址对应的代码行号,需要编译追加-g选项。
■ 实例
① 代码
1 //gcc test.c -g -rdynamic -o test 2 #include <execinfo.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <fcntl.h> 6 7 #define PRINT_DEBUG 8 9 /* Obtain a backtrace and print it to stdout. */ 10 void print_trace(void) 11 { 12 void *array[10]; 13 size_t size; 14 char **strings; 15 size_t i; 16 17 size = backtrace(array, 10); 18 #ifdef PRINT_DEBUG 19 strings = backtrace_symbols(array, size); 20 printf("Obtained %zd stack frames. ", size); 21 for(i = 0; i < size; i++) 22 printf("%s ", strings[i]); 23 free(strings); 24 #else 25 int fd = open("err.log", O_CREAT | O_WRONLY); 26 backtrace_symbols_fd(array, size, fd); 27 close(fd); 28 #endif 29 30 } 31 32 /* A dummy function to make the backtrace more interesting. */ 33 void dummy_function(void) 34 { 35 print_trace(); 36 } 37 38 int main(void) 39 { 40 dummy_function(); 41 return 0; 42 }
② 结果
root@ubuntu:/home/linux/develop/backtrace# gcc test.c -g -rdynamic -o test
root@ubuntu:/home/linux/develop/backtrace# ./test
Obtained 4 stack frames.
./test(print_trace+0x19) [0x80486ad]
./test(dummy_function+0xb) [0x8048719]
./test(main+0xb) [0x8048726]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x7fb113]
root@ubuntu:/home/linux/develop/backtrace# addr2line 0x80486ad -f -e test
print_trace
/home/linux/develop/backtrace/test.c:17
root@ubuntu:/home/linux/develop/backtrace# addr2line 0x8048719 -f -e test
dummy_function
/home/linux/develop/backtrace/test.c:36
root@ubuntu:/home/linux/develop/backtrace# addr2line 0x8048726 -f -e test
main
/home/linux/develop/backtrace/test.c:41
■ 应用
reset时,获取进程的栈信息
#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <execinfo.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include <unistd.h> #define PRINT_DEBUG static void print_reason(int sig) { void *array[10]; size_t size; size = backtrace(array, 10); #ifdef PRINT_DEBUG char **strings; int i; strings = backtrace_symbols(array, size); printf("Obtained %d stack frames. ", size); for (i = 0; i < size; i++) printf("%s ", strings[i]); free(strings); char cmd[256] = "addr2line -C -f -e "; char* prog = cmd + strlen(cmd); readlink("/proc/self/exe", prog, sizeof(cmd) - strlen(cmd) - 1);// 获取进程的完整路径 FILE* fp = popen(cmd, "w"); if (fp != NULL) { for (i = 0; i < size; ++i) { fprintf(fp, "%p ", array[i]); } pclose(fp); } #else int fd = open("err.log", O_CREAT | O_WRONLY); backtrace_symbols_fd(array, size, fd); close(fd); #endif exit(0); } void die() { char *test1; char *test2; char *test3; char *test4 = NULL; strcpy(test4, "ab"); } void test1() { die(); } int main(int argc, char **argv) { struct sigaction myAction; myAction.sa_handler = print_reason; sigemptyset(&myAction.sa_mask); myAction.sa_flags = SA_RESTART | SA_SIGINFO; sigaction(SIGSEGV, &myAction, NULL); // 无效内存引用 sigaction(SIGABRT, &myAction, NULL); // 异常终止 test1(); }