如何使用gcc和行号信息获取C ++的堆栈跟踪?
我们在像宏这样的专有assert
使用堆栈跟踪来捕捉开发人员的错误 - 当发现错误时,会打印堆栈跟踪。
我发现gcc的对backtrace()
/ backtrace_symbols()
方法不够:
第一个问题可以通过abi :: __ cxa_demangle解决。
然而第二个问题更加困难。 我找到了backtrace_symbols()的替代品。 这比gcc的backtrace_symbols()更好,因为它可以检索行号(如果使用-g编译),并且不需要使用-rdynamic编译。
Hoverer的代码是GNU许可的,所以恕我直言,我不能在商业代码中使用它。
任何提议?
PS
gdb能够打印出传递给函数的参数。 可能它已经太多要求:)
PS 2
类似的问题(谢谢nobar)
不久之前,我回答了类似的问题。 您应该查看方法#4上的源代码,该代码还会打印行号和文件名。
所以你需要一个独立的函数来打印一个堆栈跟踪,其中包含所有gdb堆栈跟踪具有的功能,并且不会终止你的应用程序。 答案是以非交互模式自动启动gdb以执行您所需的任务。
这是通过在子进程中执行gdb,使用fork()以及在您的应用程序等待完成时脚本化它来显示堆栈跟踪来完成的。 这可以在不使用核心转储的情况下执行,也不需要中止应用程序。 我从这个问题中学会了如何做到这一点:如何从程序调用gdb来打印它的堆栈跟踪更好?
这个问题发布的例子并没有像我写的那样正确工作,所以这是我的“固定”版本(我在Ubuntu 9.04上运行了这个版本)。
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
void print_trace() {
char pid_buf[30];
sprintf(pid_buf, "%d", getpid());
char name_buf[512];
name_buf[readlink("/proc/self/exe", name_buf, 511)]=0;
int child_pid = fork();
if (!child_pid) {
dup2(2,1); // redirect output to stderr
fprintf(stdout,"stack trace for %s pid=%sn",name_buf,pid_buf);
execlp("gdb", "gdb", "--batch", "-n", "-ex", "thread", "-ex", "bt", name_buf, pid_buf, NULL);
abort(); /* If gdb failed to start */
} else {
waitpid(child_pid,NULL,0);
}
}
如引用的问题所示,gdb提供了可以使用的其他选项。 例如,使用“bt full”而不是“bt”会生成更详细的报告(局部变量包含在输出中)。 gdb的manpages很轻便,但完整的文档可以在这里找到。
由于这是基于gdb的,所以输出包括demangled名字 , 行号 , 函数参数 ,甚至可选的本地变量 。 此外,gdb是线程感知的,所以你应该能够提取一些线程特定的元数据。
这里有一个我用这种方法看到的堆栈跟踪的例子。
0x00007f97e1fc2925 in waitpid () from /lib/libc.so.6
[Current thread is 0 (process 15573)]
#0 0x00007f97e1fc2925 in waitpid () from /lib/libc.so.6
#1 0x0000000000400bd5 in print_trace () at ./demo3b.cpp:496
2 0x0000000000400c09 in recursive (i=2) at ./demo3b.cpp:636
3 0x0000000000400c1a in recursive (i=1) at ./demo3b.cpp:646
4 0x0000000000400c1a in recursive (i=0) at ./demo3b.cpp:646
5 0x0000000000400c46 in main (argc=1, argv=0x7fffe3b2b5b8) at ./demo3b.cpp:70
注意:我发现这与使用valgrind不兼容(可能是由于Valgrind使用虚拟机)。 当您在gdb会话中运行程序时,它也不起作用(不能将第二个“ptrace”实例应用于进程)。
有一个关于本质上相同的问题的强大讨论:如何在我的gcc C ++应用程序崩溃时生成堆栈跟踪。 提供了许多建议,包括关于如何在运行时生成堆栈跟踪的大量讨论。
我的个人最喜欢的答案是启用核心转储,它允许您在崩溃时查看完整的应用程序状态 (包括函数参数,行号和未加密的名称)。 这种方法的另一个好处是它不仅适用于断言,而且适用于分段错误和未处理的异常。
不同的Linux shell使用不同的命令来启用核心转储,但是你可以在你的应用程序代码中使用类似这样的方法来完成它...
#include <sys/resource.h>
...
struct rlimit core_limit = { RLIM_INFINITY, RLIM_INFINITY };
assert( setrlimit( RLIMIT_CORE, &core_limit ) == 0 ); // enable core dumps for debug builds
崩溃后,运行您最喜欢的调试器来检查程序状态。
$ kdbg executable core
以下是一些示例输出...
也可以从命令行的核心转储中提取堆栈跟踪。
$ ( CMDFILE=$(mktemp); echo "bt" >${CMDFILE}; gdb 2>/dev/null --batch -x ${CMDFILE} temp.exe core )
Core was generated by `./temp.exe'.
Program terminated with signal 6, Aborted.
[New process 22857]
#0 0x00007f4189be5fb5 in raise () from /lib/libc.so.6
#0 0x00007f4189be5fb5 in raise () from /lib/libc.so.6
#1 0x00007f4189be7bc3 in abort () from /lib/libc.so.6
#2 0x00007f4189bdef09 in __assert_fail () from /lib/libc.so.6
#3 0x00000000004007e8 in recursive (i=5) at ./demo1.cpp:18
#4 0x00000000004007f3 in recursive (i=4) at ./demo1.cpp:19
#5 0x00000000004007f3 in recursive (i=3) at ./demo1.cpp:19
#6 0x00000000004007f3 in recursive (i=2) at ./demo1.cpp:19
#7 0x00000000004007f3 in recursive (i=1) at ./demo1.cpp:19
#8 0x00000000004007f3 in recursive (i=0) at ./demo1.cpp:19
#9 0x0000000000400849 in main (argc=1, argv=0x7fff2483bd98) at ./demo1.cpp:26
链接地址: http://www.djcxy.com/p/86019.html
上一篇: How to get a stack trace for C++ using gcc with line number information?
下一篇: Show a caret in a custom textarea without displaying its text