backtrace
【用途】用戶態或者內核態程序異常退出時回溯堆棧信息
【原理】通過對當前堆棧的分析,回溯上層函數在當前棧中的幀地址,直至頂層函數。幀地址是指在棧中存在局部變量、上一級函數返回地址、寄存器值的內存空間。由於不同處理器堆棧實現不同(向上增長和向下增長),此功能的具體實現是編譯器內建的__buildin_frame_address及__buildin_return_address函數。如果編譯器不支持此函數,也可以自己實現該函數。
【接口說明】具體說明可以參考man backtrace幫助文檔
execinfo.h
int backtrace (void **buffer, int size)
The backtrace function obtains a backtrace for the current thread, as a list of pointers, and places the information into buffer.
char ** backtrace_symbols (void *const *buffer, int size)
The backtrace_symbols function translates the information obtained from the backtrace function into an array of strings. The argument buffer should be a pointer to an array of addresses obtained via the backtrace function, and size is the number of entries in that array (the return value of backtrace).
void backtrace_symbols_fd(void *const *buffer, int size, int fd)
【實例】
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <execinfo.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#define PRINT_DEBUG
#define MAX_BACKTRACE_LEVEL 10
#define BACKTRACE_LOG_NAME "backtrace.log"
static void show_reason(int sig, siginfo_t *info, void *secret){
void *array[MAX_BACKTRACE_LEVEL];
size_t size;
#ifdef PRINT_DEBUG
char **strings;
size_t i;
size = backtrace(array, MAX_BACKTRACE_LEVEL);
strings = backtrace_symbols(array, size);
printf("Obtain %zd stack frames.\n", size);
for(i = 0; i < size; i++)
printf("%s\n", strings[i]);
free(strings);
#else
int fd = open(BACKSTRACE_LOG_NAME, O_CREAT | O_WRONLY);
size = backtrace(array, MAX_BACKTRACE_LEVEL);
backtrace_symbols_fd(array, size, fd);
close(fd);
#endif
exit(0);
}
void die() {
char *str1;
char *str2;
char *str3;
char *str4 = NULL;
strcpy(str4, "ab");
}
void let_it_die() {
die();
}
int main(int argc, char **argv){
struct sigaction act;
act.sa_sigaction = show_reason;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_RESTART | SA_SIGINFO;
sigaction(SIGSEGV, &act, NULL);
sigaction(SIGUSR1, &act, NULL);
sigaction(SIGFPE, &act, NULL);
sigaction(SIGILL, &act, NULL);
sigaction(SIGBUS, &act, NULL);
sigaction(SIGABRT, &act, NULL);
sigaction(SIGSYS, &act, NULL);
let_it_die();
return 0;
}
【調試】
1) 編譯
huawei@WUH1000002965:~/test$ gcc backtrace_test.c -o backtrace_test -g –rdynamic
(注:-rdynamic,這個option是傳遞給linker的,linker會將symbol放到.dydym table中,這樣backtrace_symbols才能獲取到地址對應的symbol。所以即使是使用了-g來編譯程序,如果不使用-rdynamic的話,backtrace_symbols也找不到地址對應的symbol。這是backtrace系列函數的一個缺陷)
2) 運行
huawei@WUH1000002965:~/test$ ./backtrace_test
Obtain 7 stack frames.
./backtrace_test() [0x40096e]
/lib/x86_64-linux-gnu/libc.so.6(+0x364a0) [0x7f2ff171c4a0]
./backtrace_test(die+0x18) [0x400a03]
./backtrace_test(let_it_die+0xe) [0x400a1d]
./backtrace_test(main+0xf6) [0x400b15]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed) [0x7f2ff170776d]
./backtrace_test() [0x400889]