mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-28 07:47:03 -05:00
feat: Add full exception tracing support
This commit is contained in:
@@ -3,7 +3,6 @@ project(main)
|
||||
add_executable(main ${APPLICATION_TYPE}
|
||||
source/main.cpp
|
||||
source/crash_handlers.cpp
|
||||
source/stacktrace.cpp
|
||||
|
||||
source/window/window.cpp
|
||||
source/window/win_window.cpp
|
||||
@@ -38,7 +37,7 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../lib/external/libromfs ${CMAKE
|
||||
add_dependencies(imhex_all main)
|
||||
|
||||
if (EMSCRIPTEN)
|
||||
target_link_options(main PRIVATE -sUSE_GLFW=3 -sUSE_PTHREADS=1 -sALLOW_MEMORY_GROWTH=1)
|
||||
target_link_options(main PRIVATE -sUSE_GLFW=3 -sUSE_PTHREADS=1 -sALLOW_MEMORY_GROWTH=1 -Wno-pthreads-mem-growth)
|
||||
target_link_options(main PRIVATE -sTOTAL_MEMORY=134217728)
|
||||
target_link_options(main PRIVATE -sMAX_WEBGL_VERSION=2)
|
||||
target_link_options(main PRIVATE -sEXPORTED_RUNTIME_METHODS=ccall)
|
||||
@@ -82,4 +81,4 @@ precompileHeaders(main ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
if (APPLE)
|
||||
add_compile_definitions(GL_SILENCE_DEPRECATION)
|
||||
endif ()
|
||||
endif ()
|
||||
@@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace hex::stacktrace {
|
||||
|
||||
struct StackFrame {
|
||||
std::string file;
|
||||
std::string function;
|
||||
u32 line;
|
||||
};
|
||||
|
||||
void initialize();
|
||||
|
||||
struct StackTraceResult {
|
||||
std::vector<StackFrame> stackFrames;
|
||||
std::string implementationName;
|
||||
};
|
||||
|
||||
StackTraceResult getStackTrace();
|
||||
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <condition_variable>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
@@ -37,6 +39,7 @@ namespace hex {
|
||||
void beginNativeWindowFrame();
|
||||
void endNativeWindowFrame();
|
||||
void drawTitleBar();
|
||||
void drawView(const std::string &name, const std::unique_ptr<View> &view);
|
||||
|
||||
void frameBegin();
|
||||
void frame();
|
||||
|
||||
@@ -11,11 +11,14 @@
|
||||
|
||||
#include <window.hpp>
|
||||
#include <init/tasks.hpp>
|
||||
#include <stacktrace.hpp>
|
||||
#include <hex/trace/stacktrace.hpp>
|
||||
|
||||
#include <llvm/Demangle/Demangle.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <hex/trace/stacktrace.hpp>
|
||||
#include <llvm/Demangle/Demangle.h>
|
||||
|
||||
#include <csignal>
|
||||
#include <exception>
|
||||
#include <typeinfo>
|
||||
@@ -65,7 +68,7 @@ namespace hex::crash {
|
||||
}
|
||||
|
||||
static void printStackTrace() {
|
||||
auto stackTraceResult = stacktrace::getStackTrace();
|
||||
auto stackTraceResult = trace::getStackTrace();
|
||||
log::fatal("Printing stacktrace using implementation '{}'", stackTraceResult.implementationName);
|
||||
for (const auto &stackFrame : stackTraceResult.stackFrames) {
|
||||
if (stackFrame.line == 0)
|
||||
@@ -153,7 +156,7 @@ namespace hex::crash {
|
||||
try {
|
||||
std::rethrow_exception(std::current_exception());
|
||||
} catch (std::exception &ex) {
|
||||
std::string exceptionStr = hex::format("{}()::what() -> {}", llvm::demangle(typeid(ex).name()), ex.what());
|
||||
std::string exceptionStr = hex::format("{}()::what() -> {}", llvm::demangle(std::string("_Z") + typeid(ex).name()), ex.what());
|
||||
|
||||
handleCrash(exceptionStr);
|
||||
log::fatal("Program terminated with uncaught exception: {}", exceptionStr);
|
||||
@@ -164,7 +167,7 @@ namespace hex::crash {
|
||||
|
||||
// Setup functions to handle signals, uncaught exception, or similar stuff that will crash ImHex
|
||||
void setupCrashHandlers() {
|
||||
stacktrace::initialize();
|
||||
trace::initialize();
|
||||
|
||||
// Register signal handlers
|
||||
{
|
||||
|
||||
@@ -1,212 +0,0 @@
|
||||
#include <stacktrace.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <llvm/Demangle/Demangle.h>
|
||||
|
||||
namespace {
|
||||
|
||||
[[maybe_unused]] std::string tryDemangle(const std::string &symbolName) {
|
||||
if (auto variant1 = llvm::demangle(symbolName); variant1 != symbolName)
|
||||
return variant1;
|
||||
|
||||
if (auto variant2 = llvm::demangle(std::string("_") + symbolName); variant2 != std::string("_") + symbolName)
|
||||
return variant2;
|
||||
|
||||
return symbolName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
|
||||
namespace hex::stacktrace {
|
||||
|
||||
void initialize() {
|
||||
|
||||
}
|
||||
|
||||
StackTraceResult getStackTrace() {
|
||||
std::vector<StackFrame> stackTrace;
|
||||
|
||||
HANDLE process = GetCurrentProcess();
|
||||
HANDLE thread = GetCurrentThread();
|
||||
|
||||
CONTEXT context = {};
|
||||
context.ContextFlags = CONTEXT_FULL;
|
||||
RtlCaptureContext(&context);
|
||||
|
||||
SymSetOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING);
|
||||
SymInitialize(process, nullptr, TRUE);
|
||||
|
||||
DWORD image;
|
||||
STACKFRAME64 stackFrame;
|
||||
ZeroMemory(&stackFrame, sizeof(STACKFRAME64));
|
||||
|
||||
image = IMAGE_FILE_MACHINE_AMD64;
|
||||
#if defined(_X86_)
|
||||
stackFrame.AddrPC.Offset = context.Eip;
|
||||
stackFrame.AddrPC.Mode = AddrModeFlat;
|
||||
stackFrame.AddrFrame.Offset = context.Esp;
|
||||
stackFrame.AddrFrame.Mode = AddrModeFlat;
|
||||
stackFrame.AddrStack.Offset = context.Esp;
|
||||
stackFrame.AddrStack.Mode = AddrModeFlat;
|
||||
#else
|
||||
stackFrame.AddrPC.Offset = context.Rip;
|
||||
stackFrame.AddrPC.Mode = AddrModeFlat;
|
||||
stackFrame.AddrFrame.Offset = context.Rsp;
|
||||
stackFrame.AddrFrame.Mode = AddrModeFlat;
|
||||
stackFrame.AddrStack.Offset = context.Rsp;
|
||||
stackFrame.AddrStack.Mode = AddrModeFlat;
|
||||
#endif
|
||||
|
||||
while (true) {
|
||||
if (StackWalk64(
|
||||
image, process, thread,
|
||||
&stackFrame, &context, nullptr,
|
||||
SymFunctionTableAccess64, SymGetModuleBase64, nullptr) == FALSE)
|
||||
break;
|
||||
|
||||
if (stackFrame.AddrReturn.Offset == stackFrame.AddrPC.Offset)
|
||||
break;
|
||||
|
||||
std::array<char, sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)> buffer = {};
|
||||
auto symbol = reinterpret_cast<PSYMBOL_INFO>(buffer.data());
|
||||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
symbol->MaxNameLen = MAX_SYM_NAME;
|
||||
|
||||
DWORD64 displacementSymbol = 0;
|
||||
const char *symbolName;
|
||||
if (SymFromAddr(process, stackFrame.AddrPC.Offset, &displacementSymbol, symbol) == TRUE) {
|
||||
symbolName = symbol->Name;
|
||||
} else {
|
||||
symbolName = "??";
|
||||
}
|
||||
|
||||
SymSetOptions(SYMOPT_LOAD_LINES);
|
||||
|
||||
IMAGEHLP_LINE64 line;
|
||||
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||
|
||||
DWORD displacementLine = 0;
|
||||
|
||||
u32 lineNumber = 0;
|
||||
const char *fileName;
|
||||
if (SymGetLineFromAddr64(process, stackFrame.AddrPC.Offset, &displacementLine, &line) == TRUE) {
|
||||
lineNumber = line.LineNumber;
|
||||
fileName = line.FileName;
|
||||
} else {
|
||||
lineNumber = 0;
|
||||
fileName = "??";
|
||||
}
|
||||
|
||||
auto demangledName = tryDemangle(symbolName);
|
||||
stackTrace.push_back(StackFrame { fileName, demangledName, lineNumber });
|
||||
}
|
||||
|
||||
SymCleanup(process);
|
||||
|
||||
return StackTraceResult{ stackTrace, "StackWalk" };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#elif defined(HEX_HAS_EXECINFO)
|
||||
|
||||
#if __has_include(BACKTRACE_HEADER)
|
||||
|
||||
#include BACKTRACE_HEADER
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <dlfcn.h>
|
||||
|
||||
namespace hex::stacktrace {
|
||||
|
||||
void initialize() {
|
||||
|
||||
}
|
||||
|
||||
StackTraceResult getStackTrace() {
|
||||
static std::vector<StackFrame> result;
|
||||
|
||||
std::array<void*, 128> addresses = {};
|
||||
const size_t count = backtrace(addresses.data(), addresses.size());
|
||||
|
||||
Dl_info info;
|
||||
for (size_t i = 0; i < count; i += 1) {
|
||||
dladdr(addresses[i], &info);
|
||||
|
||||
auto fileName = info.dli_fname != nullptr ? std::fs::path(info.dli_fname).filename().string() : "??";
|
||||
auto demangledName = info.dli_sname != nullptr ? tryDemangle(info.dli_sname) : "??";
|
||||
|
||||
result.push_back(StackFrame { std::move(fileName), std::move(demangledName), 0 });
|
||||
}
|
||||
|
||||
return StackTraceResult{ result, "execinfo" };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#elif defined(HEX_HAS_BACKTRACE)
|
||||
|
||||
#if __has_include(BACKTRACE_HEADER)
|
||||
|
||||
#include BACKTRACE_HEADER
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
|
||||
namespace hex::stacktrace {
|
||||
|
||||
static struct backtrace_state *s_backtraceState;
|
||||
|
||||
|
||||
void initialize() {
|
||||
if (auto executablePath = wolv::io::fs::getExecutablePath(); executablePath.has_value()) {
|
||||
static std::string path = executablePath->string();
|
||||
s_backtraceState = backtrace_create_state(path.c_str(), 1, [](void *, const char *msg, int) { log::error("{}", msg); }, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
StackTraceResult getStackTrace() {
|
||||
static std::vector<StackFrame> result;
|
||||
|
||||
result.clear();
|
||||
if (s_backtraceState != nullptr) {
|
||||
backtrace_full(s_backtraceState, 0, [](void *, uintptr_t, const char *fileName, int lineNumber, const char *function) -> int {
|
||||
if (fileName == nullptr)
|
||||
fileName = "??";
|
||||
if (function == nullptr)
|
||||
function = "??";
|
||||
|
||||
result.push_back(StackFrame { std::fs::path(fileName).filename().string(), tryDemangle(function), u32(lineNumber) });
|
||||
|
||||
return 0;
|
||||
}, nullptr, nullptr);
|
||||
|
||||
}
|
||||
|
||||
return StackTraceResult{ result, "backtrace" };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
namespace hex::stacktrace {
|
||||
|
||||
void initialize() { }
|
||||
StackTraceResult getStackTrace() {
|
||||
return StackTraceResult {
|
||||
{StackFrame { "??", "Stacktrace collecting not available!", 0 }},
|
||||
"none"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user