Linux 如何使用带有行号信息的 gcc 获取 C++ 的堆栈跟踪?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4636456/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to get a stack trace for C++ using gcc with line number information?
提问by dimba
We use stack traces in proprietary assert
like macro to catch developer mistakes - when error is caught, stack trace is printed.
我们使用assert
像宏这样的专有堆栈跟踪来捕捉开发人员的错误——当错误被发现时,堆栈跟踪被打印出来。
I find gcc's pair backtrace()
/backtrace_symbols()
methods insufficient:
我发现 gcc 的对backtrace()
/backtrace_symbols()
方法不足:
- Names are mangled
- No line information
- 名字乱了
- 没有线路信息
1st problem can be resolved by abi::__cxa_demangle.
第一个问题可以通过abi::__cxa_demangle解决。
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.
然而,第二个问题更加棘手。我找到了 backtrace_symbols() 的替代品。这比 gcc 的 backtrace_symbols() 更好,因为它可以检索行号(如果使用 -g 编译)并且您不需要使用 -rdynamic 进行编译。
Hoverer the code is GNU licenced, so IMHO I can't use it in commercial code.
Hoverer 代码是 GNU 许可的,所以恕我直言,我不能在商业代码中使用它。
Any proposal?
有什么建议吗?
P.S.
聚苯乙烯
gdb is capable to print out arguments passed to functions. Probably it's already too much to ask for :)
gdb 能够打印出传递给函数的参数。可能已经要求太多了:)
PS 2
附注 2
Similar question(thanks nobar)
类似问题(感谢 nobar)
采纳答案by karlphillip
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 上可用的源代码,它还会打印行号和文件名。
- Method #4:
- 方法#4:
A small improvement I've done on method #3 to print line numbers. This could be copied to work on method #2 also.
我在方法#3 上做了一个小的改进来打印行号。这也可以复制到方法 #2 上。
Basically, it uses addr2lineto convert addresses into file names and line numbers.
基本上,它使用addr2line将地址转换为文件名和行号。
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.
下面的源代码打印所有本地函数的行号。如果调用另一个库中的函数,您可能会看到几个??: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());
}
This code should be compiled as: gcc sighandler.c -o sighandler -rdynamic
此代码应编译为: gcc sighandler.c -o sighandler -rdynamic
The program outputs:
程序输出:
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
回答by Industrial-antidepressant
Use the google glog library for it. It has new BSD licence.
使用谷歌 glog 库。它有新的 BSD 许可证。
It contains a GetStackTrace function in the stacktrace.h file.
它在 stacktrace.h 文件中包含一个 GetStackTrace 函数。
EDIT
编辑
I found here http://blog.bigpixel.ro/2010/09/09/stack-unwinding-stack-trace-with-gcc/that there is an utility called addr2line that translates program addresses into file names and line numbers.
我在这里发现http://blog.bigpixel.ro/2010/09/09/stack-unwinding-stack-trace-with-gcc/有一个名为 addr2line 的实用程序,可以将程序地址转换为文件名和行号。
回答by osgx
The one of solutions is to start a gdb with "bt"-script in failed assert handler. It is not very easy to integrate such gdb-starting, but It will give you both backtrace and args and demangle names (or you can pass gdb output via c++filt programm).
解决方案之一是在失败的断言处理程序中使用“bt”脚本启动 gdb。集成这样的 gdb-starting 不是很容易,但它会给你回溯和 args 以及 demangle 名称(或者你可以通过 c++filt 程序传递 gdb 输出)。
Both programms (gdb and c++filt) will be not linked into your application, so GPL will not require you to opensource complete application.
这两个程序(gdb 和 c++filt)都不会链接到您的应用程序中,因此 GPL 不会要求您开源完整的应用程序。
The same approach (exec a GPL programme) you can use with backtrace-symbols. Just generate ascii list of %eip's and map of exec file (/proc/self/maps) and pass it to separate binary.
可以与回溯符号一起使用的相同方法(执行 GPL 程序)。只需生成 %eip 的 ascii 列表和 exec 文件(/proc/self/maps)的映射并将其传递给单独的二进制文件。
回答by BlackBear
I suppose line numbers are related to current eip value, right?
我想行号与当前的 eip 值有关,对吗?
SOLUTION 1:
Then you can use something like GetThreadContext(), except that you're working on linux. I googled around a bit and found something similar, ptrace():
解决方案 1:
然后你可以使用类似GetThreadContext() 的东西,除非你在 linux 上工作。我用谷歌搜索了一下,发现了类似的东西,ptrace():
The ptrace() system call provides a means by which a parent process may observe and control the execution of another process, and examine and change its core image and registers. [...] The parent can initiate a trace by calling fork(2) and having the resulting child do a PTRACE_TRACEME, followed (typically) by an exec(3). Alternatively, the parent may commence trace of an existing process using PTRACE_ATTACH.
ptrace() 系统调用提供了一种方法,父进程可以通过该方法观察和控制另一个进程的执行,并检查和更改其核心映像和寄存器。[...] 父级可以通过调用 fork(2) 并让结果子级执行 PTRACE_TRACEME 来启动跟踪,然后(通常)是 exec(3)。或者,父进程可以使用 PTRACE_ATTACH 开始跟踪现有进程。
Now I was thinking, you can do a 'main' program which checks for signals that are sent to its child, the real program you're working on. after fork()
it call waitid():
现在我在想,你可以做一个“主”程序来检查发送给它的孩子的信号,你正在处理的真正的程序。在fork()
它调用waitid() 之后:
All of these system calls are used to wait for state changes in a child of the calling process, and obtain information about the child whose state has changed.
所有这些系统调用都用于等待调用进程的子进程的状态更改,并获取有关状态已更改的子进程的信息。
and if a SIGSEGV (or something similar) is caught call ptrace()
to obtain eip
's value.
如果 SIGSEGV(或类似的东西)被捕获,则调用ptrace()
来获取eip
的值。
PS: I've never used these system calls (well, actually, I've never seen them before ;) so I don't know if it's possible neither can help you. At least I hope these links are useful. ;)
PS:我从未使用过这些系统调用(嗯,实际上,我以前从未见过它们;)所以我不知道是否可能,两者都不能帮助您。至少我希望这些链接有用。;)
SOLUTION 2:The first solution is quite complicated, right? I came up with a much simpler one: using signal()catch the signals you are interested in and call a simple function that reads the eip
value stored in the stack:
解决方案2:第一个解决方案相当复杂,对吗?我想出了一个更简单的方法:使用signal()捕获您感兴趣的信号并调用一个简单的函数来读取eip
存储在堆栈中的值:
...
signal(SIGSEGV, sig_handler);
...
void sig_handler(int signum)
{
int eip_value;
asm {
push eax;
mov eax, [ebp - 4]
mov eip_value, eax
pop eax
}
// now you have the address of the
// **next** instruction after the
// SIGSEGV was received
}
That asm syntax is Borland's one, just adapt it to GAS
. ;)
该 asm 语法是 Borland 的语法,只需将其调整为GAS
. ;)
回答by KeithB
Since the GPL licensed code is intended to help you during development, you could simply not include it in the final product. The GPL restricts you from distributing GPL licenses code linked with non-GPL compatible code. As long as you only use the GPL code inhouse, you should be fine.
由于 GPL 许可代码旨在在开发过程中为您提供帮助,因此您不能将其包含在最终产品中。GPL 限制您分发与非 GPL 兼容代码链接的 GPL 许可证代码。只要你只在内部使用 GPL 代码,你应该没问题。
回答by nobar
There is a robust discussion of essentially the same question at: How to generate a stacktrace when my gcc C++ app crashes. Many suggestions are provided, including lots of discussion about how to generate stack traces at run-time.
对基本相同的问题进行了有力的讨论:How to generate a stacktrace when my gcc C++ app crashes。提供了许多建议,包括关于如何在运行时生成堆栈跟踪的大量讨论。
My personal favorite answerfrom that thread was to enable core dumpswhich allows you to view the complete application state at the time of the crash(including function arguments, line numbers, and unmangled names). An additional benefit of this approach is that it not only works for asserts, but also for segmentation faultsand unhandled exceptions.
我个人最喜欢该线程的答案是启用核心转储,它允许您在崩溃时查看完整的应用程序状态(包括函数参数、行号和未混淆的名称)。这种方法的另一个好处是它不仅适用于断言,而且适用于分段错误和未处理的异常。
Different Linux shells use different commands to enable core dumps, but you can do it from within your application code with something like this...
不同的 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
After a crash, run your favorite debugger to examine the program state.
崩溃后,运行您最喜欢的调试器来检查程序状态。
$ kdbg executable core
Here's some sample output...
这是一些示例输出...
It is also possible to extract the stack trace from a core dump at the command line.
也可以从命令行的核心转储中提取堆栈跟踪。
$ ( 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
回答by nobar
Here's an alternative approach. A debug_assert() macro programmatically sets a conditional breakpoint. If you are running in a debugger, you will hit a breakpoint when the assert expression is false -- and you can analyze the live stack(the program doesn't terminate). If you are not running in a debugger, a failed debug_assert() causes the program to abort and you get a core dump from which you can analyze the stack(see my earlier answer).
这是另一种方法。甲debug_assert()宏编程设置一个条件断点。如果您在调试器中运行,当断言表达式为假时,您将遇到断点——并且您可以分析实时堆栈(程序不会终止)。如果您没有在调试器中运行,失败的 debug_assert() 会导致程序中止,并且您会获得一个核心转储,您可以从中分析堆栈(请参阅我之前的回答)。
The advantage of this approach, compared to normal asserts, is that you can continue running the program after the debug_assert is triggered (when running in a debugger). In other words, debug_assert() is slightly more flexible than assert().
与普通断言相比,这种方法的优点是您可以在触发 debug_assert 后(在调试器中运行时)继续运行程序。换句话说,debug_assert() 比 assert() 稍微灵活一些。
#include <iostream>
#include <cassert>
#include <sys/resource.h>
// note: The assert expression should show up in
// stack trace as parameter to this function
void debug_breakpoint( char const * expression )
{
asm("int3"); // x86 specific
}
#ifdef NDEBUG
#define debug_assert( expression )
#else
// creates a conditional breakpoint
#define debug_assert( expression ) \
do { if ( !(expression) ) debug_breakpoint( #expression ); } while (0)
#endif
void recursive( int i=0 )
{
debug_assert( i < 5 );
if ( i < 10 ) recursive(i+1);
}
int main( int argc, char * argv[] )
{
rlimit core_limit = { RLIM_INFINITY, RLIM_INFINITY };
setrlimit( RLIMIT_CORE, &core_limit ); // enable core dumps
recursive();
}
Note: Sometimes "conditional breakpoints" setup within debuggers can be slow. By establishing the breakpoint programmatically, the performance of this method should be equivalent to that of a normal assert().
注意:有时在调试器中设置“条件断点”会很慢。通过以编程方式建立断点,该方法的性能应该等同于普通的 assert()。
Note: As written, this is specific to the Intel x86 architecture -- other processors may have different instructions for generating a breakpoint.
注意:正如所写的,这是特定于 Intel x86 架构的——其他处理器可能有不同的指令来生成断点。
回答by nobar
Here's my third answer -- still trying to take advantage of core dumps.
这是我的第三个答案——仍在尝试利用核心转储。
It wasn't completely clear in the question whether the "assert-like" macros were supposed to terminate the application (the way assert does) or they were supposed to continue executing after generating their stack-trace.
“类断言”宏是应该终止应用程序(断言的方式)还是应该在生成堆栈跟踪后继续执行的问题并不完全清楚。
In this answer, I'm addressing the case where you want to show a stack-trace and continue executing. I wrote the coredump() function below to generate a core dump, automatically extract the stack-tracefrom it, then continue executing the program.
在这个答案中,我正在解决您想要显示堆栈跟踪并继续执行的情况。我在下面写了coredump()函数来生成core dump,自动从中提取stack-trace,然后继续执行程序。
Usage is the same as that of assert(). The difference, of course, is that assert() terminates the program but coredump_assert() does not.
用法与 assert() 相同。当然,不同之处在于 assert() 会终止程序,而coredump_assert() 不会。
#include <iostream>
#include <sys/resource.h>
#include <cstdio>
#include <cstdlib>
#include <boost/lexical_cast.hpp>
#include <string>
#include <sys/wait.h>
#include <unistd.h>
std::string exename;
// expression argument is for diagnostic purposes (shows up in call-stack)
void coredump( char const * expression )
{
pid_t childpid = fork();
if ( childpid == 0 ) // child process generates core dump
{
rlimit core_limit = { RLIM_INFINITY, RLIM_INFINITY };
setrlimit( RLIMIT_CORE, &core_limit ); // enable core dumps
abort(); // terminate child process and generate core dump
}
// give each core-file a unique name
if ( childpid > 0 ) waitpid( childpid, 0, 0 );
static int count=0;
using std::string;
string pid = boost::lexical_cast<string>(getpid());
string newcorename = "core-"+boost::lexical_cast<string>(count++)+"."+pid;
string rawcorename = "core."+boost::lexical_cast<string>(childpid);
int rename_rval = rename(rawcorename.c_str(),newcorename.c_str()); // try with core.PID
if ( rename_rval == -1 ) rename_rval = rename("core",newcorename.c_str()); // try with just core
if ( rename_rval == -1 ) std::cerr<<"failed to capture core file\n";
#if 1 // optional: dump stack trace and delete core file
string cmd = "( CMDFILE=$(mktemp); echo 'bt' >${CMDFILE}; gdb 2>/dev/null --batch -x ${CMDFILE} "+exename+" "+newcorename+" ; unlink ${CMDFILE} )";
int system_rval = system( ("bash -c '"+cmd+"'").c_str() );
if ( system_rval == -1 ) std::cerr.flush(), perror("system() failed during stack trace"), fflush(stderr);
unlink( newcorename.c_str() );
#endif
}
#ifdef NDEBUG
#define coredump_assert( expression ) ((void)(expression))
#else
#define coredump_assert( expression ) do { if ( !(expression) ) { coredump( #expression ); } } while (0)
#endif
void recursive( int i=0 )
{
coredump_assert( i < 2 );
if ( i < 4 ) recursive(i+1);
}
int main( int argc, char * argv[] )
{
exename = argv[0]; // this is used to generate the stack trace
recursive();
}
When I run the program, it displays three stack traces...
当我运行程序时,它显示三个堆栈跟踪...
Core was generated by `./temp.exe'.
Program terminated with signal 6, Aborted.
[New process 24251]
#0 0x00007f2818ac9fb5 in raise () from /lib/libc.so.6
#0 0x00007f2818ac9fb5 in raise () from /lib/libc.so.6
#1 0x00007f2818acbbc3 in abort () from /lib/libc.so.6
#2 0x0000000000401a0e in coredump (expression=0x403303 "i < 2") at ./demo3.cpp:29
#3 0x0000000000401f5f in recursive (i=2) at ./demo3.cpp:60
#4 0x0000000000401f70 in recursive (i=1) at ./demo3.cpp:61
#5 0x0000000000401f70 in recursive (i=0) at ./demo3.cpp:61
#6 0x0000000000401f8b in main (argc=1, argv=0x7fffc229eb98) at ./demo3.cpp:66
Core was generated by `./temp.exe'.
Program terminated with signal 6, Aborted.
[New process 24259]
#0 0x00007f2818ac9fb5 in raise () from /lib/libc.so.6
#0 0x00007f2818ac9fb5 in raise () from /lib/libc.so.6
#1 0x00007f2818acbbc3 in abort () from /lib/libc.so.6
#2 0x0000000000401a0e in coredump (expression=0x403303 "i < 2") at ./demo3.cpp:29
#3 0x0000000000401f5f in recursive (i=3) at ./demo3.cpp:60
#4 0x0000000000401f70 in recursive (i=2) at ./demo3.cpp:61
#5 0x0000000000401f70 in recursive (i=1) at ./demo3.cpp:61
#6 0x0000000000401f70 in recursive (i=0) at ./demo3.cpp:61
#7 0x0000000000401f8b in main (argc=1, argv=0x7fffc229eb98) at ./demo3.cpp:66
Core was generated by `./temp.exe'.
Program terminated with signal 6, Aborted.
[New process 24267]
#0 0x00007f2818ac9fb5 in raise () from /lib/libc.so.6
#0 0x00007f2818ac9fb5 in raise () from /lib/libc.so.6
#1 0x00007f2818acbbc3 in abort () from /lib/libc.so.6
#2 0x0000000000401a0e in coredump (expression=0x403303 "i < 2") at ./demo3.cpp:29
#3 0x0000000000401f5f in recursive (i=4) at ./demo3.cpp:60
#4 0x0000000000401f70 in recursive (i=3) at ./demo3.cpp:61
#5 0x0000000000401f70 in recursive (i=2) at ./demo3.cpp:61
#6 0x0000000000401f70 in recursive (i=1) at ./demo3.cpp:61
#7 0x0000000000401f70 in recursive (i=0) at ./demo3.cpp:61
#8 0x0000000000401f8b in main (argc=1, argv=0x7fffc229eb98) at ./demo3.cpp:66
回答by nobar
So you want a stand-alone function that prints a stack tracewith all of the features that gdb stack traces have and that doesn't terminate your application. The answer is to automate the launch of gdb in a non-interactive mode to perform just the tasks that you want.
因此,您需要一个独立的函数来打印具有gdb 堆栈跟踪具有的所有功能的堆栈跟踪,并且不会终止您的应用程序。答案是在非交互模式下自动启动 gdb 以执行您想要的任务。
This is done by executing gdb in a child process, using fork(), and scripting it to display a stack-trace while your application waits for it to complete. This can be performed without the use of a core-dump and without aborting the application. I learned how to do this from looking at this question: How it's better to invoke gdb from program to print it's stacktrace?
这是通过在子进程中执行 gdb,使用 fork() 并编写脚本以在您的应用程序等待它完成时显示堆栈跟踪来完成的。这可以在不使用核心转储和不中止应用程序的情况下执行。我通过查看这个问题学会了如何做到这一点:How it's better to invoke gdb from program to print it's stacktrace?
The example posted with that question didn't work for me exactly as written, so here's my "fixed" version (I ran this on Ubuntu 9.04).
与该问题一起发布的示例对我来说并不完全按照所写的那样工作,所以这是我的“固定”版本(我在 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=%s\n",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);
}
}
As shown in the referenced question, gdb provides additional options that you could use. For example, using "bt full" instead of "bt" produces an even more detailed report (local variables are included in the output). The manpages for gdb are kind of light, but complete documentation is available here.
如引用的问题所示,gdb 提供了您可以使用的其他选项。例如,使用“bt full”而不是“bt”会生成更详细的报告(输出中包含局部变量)。gdb 的联机帮助页很简单,但这里提供了完整的文档。
Since this is based on gdb, the output includes demangled names, line-numbers, function arguments, and optionally even local variables. Also, gdb is thread-aware, so you should be able to extract some thread-specific metadata.
由于这是基于 gdb 的,因此输出包括demangled names、line-numbers、function arguments,甚至可以选择局部变量。此外,gdb 是线程感知的,因此您应该能够提取一些特定于线程的元数据。
Here's an example of the kind of stack traces that I see with this method.
这是我使用此方法看到的堆栈跟踪类型的示例。
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
Note: I found this to be incompatible with the use of valgrind(probably due to Valgrind's use of a virtual machine). It also doesn't work when you are running the program inside of a gdb session (can't apply a second instance of "ptrace" to a process).
注意:我发现这与使用valgrind不兼容(可能是由于 Valgrind 使用了虚拟机)。当您在 gdb 会话中运行程序时,它也不起作用(不能将“ptrace”的第二个实例应用于进程)。
回答by rve
A bit late, but you can use libbfbto fetch the filename and linenumber like refdbg does in symsnarf.c. libbfb is internally used by addr2line
and gdb
有点晚了,但您可以使用libbfb来获取文件名和行号,就像 refdbg 在symsnarf.c 中所做的那样。libbfb 由addr2line
和在内部使用gdb