gpt4 book ai didi

c++ - 如何打印 C++ 中捕获的异常的堆栈跟踪和 C++ 中的代码注入(inject)

转载 作者:IT老高 更新时间:2023-10-28 12:59:53 46 4
gpt4 key购买 nike

我希望堆栈跟踪不仅适用于我的异常,还适用于 std::exception

的任何后代

据我了解,由于堆栈展开(展开)而捕获异常时,堆栈跟踪将完全丢失。

所以我看到获取它的唯一方法是在 std::exception 构造函数调用的位置注入(inject)代码保存上下文信息(堆栈跟踪)。我说的对吗?

如果是这样,请告诉我如何在 C++ 中进行代码注入(inject)(如果可以的话)。您的方法可能并不完全安全,因为我只需要它用于我的应用程序的调试版本。我可能需要使用汇编程序吗?

我只对 GCC 的解决方案感兴趣。它可以使用 c++0x 特性

最佳答案

既然您提到您对特定于 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);
}
}

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

我的示例抛出了一个 int 来证明您可以使用任何类型来执行此操作,而不仅仅是您自己的用户定义的异常。

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

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

我编译并测试了这个:

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

运行时给出以下信息:

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

请不要把这个当成一个好的建议的例子 - 这是一个你可以用一点点诡计和在内部四处寻找的例子!

关于c++ - 如何打印 C++ 中捕获的异常的堆栈跟踪和 C++ 中的代码注入(inject),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11665829/

46 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com