mirror of https://github.com/WerWolv/ImHex
impr: Unionize exception and assertion handling
This commit is contained in:
parent
49bbe7dc77
commit
cfac7ff0ba
|
|
@ -10,6 +10,7 @@
|
|||
#include <condition_variable>
|
||||
#include <source_location>
|
||||
#include <thread>
|
||||
#include <hex/trace/exceptions.hpp>
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
@ -7,6 +7,8 @@
|
|||
#include <ranges>
|
||||
|
||||
#include <jthread.hpp>
|
||||
#include <hex/helpers/debugging.hpp>
|
||||
#include <hex/trace/exceptions.hpp>
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
#include <windows.h>
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
#include <hex/helpers/debugging.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <hex/trace/stacktrace.hpp>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@
|
|||
#include <mutex>
|
||||
#include <chrono>
|
||||
#include <fmt/chrono.h>
|
||||
#include <hex/helpers/debugging.hpp>
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
#include <Windows.h>
|
||||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -6,7 +6,12 @@
|
|||
|
||||
namespace hex::trace {
|
||||
|
||||
using AssertionHandler = void(*)(const char* file, int line, const char *function, const char* exprString);
|
||||
|
||||
std::optional<StackTraceResult> getLastExceptionStackTrace();
|
||||
void setAssertionHandler(AssertionHandler handler);
|
||||
|
||||
void enableExceptionCaptureForCurrentThread();
|
||||
void disableExceptionCaptureForCurrentThread();
|
||||
|
||||
}
|
||||
|
|
@ -2,8 +2,9 @@
|
|||
|
||||
namespace hex::trace {
|
||||
|
||||
static std::optional<StackTraceResult> s_lastExceptionStackTrace;
|
||||
static thread_local std::optional<StackTraceResult> s_lastExceptionStackTrace;
|
||||
static thread_local bool s_threadExceptionCaptureEnabled = false;
|
||||
static AssertionHandler s_assertionHandler = nullptr;
|
||||
|
||||
std::optional<StackTraceResult> 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <csignal>
|
||||
#include <exception>
|
||||
#include <typeinfo>
|
||||
#include <hex/helpers/debugging.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
|
||||
#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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in New Issue