在Unix系统上自动获取堆栈跟踪,自动


7

有什么方法可以在Unix系统上自动获取堆栈跟踪?我并不是说只是获取核心文件或者与GDB进行交互式附件,而是使用SIGSEGV处理程序将回溯转储为文本文件。

以下可选功能加分点:(例如配置文件)

  • 额外的信息收集在崩溃的时间。
  • 向开发人员发送崩溃信息包。
  • 能够在dlopen编共享库添加此
  • 不需要的GUI
7

如果你在与可用的BSD backtrace功能(Linux操作系统,OSX 1.5,当然BSD),可以系统在您的信号处理程序中以编程方式执

例如(backtrace code derived from IBM example):

#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),但是,它确实有非常简单的优点,并且不需要任何额外的库或程序。


2

Dereks的解决方案可能是最好的,但这里的一个替代反正:

最新的Linux内核版本允许你管核心转储到一个脚本或程序。您可以编写一个脚本来捕捉核心转储,收集您需要的任何额外信息并将所有信息邮寄回去。 尽管这是一个全局设置,所以它适用于系统上的任何崩溃程序。它还需要root权限才能设置。 它可以通过/ proc/sys/kernel/core_pattern文件进行配置。将其设置为“|” /家/ myuser的/ bin中/我的核心处理程序脚本”。

Ubuntu的人也使用这个功能。


4

下面是如何使用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); 
} 
+1

在信号处理程序中使用C++ - 基因组中的错误。 05 1月. 102010-01-05 08:54:40


15

FYI,

建议的解决方案(使用backtrace_symbols在信号处理程序)是危险的破裂。不要使用它 -

是,回溯和backtrace_symbols会产生回溯和。然而它转换为符号名,:

  1. backtrace_symbols分配使用malloc的内存,您使用免费的释放它 - 如果你由于内存损坏您的malloc竞技场很可能会损坏并导致双重故障。

  2. malloc和自由保护内部锁的malloc竞技场。你可能在malloc/free的过程中发生了故障,并且会导致锁定,这会导致这些函数或任何调用它的死锁。

  3. 您可以使用使用标准流的puts,它也受到锁的保护。如果你在printf中间发生故障,你又会陷入僵局。

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

因此,本例中的代码是最坏的一种错误的 - 它看起来像它的工作,但它真的会失败,你在生产意想不到的方式。

顺便说一句,有兴趣做正确的事吗?检查this了。

干杯, 吉拉德。

+3

提供的链接已死亡。 27 5月. 102010-05-27 13:23:39

  0

我并不擅长这方面的工作。但是如果你在子进程中执行回溯操作,那么你可以逃避,不是吗? 26 2月. 182018-02-26 21:40:10