程序崩潰時,Java 等托管語言自動拋出完整堆棧跟蹤,而 C/C++ 默認僅輸出錯誤信號。編譯期插桩(__FILE__、__LINE__、__func__)僅能覆蓋預埋點,無法捕獲未預期崩潰現場。運行時堆棧回溯 能在信號處理函數中自動獲取完整調用鏈,是生產環境診斷核心利器。
#include <execinfo.h>
int backtrace(void **buffer, int size);
// 獲取當前調用棧,將返回地址填入 buffer,最多 size 幀,返回實際幀數
char **backtrace_symbols(void *const *buffer, int size);
// 將 buffer 中的地址解析為可讀字符串:"可執行文件(函數名+偏移) [實際地址]"
// 內部 malloc 分配,調用者需 free 返回的指針數組(無需釋放每個字符串)
void backtrace_symbols_fd(void *const *buffer, int size, int fd);
// 直接將解析結果寫入文件描述符,避免 malloc 失敗風險,適合信號處理上下文
#define MAX_LEN 256
void show_stack_info() {
void *buffer[MAX_LEN];
int n = backtrace(buffer, MAX_LEN);
char **strings = backtrace_symbols(buffer, n);
for (int i = 0; i < n; ++i) printf("%s\n", strings[i]);
free(strings);
}
void func4(int a) { if (a > 0) func4(--a); else show_stack_info(); }
static void func3(int a) { func4(--a); }
void func2(int a) { func3(--a); }
void func1(int a) { func2(--a); }
int main() { func1(10); return 0; }
編譯關鍵:gcc traceback.c -o traceback -rdynamic(導出動態符號表,否則靜態函數、內聯函數、尾遞歸優化幀無法解析)。
示例輸出顯示 15 幀,包含 func4 遞歸 10 次、func2、func1、main、__libc_start_main。注意 func3 因 static 不見符號名。四大盲區:內聯函數無棧幀、高優化等級(-O2/-O3)可能省略幀指針、尾遞歸合併幀、靜態函數符號隱藏。
SIGSEGV、SIGABRT 等致命信號處理器中調用 backtrace_symbols_fd 寫入日誌文件;-rdynamic 或 -Wl,--export-dynamic;addr2line 離線解析地址至源碼行號;libunwind 或 google-glog 獲得更穩健的跨平台支持。