I'm trying to "see" the call to std::terminate() when throwing from a destructor with the following code:
#include <stdexcept>
struct boom {
~boom() {
throw std::logic_error("something went wrong");
}
};
int main() {
boom();
}
compiled with g++ and run the code:
# ./a.out
terminate called after throwing an instance of 'std::logic_error'
what(): something went wrong
Aborted (core dumped)
so far so good, seems to work as expected (call terminate()). But when trying to break on terminate in gdb, the function does not get hit and the backtrace shows only abort:
(gdb) b std::terminate
Breakpoint 2 at 0x7f60a3772240 (2 locations)
(gdb) r
Starting program: a.out
terminate called after throwing an instance of 'std::logic_error'
what(): something went wrong
Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig#entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
51 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0 __GI_raise (sig=sig#entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1 0x00007f2c241eb921 in __GI_abort () at abort.c:79
#2 0x00007f2c24628957 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3 0x00007f2c2462eae6 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4 0x00007f2c2462db49 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#5 0x00007f2c2462e4b8 in __gxx_personality_v0 () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#6 0x00007f2c23c05573 in ?? () from /lib/x86_64-linux-gnu/libgcc_s.so.1
#7 0x00007f2c23c05ad1 in _Unwind_RaiseException () from /lib/x86_64-linux-gnu/libgcc_s.so.1
#8 0x00007f2c2462ed47 in __cxa_throw () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#9 0x0000558cee09593a in boom::~boom() ()
#10 0x0000558cee0958dd in main ()
(gdb)
This behavior is puzzling me somehow, is there something I am missing?
EDIT:
same test using clang++ and the std::terminate breakpoint is hit:
Breakpoint 1, 0x0000000000400750 in std::terminate()#plt ()
(gdb) bt
#0 0x0000000000400750 in std::terminate()#plt ()
#1 0x00000000004009bf in __clang_call_terminate ()
#2 0x000000000187eef0 in ?? ()
#3 0x00000000004009a4 in boom::~boom() ()
#4 0x00000000004008f1 in main ()
(gdb)
You are running into the as-if rule.
C++ compilers are allowed to essentially rewrite your entire program as they see fit as long as the side effects remain the same.
What a side effect is very well defined, and "a certain function gets called" is not part of that. So the compiler is perfectly allowed to just call abort() directly if it can determine that this is the only side effect std::terminate() would have on the program.
You can get more details at https://en.cppreference.com/w/cpp/language/as_if
Related
In below code, I am calling make_shared<MyClass>, and the constructor of MyClass throws exception. If the core file is available, is it possible to find out the origin of crash [e.g: whether crash was from foo() or fun()] with the help of gdb?
#include <iostream>
#include <memory>
using namespace std;
class MyClass
{
public:
MyClass()
{
foo();
fun();
}
~MyClass() { }
void foo()
{
throw("error 1");
}
void fun()
{
throw("error 2");
}
};
shared_ptr<MyClass> createMyClass()
{
return make_shared<MyClass>();
}
int main()
{
shared_ptr<MyClass> c = createMyClass();
return 0;
}
backtrace just points to this line:
29 return make_shared<MyClass>();
backtrace:
Program received signal SIGABRT, Aborted.
0x00007ffff722d5f7 in raise () from /lib64/libc.so.6
Missing separate debuginfos, use: debuginfo-install glibc-2.17-106.el7_2.6.x86_64 libgcc-4.8.5-4.el7.x86_64 libstdc++-4.8.5-4.el7.x86_64
(gdb) bt
#0 0x00007ffff722d5f7 in raise () from /lib64/libc.so.6
#1 0x00007ffff722ece8 in abort () from /lib64/libc.so.6
#2 0x00007ffff7b329d5 in __gnu_cxx::__verbose_terminate_handler() () from /lib64/libstdc++.so.6
#3 0x00007ffff7b30946 in ?? () from /lib64/libstdc++.so.6
#4 0x00007ffff7b30973 in std::terminate() () from /lib64/libstdc++.so.6
#5 0x00007ffff7b30be9 in __cxa_rethrow () from /lib64/libstdc++.so.6
#6 0x000000000040121e in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count<MyClass, std::allocator<MyClass>>(std::_Sp_make_shared_tag, MyClass*, std::allocator<MyClass> const&) (this=0x7fffffffe178, __a=...) at /usr/include/c++/4.8.2/bits/shared_ptr_base.h:509
#7 0x00000000004010ba in std::__shared_ptr<MyClass, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<std::allocator<MyClass>>(std::_Sp_make_shared_tag, std::allocator<MyClass> const&) (this=0x7fffffffe170, __tag=..., __a=...) at /usr/include/c++/4.8.2/bits/shared_ptr_base.h:957
#8 0x0000000000401052 in std::shared_ptr<MyClass>::shared_ptr<std::allocator<MyClass>>(std::_Sp_make_shared_tag, std::allocator<MyClass> const&) (this=0x7fffffffe170,
__tag=..., __a=...) at /usr/include/c++/4.8.2/bits/shared_ptr.h:316
#9 0x0000000000400f98 in std::allocate_shared<MyClass, std::allocator<MyClass>>(std::allocator<MyClass> const&) (__a=...) at /usr/include/c++/4.8.2/bits/shared_ptr.h:598
#10 0x0000000000400ee0 in std::make_shared<MyClass<> > () at /usr/include/c++/4.8.2/bits/shared_ptr.h:614
#11 0x0000000000400ce3 in createMyClass () at abrt.cpp:29
#12 0x0000000000400cfe in main () at abrt.cpp:34
(gdb) q
That information is lost by the time the core file is generated. The reason is that the shared_ptr constructor has to catch any exceptions from the object constructor in order to be able to deallocate the memory it allocated earlier (to prevent leaks induced by constructors that throw). By the time it catches the exception to deallocate the memory it no longer knows where in the constructor the exception was thrown.
From the back trace of gdb am getting the below stack trace.
What might be the reason .
Is it that new failed to allocate memory or something else.
#0 0x00c69eff in raise () from /lib/tls/libc.so.6
#1 0x00c6b705 in abort () from /lib/tls/libc.so.6
#2 0x005564f7 in __cxa_call_unexpected () from /usr/lib/libstdc++.so.5
#3 0x00556544 in std::terminate () from /usr/lib/libstdc++.so.5
#4 0x005566b6 in __cxa_throw () from /usr/lib/libstdc++.so.5
#5 0x005568d2 in operator new () from /usr/lib/libstdc++.so.5
#6 0x005569bf in operator new[] () from /usr/lib/libstdc++.so.5
I've been strong exception guarantee testing a class, especially on what happens on an out of memory condition, by randomly making malloc() return nullptr. It uses nested exceptions.
Let's say I have the following code:
static std::unordered_map<size_t, size_t> map;
try {
map.at(0); // Throws std::out_of_range
} catch (...) {
std::throw_with_nested(std::runtime_error("Input not in map")); // Out of memory here
}
std::throw_with_nested() ended up calling std::terminate():
terminate called after throwing an instance of 'std::out_of_range'
what(): _Map_base::at
Program received signal SIGABRT, Aborted.
0x00007ffff6d96418 in __GI_raise (sig=sig#entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
54 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0 0x00007ffff6d96418 in __GI_raise (sig=sig#entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1 0x00007ffff6d9801a in __GI_abort () at abort.c:89
#2 0x00007ffff76d884d in __gnu_cxx::__verbose_terminate_handler() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3 0x00007ffff76d66b6 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4 0x00007ffff76d6701 in std::terminate() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#5 0x00007ffff76d5472 in __cxa_allocate_exception () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#6 0x0000000000425d4c in std::_Throw_with_nested_impl<std::runtime_error, true>::_S_throw<std::runtime_error>(std::runtime_error&&) (
__t=<unknown type in /path/to/<redacted>, CU 0x2a2a, DIE 0xae780>) at /usr/include/c++/5/bits/nested_exception.h:100
#7 0x000000000041d09f in std::throw_with_nested<std::runtime_error>(std::runtime_error&&) (__t=<unknown type in /path/to/<redacted>, CU 0x2a2a, DIE 0xa3e18>)
at /usr/include/c++/5/bits/nested_exception.h:137
Is this the intended behaviour according to the standard? Personally, it feels like that it should just overwrite the old exception or throw std::bad_alloc if it fails to allocate a nested exception.
As far as I understand it, both the constructor of std::nested_exception and the global function std::current_exception() are noexcept, so if an exception occurs in either one, the only allowable course of action is std::terminate.
I am using boost threads, upon calling notify_all() within the destructor i am seeing a segmentation fault. Here is the stack:
(gdb) where
#0 0x00007ffff752de84 in pthread_mutex_lock ()
from /lib/x86_64-linux-gnu/libpthread.so.0
#1 0x00007fffe85ab22e in boost::pthread::pthread_mutex_scoped_lock::pthread_mutex_scoped_lock (this=0x7fffffffdba0, m_=0x0)
at /usr/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp:26
#2 0x00007fffe85abb5d in boost::condition_variable::notify_one (this=0x0)
at /usr/include/boost/thread/pthread/condition_variable.hpp:88
#3 0x00007fffe8690864 in CampaignFrequency::stopFlushThread (this=0x6ad590)
at /home/git/gitRTB/infinityplus/src/common/shm/CampaignFrequency.cpp:197
#4 0x00007fffe868ffd7 in CampaignFrequency::~CampaignFrequency (
this=0x6ad590, __in_chrg=<optimised out>)
at /home/git/gitRTB/infinityplus/src/common/shm/CampaignFrequency.cpp:81
#5 0x00007fffe85bdc37 in rtb_child_init (s=0x7ffff7fc3238)
at /home/git/gitRTB/infinityplus/src/bidder/mod_rtb.cpp:265
#6 0x000000000044784c in ap_run_child_init ()
#7 0x000000000042817c in ?? ()
#8 0x0000000000463594 in ?? ()
#9 0x00000000004635f4 in ?? ()
#10 0x00000000004643fd in ?? ()
#11 0x000000000042f026 in ap_run_mpm ()
#12 0x0000000000428d74 in main ()
Without actually seeing the code, this is mostly conjecture.
From your debug:
#2 0x00007fffe85abb5d in boost::condition_variable::notify_one (this=0x0)
at /usr/include/boost/thread/pthread/condition_variable.hpp:88
It's saying that this (inside the condition variable) is nullptr. It appears that you are calling cv->notify_all() where cv is nullptr (aka 0). Is it possible you are deleting the condition variable prior to trying to use it?
Let's call it x.dylib. I only want x.dylib to be loaded sometimes.
In the initialization of the dylib is there any way to have some logic which would cause the dlopen() call that tried to load x.dylib to fail to load x.dylib and return NULL?
Renaming x.dylib is not an option.
I looked through http://opensource.apple.com/source/dyld/dyld-210.2.3/src/dyldAPIs.cpp but I am unfamiliar with the code.
I thought maybe this would do it:
__attribute__((constructor))
void initializer(void) {
fprintf(stderr, "initializer\n");
throw;
}
but when I call dlopen() on the dylib with that initializer, i just get "initializer
terminate called without an active exceptionAbort trap: 6"
So I'm stumped; any help would be great.
Edit:
The stack trace, when viewed with gdb is as follows:
Program received signal SIGABRT, Aborted.
0x00007fff9128a82a in __kill ()
(gdb) bt
#0 0x00007fff9128a82a in __kill ()
#1 0x00007fff93539a9c in abort ()
#2 0x00007fff987f07bc in abort_message ()
#3 0x00007fff987edfcf in default_terminate ()
#4 0x00007fff987ee001 in safe_handler_caller ()
#5 0x00007fff987ee05c in std::terminate ()
#6 0x00007fff987ef152 in __cxa_throw ()
#7 0x0000000100003eb5 in initializer ()
#8 0x00007fff5fc0fda6 in __dyld__ZN16ImageLoaderMachO18doModInitFunctionsERKN11ImageLoader11LinkContextE ()
#9 0x00007fff5fc0faf2 in __dyld__ZN16ImageLoaderMachO16doInitializationERKN11ImageLoader11LinkContextE ()
#10 0x00007fff5fc0d2e4 in __dyld__ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEjRNS_21InitializerTimingListE ()
#11 0x00007fff5fc0d27d in __dyld__ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEjRNS_21InitializerTimingListE ()
#12 0x00007fff5fc0e0b7 in __dyld__ZN11ImageLoader15runInitializersERKNS_11LinkContextERNS_21InitializerTimingListE ()
#13 0x00007fff5fc034dd in __dyld__ZN4dyld24initializeMainExecutableEv ()
#14 0x00007fff5fc0760b in __dyld__ZN4dyld5_mainEPK12macho_headermiPPKcS5_S5_ ()
#15 0x00007fff5fc01059 in __dyld__dyld_start ()
which come from:
http://opensource.apple.com/source/dyld/dyld-210.2.3/src/dyld.cpp
http://opensource.apple.com/source/dyld/dyld-210.2.3/src/ImageLoader.cpp
http://opensource.apple.com/source/dyld/dyld-210.2.3/src/ImageLoaderMachO.cpp
I'm surprised that I don't see dlopen() in the stack trace though.