146 lines
3.5 KiB
C++
146 lines
3.5 KiB
C++
|
|
#include "./stacktrace.hpp"
|
|
|
|
#include <optional>
|
|
#include <string>
|
|
#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 <backtrace.h>
|
|
#endif
|
|
|
|
|
|
namespace mijin
|
|
{
|
|
namespace
|
|
{
|
|
//
|
|
// internal defines
|
|
//
|
|
|
|
//
|
|
// internal constants
|
|
//
|
|
|
|
//
|
|
// internal types
|
|
//
|
|
|
|
struct BacktraceData
|
|
{
|
|
std::optional<std::string> error;
|
|
std::vector<Stackframe> stackframes;
|
|
};
|
|
|
|
//
|
|
// internal variables
|
|
//
|
|
|
|
thread_local Optional<Stacktrace> 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<BacktraceData*>(data);
|
|
btData.stackframes.push_back({
|
|
.address = reinterpret_cast<void*>(programCounter), // NOLINT(performance-no-int-to-ptr)
|
|
.filename = filename ? filename : "<unknown>",
|
|
.function = function ? function : "<unknown>",
|
|
.lineNumber = lineno
|
|
});
|
|
return 0;
|
|
}
|
|
|
|
void backtraceErrorCallback(void* data, const char* msg, int /* errnum */)
|
|
{
|
|
BacktraceData& btData = *static_cast<BacktraceData*>(data);
|
|
btData.error = msg;
|
|
}
|
|
|
|
thread_local backtrace_state* gBacktraceState = nullptr;
|
|
#endif // MIJIN_USE_LIBBACKTRACE
|
|
} // namespace
|
|
|
|
//
|
|
// public functions
|
|
//
|
|
|
|
Result<Stacktrace> 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<int>(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 {}; // TODO
|
|
#endif // MIJIN_USE_LIBBACKTRACE
|
|
}
|
|
|
|
const Optional<Stacktrace>& getExceptionStacktrace() MIJIN_NOEXCEPT
|
|
{
|
|
return gCurrentExceptionStackTrace;
|
|
}
|
|
} // namespace mijin
|
|
|
|
#if MIJIN_STDLIB == MIJIN_STDLIB_GLIBC
|
|
#include <dlfcn.h>
|
|
|
|
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<cxa_throw_type>(dlsym(RTLD_NEXT, "__cxa_throw"));
|
|
}
|
|
if (mijin::Result<mijin::Stacktrace> 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
|