有没有办法从Linux发布二进制文件中转储带有行号的堆栈跟踪?

Ani*_*mar 11 c++ linux backtrace

当我的c ++ linux应用程序崩溃时,我需要转储堆栈跟踪.我使用backtrace()和backtrace_symbols()成功地做到了这一点.现在,另外我想得到崩溃的行号.它是怎么做到的?

Saq*_*ain 16

我接受了帮助

http://www.linuxjournal.com/files/linuxjournal.com/linuxjournal/articles/063/6391/6391l2.htmlhttp://www.linuxjournal.com/article/6391?page=0,0来了使用示例代码显示如何实现此目的.

基本上它是关于将堆栈回溯放在信号处理程序中并使后者捕获程序可以接收的所有"坏"信号(SIGSEGV,SIGBUS,SIGILL,SIGFPE等).这样,如果您的程序崩溃并且您没有使用调试器运行它,您可以获得堆栈跟踪并知道故障发生的位置.此技术还可用于了解程序循环的位置,以防它停止响应...

下面的代码为跟踪中的每个地址运行外部程序addr2line,将其转换为文件名和行号.

下面的源代码打印所有本地函数的行号.如果调用另一个库中的函数,您可能会看到几个??:0而不是文件名.

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

void bt_sighandler(int sig, struct sigcontext ctx) {

  void *trace[16];
  char **messages = (char **)NULL;
  int i, trace_size = 0;

  if (sig == SIGSEGV)
    printf("Got signal %d, faulty address is %p, "
           "from %p\n", sig, ctx.cr2, ctx.eip);
  else
    printf("Got signal %d\n", sig);

  trace_size = backtrace(trace, 16);
  /* overwrite sigaction with caller's address */
  trace[1] = (void *)ctx.eip;
  messages = backtrace_symbols(trace, trace_size);
  /* skip first stack frame (points here) */
  printf("[bt] Execution path:\n");
  for (i=1; i<trace_size; ++i)
  {
    printf("[bt] #%d %s\n", i, messages[i]);

    char syscom[256];
    sprintf(syscom,"addr2line %p -e sighandler", trace[i]); //last parameter is the name of this app
    system(syscom);
  }

  exit(0);
}


int func_a(int a, char b) {

  char *p = (char *)0xdeadbeef;

  a = a + b;
  *p = 10;  /* CRASH here!! */

  return 2*a;
}


int func_b() {

  int res, a = 5;

  res = 5 + func_a(a, 't');

  return res;
}


int main() {

  /* Install our signal handler */
  struct sigaction sa;

  sa.sa_handler = (void *)bt_sighandler;
  sigemptyset(&sa.sa_mask);
  sa.sa_flags = SA_RESTART;

  sigaction(SIGSEGV, &sa, NULL);
  sigaction(SIGUSR1, &sa, NULL);
  /* ... add any other signal here */

  /* Do something */
  printf("%d\n", func_b());
}
Run Code Online (Sandbox Code Playgroud)

此代码应编译为:gcc sighandler.c -o sighandler -rdynamic

该方案产出:

Got signal 11, faulty address is 0xdeadbeef, from 0x8048975
[bt] Execution path:
[bt] #1 ./sighandler(func_a+0x1d) [0x8048975]
/home/karl/workspace/stacktrace/sighandler.c:44
[bt] #2 ./sighandler(func_b+0x20) [0x804899f]
/home/karl/workspace/stacktrace/sighandler.c:54
[bt] #3 ./sighandler(main+0x6c) [0x8048a16]
/home/karl/workspace/stacktrace/sighandler.c:74
[bt] #4 /lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6) [0x3fdbd6]
??:0
[bt] #5 ./sighandler() [0x8048781]
??:0
Run Code Online (Sandbox Code Playgroud)

  • 通过以下调用在 for 循环外部使用系统调用:addr2line -psfCeighandler &lt;list_of_addresses&gt; (2认同)

Bas*_*tch 3

仅当程序已使用调试信息(即 withgcc -Wall -g或 with g++ -Wall -g)编译时才有可能。没有-g可执行文件不包含任何源行信息。如果使用,gcc您可以使用优化和调试信息(例如g++ -Wall -g -O2)进行编译,但有时行位置会“令人惊讶”。

-Wall标志要求 GCC 显示所有警告。它非常有用(因此我建议使用它),但与-g调试信息无关。

至于如何提取行号,最简单的方法就是fork一个gdb进程。或者,您可以获取调试信息(DWARF格式)并解析它,也许可以使用libdwarfELF工具链。我不确定这是否值得这么麻烦......

为了获得回溯,您可以简单地运行您的程序,gdb也许gdb --args yourprogram itsarguments...


附加物

您还可以使用最近的 GCC 中的libbacktrace (实际上是Ian Taylor 的 libbacktrace),它旨在解决您的问题(它“解释”当前可执行文件的 DWARF 格式,您可以使用它进行编译g++ -O -g)。

  • 产生另一个进程,尤其是 gdb,不能被认为是一种“简单的方式”做事...... (4认同)