更新时间: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!