怎样才能在C中抓取堆栈跟踪?
我知道有没有标准的C函数来做到这一点。 我想知道什么是在Windows和* nix这个技术? (Windows XP是我现在要做的最重要的操作系统。)
我们已经将这个用于我们的项目:
https://www.codeproject.com/kb/threads/stackwalker.aspx
该代码是一个凌乱的恕我直言,但它运作良好。 仅限Windows。
glibc提供了backtrace()函数。
http://www.gnu.org/software/libc/manual/html_node/Backtraces.html
有backtrace()和backtrace_symbols():
从手册页:
#include <execinfo.h> #include <stdio.h> ... void* callstack[128]; int i, frames = backtrace(callstack, 128); char** strs = backtrace_symbols(callstack, frames); for (i = 0; i < frames; ++i) { printf("%s\n", strs[i]); } free(strs); ...
以更方便/ OOP方式使用这种方法的一种方法是将backtrace_symbols()的结果保存在exception类构造函数中。 因此,每当你抛出这种types的exception,你有堆栈跟踪。 然后,只需提供打印出来的function。 例如:
class MyException : public std::exception { char ** strs; MyException( const std::string & message ) { int i, frames = backtrace(callstack, 128); strs = backtrace_symbols(callstack, frames); } void printStackTrace() { for (i = 0; i
…
尝试{ 抛出MyException(“糟糕!”); catch(MyException e){ e.printStackTrace(); }
达达!
注意:启用优化标志可能会导致堆栈跟踪不准确。 理想情况下,人们可以使用此function打开debugging标志和优化标志。
对于Windows,检查StackWalk64()API(也在32位Windows上)。 对于UNIX,您应该使用操作系统的本地方式来执行此操作,或者回退到glibc的backtrace()(如果可用)。
但是请注意,在本地代码中使用Stacktrace并不是一个好主意 – 不是因为这是不可能的,而是因为你试图实现错误的东西。
大多数情况下,人们试图在特殊情况下进行堆栈跟踪,比如发生exception时,断言失败,或者 – 最糟糕,最糟糕的是 – 当您遇到致命的“exception”或信号时分割违规。
考虑到最后一个问题,大多数的API将要求你明确地分配内存,或者可以在内部完成。 在你的程序目前处于脆弱的状态下这样做,可能会使事情变得更糟。 例如,崩溃报告(或coredump)将不会反映问题的实际原因,而是您尝试处理它的失败)。
我假设你正试图实现这个致命的error handling的事情,因为大多数人似乎试图获得堆栈跟踪。 如果是这样的话,我会依赖debugging器(在开发过程中),并让生产过程中的coredump(或Windows上的小型转储)。 加上正确的符号pipe理,你应该毫不费力地确定导致指令后验。
对于Windows, CaptureStackBackTrace()
也是一个选项,它比StackWalk64()
在用户端需要更less的准备代码。 (另外,对于类似的情况, CaptureStackBackTrace()
)比StackWalk64()
更好地工作(更可靠) StackWalk64()
。
你应该使用unwind库 。
unw_cursor_t cursor; unw_context_t uc; unw_word_t ip, sp; unw_getcontext(&uc); unw_init_local(&cursor, &uc); unsigned long a[100]; int ctr = 0; while (unw_step(&cursor) > 0) { unw_get_reg(&cursor, UNW_REG_IP, &ip); unw_get_reg(&cursor, UNW_REG_SP, &sp); if (ctr >= 10) break; a[ctr++] = ip; }
除非您从共享库拨打电话,否则您的方法也可以正常工作。
您可以在Linux上使用addr2line
命令来获取相应PC的源函数/行号。
没有平台独立的方式来做到这一点。
您可以做的最近的事情是运行代码而不进行优化。 这样你可以附加到进程(使用visual c + +debugging器或GDB),并获得一个可用的堆栈跟踪。
我可以指给你我的文章吗? 这只是几行代码。
Post Mortemdebugging
虽然我目前在x64的这个实现方面有问题。
Solaris具有pstack命令,该命令也被复制到Linux中。
你可以通过向后走栈来做到这一点。 但事实上,在每个函数的开头添加一个标识符到一个调用堆栈上并在最后popup,然后再打印内容就变得容易了。 这是一个PITA,但它运作良好,最终会节省您的时间。