Skip to content

Commit e952d18

Browse files
committed
frontend: Switch crash handling to new CrashHandler class
The new CrashHandler class is used by the OBSApp class (as it's an application concern) to handle detection of a prior crash and also to provide crash upload functionality. The concerns are separated by using Qt signals: The OBSBasic window asks the application to upload the most recent crash log and is informed by the OBSApp instance via bespoke signals about the result (thus the OBSBasic instance can choose what to do in reaction to these events). The OBSApp class itself reacts to events emitted by its CrashHandler instance, thus each part of the application is only concerned about its own functionality and allows other parts to react to these events accordingly.
1 parent df830fb commit e952d18

File tree

9 files changed

+186
-85
lines changed

9 files changed

+186
-85
lines changed

frontend/OBSApp.cpp

Lines changed: 114 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
#include "OBSApp.hpp"
1919

2020
#include <components/Multiview.hpp>
21+
#include <dialogs/OBSLogReply.hpp>
22+
#include <utility/CrashHandler.hpp>
2123
#include <utility/OBSEventFilter.hpp>
2224
#if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER)
2325
#include <utility/models/branches.hpp>
@@ -29,6 +31,8 @@
2931
#endif
3032
#include <qt-wrappers.hpp>
3133

34+
#include <QCheckBox>
35+
#include <QDesktopServices>
3236
#if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER)
3337
#include <QFile>
3438
#endif
@@ -42,6 +46,8 @@
4246
#include <qpa/qplatformnativeinterface.h>
4347
#endif
4448

49+
#include <chrono>
50+
4551
#ifdef _WIN32
4652
#include <sstream>
4753
#define WIN32_LEAN_AND_MEAN
@@ -61,6 +67,7 @@ string lastCrashLogFile;
6167

6268
extern bool portable_mode;
6369
extern bool safe_mode;
70+
extern bool multi;
6471
extern bool disable_3p_plugins;
6572
extern bool opt_disable_updater;
6673
extern bool opt_disable_missing_files_check;
@@ -79,6 +86,57 @@ extern "C" __declspec(dllexport) DWORD NvOptimusEnablement = 1;
7986
extern "C" __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
8087
#endif
8188

89+
namespace {
90+
91+
typedef struct UncleanLaunchAction {
92+
bool useSafeMode = false;
93+
bool sendCrashReport = false;
94+
95+
} UncleanLaunchAction;
96+
97+
UncleanLaunchAction handleUncleanShutdown()
98+
{
99+
UncleanLaunchAction launchAction;
100+
101+
blog(LOG_WARNING, "Crash or unclean shutdown detected");
102+
103+
QMessageBox crashWarning = QMessageBox(QMessageBox::Warning, QTStr("CrashHandling.Dialog.Title"),
104+
QTStr("CrashHandling.Dialog.Text"));
105+
106+
QCheckBox *sendCrashReportCheckbox = new QCheckBox(QTStr("CrashHandling.Dialog.SendReport"));
107+
crashWarning.setCheckBox(sendCrashReportCheckbox);
108+
109+
QPushButton *launchSafeButton =
110+
crashWarning.addButton(QTStr("CrashHandling.Dialog.LaunchSafe"), QMessageBox::AcceptRole);
111+
QPushButton *launchNormalButton =
112+
crashWarning.addButton(QTStr("CrashHandling.Dialog.LaunchNormal"), QMessageBox::RejectRole);
113+
114+
crashWarning.setDefaultButton(launchNormalButton);
115+
116+
crashWarning.exec();
117+
118+
bool useSafeMode = crashWarning.clickedButton() == launchSafeButton;
119+
120+
if (useSafeMode) {
121+
launchAction.useSafeMode = true;
122+
123+
blog(LOG_INFO, "[Safe Mode] Safe mode launch selected, loading 3rd party plugins is disabled");
124+
} else {
125+
blog(LOG_WARNING, "[Safe Mode] Normal launch selected, loading 3rd party plugins is enabled");
126+
}
127+
128+
bool sendCrashReport = crashWarning.checkBox()->isChecked();
129+
130+
if (sendCrashReport) {
131+
launchAction.sendCrashReport = true;
132+
133+
blog(LOG_INFO, "User selected to send crash report");
134+
}
135+
136+
return launchAction;
137+
}
138+
} // namespace
139+
82140
QObject *CreateShortcutFilter()
83141
{
84142
return new OBSEventFilter([](QObject *obj, QEvent *event) {
@@ -751,7 +809,8 @@ std::vector<UpdateBranch> OBSApp::GetBranches()
751809

752810
OBSApp::OBSApp(int &argc, char **argv, profiler_name_store_t *store)
753811
: QApplication(argc, argv),
754-
profilerNameStore(store)
812+
profilerNameStore(store),
813+
appLaunchUUID_(QUuid::createUuid())
755814
{
756815
/* fix float handling */
757816
#if defined(Q_OS_UNIX)
@@ -767,6 +826,11 @@ OBSApp::OBSApp(int &argc, char **argv, profiler_name_store_t *store)
767826
#else
768827
connect(qApp, &QGuiApplication::commitDataRequest, this, &OBSApp::commitData);
769828
#endif
829+
if (multi) {
830+
crashHandler_ = std::make_unique<OBS::CrashHandler>();
831+
} else {
832+
crashHandler_ = std::make_unique<OBS::CrashHandler>(appLaunchUUID_);
833+
}
770834

771835
sleepInhibitor = os_inhibit_sleep_create("OBS Video/audio");
772836

@@ -958,6 +1022,21 @@ void OBSApp::AppInit()
9581022
throw "Failed to create profile directories";
9591023
}
9601024

1025+
void OBSApp::checkForPriorCrash()
1026+
{
1027+
bool crashDetected = crashHandler_->didOBSCrashBefore();
1028+
1029+
if (crashDetected) {
1030+
UncleanLaunchAction launchAction = handleUncleanShutdown();
1031+
1032+
safe_mode = launchAction.useSafeMode;
1033+
1034+
if (launchAction.sendCrashReport) {
1035+
crashHandler_->uploadLastCrashLog();
1036+
}
1037+
}
1038+
}
1039+
9611040
const char *OBSApp::GetRenderModule() const
9621041
{
9631042
const char *renderer = config_get_string(appConfig, "Video", "Renderer");
@@ -1090,6 +1169,24 @@ bool OBSApp::OBSInit()
10901169
connect(this, &QGuiApplication::applicationStateChanged,
10911170
[this](Qt::ApplicationState state) { ResetHotkeyState(state == Qt::ApplicationActive); });
10921171
ResetHotkeyState(applicationState() == Qt::ApplicationActive);
1172+
1173+
connect(crashHandler_.get(), &OBS::CrashHandler::crashLogUploadFailed, this, [this](QString errorMessage) {
1174+
OBSBasic *basicWindow = static_cast<OBSBasic *>(GetMainWindow());
1175+
1176+
OBSMessageBox::critical(basicWindow, QTStr("CrashHandling.Errors.Title"), errorMessage);
1177+
1178+
emit this->crashLogUploadFailed();
1179+
});
1180+
1181+
connect(crashHandler_.get(), &OBS::CrashHandler::crashLogUploadFinished, this, [this](QString crashLogURL) {
1182+
OBSBasic *basicWindow = static_cast<OBSBasic *>(GetMainWindow());
1183+
1184+
OBSLogReply logReplyDialog = OBSLogReply(basicWindow, crashLogURL, true);
1185+
logReplyDialog.exec();
1186+
1187+
emit this->crashLogUploadFinished();
1188+
});
1189+
10931190
return true;
10941191
}
10951192

@@ -1173,9 +1270,23 @@ const char *OBSApp::GetCurrentLog() const
11731270
return currentLogFile.c_str();
11741271
}
11751272

1176-
const char *OBSApp::GetLastCrashLog() const
1273+
void OBSApp::openCrashLogDirectory() const
1274+
{
1275+
std::filesystem::path crashLogDirectory = crashHandler_->getCrashLogDirectory();
1276+
1277+
if (crashLogDirectory.empty()) {
1278+
return;
1279+
}
1280+
1281+
QString crashLogDirectoryString = QString::fromStdString(crashLogDirectory.u8string());
1282+
1283+
QUrl crashLogDirectoryURL = QUrl::fromLocalFile(crashLogDirectoryString);
1284+
QDesktopServices::openUrl(crashLogDirectoryURL);
1285+
}
1286+
1287+
void OBSApp::uploadLastCrashLog()
11771288
{
1178-
return lastCrashLogFile.c_str();
1289+
crashHandler_->uploadLastCrashLog();
11791290
}
11801291

11811292
bool OBSApp::TranslateString(const char *lookupVal, const char **out) const

frontend/OBSApp.hpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <QApplication>
2929
#include <QPalette>
3030
#include <QPointer>
31+
#include <QUuid>
3132

3233
#include <deque>
3334
#include <functional>
@@ -41,6 +42,10 @@ Q_DECLARE_METATYPE(VoidFunc)
4142
class QFileSystemWatcher;
4243
class QSocketNotifier;
4344

45+
namespace OBS {
46+
class CrashHandler;
47+
}
48+
4449
struct UpdateBranch {
4550
QString name;
4651
QString display_name;
@@ -53,6 +58,9 @@ class OBSApp : public QApplication {
5358
Q_OBJECT
5459

5560
private:
61+
QUuid appLaunchUUID_;
62+
std::unique_ptr<OBS::CrashHandler> crashHandler_;
63+
5664
std::string locale;
5765

5866
ConfigFile appConfig;
@@ -115,6 +123,7 @@ private slots:
115123
~OBSApp();
116124

117125
void AppInit();
126+
void checkForPriorCrash();
118127
bool OBSInit();
119128

120129
void UpdateHotkeyFocusSetting(bool reset = true);
@@ -152,7 +161,8 @@ private slots:
152161
const char *GetLastLog() const;
153162
const char *GetCurrentLog() const;
154163

155-
const char *GetLastCrashLog() const;
164+
void openCrashLogDirectory() const;
165+
void uploadLastCrashLog();
156166

157167
std::string GetVersionString(bool platform = true) const;
158168
bool IsPortableMode();
@@ -195,6 +205,9 @@ public slots:
195205

196206
signals:
197207
void StyleChanged();
208+
209+
void crashLogUploadFinished();
210+
void crashLogUploadFailed();
198211
};
199212

200213
int GetAppConfigPath(char *path, size_t size, const char *name);

frontend/cmake/ui-widgets.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ target_sources(
2222
widgets/OBSBasic_Hotkeys.cpp
2323
widgets/OBSBasic_Icons.cpp
2424
widgets/OBSBasic_MainControls.cpp
25+
widgets/OBSBasic_MainMenu.cpp
2526
widgets/OBSBasic_OutputHandler.cpp
2627
widgets/OBSBasic_Preview.cpp
2728
widgets/OBSBasic_Profiles.cpp

frontend/data/locale/en-US.ini

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,16 @@ AlreadyRunning.Title="OBS is already running"
126126
AlreadyRunning.Text="OBS is already running! Unless you meant to do this, please shut down any existing instances of OBS before trying to run a new instance. If you have OBS set to minimize to the system tray, please check to see if it's still running there."
127127
AlreadyRunning.LaunchAnyway="Launch Anyway"
128128

129-
# warning if auto Safe Mode has engaged
130-
AutoSafeMode.Title="Safe Mode"
131-
AutoSafeMode.Text="OBS did not shut down properly during your last session.\n\nWould you like to start in Safe Mode (third-party plugins, scripting, and WebSockets disabled)?"
132-
AutoSafeMode.LaunchSafe="Run in Safe Mode"
133-
AutoSafeMode.LaunchNormal="Run Normally"
134-
## Restart Option
129+
# Prior Crash Detection
130+
CrashHandling.Dialog.Title="OBS Studio Crash Detected"
131+
CrashHandling.Dialog.Text="OBS Studio did not properly shut down.\n\nRun in Safe Mode (third-party plugins, scripting, and WebSockets disabled)?"
132+
CrashHandling.Dialog.SendReport="Also upload most recent OBS Studio crash report?"
133+
CrashHandling.Dialog.LaunchSafe="Run in Safe Mode"
134+
CrashHandling.Dialog.LaunchNormal="Run in Normal Mode"
135+
CrashHandling.Errors.UploadJSONError="An error occurred while trying to upload the most recent crash log. Please try again later."
136+
CrashHandling.Errors.Title="Crash Log Upload Error"
137+
138+
## Safe Mode Restart Option
135139
SafeMode.Restart="Do you want to restart OBS in Safe Mode (third-party plugins, scripting, and WebSockets disabled)?"
136140
SafeMode.RestartNormal="Do you want to restart OBS in Normal Mode?"
137141

0 commit comments

Comments
 (0)