You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 22:14:59