Bug 1278325 - Hook exit to catch early 3rd party exits in crash reports. r=ted
MozReview-Commit-ID: 6x6DLcqqkAe
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -7,22 +7,23 @@
#include "mozilla/dom/ContentChild.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Attributes.h"
#include "mozilla/ChaosMode.h"
#include "mozilla/IOInterposer.h"
#include "mozilla/Likely.h"
+#include "mozilla/MemoryChecking.h"
#include "mozilla/Poison.h"
#include "mozilla/Preferences.h"
+#include "mozilla/ScopeExit.h"
#include "mozilla/Services.h"
#include "mozilla/ServoBindings.h"
#include "mozilla/Telemetry.h"
-#include "mozilla/MemoryChecking.h"
#include "nsAppRunner.h"
#include "mozilla/AppData.h"
#if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID)
#include "nsUpdateDriver.h"
#endif
#include "ProfileReset.h"
@@ -373,16 +374,35 @@ strimatch(const char* lowerstr, const ch
++mixedstr;
}
if (*mixedstr) return false; // lowerstr is shorter
return true;
}
+static bool gIsExpectedExit = false;
+
+void MozExpectedExit() {
+ gIsExpectedExit = true;
+}
+
+/**
+ * Runs atexit() to catch unexpected exit from 3rd party libraries like the
+ * Intel graphics driver calling exit in an error condition. When they
+ * call exit() to report an error we won't shutdown correctly and wont catch
+ * the issue with our crash reporter.
+ */
+static void UnexpectedExit() {
+ if (!gIsExpectedExit) {
+ gIsExpectedExit = true; // Don't risk re-entrency issues when crashing.
+ MOZ_CRASH("Exit called by third party code.");
+ }
+}
+
/**
* Output a string to the user. This method is really only meant to be used to
* output last-ditch error messages designed for developers NOT END USERS.
*
* @param isError
* Pass true to indicate severe errors.
* @param fmt
* printf-style format string followed by arguments.
@@ -3013,16 +3033,21 @@ public:
*/
int
XREMain::XRE_mainInit(bool* aExitFlag)
{
if (!aExitFlag)
return 1;
*aExitFlag = false;
+ atexit(UnexpectedExit);
+ auto expectedShutdown = mozilla::MakeScopeExit([&] {
+ MozExpectedExit();
+ });
+
StartupTimeline::Record(StartupTimeline::MAIN);
if (PR_GetEnv("MOZ_CHAOSMODE")) {
ChaosFeature feature = ChaosFeature::Any;
long featureInt = strtol(PR_GetEnv("MOZ_CHAOSMODE"), nullptr, 16);
if (featureInt) {
// NOTE: MOZ_CHAOSMODE=0 or a non-hex value maps to Any feature.
feature = static_cast<ChaosFeature>(featureInt);
--- a/toolkit/xre/nsAppRunner.h
+++ b/toolkit/xre/nsAppRunner.h
@@ -92,16 +92,23 @@ NS_LockProfilePath(nsIFile* aPath, nsIFi
nsIProfileUnlocker* *aUnlocker, nsIProfileLock* *aResult);
void
WriteConsoleLog();
void
OverrideDefaultLocaleIfNeeded();
+/**
+ * Allow exit() calls to complete. This should be done from a proper Gecko
+ * shutdown path. Otherwise we aim to catch improper shutdowns.
+ */
+void
+MozExpectedExit();
+
#ifdef XP_WIN
void
UseParentConsole();
BOOL
WinLaunchChild(const wchar_t *exePath, int argc,
char **argv, HANDLE userToken = nullptr,
HANDLE *hProcess = nullptr);
--- a/toolkit/xre/nsNativeAppSupportUnix.cpp
+++ b/toolkit/xre/nsNativeAppSupportUnix.cpp
@@ -467,16 +467,17 @@ nsNativeAppSupportUnix::Start(bool *aRet
GTK_BUTTONS_OK,
UNSUPPORTED_GTK_MSG,
gtk_major_version,
gtk_minor_version,
MIN_GTK_MAJOR_VERSION,
MIN_GTK_MINOR_VERSION);
gtk_dialog_run(GTK_DIALOG(versionErrDialog));
gtk_widget_destroy(versionErrDialog);
+ MozExpectedExit();
exit(0);
}
#endif
*aRetVal = true;
#ifdef MOZ_X11
gboolean sm_disable = FALSE;