由于种种原因,我还是不太推荐在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