且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

我如何打印堆栈跟踪捕获的异常在C ++&代码注入在C ++

更新时间:2023-11-19 12:30:34

由于你提到你对GCC特定的东西感到满意组织一个例子,你可能会这样做的方式。这是纯粹的邪恶,插入在C ++支持库的内部。我不知道我想在生产代码中使用这个。无论如何:

Since you mentioned that you're happy with something that is GCC specific I've put together an example of a way you might do this. It's pure evil though, interposing on internals of the C++ support library. I'm not sure I'd want to use this in production code. Anyway:

#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);
  }
}

我们基本上窃取了对GCC内部实现函数的调用用于分派抛出的异常。此时,我们采取堆栈跟踪并将其保存在全局变量中。然后,当我们在try / catch中遇到该异常时,我们可以使用stacktrace来打印/保存或者你想要做的任何事情。我们使用 dlsym()找到 __ cxa_throw 的真实版本。

We basically steal calls to the internal implementation function that GCC uses for dispatching thrown exceptions. At that point we take a stack trace and save it in a global variable. Then when we come across that exception later on in our try/catch we can work with the stacktrace to print/save or whatever it is you want to do. We use dlsym() to find the real version of __cxa_throw.

我的例子引发一个 int ,证明你可以用字面上的任何类型,而不只是你自己的用户定义的异常。

My example throws an int to prove that you can do this with literally any type, not just your own user defined exceptions.

它使用 type_info 获取被抛出类型的名称,然后对其进行解构。

It uses the type_info to get the name of the type that was thrown and then demangles it.

您可以封装存储stacktrace的全局变量,如果你愿意的话。

You could encapsulate the global variables that store the stacktrace a bit better if you wanted to.

我编译并测试了与:

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


$ b b

运行时提供以下内容:

Which gave the following when run:


./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]

请不要把这作为一个好的建议的例子 - 这是一个例子,你可以做一点点的诡计和在内部戳戳。

Please don't take this as an example of good advice though - it's an example of what you can do with a little bit of trickery and poking around at the internals!