如何使用带有行号信息的 gcc 获取 C++ 的堆栈跟踪? [英] How to get a stack trace for C++ using gcc with line number information?
问题描述
我们在专有的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:
- 名称被破坏
- 没有线路信息
第一个问题可以通过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屋!