#include "./stacktrace.hpp" #include #include #include #include "../detect.hpp" namespace mijin { namespace { // // internal defines // // // internal constants // // // internal types // struct BacktraceData { std::optional error; std::vector stackframes; }; // // internal variables // thread_local Optional gCurrentExceptionStackTrace; // // internal functions // int backtraceFullCallback(void* data, std::uintptr_t programCounter, const char* filename, int lineno, const char* function) { BacktraceData& btData = *static_cast(data); btData.stackframes.push_back({ .address = reinterpret_cast(programCounter), .filename = filename ? filename : "", .function = function ? function : "", .lineNumber = lineno }); return 0; } void backtraceErrorCallback(void* data, const char* msg, int /* errnum */) { BacktraceData& btData = *static_cast(data); btData.error = msg; } thread_local backtrace_state* gBacktraceState = nullptr; } // namespace // // public functions // Result captureStacktrace(unsigned skipFrames) noexcept { BacktraceData btData; if (gBacktraceState == nullptr) { gBacktraceState = backtrace_create_state(/*filename = */ nullptr, /* threaded = */ false, &backtraceErrorCallback, &btData); } if (btData.error.has_value()) { return ResultError(std::move(*btData.error)); } if (gBacktraceState == nullptr) { return ResultError("Error initializing libbacktrace."); } backtrace_full(gBacktraceState, static_cast(skipFrames) + 1, &backtraceFullCallback, &backtraceErrorCallback, &btData); if (btData.error.has_value()) { return ResultError(std::move(*btData.error)); } return Stacktrace(std::move(btData.stackframes)); } const Optional& getExceptionStacktrace() noexcept { return gCurrentExceptionStackTrace; } } // namespace mijin #if MIJIN_STDLIB == MIJIN_STDLIB_GLIBC #include namespace { using cxa_throw_type = void (*)(void*, std::type_info*, void(*)(void*)); using cxa_rethrow_type = void (*)(); cxa_throw_type orig_cxa_throw = nullptr; // Address of the original __cxa_throw // cxa_rethrow_type orig_cxa_rethrow = nullptr; // Address of the original __cxa_rethrow extern "C" void __cxa_throw(void* thrown_exception, std::type_info* tinfo, void (*dest)(void*)) { if (!orig_cxa_throw) { orig_cxa_throw = reinterpret_cast(dlsym(RTLD_NEXT, "__cxa_throw")); } if (mijin::Result stacktrace = mijin::captureStacktrace(); stacktrace.isSuccess()) { mijin::gCurrentExceptionStackTrace = std::move(*stacktrace); } else { mijin::gCurrentExceptionStackTrace.reset(); } if (orig_cxa_throw) { orig_cxa_throw(thrown_exception, tinfo, dest); } else { std::abort(); } } } #endif