I use C++17, GCC, Qt Creator with its integrated GDB debugger.
I have code that simplifies down to this:
#include <iostream>
#include <iomanip>
// Example-implementation
#define assert(Condition) { if (!(Condition)) { std::cerr << "Assert failed, condition is false: " #Condition << std::endl; } }
#include <execinfo.h>
#include <signal.h>
#include <unistd.h>
void printStackTrace()
{
constexpr int requestedFrameCount = 20;
void* frames[requestedFrameCount];
auto actualFrameCount = backtrace(frames, requestedFrameCount);
std::cout << "Stack trace (" << actualFrameCount << " of " << requestedFrameCount << " requested frames):" << std::endl;
backtrace_symbols_fd(frames, actualFrameCount, STDOUT_FILENO);
std::cout << "End of stack trace." << std::endl;
}
void signalHandler(int signalNumber)
{
std::cout << "Signal " << signalNumber << " (" << sys_siglist[signalNumber] << ") happened!" << std::endl;
assert(signalNumber == SIGABRT);
printStackTrace();
}
__attribute_noinline__ void someFunction()
{
throw std::invalid_argument("Bad things happened");
}
__attribute_noinline__ void someFunctionInTheStandardLibraryThatICantChange()
{
try
{
someFunction();
}
catch (...)
{
throw;
}
}
__attribute_noinline__ int main()
{
signal(SIGABRT, signalHandler);
someFunctionInTheStandardLibraryThatICantChange();
return 0;
}
someFunctionInTheStandardLibraryThatICantChange is a placeholder for this thing:
template<bool _TrivialValueTypes>
struct __uninitialized_copy
{
template<typename _InputIterator, typename _ForwardIterator>
static _ForwardIterator
__uninit_copy(_InputIterator __first, _InputIterator __last,
_ForwardIterator __result)
{
_ForwardIterator __cur = __result;
__try
{
for (; __first != __last; ++__first, (void)++__cur)
std::_Construct(std::__addressof(*__cur), *__first);
return __cur;
}
__catch(...)
{
std::_Destroy(__result, __cur);
__throw_exception_again;
}
}
};
The program's output looks something like this:
On standard output:
Signal 6 (Aborted) happened!
Stack trace (13 of 20 requested frames):
/foo/Test(_Z15printStackTracev+0x1c)[0xaaaab9886d30]
/foo/Test(_Z13signalHandleri+0xbc)[0xaaaab9886e94]
linux-vdso.so.1(__kernel_rt_sigreturn+0x0)[0xffff95f3a5c8]
/lib64/libc.so.6(gsignal+0xc8)[0xffff94e15330]
/lib64/libc.so.6(abort+0xfc)[0xffff94e02b54]
/lib64/libstdc++.so.6(_ZN9__gnu_cxx27__verbose_terminate_handlerEv+0x188)[0xffff950d9358]
/lib64/libstdc++.so.6(_ZN10__cxxabiv111__terminateEPFvvE+0xc)[0xffff950d70ac]
/lib64/libstdc++.so.6(_ZN10__cxxabiv112__unexpectedEPFvvE+0x0)[0xffff950d7100]
/lib64/libstdc++.so.6(__cxa_rethrow+0x60)[0xffff950d7428]
/foo/Test(_Z47someFunctionInTheStandardLibraryThatICantChangev+0x1c)[0xaaaab9886f10]
/foo/Test(main+0x1c)[0xaaaab9886f48]
/lib64/libc.so.6(__libc_start_main+0xe4)[0xffff94e02fac]
/foo/Test(+0x2774)[0xaaaab9886774]
End of stack trace.
On standard error:
terminate called after throwing an instance of 'std::invalid_argument'
what(): Bad things happened
Note how the stack trace goes directly from someFunctionInTheStandardLibraryThatICantChange to rethrow. someFunction was not inlined (call printStackTrace from someFunction if you don't trust me).
I can't change the library function, but I need to know where the exception was originally thrown. How do I get that information?
One possible way is to use the debugger and set a "Break when C++ exception is thrown" breakpoint. But that has the significant drawbacks that it only works when debugging, it's external to the program and it is only really viable if you don't throw a bunch of exceptions that you don't care about.
catch throwingdbto break whenever an exception is thrown, then usethread apply all btto get stacktraces for all threads at that point.