菜鸟笔记
提升您的技术认知

c 捕获异常时的栈信息-ag真人游戏

由于种种原因,我还是不太推荐在c 使用异常机制。所以也不捕获异常,如果有问题直接让它挂掉。
最近遇到一个问题,我的框架“帮”我捕获了vector抛出的越界异常,没有了core文件,很难定位问题具体出在哪一行。

backtrace 是可以捕获的栈信息的,但是捕获到异常时已经丢失栈信息了。
__cxa_throw 是在抛出异常时被调用的函数,在这个函数中可以捕获栈信息。

示例代码如下:

extern "c" {
  void __cxa_throw(void *ex, void *info, void (*dest)(void *)) {
    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);
}

这段代码比较有趣,先是重载了__cxa_throw这个函数,然后又通过dlsym找到原函数。
这种做法虽然不是很好,但对于我这种不使用异常的人很合适。

完整的代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
namespace {
  void * last_frames[100];
  size_t last_size;
  std::string exception_name;
  std::string demangle(const char *name) {
    int status;
    std::unique_ptr 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(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);
  }
}

编译、执行后会输出:

g   -std=c  0x -g -rdynamic -ldl  test.cpp
./a.out
caught a: int
./a.out(__cxa_throw 0x82)[0x401e8a]
./a.out(main 0x0)[0x401f18]
./a.out(main 0xc)[0x401f24]
/lib64/libc.so.6(__libc_start_main 0xfd)[0x3b6641ed5d]
./a.out[0x401c69]

然后使用 addr2line 命令可以定位到代码中的位置。

addr2line 0x401f24 -e ./a.out
./test.cpp:38
网站地图