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 <condition_variable>
|
||||||
#include <source_location>
|
#include <source_location>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <hex/trace/exceptions.hpp>
|
||||||
|
|
||||||
EXPORT_MODULE namespace hex {
|
EXPORT_MODULE namespace hex {
|
||||||
|
|
||||||
|
|
@ -94,7 +95,12 @@ EXPORT_MODULE namespace hex {
|
||||||
std::atomic_flag m_hadException;
|
std::atomic_flag m_hadException;
|
||||||
std::string m_exceptionMessage;
|
std::string m_exceptionMessage;
|
||||||
|
|
||||||
struct TaskInterruptor { virtual ~TaskInterruptor() = default; };
|
struct TaskInterruptor {
|
||||||
|
TaskInterruptor() {
|
||||||
|
trace::disableExceptionCaptureForCurrentThread();
|
||||||
|
}
|
||||||
|
virtual ~TaskInterruptor() = default;
|
||||||
|
};
|
||||||
|
|
||||||
friend class TaskHolder;
|
friend class TaskHolder;
|
||||||
friend class TaskManager;
|
friend class TaskManager;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,10 @@
|
||||||
static_assert(false, "Debug variables are only intended for use during development.");
|
static_assert(false, "Debug variables are only intended for use during development.");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace hex::trace {
|
||||||
|
struct StackTraceResult;
|
||||||
|
}
|
||||||
|
|
||||||
namespace hex::dbg {
|
namespace hex::dbg {
|
||||||
|
|
||||||
namespace impl {
|
namespace impl {
|
||||||
|
|
@ -47,4 +51,6 @@ namespace hex::dbg {
|
||||||
bool debugModeEnabled();
|
bool debugModeEnabled();
|
||||||
void setDebugModeEnabled(bool enabled);
|
void setDebugModeEnabled(bool enabled);
|
||||||
|
|
||||||
|
void printStackTrace(const trace::StackTraceResult &stackTrace);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -7,6 +7,8 @@
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
|
||||||
#include <jthread.hpp>
|
#include <jthread.hpp>
|
||||||
|
#include <hex/helpers/debugging.hpp>
|
||||||
|
#include <hex/trace/exceptions.hpp>
|
||||||
|
|
||||||
#if defined(OS_WINDOWS)
|
#if defined(OS_WINDOWS)
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
@ -310,6 +312,8 @@ namespace hex {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
trace::enableExceptionCaptureForCurrentThread();
|
||||||
|
|
||||||
// Set the thread name to the name of the task
|
// Set the thread name to the name of the task
|
||||||
TaskManager::setCurrentThreadName(Lang(task->m_unlocalizedName));
|
TaskManager::setCurrentThreadName(Lang(task->m_unlocalizedName));
|
||||||
|
|
||||||
|
|
@ -323,15 +327,21 @@ namespace hex {
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
log::error("Exception in task '{}': {}", task->m_unlocalizedName.get(), e.what());
|
log::error("Exception in task '{}': {}", task->m_unlocalizedName.get(), e.what());
|
||||||
|
|
||||||
|
dbg::printStackTrace(trace::getStackTrace());
|
||||||
|
|
||||||
// Handle the task throwing an uncaught exception
|
// Handle the task throwing an uncaught exception
|
||||||
task->exception(e.what());
|
task->exception(e.what());
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
log::error("Exception in task '{}'", task->m_unlocalizedName.get());
|
log::error("Exception in task '{}'", task->m_unlocalizedName.get());
|
||||||
|
|
||||||
|
dbg::printStackTrace(trace::getStackTrace());
|
||||||
|
|
||||||
// Handle the task throwing an uncaught exception of unknown type
|
// Handle the task throwing an uncaught exception of unknown type
|
||||||
task->exception("Unknown Exception");
|
task->exception("Unknown Exception");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trace::disableExceptionCaptureForCurrentThread();
|
||||||
|
|
||||||
s_currentTask = nullptr;
|
s_currentTask = nullptr;
|
||||||
task->finish();
|
task->finish();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
#include <hex/helpers/debugging.hpp>
|
#include <hex/helpers/debugging.hpp>
|
||||||
|
#include <hex/helpers/logger.hpp>
|
||||||
|
#include <hex/trace/stacktrace.hpp>
|
||||||
|
|
||||||
namespace hex::dbg {
|
namespace hex::dbg {
|
||||||
|
|
||||||
|
|
@ -21,4 +23,23 @@ namespace hex::dbg {
|
||||||
s_debugMode = enabled;
|
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 <mutex>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <fmt/chrono.h>
|
#include <fmt/chrono.h>
|
||||||
|
#include <hex/helpers/debugging.hpp>
|
||||||
|
|
||||||
#if defined(OS_WINDOWS)
|
#if defined(OS_WINDOWS)
|
||||||
#include <Windows.h>
|
#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 {
|
namespace color {
|
||||||
|
|
||||||
fmt::color debug() { return fmt::color::medium_sea_green; }
|
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.
|
// 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) MyAssert(_EXPR)
|
||||||
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
|
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
|
||||||
namespace hex::log::impl {
|
namespace hex::dbg {
|
||||||
void assertionHandler(const char* expr_str, const char* file, int line);
|
[[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
|
//---- 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.
|
// 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 {
|
namespace hex::trace {
|
||||||
|
|
||||||
|
using AssertionHandler = void(*)(const char* file, int line, const char *function, const char* exprString);
|
||||||
|
|
||||||
std::optional<StackTraceResult> getLastExceptionStackTrace();
|
std::optional<StackTraceResult> getLastExceptionStackTrace();
|
||||||
|
void setAssertionHandler(AssertionHandler handler);
|
||||||
|
|
||||||
void enableExceptionCaptureForCurrentThread();
|
void enableExceptionCaptureForCurrentThread();
|
||||||
|
void disableExceptionCaptureForCurrentThread();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -2,8 +2,9 @@
|
||||||
|
|
||||||
namespace hex::trace {
|
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 thread_local bool s_threadExceptionCaptureEnabled = false;
|
||||||
|
static AssertionHandler s_assertionHandler = nullptr;
|
||||||
|
|
||||||
std::optional<StackTraceResult> getLastExceptionStackTrace() {
|
std::optional<StackTraceResult> getLastExceptionStackTrace() {
|
||||||
if (!s_lastExceptionStackTrace.has_value())
|
if (!s_lastExceptionStackTrace.has_value())
|
||||||
|
|
@ -15,18 +16,26 @@ namespace hex::trace {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setAssertionHandler(AssertionHandler handler) {
|
||||||
|
s_assertionHandler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
void enableExceptionCaptureForCurrentThread() {
|
void enableExceptionCaptureForCurrentThread() {
|
||||||
s_threadExceptionCaptureEnabled = true;
|
s_threadExceptionCaptureEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void disableExceptionCaptureForCurrentThread() {
|
||||||
|
s_threadExceptionCaptureEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(HEX_WRAP_CXA_THROW)
|
#if defined(HEX_WRAP_CXA_THROW)
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
[[noreturn]] void __real___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, void* type, void (*destructor)(void*)) {
|
[[noreturn]] void __wrap___cxa_throw(void* thrownException, std::type_info* type, void (*destructor)(void*)) {
|
||||||
if (hex::trace::s_threadExceptionCaptureEnabled)
|
if (hex::trace::s_threadExceptionCaptureEnabled)
|
||||||
hex::trace::s_lastExceptionStackTrace = hex::trace::getStackTrace();
|
hex::trace::s_lastExceptionStackTrace = hex::trace::getStackTrace();
|
||||||
|
|
||||||
|
|
@ -41,19 +50,15 @@ namespace hex::trace {
|
||||||
|
|
||||||
extern "C" {
|
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) {
|
[[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) {
|
if (hex::trace::s_assertionHandler != nullptr) {
|
||||||
fprintf(stderr, "Assertion failed (glibc++): (%s), function %s, file %s, line %d.\n", condition, function, file, line);
|
hex::trace::s_assertionHandler(file, line, function, condition);
|
||||||
} else if (function != nullptr) {
|
} else {
|
||||||
fprintf(stderr, "%s: Undefined behavior detected (glibc++).\n", function);
|
__real__ZSt21__glibcxx_assert_failPKciS0_S0_(file, line, function, condition);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto stackTrace = hex::trace::getStackTrace();
|
std::abort();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <typeinfo>
|
#include <typeinfo>
|
||||||
|
#include <hex/helpers/debugging.hpp>
|
||||||
#include <hex/helpers/utils.hpp>
|
#include <hex/helpers/utils.hpp>
|
||||||
|
|
||||||
#if defined(IMGUI_TEST_ENGINE)
|
#if defined(IMGUI_TEST_ENGINE)
|
||||||
|
|
@ -71,23 +72,12 @@ namespace hex::crash {
|
||||||
log::warn("Could not write crash.json file!");
|
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) {
|
static void callCrashHandlers(const std::string &msg) {
|
||||||
// Call the crash callback
|
// Call the crash callback
|
||||||
crashCallback(msg);
|
crashCallback(msg);
|
||||||
|
|
||||||
// Print the stacktrace to the console or log file
|
// Print the stacktrace to the console or log file
|
||||||
printStackTrace();
|
dbg::printStackTrace(trace::getStackTrace());
|
||||||
|
|
||||||
// Flush all streams
|
// Flush all streams
|
||||||
std::fflush(stdout);
|
std::fflush(stdout);
|
||||||
|
|
@ -188,6 +178,7 @@ namespace hex::crash {
|
||||||
// Setup functions to handle signals, uncaught exception, or similar stuff that will crash ImHex
|
// Setup functions to handle signals, uncaught exception, or similar stuff that will crash ImHex
|
||||||
void setupCrashHandlers() {
|
void setupCrashHandlers() {
|
||||||
trace::initialize();
|
trace::initialize();
|
||||||
|
trace::setAssertionHandler(dbg::assertionHandler);
|
||||||
|
|
||||||
// Register signal handlers
|
// Register signal handlers
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1707,6 +1707,9 @@ namespace hex::plugin::builtin {
|
||||||
EventHighlightingChanged::post();
|
EventHighlightingChanged::post();
|
||||||
|
|
||||||
TaskManager::createTask("hex.builtin.view.pattern_editor.evaluating", TaskManager::NoProgress, [this, code, provider](auto &task) {
|
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 runtimeLock = std::scoped_lock(ContentRegistry::PatternLanguage::getRuntimeLock());
|
||||||
|
|
||||||
auto &runtime = ContentRegistry::PatternLanguage::getRuntime();
|
auto &runtime = ContentRegistry::PatternLanguage::getRuntime();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue