一般察看函數運行時堆棧的方法是使用GDB之類的外部調試器,但是,有些時候為了分析程序的BUG,(主要針對長時間運行程序的分析),在程序出錯時打印出函數的調用堆棧是非常有用的。
在頭文件"execinfo.h"中聲明了三個函數用於獲取當前線程的函數調用堆棧
Function: int backtrace(void **buffer,int size)
該函數用與獲取當前線程的調用堆棧,獲取的信息將會被存放在buffer中,它是一個指針列表。參數 size 用來指定buffer中可以保存多少個void* 元素。函數返回值是實際獲取的指針個數,最大不超過size大小,在buffer中的指針實際是從堆棧中獲取的返回地址,每一個堆棧框架有一個返回地址,注意某些編譯器的優化選項對獲取正確的調用堆棧有干擾,另外內聯函數沒有堆棧框架;刪除框架指針也會使無法正確解析堆棧內容。
Function: char ** backtrace_symbols (void *const *buffer, int size)
backtrace_symbols將從backtrace函數獲取的信息轉化為一個字符串數組. 參數buffer應該是從backtrace函數獲取的數組指針,size是該數組中的元素個數(backtrace的返回值)。函數返回值是一個指向字符串數組的指針,它的大小同buffer相同.每個字符串包含了一個相對於buffer中對應元素的可打印信息.它包括函數名,函數的偏移地址,和實際的返回地址。
現在,只有使用ELF二進制格式的程序和苦衷才能獲取函數名稱和偏移地址.在其他系統,只有16進制的返回地址能被獲取.另外,你可能需要傳遞相應的標志給鏈接器,以能支持函數名功能(比如,在使用GNU ld的系統中,你需要傳遞(-rdynamic))。
該函數的返回值是通過malloc函數申請的空間,因此調用這必須使用free函數來釋放指針.
注意:如果不能為字符串獲取足夠的空間函數的返回值將會為NULL
Function:void backtrace_symbols_fd (void *const *buffer, int size, int fd)
backtrace_symbols_fd與backtrace_symbols 函數具有相同的功能,不同的是它不會給調用者返回字符串數組,而是將結果寫入文件描述符為fd的文件中,每個函數對應一行.它不需要調用malloc函數,因此適用於有可能調用該函數會失敗的情況。
下面是一個使用backtrace捕獲異常並打印函數調用堆棧的例子:
#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.\n", size); for (i = 0; i < size; i++) printf("%s\n", strings[i]); free(strings); char cmd[64] = "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\n", 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(); }
我本機測試打印出的信息如下:
Obtained 7 stack frames. /root/workspace/test/Debug/test(__gxx_personality_v0+0x12d) [0x80486c1] [0x71b440] /root/workspace/test/Debug/test(__gxx_personality_v0+0x2ac) [0x8048840] /root/workspace/test/Debug/test(__gxx_personality_v0+0x2c0) [0x8048854] /root/workspace/test/Debug/test(__gxx_personality_v0+0x339) [0x80488cd] /lib/libc.so.6(__libc_start_main+0xdc) [0xbf3e9c] /root/workspace/test/Debug/test(__gxx_personality_v0+0x5d) [0x80485f1] print_reason /root/workspace/test/Debug/../main.cpp:15 ?? ??:0 die() /root/workspace/test/Debug/../main.cpp:51 test1() /root/workspace/test/Debug/../main.cpp:56 main /root/workspace/test/Debug/../main.cpp:65 ?? ??:0 _start ??:0
