如何使用带有行号信息的 gcc 获取 C++ 的堆栈跟踪? [英] How to get a stack trace for C++ using gcc with line number information?

查看:17
本文介绍了如何使用带有行号信息的 gcc 获取 C++ 的堆栈跟踪?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们在专有的assert 中使用堆栈跟踪,如宏来捕捉开发人员的错误——当错误被发现时,堆栈跟踪被打印出来.

We use stack traces in proprietary assert like macro to catch developer mistakes - when error is caught, stack trace is printed.

我发现gcc的一对backtrace()/backtrace_symbols()方法不够:

I find gcc's pair backtrace()/backtrace_symbols() methods insufficient:

  1. 名称被破坏
  2. 没有线路信息

第一个问题可以通过abi::__cxa_demangle解决.

1st problem can be resolved by abi::__cxa_demangle.

然而,第二个问题更难.我找到了 替换 backtrace_symbols().这比 gcc 的 backtrace_symbols() 更好,因为它可以检索行号(如果使用 -g 编译)并且您不需要使用 -rdynamic 进行编译.

However 2nd problem s more tough. I found replacement for backtrace_symbols(). This is better than gcc's backtrace_symbols(), since it can retrieve line numbers (if compiled with -g) and you don't need to compile with -rdynamic.

Hoverer 代码是 GNU 许可的,所以恕我直言,我不能在商业代码中使用它.

Hoverer the code is GNU licenced, so IMHO I can't use it in commercial code.

有什么建议吗?

附言

gdb 能够打印出传递给函数的参数.可能已经要求太多了:)

gdb is capable to print out arguments passed to functions. Probably it's already too much to ask for :)

PS 2

类似问题(谢谢nobar)

推荐答案

不久前 我回答了一个类似的问题.您应该查看方法 #4 中可用的源代码,它还会打印行号和文件名.

Not too long ago I answered a similar question. You should take a look at the source code available on method #4, which also prints line numbers and filenames.

  • 方法 4:

我对方法#3 进行了一个小的改进来打印行号.这也可以复制到方法 #2 上.

A small improvement I've done on method #3 to print line numbers. This could be copied to work on method #2 also.

基本上,它使用 addr2line 将地址转换为文件名和行号.

Basically, it uses addr2line to convert addresses into file names and line numbers.

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

The source code below prints line numbers for all local functions. If a function from another library is called, you might see a couple of ??:0 instead of file names.

#include <stdio.h>
#include <signal.h>
#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
", sig, ctx.cr2, ctx.eip);
  else
    printf("Got signal %d
", 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:
");
  for (i=1; i<trace_size; ++i)
  {
    printf("[bt] #%d %s
", i, messages[i]);

    /* find first occurence of '(' or ' ' in message[i] and assume
     * everything before that is the file name. (Don't go beyond 0 though
     * (string terminator)*/
    size_t p = 0;
    while(messages[i][p] != '(' && messages[i][p] != ' '
            && messages[i][p] != 0)
        ++p;

    char syscom[256];
    sprintf(syscom,"addr2line %p -e %.*s", trace[i], p, messages[i]);
        //last parameter is the file name of the symbol
    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
", func_b());
}

这段代码应该编译为: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

这篇关于如何使用带有行号信息的 gcc 获取 C++ 的堆栈跟踪?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆