如何在C++中使用C++和代码注入为已捕获的异常打印堆栈跟踪

boq*_*apt 25 c++ gcc exception-handling code-injection c++11

我希望堆栈跟踪不仅适用于我的例外,也适用于任何后代 std::exception

据我所知,由于堆栈展开(展开),捕获异常时堆栈跟踪完全丢失.

所以我看到抓住它的唯一方法是在std::exception构造函数调用的地方注入代码保存上下文信息(堆栈跟踪).我对吗?

如果是这种情况,请告诉我如何在C++中完成代码注入(如果可以).您的方法可能不完全安全,因为我只需要我的应用程序的调试版本.可能是我需要使用汇编程序?

我只对GCC的解决方案感兴趣.它可以使用c ++ 0x功能

Fle*_*exo 33

既然你提到你对GCC具体的东西感到满意,我就把你可能做的一个例子放在一起.虽然这是纯粹的邪恶,介入C++支持库的内部.我不确定我是否想在生产代码中使用它.无论如何:

#include <iostream>
#include <dlfcn.h>
#include <execinfo.h>
#include <typeinfo>
#include <string>
#include <memory>
#include <cxxabi.h>
#include <cstdlib>

namespace {
  void * last_frames[20];
  size_t last_size;
  std::string exception_name;

  std::string demangle(const char *name) {
    int status;
    std::unique_ptr<char,void(*)(void*)> realname(abi::__cxa_demangle(name, 0, 0, &status), &std::free);
    return status ? "failed" : &*realname;
  }
}

extern "C" {
  void __cxa_throw(void *ex, void *info, void (*dest)(void *)) {
    exception_name = demangle(reinterpret_cast<const std::type_info*>(info)->name());
    last_size = backtrace(last_frames, sizeof last_frames/sizeof(void*));

    static void (*const rethrow)(void*,void*,void(*)(void*)) __attribute__ ((noreturn)) = (void (*)(void*,void*,void(*)(void*)))dlsym(RTLD_NEXT, "__cxa_throw");
    rethrow(ex,info,dest);
  }
}

void foo() {
  throw 0;
}

int main() {
  try {
    foo();
  }
  catch (...) {
    std::cerr << "Caught a: " << exception_name << std::endl;
    // print to stderr
    backtrace_symbols_fd(last_frames, last_size, 2);
  }
}
Run Code Online (Sandbox Code Playgroud)

我们基本上窃取了GCC用于调度抛出异常的内部实现函数的调用.此时,我们采用堆栈跟踪并将其保存在全局变量中.然后,当我们稍后在try/catch中遇到该异常时,我们可以使用stacktrace来打印/保存或者您想要做什么.我们dlsym()用来找到真正的版本__cxa_throw.

我的示例抛出一个int证明您可以使用字面上的任何类型,而不仅仅是您自己的用户定义的异常.

它使用the type_info来获取抛出的类型的名称,然后对其进行解码.

如果您愿意,可以封装存储堆栈跟踪的全局变量.

我编译并测试了这个:

g++ -Wall -Wextra test.cc -g -O0 -rdynamic -ldl

运行时给出了以下内容:

./a.out
Caught a: int
./a.out(__cxa_throw+0x74)[0x80499be]
./a.out(main+0x0)[0x8049a61]
./a.out(main+0x10)[0x8049a71]
/lib/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0xb75c2ca6]
./a.out[0x80497e1]

请不要将此作为一个好建议的例子 - 这是一个例子,你可以用一点点诡计和内部探索来做些什么!


Max*_*kin 5

在 Linux 上,这可以通过backtrace()在异常构造函数中添加对 的调用来实现,以将堆栈跟踪捕获到异常的成员变量中。不幸的是,它不适用于标准异常,仅适用于您定义的异常。