diff --git a/lib/libimhex/include/hex/api/task_manager.hpp b/lib/libimhex/include/hex/api/task_manager.hpp index 249960498..4d0f75487 100644 --- a/lib/libimhex/include/hex/api/task_manager.hpp +++ b/lib/libimhex/include/hex/api/task_manager.hpp @@ -10,6 +10,7 @@ #include #include #include +#include EXPORT_MODULE namespace hex { @@ -94,7 +95,12 @@ EXPORT_MODULE namespace hex { std::atomic_flag m_hadException; std::string m_exceptionMessage; - struct TaskInterruptor { virtual ~TaskInterruptor() = default; }; + struct TaskInterruptor { + TaskInterruptor() { + trace::disableExceptionCaptureForCurrentThread(); + } + virtual ~TaskInterruptor() = default; + }; friend class TaskHolder; friend class TaskManager; diff --git a/lib/libimhex/include/hex/helpers/debugging.hpp b/lib/libimhex/include/hex/helpers/debugging.hpp index 5605cbb63..3d077e860 100644 --- a/lib/libimhex/include/hex/helpers/debugging.hpp +++ b/lib/libimhex/include/hex/helpers/debugging.hpp @@ -14,6 +14,10 @@ static_assert(false, "Debug variables are only intended for use during development."); #endif +namespace hex::trace { + struct StackTraceResult; +} + namespace hex::dbg { namespace impl { @@ -47,4 +51,6 @@ namespace hex::dbg { bool debugModeEnabled(); void setDebugModeEnabled(bool enabled); + void printStackTrace(const trace::StackTraceResult &stackTrace); + } \ No newline at end of file diff --git a/lib/libimhex/source/api/task_manager.cpp b/lib/libimhex/source/api/task_manager.cpp index 50f46b8cb..7c0056822 100644 --- a/lib/libimhex/source/api/task_manager.cpp +++ b/lib/libimhex/source/api/task_manager.cpp @@ -7,6 +7,8 @@ #include #include +#include +#include #if defined(OS_WINDOWS) #include @@ -310,6 +312,8 @@ namespace hex { } try { + trace::enableExceptionCaptureForCurrentThread(); + // Set the thread name to the name of the task TaskManager::setCurrentThreadName(Lang(task->m_unlocalizedName)); @@ -323,15 +327,21 @@ namespace hex { } catch (const std::exception &e) { log::error("Exception in task '{}': {}", task->m_unlocalizedName.get(), e.what()); + dbg::printStackTrace(trace::getStackTrace()); + // Handle the task throwing an uncaught exception task->exception(e.what()); } catch (...) { log::error("Exception in task '{}'", task->m_unlocalizedName.get()); + dbg::printStackTrace(trace::getStackTrace()); + // Handle the task throwing an uncaught exception of unknown type task->exception("Unknown Exception"); } + trace::disableExceptionCaptureForCurrentThread(); + s_currentTask = nullptr; task->finish(); } diff --git a/lib/libimhex/source/helpers/debugging.cpp b/lib/libimhex/source/helpers/debugging.cpp index 50115f49d..3b173a6ba 100644 --- a/lib/libimhex/source/helpers/debugging.cpp +++ b/lib/libimhex/source/helpers/debugging.cpp @@ -1,4 +1,6 @@ #include +#include +#include namespace hex::dbg { @@ -21,4 +23,23 @@ namespace hex::dbg { s_debugMode = enabled; } + [[noreturn]] void assertionHandler(const char* file, int line, const char *function, const char* exprString) { + log::error("Assertion failed: {} at {}:{} => {}", exprString, file, line, function); + + const auto stackTrace = trace::getStackTrace(); + dbg::printStackTrace(stackTrace); + + std::abort(); + } + + void printStackTrace(const trace::StackTraceResult &stackTrace) { + log::fatal("Printing stacktrace using implementation '{}'", stackTrace.implementationName); + for (const auto &stackFrame : stackTrace.stackFrames) { + if (stackFrame.line == 0) + log::fatal(" ({}) | {}", stackFrame.file, stackFrame.function); + else + log::fatal(" ({}:{}) | {}", stackFrame.file, stackFrame.line, stackFrame.function); + } + } + } \ No newline at end of file diff --git a/lib/libimhex/source/helpers/logger.cpp b/lib/libimhex/source/helpers/logger.cpp index 770f96ac1..a6677827d 100644 --- a/lib/libimhex/source/helpers/logger.cpp +++ b/lib/libimhex/source/helpers/logger.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #if defined(OS_WINDOWS) #include @@ -149,14 +150,6 @@ namespace hex::log { ); } - void assertionHandler(const char* exprString, const char* file, int line) { - log::error("Assertion failed: {} at {}:{}", exprString, file, line); - - #if defined (DEBUG) - std::abort(); - #endif - } - namespace color { fmt::color debug() { return fmt::color::medium_sea_green; } diff --git a/lib/third_party/imgui/imgui/include/imconfig.h b/lib/third_party/imgui/imgui/include/imconfig.h index 446d46f95..26c72216a 100644 --- a/lib/third_party/imgui/imgui/include/imconfig.h +++ b/lib/third_party/imgui/imgui/include/imconfig.h @@ -25,10 +25,17 @@ // If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement. //#define IM_ASSERT(_EXPR) MyAssert(_EXPR) //#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts -namespace hex::log::impl { - void assertionHandler(const char* expr_str, const char* file, int line); +namespace hex::dbg { + [[noreturn]] void assertionHandler(const char* file, int line, const char *function, const char* exprString); } -#define IM_ASSERT(_EXPR) do { if (!(_EXPR)) [[unlikely]] { hex::log::impl::assertionHandler(#_EXPR, __FILE__, __LINE__); } } while(0) + +#if defined(__PRETTY_FUNCTION__) + #define IM_ASSERT(_EXPR) do { if (!(_EXPR)) [[unlikely]] { hex::dbg::assertionHandler(__FILE__, __LINE__, __PRETTY_FUNCTION__, #_EXPR); } } while(0) +#elif defined(__FUNCSIG__) + #define IM_ASSERT(_EXPR) do { if (!(_EXPR)) [[unlikely]] { hex::dbg::assertionHandler(__FILE__, __LINE__, __FUNCSIG__, #_EXPR); } } while(0) +#else + #define IM_ASSERT(_EXPR) do { if (!(_EXPR)) [[unlikely]] { hex::dbg::assertionHandler(__FILE__, __LINE__, __FUNCTION__, #_EXPR); } } while(0) +#endif //---- Define attributes of all API symbols declarations, e.g. for DLL under Windows // Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility. diff --git a/lib/trace/include/hex/trace/exceptions.hpp b/lib/trace/include/hex/trace/exceptions.hpp index 96fd06d1f..c835a15e6 100644 --- a/lib/trace/include/hex/trace/exceptions.hpp +++ b/lib/trace/include/hex/trace/exceptions.hpp @@ -6,7 +6,12 @@ namespace hex::trace { + using AssertionHandler = void(*)(const char* file, int line, const char *function, const char* exprString); + std::optional getLastExceptionStackTrace(); + void setAssertionHandler(AssertionHandler handler); + void enableExceptionCaptureForCurrentThread(); + void disableExceptionCaptureForCurrentThread(); } \ No newline at end of file diff --git a/lib/trace/source/exceptions.cpp b/lib/trace/source/exceptions.cpp index 1c49b06ca..1d39ffded 100644 --- a/lib/trace/source/exceptions.cpp +++ b/lib/trace/source/exceptions.cpp @@ -2,8 +2,9 @@ namespace hex::trace { - static std::optional s_lastExceptionStackTrace; + static thread_local std::optional s_lastExceptionStackTrace; static thread_local bool s_threadExceptionCaptureEnabled = false; + static AssertionHandler s_assertionHandler = nullptr; std::optional getLastExceptionStackTrace() { if (!s_lastExceptionStackTrace.has_value()) @@ -15,18 +16,26 @@ namespace hex::trace { return result; } + void setAssertionHandler(AssertionHandler handler) { + s_assertionHandler = handler; + } + void enableExceptionCaptureForCurrentThread() { s_threadExceptionCaptureEnabled = true; } + void disableExceptionCaptureForCurrentThread() { + s_threadExceptionCaptureEnabled = false; + } + } #if defined(HEX_WRAP_CXA_THROW) extern "C" { - [[noreturn]] void __real___cxa_throw(void* thrownException, void* type, void (*destructor)(void*)); - [[noreturn]] void __wrap___cxa_throw(void* thrownException, void* type, void (*destructor)(void*)) { + [[noreturn]] void __real___cxa_throw(void* thrownException, std::type_info* type, void (*destructor)(void*)); + [[noreturn]] void __wrap___cxa_throw(void* thrownException, std::type_info* type, void (*destructor)(void*)) { if (hex::trace::s_threadExceptionCaptureEnabled) hex::trace::s_lastExceptionStackTrace = hex::trace::getStackTrace(); @@ -41,19 +50,15 @@ namespace hex::trace { extern "C" { + [[noreturn]] void __real__ZSt21__glibcxx_assert_failPKciS0_S0_(const char* file, int line, const char* function, const char* condition); [[noreturn]] void __wrap__ZSt21__glibcxx_assert_failPKciS0_S0_(const char* file, int line, const char* function, const char* condition) { - if (file != nullptr && function != nullptr && condition != nullptr) { - fprintf(stderr, "Assertion failed (glibc++): (%s), function %s, file %s, line %d.\n", condition, function, file, line); - } else if (function != nullptr) { - fprintf(stderr, "%s: Undefined behavior detected (glibc++).\n", function); + if (hex::trace::s_assertionHandler != nullptr) { + hex::trace::s_assertionHandler(file, line, function, condition); + } else { + __real__ZSt21__glibcxx_assert_failPKciS0_S0_(file, line, function, condition); } - auto stackTrace = hex::trace::getStackTrace(); - for (const auto &entry : stackTrace.stackFrames) { - fprintf(stderr, " %s at %s:%d\n", entry.function.c_str(), entry.file.c_str(), entry.line); - } - - std::terminate(); + std::abort(); } } diff --git a/main/gui/source/crash_handlers.cpp b/main/gui/source/crash_handlers.cpp index 8a24b6bdf..01a5f77dd 100644 --- a/main/gui/source/crash_handlers.cpp +++ b/main/gui/source/crash_handlers.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #if defined(IMGUI_TEST_ENGINE) @@ -71,23 +72,12 @@ namespace hex::crash { log::warn("Could not write crash.json file!"); } - static void printStackTrace() { - auto stackTraceResult = trace::getStackTrace(); - log::fatal("Printing stacktrace using implementation '{}'", stackTraceResult.implementationName); - for (const auto &stackFrame : stackTraceResult.stackFrames) { - if (stackFrame.line == 0) - log::fatal(" ({}) | {}", stackFrame.file, stackFrame.function); - else - log::fatal(" ({}:{}) | {}", stackFrame.file, stackFrame.line, stackFrame.function); - } - } - static void callCrashHandlers(const std::string &msg) { // Call the crash callback crashCallback(msg); // Print the stacktrace to the console or log file - printStackTrace(); + dbg::printStackTrace(trace::getStackTrace()); // Flush all streams std::fflush(stdout); @@ -188,6 +178,7 @@ namespace hex::crash { // Setup functions to handle signals, uncaught exception, or similar stuff that will crash ImHex void setupCrashHandlers() { trace::initialize(); + trace::setAssertionHandler(dbg::assertionHandler); // Register signal handlers { diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index 5e26d54c2..743880c48 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -1707,6 +1707,9 @@ namespace hex::plugin::builtin { EventHighlightingChanged::post(); TaskManager::createTask("hex.builtin.view.pattern_editor.evaluating", TaskManager::NoProgress, [this, code, provider](auto &task) { + // Disable exception tracing to speed up evaluation + trace::disableExceptionCaptureForCurrentThread(); + auto runtimeLock = std::scoped_lock(ContentRegistry::PatternLanguage::getRuntimeLock()); auto &runtime = ContentRegistry::PatternLanguage::getRuntime();