7

有哪些方法可以在 Unix 系统上自动获取堆栈跟踪?我的意思不是仅仅获取一个核心文件或与 GDB 交互地附加,而是有一个 SIGSEGV 处理程序将回溯转储到文本文件。

以下可选功能的奖励积分:

  • 崩溃时收集的额外信息(例如配置文件)。
  • 通过电子邮件将崩溃信息包发送给开发人员。
  • 能够将其添加到dlopened 共享库中
  • 不需要图形用户界面
4

4 回答 4

15

供参考,

建议的解决方案(在信号处理程序中使用 backtrace_symbols)被危险地破坏了。不要使用它 -

是的, backtrace 和 backtrace_symbols 将产生一个回溯并将其转换为符号名称,但是:

  1. backtrace_symbols 使用 malloc 分配内存,您使用 free 来释放它 - 如果您因内存损坏而崩溃,您的 malloc arena 很可能已损坏并导致双重错误。

  2. malloc 和 free 用内部锁保护 malloc 竞技场。您可能在 malloc/free 的过程中发生了错误,并获得了锁,这将导致这些函数或任何调用它们的函数进入死锁。

  3. 您使用 puts ,它使用标准流,它也受锁保护。如果您在 printf 中间出错,您将再次陷入僵局。

  4. 在 32 位平台上(例如 2 年前的普通 PC),内核会将返回地址植入内部 glibc 函数,而不是堆栈中的错误函数,因此您感兴趣的最重要的一条信息 - 其中功能没有程序故障,实际上会在那些平台上被破坏。

因此,示例中的代码是最严重的错误——它看起来像是在工作,但它确实会让你在生产中以意想不到的方式失败。

顺便说一句,有兴趣做对吗?看看这个

干杯,吉拉德。

于 2008-11-20T13:36:48.563 回答
7

如果您使用的是具有backtrace可用 BSD 功能的系统(Linux、OSX 1.5,当然还有 BSD),您可以在信号处理程序中以编程方式执行此操作。

例如(backtrace来自 IBM 示例的代码):

#include <execinfo.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

void sig_handler(int sig)
{
    void * array[25];
    int nSize = backtrace(array, 25);
    char ** symbols = backtrace_symbols(array, nSize);

    for (int i = 0; i < nSize; i++)
    {
        puts(symbols[i]);;
    }

    free(symbols);

    signal(sig, &sig_handler);
}

void h()
{
    kill(0, SIGSEGV);
}

void g()
{
    h();
}

void f()
{
    g();
}

int main(int argc, char ** argv)
{
    signal(SIGSEGV, &sig_handler);
    f();
}

输出:

0   a.out                               0x00001f2d sig_handler + 35
1   libSystem.B.dylib                   0x95f8f09b _sigtramp + 43
2   ???                                 0xffffffff 0x0 + 4294967295
3   a.out                               0x00001fb1 h + 26
4   a.out                               0x00001fbe g + 11
5   a.out                               0x00001fcb f + 11
6   a.out                               0x00001ff5 main + 40
7   a.out                               0x00001ede start + 54

这不会因为可选功能(除了不需要 GUI)而获得奖励积分,但是,它确实具有非常简单的优点,并且不需要任何额外的库或程序。

于 2008-08-20T17:36:55.697 回答
3

这是一个如何使用 demangler 获取更多信息的示例。正如您所看到的,这也将堆栈跟踪记录到文件中。

#include <iostream>
#include <sstream>
#include <string>
#include <fstream>
#include <cxxabi.h>

void sig_handler(int sig)
{
    std::stringstream stream;
    void * array[25];
    int nSize = backtrace(array, 25);
    char ** symbols = backtrace_symbols(array, nSize);
    for (unsigned int i = 0; i < size; i++) {
        int status;
        char *realname;
        std::string current = symbols[i];
        size_t start = current.find("(");
        size_t end = current.find("+");
        realname = NULL;
        if (start != std::string::npos && end != std::string::npos) {
            std::string symbol = current.substr(start+1, end-start-1);
            realname = abi::__cxa_demangle(symbol.c_str(), 0, 0, &status);
        }
        if (realname != NULL)
            stream << realname << std::endl;
        else
            stream << symbols[i] << std::endl;
        free(realname);
    }
    free(symbols);
    std::cerr << stream.str();
    std::ofstream file("/tmp/error.log");
    if (file.is_open()) {
        if (file.good())
            file << stream.str();
        file.close();
    }
    signal(sig, &sig_handler);
}
于 2008-09-03T15:22:20.570 回答
2

Dereks 解决方案可能是最好的,但无论如何这里有一个替代方案:

最近的 Linux 内核版本允许您将核心转储通过管道传输到脚本或程序。您可以编写一个脚本来捕获核心转储,收集您需要的任何额外信息并将所有内容邮寄回来。这是一个全局设置,因此它适用于系统上的任何崩溃程序。它还需要root权限才能设置。可以通过 /proc/sys/kernel/core_pattern 文件进行配置。将其设置为类似 ' | /home/myuser/bin/my-core-handler-script'。

Ubuntu 人员也使用此功能。

于 2008-08-23T06:31:11.717 回答