自定义Backtrace工具触发Segmentation fault时无栈回溯输出的原因排查
程序崩溃时调用栈偶尔无法输出的原因分析
问题背景
我编写了backtrace.hpp头文件,用于在程序触发Segmentation fault等崩溃信号时输出调用栈信息,代码如下:
#pragma once #include <map> #include <iostream> #include <signal.h> #include <sys/stat.h> #include <execinfo.h> #include <sys/types.h> #include <unistd.h> static const std::map<int, std::string> signals_map = { {SIGSEGV, "SIGSEGV"}, {SIGABRT, "SIGABRT"}, {SIGINT, "SIGINT"}, {SIGFPE, "SIGFPE"}, {SIGILL, "SIGILL"}, {SIGSYS, "SIGSYS"}, {SIGBUS, "SIGBUS"}, {SIGIOT, "SIGIOT"}, {SIGTRAP, "SIGTRAP"}, }; class Backtrace { private: static void ExceptionHandler(int signum, siginfo_t* info, void* ctx){ int nptrs; void *buffer[1024]; char **strings; nptrs = backtrace(buffer, 1024); strings = backtrace_symbols(buffer, nptrs); if (strings == NULL) { exit(EXIT_FAILURE); } std::cout<<std::endl<<"****** Crash Backtrace signal: "<<signals_map.at(signum)<<"******"<<std::endl<<std::endl; for (int i = 2; i < nptrs; i++) { std::cout<<" "<<strings[i]<<std::endl; } free(strings); signal(signum, SIG_DFL); } public: Backtrace(){ struct sigaction action; sigemptyset(&action.sa_mask); action.sa_sigaction = &ExceptionHandler; action.sa_flags = SA_SIGINFO; for (const auto& signals: signals_map) { if (0 > sigaction(signals.first, &action, NULL)) { std::cout<<"sigaction failed:"<<signals.second<<std::endl; } } } ~Backtrace(){} };
我在代码中按如下方式使用该工具:
#include "backtrace.hpp" int main(){ Backtrace b; ... }
该工具多数情况下能正常输出栈回溯,但偶尔仅显示shell的崩溃提示:
$ /bin/sh: line 1: 6571 Segmentation fault
无任何栈回溯内容。请问出现这种情况的原因是什么?
原因分析
信号处理函数调用了非异步安全函数:
ExceptionHandler中使用的std::cout、std::map::at()都属于非异步信号安全的函数。当程序在执行这些函数的内部逻辑(比如持有锁、操作内存结构)时触发崩溃信号,信号处理函数再次调用它们会引发二次崩溃,直接终止进程,来不及输出调用栈。栈结构被严重破坏:如果崩溃是由栈溢出、栈帧被越界写入覆盖导致的,
backtrace()无法正确解析栈帧,可能返回0个有效指针,backtrace_symbols会返回NULL,此时信号处理函数直接调用exit(),自然没有任何输出。信号处理流程被提前终止:信号处理函数末尾调用
signal(signum, SIG_DFL)后,某些场景下内核可能在输出完成前就触发默认的信号处理动作(终止进程),导致输出流程被中断。标准输出缓冲未及时刷新:
std::cout默认是行缓冲模式,极端情况下输出内容还在缓冲区中未刷新到终端,进程就已经终止,缓冲区内容丢失,看起来像是没有输出。
内容的提问来源于stack exchange,提问作者BigFaceBoy
相关产品推荐
相关产品推荐

