C/C++ backtrace和addr2line 实现堆栈信息追踪

发布时间 2024-01-08 16:18:14作者: 蔡头一枚
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
#include <unistd.h>
#include <execinfo.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <iostream>
#include <thread>
#include <functional>

using namespace std;

#define BACKTRACE_SIZE   16

// 获取当前运行进程的绝对路径
char *get_current_task_name()
{
    static char task_name[1024] = {0};
    char *pstr = NULL;
    readlink("/proc/self/exe", task_name, 1024);
    pstr = task_name;
    return pstr;
}

void addr2lineAnalyze(const char *paddress, const char *proName)
{
    static char addr2line_str[256] = {0};
    snprintf(addr2line_str, 256, "addr2line %s -e %s -f -p", paddress, proName);

    FILE *pFd = popen(addr2line_str, "r");
    if ( pFd == nullptr )
    {
        return ;
    }
    char buf[512] = {0};
    fread(buf, sizeof(buf)/sizeof (buf[0]), 1, pFd);
    printf("  %s\n", buf);

    pclose(pFd);
}

void dump(void)
{
    int j, nptrs;
    void *buffer[BACKTRACE_SIZE];
    char **strings;

    nptrs = backtrace(buffer, BACKTRACE_SIZE);

    printf("backtrace() returned %d addresses\n", nptrs);

    strings = backtrace_symbols(buffer, nptrs);
    if (strings == NULL)
    {
        perror("backtrace_symbols");
        exit(EXIT_FAILURE);
    }
    char *ptr = nullptr, *ptr_end = nullptr, *pstr = nullptr;
    // 打印堆栈信息, 并且使用addr2line进行分析
    for (j = 0; j < nptrs; j++)
    {
        printf("  [%02d] %s\n", j, strings[j]);
        ptr = strstr(strings[j], "[");
        if (ptr != nullptr)
        {
            ptr++;
            ptr_end = strstr(ptr, "]");
            if (ptr_end != nullptr)
            {
                *ptr_end = '\0';
            }
            addr2lineAnalyze(ptr, get_current_task_name());
        }
    }
    free(strings);
}

void signal_handler(int signo)
{
    printf("<<<<<<<<<<<<<<<<<catch signal %d>>>>>>>>>>>>>>>>>>>>>>>>>\n", signo);
    printf("Dump stack start...\n");
    dump();
    printf("Dump stack end...\n");

    signal(signo, SIG_DFL); /* 恢复信号默认处理 */
    raise(signo);           /* 重新发送信号 */
}


int main(void)
{
    // 注册信号
    signal(SIGSEGV, signal_handler);
    signal(SIGINT, signal_handler);
    signal(SIGABRT, signal_handler);

    // 异常代码段, 抛越界操作
    char *ptr = nullptr;
    memcpy(ptr, "123", 3 );
    printf("ptr: %s\n", ptr);

    return 0;
}

运行情况:

 异常抛出进来后,信号注册函数进行回调,分析堆栈信息