129 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			129 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
 | |
| #include "./stacktrace.hpp"
 | |
| 
 | |
| #include <optional>
 | |
| #include <string>
 | |
| #include <backtrace.h>
 | |
| #include "../detect.hpp"
 | |
| 
 | |
| 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
 | |
| //
 | |
| 
 | |
| 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;
 | |
| } // namespace
 | |
| 
 | |
| //
 | |
| // public functions
 | |
| //
 | |
| 
 | |
| Result<Stacktrace> 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<int>(skipFrames) + 1, &backtraceFullCallback, &backtraceErrorCallback, &btData);
 | |
|     if (btData.error.has_value())
 | |
|     {
 | |
|         return ResultError(std::move(*btData.error));
 | |
|     }
 | |
| 
 | |
|     return Stacktrace(std::move(btData.stackframes));
 | |
| }
 | |
| 
 | |
| 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
 |