Linux 在 C 或 C++ 中打印调用堆栈
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3899870/
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
print call stack in C or C++
提问by Nathan Fellman
Is there any way to dump the call stack in a running process in C or C++ every time a certain function is called? What I have in mind is something like this:
每次调用某个函数时,有没有办法在 C 或 C++ 中的运行进程中转储调用堆栈?我的想法是这样的:
void foo()
{
print_stack_trace();
// foo's body
return
}
Where print_stack_trace
works similarly to caller
in Perl.
Whereprint_stack_trace
与caller
Perl 中的工作方式类似。
Or something like this:
或者像这样:
int main (void)
{
// will print out debug info every time foo() is called
register_stack_trace_function(foo);
// etc...
}
where register_stack_trace_function
puts some sort of internal breakpoint that will cause a stack trace to be printed whenever foo
is called.
whereregister_stack_trace_function
放置了某种内部断点,无论何时foo
调用都会导致打印堆栈跟踪。
Does anything like this exist in some standard C library?
某些标准 C 库中是否存在这样的东西?
I am working on Linux, using GCC.
我正在 Linux 上工作,使用 GCC。
Background
背景
I have a test run that behaves differently based on some commandline switches that shouldn't affect this behavior. My code has a pseudo-random number generator that I assume is being called differently based on these switches. I want to be able to run the test with each set of switches and see if the random number generator is called differently for each one.
我有一个测试运行,它的行为基于一些不应影响此行为的命令行开关。我的代码有一个伪随机数生成器,我认为它根据这些开关被不同地调用。我希望能够使用每组开关运行测试,并查看随机数生成器是否为每个开关调用不同。
采纳答案by Idan K
For a linux-only solution you can use backtrace(3)that simply returns an array of void *
(in fact each of these point to the return address from the corresponding stack frame). To translate these to something of use, there's backtrace_symbols(3).
对于仅适用于 linux 的解决方案,您可以使用backtrace(3)来简单地返回一个数组void *
(实际上,这些数组中的每一个都指向相应堆栈帧的返回地址)。要将这些转换为有用的东西,有backtrace_symbols(3)。
Pay attention to the notes section in backtrace(3):
The symbol names may be unavailable without the use of special linker options. For systems using the GNU linker, it is necessary to use the -rdynamic linker option. Note that names of "static" functions are not exposed, and won't be available in the backtrace.
如果不使用特殊的链接器选项,符号名称可能不可用。对于使用 GNU 链接器的系统,必须使用 -rdynamic 链接器选项。请注意,“静态”函数的名称未公开,并且在回溯中不可用。
回答by sbi
Is there any way to dump the call stack in a running process in C or C++ every time a certain function is called?
每次调用某个函数时,有没有办法在 C 或 C++ 中的运行进程中转储调用堆栈?
No there is not, although platform-dependent solutions might exist.
不,没有,尽管可能存在依赖于平台的解决方案。
回答by slashmais
You can implement the functionality yourself:
您可以自己实现该功能:
Use a global (string)stack and at start of each function push the function name and such other values (eg parameters) onto this stack; at exit of function pop it again.
使用全局(字符串)堆栈,并在每个函数开始时将函数名称和其他值(例如参数)压入该堆栈;在函数退出时再次弹出它。
Write a function that will printout the stack content when it is called, and use this in the function where you want to see the callstack.
编写一个函数,在调用时会打印出堆栈内容,并在要查看调用堆栈的函数中使用它。
This may sound like a lot of work but is quite useful.
这可能听起来像很多工作,但非常有用。
回答by Paul Michalik
回答by Matthieu M.
Of course the next question is: will this be enough ?
当然,下一个问题是:这样就足够了吗?
The main disadvantage of stack-traces is that why you have the precise function being called you do not have anything else, like the value of its arguments, which is very useful for debugging.
堆栈跟踪的主要缺点是,为什么你有被调用的精确函数,你没有其他任何东西,比如它的参数值,这对于调试非常有用。
If you have access to gcc and gdb, I would suggest using assert
to check for a specific condition, and produce a memory dump if it is not met. Of course this means the process will stop, but you'll have a full fledged report instead of a mere stack-trace.
如果您可以访问 gcc 和 gdb,我建议您使用它assert
来检查特定条件,如果不满足则生成内存转储。当然,这意味着该过程将停止,但您将获得完整的报告,而不仅仅是堆栈跟踪。
If you wish for a less obtrusive way, you can always use logging. There are very efficient logging facilities out there, like Pantheiosfor example. Which once again could give you a much more accurate image of what is going on.
如果您希望采用一种不那么突兀的方式,您可以随时使用日志记录。那里有非常有效的日志记录工具,例如Pantheios。这再次可以让您更准确地了解正在发生的事情。
回答by Saurabh Shah
You can use the GNU profiler. It shows the call-graph as well! the command is gprof
and you need to compile your code with some option.
您可以使用 GNU 分析器。它还显示了调用图!命令是gprof
,你需要用一些选项编译你的代码。
回答by NullPointerException
Is there any way to dump the call stack in a running process in C or C++ every time a certain function is called?
每次调用某个函数时,有没有办法在 C 或 C++ 中的运行进程中转储调用堆栈?
You can use a macro function instead of return statement in the specific function.
您可以在特定函数中使用宏函数代替 return 语句。
For example, instead of using return,
例如,而不是使用 return,
int foo(...)
{
if (error happened)
return -1;
... do something ...
return 0
}
You can use a macro function.
您可以使用宏功能。
#include "c-callstack.h"
int foo(...)
{
if (error happened)
NL_RETURN(-1);
... do something ...
NL_RETURN(0);
}
Whenever an error happens in a function, you will see Java-style call stack as shown below.
每当函数中发生错误时,您将看到 Java 风格的调用堆栈,如下所示。
Error(code:-1) at : so_topless_ranking_server (sample.c:23)
Error(code:-1) at : nanolat_database (sample.c:31)
Error(code:-1) at : nanolat_message_queue (sample.c:39)
Error(code:-1) at : main (sample.c:47)
Full source code is available here.
完整源代码可在此处获得。
回答by Orlin Georgiev
You can use Poppyfor this. It is normally used to gather the stack trace during a crash but it can also output it for a running program as well.
为此,您可以使用Poppy。它通常用于在崩溃期间收集堆栈跟踪,但它也可以为正在运行的程序输出它。
Now here's the good part: it can output the actual parameter values for each function on the stack, and even local variables, loop counters, etc.
现在是好的部分:它可以输出堆栈上每个函数的实际参数值,甚至是局部变量、循环计数器等。
回答by Fran?ois
I know this thread is old, but I think it can be useful for other people. If you are using gcc, you can use its instrument features (-finstrument-functions option) to log any function call (entry and exit). Have a look at this for more information: http://hacktalks.blogspot.fr/2013/08/gcc-instrument-functions.html
我知道这个帖子很旧,但我认为它对其他人有用。如果您使用 gcc,您可以使用它的仪器功能(-finstrument-functions 选项)来记录任何函数调用(进入和退出)。看看这个了解更多信息:http: //hacktalks.blogspot.fr/2013/08/gcc-instrument-functions.html
You can thus for instance push and pop every calls into a stack, and when you want to print it, you just look at what you have in your stack.
例如,您可以将每个调用推入和弹出到一个堆栈中,当您想打印它时,您只需查看堆栈中的内容。
I've tested it, it works perfectly and is very handy
我已经测试过了,它运行良好,非常方便
UPDATE: you can also find information about the -finstrument-functions compile option in the GCC doc concerning the Instrumentation options: https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html
更新:您还可以在 GCC 文档中找到有关仪器选项的 -finstrument-functions 编译选项的信息:https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html
回答by Paul Floyd
Another answer to an old thread.
旧线程的另一个答案。
When I need to do this, I usually just use system()
and pstack
当我需要这样做时,我通常只使用system()
和pstack
So something like this:
所以像这样:
#include <sys/types.h>
#include <unistd.h>
#include <string>
#include <sstream>
#include <cstdlib>
void f()
{
pid_t myPid = getpid();
std::string pstackCommand = "pstack ";
std::stringstream ss;
ss << myPid;
pstackCommand += ss.str();
system(pstackCommand.c_str());
}
void g()
{
f();
}
void h()
{
g();
}
int main()
{
h();
}
This outputs
这输出
#0 0x00002aaaab62d61e in waitpid () from /lib64/libc.so.6
#1 0x00002aaaab5bf609 in do_system () from /lib64/libc.so.6
#2 0x0000000000400c3c in f() ()
#3 0x0000000000400cc5 in g() ()
#4 0x0000000000400cd1 in h() ()
#5 0x0000000000400cdd in main ()
This should work on Linux, FreeBSD and Solaris. I don't think that macOS has pstack or a simple equivalent, but this thread seems to have an alternative.
这应该适用于 Linux、FreeBSD 和 Solaris。我不认为 macOS 具有 pstack 或简单的等效项,但此线程似乎有替代.
If you are using C
, then you will need to use C
string functions.
如果您正在使用C
,那么您将需要使用C
字符串函数。
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
void f()
{
pid_t myPid = getpid();
/*
length of command 7 for 'pstack ', 7 for the PID, 1 for nul
*/
char pstackCommand[7+7+1];
sprintf(pstackCommand, "pstack %d", (int)myPid);
system(pstackCommand);
}
I've used 7 for the max number of digits in the PID, based on this post.
根据这篇文章,我使用 7 作为 PID 中的最大位数。