当前位置: 首页 > 面试题库 >

如何使用带有行号信息的gcc获取C ++的堆栈跟踪?

袁元明
2023-03-14
问题内容

我们在专有的assert宏(如宏)中使用堆栈跟踪来捕获开发人员的错误-发现错误时,将打印堆栈跟踪。

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

  1. 名称混乱
  2. 没有行信息

第一个问题可以通过abi :: __
cxa_demangle
解决。

但是第二个问题更加棘手。我找到了backtrace_symbols()的替代品。这比gcc的backtrace_symbols()更好,因为它可以检索行号(如果使用-
g编译),并且您不需要使用-rdynamic进行编译。

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

有什么建议吗?

ps

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


问题答案:

不久之前,我回答了类似的问题to-print-its-stacktrace/4611112#4611112)。您应该看一下方法#4上可用的源代码,该方法还会打印行号和文件名。

  • 方法4:

我对方法3进行了一点改进,以打印行号。也可以将其复制以使用方法2。

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

下面的源代码显示所有本地功能的行号。如果调用了另一个库中的函数,则可能会看到几个??:0而不是文件名。

#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\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]);

    /* 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\n", 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


 类似资料:
  • 在我们的日志文件中,我们发现以下内容: 只有这一行,没有异常堆栈跟踪。 出现此异常的try块正在执行使用javassist创建的动态生成的Java字节码。 我想知道两件事: : null 丢失的堆栈跟踪,尽管在块内使用调用日志挂钩,这通常会导致完整的堆栈跟踪被打印在日志文件中。 我的问题: > 什么样的代码会导致日志输出“java.lang.ArrayIndexOutOfBoundsExcepti

  • 我得到了堆栈跟踪。我所能跟踪的最远的回调是中的调用。没有留下是从调用的信息。 有没有一种方法可以获得完整的堆栈跟踪?

  • 问题内容: 如何获得Java中的当前堆栈跟踪,就像你可以在.NET中那样? 我找到了,但这不是我想要的-我想找回堆栈跟踪信息,而不是打印出来。 问题答案: 你可以使用。 这将返回一个数组,该数组代表程序的当前堆栈跟踪。

  • 问题内容: 最近,我问是否要报告捕获到的异常的文本。出乎意料的是,大多数答案都误解了我的问题,并以为我在问是否要报告堆栈跟踪以获取捕获的异常,这表明这样做是正常的。所以我要问一个后续问题。 在例外情况下,应该或不应该在什么情况下报告堆栈跟踪?通过“报告”,我包括要求一个日志记录框架为您记录堆栈跟踪 我不问是否报告 的东西 。我在问那个报告是否应该包括堆栈跟踪。 问题答案: 它取决于上下文。例如,当

  • 问题内容: 我确实有一个jenkins实例,它陷入了某种无休止的循环,没有任何可见的活动。 我可以获得正在运行的进程的信息,那么如何生成可用于错误报告的跟踪? 我在linux上运行。 问题答案: 尝试使用jstack。它会为您提供线程正在执行的操作的完整列表。它所需要的只是进程pid。

  • 我正在使用ant在Eclipse中构建我的项目的一个jar。我在Tomcat上部署了jar。但是,每当我的代码(在jar中)中发生异常时,错误堆栈跟踪就会出现,但行号不会出现--相反,它会显示未知源。 如何获取错误堆栈跟踪中的行号?