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

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

  1. 名字被弄乱了
  2. 无线路信息

第一个问题可以在abi::__cxa_demangle分钟内解决.

然而,第二个问题更加棘手.我找到了replacement for backtrace_symbols()个.

hover这个代码是GNU授权的,所以我不能在商业代码中使用它.

有什么建议吗?

P.S.

gdb能够打印出传递给函数的参数.

PS 2

Similar question(谢谢nobar)

推荐答案

就在不久前.你应该看看方法#4上的源代码,它也会打印行号和文件名.

  • Method #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

Linux相关问答推荐

在PowerShell for Linux上,打印出的maven命令还有其他字符

pci_user_write_config_word在哪里实现?

在树莓派内核中找不到arch/arm/kernel/calls.S

为什么waitpid(2)可以指定非子进程?

重新运行时避免 Linux bash 脚本中的文件重命名重复

无法在嵌入式 Linux 中使用 libjpeg 以 RGB888 在 LCD(黑色)上显示 JPEG

+后移动下一行到当前行

Bash - 如何根据 names.txt 重命名目录中的文件

如何在 Linux 上使用 Python 判断进程是否仍在运行?

Linux 进程在后台 - 在作业(job)中 Stopped停止?

如何使用该位置的相对路径在单个位置创建多个文件夹?

如何在python中检索进程开始时间(或正常运行时间)

bash 中的sed命令

使远程目录保持最新

如果关键字触发然后执行命令,Shell 脚本来监视日志(log)文件?

vscode 总是请求保存权限

linux中netstat和ss的区别?

UNIX `time` 命令对于基准测试是否足够准确?

如何在没有 root 用户的情况下在 Linux (CentOS) 中安装软件包并进行自动依赖处理?

在tmux中绑定Ctrl+Tab和Ctrl+Shift+Tab