如何在C中获取堆栈跟踪?

时间:2020-03-06 14:27:52  来源:igfitidea点击:

我知道没有标准的C函数可以执行此操作。我想知道在Windows和* nix上有什么技巧? (Windows XP是我目前最重要的操作系统。)

解决方案

我们可以通过向后移动堆栈来实现。但是,实际上,在每个函数的开头将标识符添加到调用堆栈中并在结尾将其弹出通常会更容易,然后遍历打印内容。它有点像PITA,但是效果很好,最终可以节省时间。

没有平台独立的方法可以做到这一点。

我们可以做的最接近的事情是无需优化即可运行代码。这样,我们可以添加到进程(使用可视c ++调试器或者GDB)并获得可用的堆栈跟踪。

glibc提供了backtrace()函数。

http://www.gnu.org/software/libc/manual/html_node/Backtraces.html

我们已经在项目中使用了它:

https://www.codeproject.com/kb/threads/stackwalker.aspx

该代码有点杂乱恕我直言,但效果很好。仅Windows。

Solaris具有pstack命令,该命令也已复制到Linux中。

对于Windows,请检查StackWalk64()API(也在32位Windows上)。对于UNIX,我们应该使用操作系统的本机方式执行此操作,如果可用,则应回退至glibc的backtrace()。

但是请注意,在本机代码中使用Stacktrace并不是一个好主意,这不是因为不可能,而是因为我们通常试图实现错误的目标。

在大多数情况下,人们会尝试在异常情况下获取堆栈跟踪,例如,当捕获异常,断言失败或者最坏情况时,以及当我们遇到致命的"异常"或者信号(例如分段违规)时,大多数错误都是在这种情况下。

考虑到最后一个问题,大多数API都将要求我们显式分配内存或者可以在内部进行分配。在程序当前处于脆弱状态下这样做可能会使情况变得更糟。例如,崩溃报告(或者coredump)不会反映问题的实际原因,但是我们处理该问题的尝试失败。

我认为我们正在尝试实现致命错误处理,因为大多数人在获取堆栈跟踪时似乎都尝试这样做。如果是这样,我将依靠调试器(在开发过程中)并使进程在生产环境中转储(或者在Windows上进行小型转储)。连同适当的符号管理,我们应该毫不费力地确定事后说明的原因。

对于Windows,CaptureStackBackTrace()也是一个选项,与StackWalk64()相比,它在用户端需要的准备代码更少。 (此外,对于我遇到的类似情况,CaptureStackBackTrace()比" StackWalk64()"更好(更可靠)地工作。)

我可以指出文章吗?只是几行代码。

事后制式调试

尽管我目前在x64实现上遇到问题。

我们应该使用展开库。

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的源功能/行号。

有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()的结果保存在异常类构造函数中。因此,无论何时抛出这种类型的异常,我们都有堆栈跟踪。然后,只需提供将其打印出来的功能即可。例如:

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 

...

try {
   throw MyException("Oops!");
} catch ( MyException e ) {
    e.printStackTrace();
}

da!

注意:启用优化标志可能会使生成的堆栈跟踪不准确。理想情况下,将在调试标志打开和优化标志关闭的情况下使用此功能。