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 ,或者可以用更简单的办法:
2.代码 hook __cxa_throw
,让 coredump 带上堆栈
一个解决办法是通过改代码,hook __cxa_throw()
让每次生成的 coredump 都带上堆栈:
https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/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
|
#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
|
3. gdb catch throw
如果是对已有的二进制,或者已经在运行的进程:
gdb 里面输入
catch throw
然后运行,gdb 就会在任何 throw 的时候暂停,即可看到 throw 时候的栈。