mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-30 05:05:19 -05:00
impr: Handle crashes that may happen before the main loop (#1115)
Draft because I absolutely do not trust myself writing good code at 2AM. I will review it tomorrow
This commit is contained in:
@@ -22,7 +22,6 @@
|
||||
#include <cassert>
|
||||
|
||||
#include <romfs/romfs.hpp>
|
||||
#include <llvm/Demangle/Demangle.h>
|
||||
|
||||
#include <imgui.h>
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
@@ -45,61 +44,13 @@ namespace hex {
|
||||
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
static void saveCrashFile(){
|
||||
nlohmann::json crashData{
|
||||
{"logFile", wolv::util::toUTF8String(hex::log::getFile().getPath())},
|
||||
{"project", wolv::util::toUTF8String(ProjectFile::getPath())},
|
||||
};
|
||||
|
||||
for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Config)) {
|
||||
wolv::io::File file(path / "crash.json", wolv::io::File::Mode::Write);
|
||||
if (file.isValid()) {
|
||||
file.writeString(crashData.dump(4));
|
||||
file.close();
|
||||
log::info("Wrote crash.json file to {}", wolv::util::toUTF8String(file.getPath()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
log::warn("Could not write crash.json file !");
|
||||
}
|
||||
static std::fs::path s_imguiSettingsPath;
|
||||
|
||||
// Custom signal handler to print various information and a stacktrace when the application crashes
|
||||
static void signalHandler(int signalNumber, const std::string &signalName) {
|
||||
log::fatal("Terminating with signal '{}' ({})", signalName, signalNumber);
|
||||
|
||||
// save crash.json file
|
||||
saveCrashFile();
|
||||
|
||||
// Trigger an event so that plugins can handle crashes
|
||||
// It may affect things (like the project path),
|
||||
// so we do this after saving the crash file
|
||||
EventManager::post<EventAbnormalTermination>(signalNumber);
|
||||
|
||||
// Detect if the crash was due to an uncaught exception
|
||||
if (std::uncaught_exceptions() > 0) {
|
||||
log::fatal("Uncaught exception thrown!");
|
||||
}
|
||||
|
||||
// Reset the signal handler to the default handler
|
||||
std::signal(SIGSEGV, SIG_DFL);
|
||||
std::signal(SIGILL, SIG_DFL);
|
||||
std::signal(SIGABRT, SIG_DFL);
|
||||
std::signal(SIGFPE, SIG_DFL);
|
||||
|
||||
// Print stack trace
|
||||
for (const auto &stackFrame : stacktrace::getStackTrace()) {
|
||||
if (stackFrame.line == 0)
|
||||
log::fatal(" {}", stackFrame.function);
|
||||
else
|
||||
log::fatal(" ({}:{}) | {}", stackFrame.file, stackFrame.line, stackFrame.function);
|
||||
}
|
||||
|
||||
// Trigger a breakpoint if we're in a debug build or raise the signal again for the default handler to handle it
|
||||
#if defined(DEBUG)
|
||||
assert(!"Debug build, triggering breakpoint");
|
||||
#else
|
||||
std::raise(signalNumber);
|
||||
#endif
|
||||
/**
|
||||
* @brief returns the path to load/save imgui settings to, or an empty path if no location was found
|
||||
*/
|
||||
std::fs::path getImGuiSettingsPath() {
|
||||
return s_imguiSettingsPath;
|
||||
}
|
||||
|
||||
Window::Window() {
|
||||
@@ -152,8 +103,6 @@ namespace hex {
|
||||
this->exitGLFW();
|
||||
}
|
||||
|
||||
static std::terminate_handler originalHandler;
|
||||
|
||||
void Window::registerEventHandlers() {
|
||||
// Initialize default theme
|
||||
EventManager::post<RequestChangeTheme>("Dark");
|
||||
@@ -197,60 +146,12 @@ namespace hex {
|
||||
glfwSetWindowTitle(this->m_window, title.c_str());
|
||||
});
|
||||
|
||||
constexpr static auto CrashBackupFileName = "crash_backup.hexproj";
|
||||
|
||||
// Save a backup project when the application crashes
|
||||
// We need to save the project no mater if it is dirty,
|
||||
// because this save is responsible for telling us which files
|
||||
// were opened in case there wasn't a project
|
||||
EventManager::subscribe<EventAbnormalTermination>(this, [this](int) {
|
||||
ImGui::SaveIniSettingsToDisk(wolv::util::toUTF8String(this->m_imguiSettingsPath).c_str());
|
||||
|
||||
for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Config)) {
|
||||
if (ProjectFile::store(path / CrashBackupFileName))
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Handle opening popups
|
||||
EventManager::subscribe<RequestOpenPopup>(this, [this](auto name) {
|
||||
std::scoped_lock lock(this->m_popupMutex);
|
||||
|
||||
this->m_popupsToOpen.push_back(name);
|
||||
});
|
||||
|
||||
// Register signal handlers
|
||||
{
|
||||
#define HANDLE_SIGNAL(name) \
|
||||
std::signal(name, [](int signalNumber){ \
|
||||
signalHandler(signalNumber, #name); \
|
||||
})
|
||||
|
||||
HANDLE_SIGNAL(SIGSEGV);
|
||||
HANDLE_SIGNAL(SIGILL);
|
||||
HANDLE_SIGNAL(SIGABRT);
|
||||
HANDLE_SIGNAL(SIGFPE);
|
||||
|
||||
#undef HANDLE_SIGNAL
|
||||
}
|
||||
|
||||
originalHandler = std::set_terminate([]{
|
||||
try {
|
||||
std::rethrow_exception(std::current_exception());
|
||||
} catch (std::exception &ex) {
|
||||
log::fatal(
|
||||
"Program terminated with uncaught exception: {}()::what() -> {}",
|
||||
llvm::itaniumDemangle(typeid(ex).name(), nullptr, nullptr, nullptr),
|
||||
ex.what()
|
||||
);
|
||||
}
|
||||
|
||||
// the handler should eventually release a signal, which will be caught and used to handle the crash
|
||||
originalHandler();
|
||||
|
||||
log::error("Should not happen: original std::set_terminate handler returned. Terminating manually");
|
||||
exit(EXIT_FAILURE);
|
||||
});
|
||||
}
|
||||
|
||||
void Window::loop() {
|
||||
@@ -1100,15 +1001,16 @@ namespace hex {
|
||||
ImGui::GetCurrentContext()->SettingsHandlers.push_back(handler);
|
||||
|
||||
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Config)) {
|
||||
if (std::fs::exists(dir) && fs::isPathWritable(dir)) {
|
||||
this->m_imguiSettingsPath = dir / "interface.ini";
|
||||
io.IniFilename = nullptr;
|
||||
if (std::fs::exists(dir) && (fs::isPathWritable(dir))) {
|
||||
s_imguiSettingsPath = dir / "interface.ini";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->m_imguiSettingsPath.empty() && wolv::io::fs::exists(this->m_imguiSettingsPath))
|
||||
ImGui::LoadIniSettingsFromDisk(wolv::util::toUTF8String(this->m_imguiSettingsPath).c_str());
|
||||
if (!s_imguiSettingsPath.empty() && wolv::io::fs::exists(s_imguiSettingsPath)) {
|
||||
io.IniFilename = nullptr;
|
||||
ImGui::LoadIniSettingsFromDisk(wolv::util::toUTF8String(s_imguiSettingsPath).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
ImGui_ImplGlfw_InitForOpenGL(this->m_window, true);
|
||||
@@ -1135,7 +1037,7 @@ namespace hex {
|
||||
void Window::exitImGui() {
|
||||
delete static_cast<ImGui::ImHexCustomData *>(ImGui::GetIO().UserData);
|
||||
|
||||
ImGui::SaveIniSettingsToDisk(wolv::util::toUTF8String(this->m_imguiSettingsPath).c_str());
|
||||
ImGui::SaveIniSettingsToDisk(wolv::util::toUTF8String(s_imguiSettingsPath).c_str());
|
||||
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
|
||||
Reference in New Issue
Block a user