1. 问题
一个 C 程序,如果 throw 了 exception ,但是又没有 catch,那么一般会产生 coredump, 问题是,在 gcc 4.x 版本产生的 coredump 文件中,没有 throw 时候的堆栈信息,导致不知道是哪里 throw 的,没法查问题。
原因是 gcc 4.x 的 /libstdc -v3/src/c 11/thread.cc:92 里面有个 catch(…),所以 stack unwind 了,就没了 throw 时候的 stack 。
1 2 3 4 5 6 7 | void * execute_native_thread_routine(){ try { ... }catch(...){ std::terminate(); } } |
---|
https://abcdabcd987.com/libstdc -bug/
一个解决办法是可以升级 GCC 7 ,或者可以用更简单的办法:
1.代码 hook __cxa_throw
,让 coredump 带上堆栈
一个解决办法是通过改代码,hook __cxa_throw()
让每次生成的 coredump 都带上堆栈:
https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/cxxabi.h#L616
1 2 | // Throw the exception. void __cxa_throw(void*, std::type_info*, void (_GLIBCXX_CDTOR_CALLABI *) (void *)) __attribute__((__noreturn__)); |
---|
__cxa_throw()
是 libstdc /libc 用于实现 throw 的函数。
https://libcxxabi.llvm.org/spec.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #include <iostream> #include <stdexcept> #include <thread> extern "C" { //加这3行代码,通过 hook __cxa_throw,直接 abort,可以避免 stack unwind。 void __cxa_throw(void* ex, void* info, void (*dest)(void*)) { ::abort(); } } void func(){ throw std::runtime_error("die"); } int main() { std::thread t(func); t.join(); return 0; } |
---|
效果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | (gdb) bt #0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51 #1 0x00007ffff7264801 in __GI_abort () at abort.c:79 #2 0x0000555555554f53 in __cxa_throw (ex=0x7ffff0000ba0, info=0x555555756ce0 <typeinfo for std::runtime_error@@GLIBCXX_3.4>, dest=0x7ffff7af4140 <std::runtime_error::~runtime_error()>) at test.cpp:6 #3 0x0000555555554f8f in func () at test.cpp:10 #4 0x0000555555555371 in std::__invoke_impl<void, void (*)()> (__f=@0x555555769e78: 0x555555554f53 <func()>) at /usr/include/c /7/bits/invoke.h:60 #5 0x000055555555517e in std::__invoke<void (*)()> (__fn=@0x555555769e78: 0x555555554f53 <func()>) at /usr/include/c /7/bits/invoke.h:95 #6 0x000055555555584c in std:: thread::_Invoker<std::tuple<void (*)()> >::_M_invoke<0ul> (this=0x555555769e78) at /usr/include/c /7/thread:234 #7 0x0000555555555808 in std:: thread::_Invoker<std::tuple<void (*)()> >::operator() (this=0x555555769e78) at /usr/include/c /7/thread:243 #8 0x00005555555557d8 in std:: thread::_State_impl<std:: thread::_Invoker<std::tuple<void (*)()> > >::_M_run (this=0x555555769e70) at /usr/include/c /7/thread:186 #9 0x00007ffff7b096ef in ?? () from /usr/lib/x86_64-linux-gnu/libstdc .so.6 #10 0x00007ffff761c6db in start_thread (arg=0x7ffff6e85700) at pthread_create.c:463 #11 0x00007ffff734588f in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95 |
---|
2. gdb catch throw
如果是对已有的二进制,或者已经在运行的进程:
gdb 里面输入 catch throw
然后运行,gdb 就会在任何 throw 的时候暂停,即可看到 throw 时候的栈。