I'm trying to add a std::terminate handler with std::set_terminate for debugging in my app but i ran into some unexplained behaviour. The basic idea is that if i'm throwing from constructors (or members running in the same thread) the backtrace (as retrieved with libunwind) is as expected, I can see the place where the throw is in the call stack, but if i create a std::thread and throw from it, the call stack only shows execute_native_thread_routine where I'm expecting to see the thrower. If I start a thread with pthread_create things work as expected. If I explicitly call std::terminate instead of throw from std::thread, I can see it on the stack, indicating that terminate is called from somewhere else. Trouble is I tried to replicate this in a stand-alone app and there the behaviour is correct in all situations and I don't know where to go from here. This is the code I'm testing with:
#define UNW_LOCAL_ONLY
#include <cxxabi.h>
#include <iomanip>
#include <iostream>
#include <libunwind.h>
#include <memory>
#include <stdlib.h>
#include <thread>
#include <unistd.h>
char const *get_demangled_name(char const *symbol) noexcept {
if (!symbol) {
return "<null>";
}
int status = -4;
static std::unique_ptr<char, decltype(std::free) &> demangled_name{nullptr,
std::free};
demangled_name.reset(::abi::__cxa_demangle(symbol, demangled_name.release(),
nullptr, &status));
return ((status == 0) ? demangled_name.get() : symbol);
}
// Taken from https://eli.thegreenplace.net/2015/programmatic-access-to-the-call-stack-in-c/
void backtrace() {
unw_cursor_t cursor;
unw_context_t context;
// Initialize cursor to current frame for local unwinding.
unw_getcontext(&context);
unw_init_local(&cursor, &context);
// Unwind frames one by one, going up the frame stack.
while (unw_step(&cursor) > 0) {
unw_word_t offset, pc;
unw_get_reg(&cursor, UNW_REG_IP, &pc);
if (pc == 0) {
break;
}
std::cout << pc << " : ";
char sym[256];
if (unw_get_proc_name(&cursor, sym, sizeof(sym), &offset) == 0) {
std::cout << sym << " + " << offset << "\n " << get_demangled_name(sym) << std::endl;
} else {
std::cout << " -- error: unable to obtain symbol name for this frame" << std::endl;
}
}
}
void backtrace_on_terminate() noexcept {
backtrace();
std::abort(); // or any other appropriate action
}
class C {
public:
void thread_func_member() {
sleep(1);
throw std::bad_alloc();
}
};
int main(int argc, char *argv[]) {
std::set_terminate(backtrace_on_terminate);
C c;
std::thread thr = std::thread(&C::thread_func_member, &c);
sleep(3);
}
Answering my own question, this is because of gcc implementation. Solution is to make the worker of the std::thread noexcept. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55917, https://gcc.gnu.org/legacy-ml/gcc-help/2013-01/msg00068.html and https://gcc.gnu.org/legacy-ml/gcc-help/2011-11/msg00141.html
Not fixed though:
Because: