146 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			146 lines
		
	
	
		
			3.4 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),
 | 
						|
        .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) 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() 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*))
 | 
						|
{
 | 
						|
    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
 |