From 1bfa44c317479412a095ad331a191288757a67bc Mon Sep 17 00:00:00 2001 From: MerryMage Date: Mon, 19 Dec 2016 19:25:27 +0000 Subject: [PATCH] citra_qt: Show critical QMessageBox when EmuThread crashes --- src/citra_qt/bootmanager.cpp | 60 ++++++++++++++++++++---------------- src/citra_qt/bootmanager.h | 12 ++++++++ src/citra_qt/main.cpp | 19 ++++++++++++ src/citra_qt/main.h | 1 + 4 files changed, 66 insertions(+), 26 deletions(-) diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 7699ca8d0..77ea700ad 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -9,6 +9,7 @@ #endif #include "citra_qt/bootmanager.h" +#include "common/crash_handler.h" #include "common/key_map.h" #include "common/microprofile.h" #include "common/scm_rev.h" @@ -25,7 +26,10 @@ #define COPYRIGHT "Copyright (C) 2013-2014 Citra Team" EmuThread::EmuThread(GRenderWindow* render_window) - : exec_step(false), running(false), stop_run(false), render_window(render_window) {} + : exec_step(false), running(false), stop_run(false), render_window(render_window) { + // Custom types must be registered to be used with Qt signals/slots. + qRegisterMetaType("Common::CrashInformation"); +} void EmuThread::run() { render_window->MakeCurrent(); @@ -34,35 +38,39 @@ void EmuThread::run() { stop_run = false; - // holds whether the cpu was running during the last iteration, - // so that the DebugModeLeft signal can be emitted before the - // next execution step - bool was_active = false; - while (!stop_run) { - if (running) { - if (!was_active) - emit DebugModeLeft(); + Common::CrashHandler( + [&]() { + // holds whether the cpu was running during the last iteration, + // so that the DebugModeLeft signal can be emitted before the + // next execution step + bool was_active = false; + while (!stop_run) { + if (running) { + if (!was_active) + emit DebugModeLeft(); - Core::RunLoop(); + Core::RunLoop(); - was_active = running || exec_step; - if (!was_active && !stop_run) - emit DebugModeEntered(); - } else if (exec_step) { - if (!was_active) - emit DebugModeLeft(); + was_active = running || exec_step; + if (!was_active && !stop_run) + emit DebugModeEntered(); + } else if (exec_step) { + if (!was_active) + emit DebugModeLeft(); - exec_step = false; - Core::SingleStep(); - emit DebugModeEntered(); - yieldCurrentThread(); + exec_step = false; + Core::SingleStep(); + emit DebugModeEntered(); + yieldCurrentThread(); - was_active = false; - } else { - std::unique_lock lock(running_mutex); - running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; }); - } - } + was_active = false; + } else { + std::unique_lock lock(running_mutex); + running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; }); + } + } + }, + [&](const Common::CrashInformation& crash_info) { emit Crashed(crash_info); }); // Shutdown the core emulation System::Shutdown(); diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h index af52f369b..784736371 100644 --- a/src/citra_qt/bootmanager.h +++ b/src/citra_qt/bootmanager.h @@ -19,6 +19,10 @@ class GGLWidgetInternal; class GMainWindow; class GRenderWindow; +namespace Common { +struct CrashInformation; +} + class EmuThread : public QThread { Q_OBJECT @@ -96,6 +100,14 @@ signals: * Qt::BlockingQueuedConnection (additionally block source thread until slot returns) */ void DebugModeLeft(); + + /** + * Emitted when the emulation thread crashes + * + * @warning Terminate the application soon after this signal is emitted. The program may + * not be in a well-defined state. + */ + void Crashed(const Common::CrashInformation&); }; class GRenderWindow : public QWidget, public EmuWindow { diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index a3887f9ab..362e47e45 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -14,6 +14,12 @@ #include "citra_qt/bootmanager.h" #include "citra_qt/config.h" #include "citra_qt/configure_dialog.h" +#include "citra_qt/game_list.h" +#include "citra_qt/hotkeys.h" +#include "citra_qt/main.h" +#include "citra_qt/ui_settings.h" + +// Debugger #include "citra_qt/debugger/callstack.h" #include "citra_qt/debugger/disassembler.h" #include "citra_qt/debugger/graphics.h" @@ -30,6 +36,7 @@ #include "citra_qt/hotkeys.h" #include "citra_qt/main.h" #include "citra_qt/ui_settings.h" +#include "common/crash_handler.h" #include "common/logging/backend.h" #include "common/logging/filter.h" #include "common/logging/log.h" @@ -378,6 +385,8 @@ void GMainWindow::BootGame(const std::string& filename) { Qt::BlockingQueuedConnection); connect(emu_thread.get(), SIGNAL(DebugModeLeft()), waitTreeWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection); + connect(emu_thread.get(), SIGNAL(Crashed(Common::CrashInformation)), this, + SLOT(OnCrashed(Common::CrashInformation)), Qt::BlockingQueuedConnection); // Update the GUI registersWidget->OnDebugModeEntered(); @@ -576,6 +585,16 @@ void GMainWindow::OnCreateGraphicsSurfaceViewer() { graphicsSurfaceViewerWidget->show(); } +void GMainWindow::OnCrashed(const Common::CrashInformation& crash_info) { + QString message = tr("Citra has crashed. Crash information follows:\n"); + for (const auto& line : crash_info.stack_trace) { + message += QString::fromStdString(line); + message += '\n'; + } + QMessageBox::critical(this, tr("Citra"), message); + QCoreApplication::exit(EXIT_FAILURE); +} + bool GMainWindow::ConfirmClose() { if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) return true; diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index f87178227..57965d0ef 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -115,6 +115,7 @@ private slots: void OnDisplayTitleBars(bool); void ToggleWindowMode(); void OnCreateGraphicsSurfaceViewer(); + void OnCrashed(const Common::CrashInformation&); private: Ui::MainWindow ui;