#include "./stacktrace.hpp" #include #include #include "../detect.hpp" #if MIJIN_COMPILER == MIJIN_COMPILER_CLANG || MIJIN_COMPILER == MIJIN_COMPILER_GCC #define MIJIN_USE_LIBBACKTRACE 1 #else #define MIJIN_USE_LIBBACKTRACE 0 #endif #if MIJIN_USE_LIBBACKTRACE #include #endif 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 // #if MIJIN_USE_LIBBACKTRACE 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), // NOLINT(performance-no-int-to-ptr) .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; #endif // MIJIN_USE_LIBBACKTRACE } // namespace // // public functions // Result captureStacktrace(unsigned skipFrames) MIJIN_NOEXCEPT { #if MIJIN_USE_LIBBACKTRACE 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)); #else // MIJIN_USE_LIBBACKTRACE (void) skipFrames; return ResultError("not implemented"); #endif // MIJIN_USE_LIBBACKTRACE } const Optional& getExceptionStacktrace() MIJIN_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*)) // NOLINT(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) { 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