如何使backtrace()/ backtrace_symbols()打印函数名?

Linux特定的backtrace()backtrace_symbols()允许你产生程序的调用轨迹。 但是,它只打印function地址,而不是我的程序的名称。 我怎样才能让他们打印函数名称呢? 我试着用-g-ggdb编译程序。 下面的testing用例只是打印这个:


    后退------------
     ./a.out()[0x8048616]
     ./a.out()[0x8048623]
     /lib/libc.so.6(__libc_start_main+0xf3)[0x4a937413]
     ./a.out()[0x8048421]
     ----------------------
    

我想要前两个项目也显示函数名称, foomain

码:

 #include <execinfo.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <stdlib.h> static void full_write(int fd, const char *buf, size_t len) { while (len > 0) { ssize_t ret = write(fd, buf, len); if ((ret == -1) && (errno != EINTR)) break; buf += (size_t) ret; len -= (size_t) ret; } } void print_backtrace(void) { static const char start[] = "BACKTRACE ------------\n"; static const char end[] = "----------------------\n"; void *bt[1024]; int bt_size; char **bt_syms; int i; bt_size = backtrace(bt, 1024); bt_syms = backtrace_symbols(bt, bt_size); full_write(STDERR_FILENO, start, strlen(start)); for (i = 1; i < bt_size; i++) { size_t len = strlen(bt_syms[i]); full_write(STDERR_FILENO, bt_syms[i], len); full_write(STDERR_FILENO, "\n", 1); } full_write(STDERR_FILENO, end, strlen(end)); free(bt_syms); } void foo() { print_backtrace(); } int main() { foo(); return 0; } 

符号取自dynamic符号表; 你需要gcc-rdynamic选项,这使得它传递一个标志给链接器,确保所有的符号都被放置在表格中。

(请参阅GCC手册的链接选项页面和/或glibc手册的Backtraces页面。)

使用addr2line命令将可执行文件地址映射到源代码文件名+行号。 给出-f选项以获取函数名称。

或者,尝试libunwind 。

Ian Lance Taylor的优秀Libbacktrace解决了这个问题。 它处理堆栈展开,并支持普通的ELF符号和DWARFdebugging符号。

Libbacktrace不需要导出所有符号,这将是丑陋的,ASLR不会打破它。

Libbacktrace最初是GCC发行版的一部分。 现在,在Github上可以find一个独立的版本:

https://github.com/ianlancetaylor/libbacktrace

在顶部的答案有一个错误,如果ret == -1和errno是EINTER,你应该再试一次,但是不要把复制的ret计算在内(如果你不喜欢它,就不要为此设置一个帐户)

 static void full_write(int fd, const char *buf, size_t len) { while (len > 0) { ssize_t ret = write(fd, buf, len); if ((ret == -1) { if (errno != EINTR)) break; //else continue; } buf += (size_t) ret; len -= (size_t) ret; } }