1
0
mirror of synced 2025-02-17 18:59:21 +01:00

impr: Added better crash backup and restore mechanism (#1094)

- Add a new file 'crash.json' to store metadata about the crash, like
the log file or project opened
- show the log file of the session that caused the crash to the user
- Correctly restore the project path
This commit is contained in:
iTrooz 2023-05-22 13:24:48 +02:00 committed by GitHub
parent 91635346be
commit 7ce0613977
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 103 additions and 19 deletions

View File

@ -9,9 +9,12 @@
#include <fmt/color.h>
#include <fmt/chrono.h>
#include <wolv/io/file.hpp>
namespace hex::log {
FILE *getDestination();
wolv::io::File& getFile();
bool isRedirected();
namespace {

View File

@ -15,6 +15,10 @@ namespace hex::log {
return stdout;
}
wolv::io::File& getFile() {
return g_loggerFile;
}
bool isRedirected() {
return g_loggerFile.isValid();
}

View File

@ -45,11 +45,34 @@ namespace hex {
using namespace std::literals::chrono_literals;
static void saveCrashFile(){
nlohmann::json crashData{
{"logFile", hex::log::getFile().getPath()},
{"project", 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 !");
}
// 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
@ -175,12 +198,12 @@ namespace hex {
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());
if (!ImHexApi::Provider::isDirty())
return;
for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Config)) {
if (ProjectFile::store(path / CrashBackupFileName))
break;
@ -219,6 +242,12 @@ namespace hex {
ex.what()
);
}
// save crash.json file
saveCrashFile();
// send the event. It may affect things (like the project path),
// so we do this after saving the crash file
EventManager::post<EventAbnormalTermination>(0);
});
}

View File

@ -842,6 +842,7 @@
"hex.builtin.welcome.plugins.plugin",
"hex.builtin.popup.safety_backup.delete",
"hex.builtin.popup.safety_backup.desc",
"hex.builtin.popup.safety_backup.log_file",
"hex.builtin.popup.safety_backup.restore",
"hex.builtin.popup.safety_backup.title",
"hex.builtin.welcome.start.create_file",

View File

@ -921,6 +921,7 @@
"hex.builtin.welcome.plugins.plugin": "Plugin",
"hex.builtin.popup.safety_backup.delete": "No, Delete",
"hex.builtin.popup.safety_backup.desc": "Oh no, ImHex crashed last time.\nDo you want to restore your past work?",
"hex.builtin.popup.safety_backup.log_file": "Log file: ",
"hex.builtin.popup.safety_backup.restore": "Yes, Restore",
"hex.builtin.popup.safety_backup.title": "Restore lost data",
"hex.builtin.welcome.start.create_file": "Create New File",

View File

@ -68,30 +68,39 @@ namespace hex::plugin::builtin {
};
class PopupRestoreBackup : public Popup<PopupRestoreBackup> {
private:
std::fs::path m_logFilePath;
std::function<void()> m_restoreCallback;
std::function<void()> m_deleteCallback;
public:
PopupRestoreBackup() : Popup("hex.builtin.popup.safety_backup.title") { }
PopupRestoreBackup(std::fs::path logFilePath, std::function<void()> restoreCallback, std::function<void()> deleteCallback)
: Popup("hex.builtin.popup.safety_backup.title"),
m_logFilePath(logFilePath),
m_restoreCallback(restoreCallback),
m_deleteCallback(deleteCallback) { }
void drawContent() override {
ImGui::TextUnformatted("hex.builtin.popup.safety_backup.desc"_lang);
ImGui::NewLine();
if (!this->m_logFilePath.empty()) {
ImGui::TextUnformatted("hex.builtin.popup.safety_backup.log_file"_lang);
if (ImGui::Hyperlink(this->m_logFilePath.filename().string().c_str())) {
fs::openFolderWithSelectionExternal(this->m_logFilePath);
}
ImGui::NewLine();
}
auto width = ImGui::GetWindowWidth();
ImGui::SetCursorPosX(width / 9);
if (ImGui::Button("hex.builtin.popup.safety_backup.restore"_lang, ImVec2(width / 3, 0))) {
ProjectFile::load(s_safetyBackupPath);
ProjectFile::clearPath();
for (const auto &provider : ImHexApi::Provider::getProviders())
provider->markDirty();
wolv::io::fs::remove(s_safetyBackupPath);
this->m_restoreCallback();
this->m_deleteCallback();
this->close();
}
ImGui::SameLine();
ImGui::SetCursorPosX(width / 9 * 5);
if (ImGui::Button("hex.builtin.popup.safety_backup.delete"_lang, ImVec2(width / 3, 0))) {
wolv::io::fs::remove(s_safetyBackupPath);
this->m_deleteCallback();
this->close();
}
@ -608,17 +617,54 @@ namespace hex::plugin::builtin {
});
// Check for crash backup
constexpr static auto CrashBackupFileName = "crash_backup.hexproj";
constexpr static auto CrashFileName = "crash.json";
constexpr static auto BackupFileName = "crash_backup.hexproj";
bool hasCrashed = false;
for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Config)) {
if (auto filePath = std::fs::path(path) / CrashBackupFileName; wolv::io::fs::exists(filePath)) {
s_safetyBackupPath = filePath;
PopupRestoreBackup::open();
if (auto crashFilePath = std::fs::path(path) / CrashFileName; wolv::io::fs::exists(crashFilePath)) {
hasCrashed = true;
log::info("Found crash.json file at {}", wolv::util::toUTF8String(crashFilePath));
wolv::io::File crashFile(crashFilePath, wolv::io::File::Mode::Read);
auto crashFileData = nlohmann::json::parse(crashFile.readString());
crashFile.close();
bool hasProject = !crashFileData.value("project", "").empty();
auto backupFilePath = path / BackupFileName;
bool hasBackupFile = wolv::io::fs::exists(backupFilePath);
PopupRestoreBackup::open(
// path of log file
crashFileData.value("logFile", ""),
// restore callback
[=]{
if (hasBackupFile) {
ProjectFile::load(backupFilePath);
if (hasProject) {
ProjectFile::setPath(crashFileData["project"].get<std::string>());
} else {
ProjectFile::setPath("");
}
EventManager::post<RequestUpdateWindowTitle>();
}else{
if (hasProject) {
ProjectFile::setPath(crashFileData["project"].get<std::string>());
}
}
},
// delete callback (also executed after restore)
[crashFilePath, backupFilePath]{
wolv::io::fs::remove(crashFilePath);
wolv::io::fs::remove(backupFilePath);
}
);
}
}
// Tip of the day
auto tipsData = romfs::get("tips.json");
if(s_safetyBackupPath.empty() && tipsData.valid()){
if (!hasCrashed && tipsData.valid()) {
auto tipsCategories = nlohmann::json::parse(tipsData.string());
auto now = std::chrono::system_clock::now();