Bug 1379857 - Record Rust panics for child process crashes. r=dmajor
authorJ. Ryan Stinnett <jryans@gmail.com>
Wed, 12 Jul 2017 14:41:19 -0500
changeset 368857 b972eadf54e82377b66f985a697feb5604b2ca5c
parent 368856 847f584256c874be2f6a1aab050ab52e771d5188
child 368858 d475a3bedd9303c284b6aab9bed3152047349ff1
push id32176
push userryanvm@gmail.com
push dateFri, 14 Jul 2017 13:16:13 +0000
treeherdermozilla-central@7d92f47379da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdmajor
bugs1379857
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1379857 - Record Rust panics for child process crashes. r=dmajor Install crash reporter's panic hook in child processes (and also delay the main process installation until we know crash reporter is enabled). When collecting child crash annotations, read the Rust panic message if it exists. MozReview-Commit-ID: Gfp2E8IHjw8
toolkit/crashreporter/nsExceptionHandler.cpp
toolkit/crashreporter/test/unit/head_crashreporter.js
toolkit/crashreporter/test/unit_ipc/test_content_rust_panic.js
toolkit/crashreporter/test/unit_ipc/xpcshell.ini
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -1386,17 +1386,24 @@ PrepareChildExceptionTimeAnnotations()
   if (gOOMAllocationSize) {
     XP_STOA(gOOMAllocationSize, oomAllocationSizeBuffer, 10);
   }
 
   if (oomAllocationSizeBuffer[0]) {
     WriteAnnotation(apiData, "OOMAllocationSize", oomAllocationSizeBuffer);
   }
 
-  if (gMozCrashReason) {
+  char* rust_panic_reason;
+  size_t rust_panic_len;
+  if (get_rust_panic_reason(&rust_panic_reason, &rust_panic_len)) {
+    // rust_panic_reason is not null-terminated.
+    WriteLiteral(apiData, "MozCrashReason=");
+    apiData.WriteBuffer(rust_panic_reason, rust_panic_len);
+    WriteLiteral(apiData, "\n");
+  } else if (gMozCrashReason) {
     WriteAnnotation(apiData, "MozCrashReason", gMozCrashReason);
   }
 
   char numOfPendingIPCBuffer[32] = "";
   char topPendingIPCCountBuffer[32] = "";
   char topPendingIPCTypeBuffer[11] = "0x";
   if (gNumOfPendingIPC) {
     XP_STOA(gNumOfPendingIPC, numOfPendingIPCBuffer, 10);
@@ -1589,18 +1596,16 @@ LocateExecutable(nsIFile* aXREDirectory,
 #endif // !defined(MOZ_WIDGET_ANDROID)
 
 nsresult SetExceptionHandler(nsIFile* aXREDirectory,
                              bool force/*=false*/)
 {
   if (gExceptionHandler)
     return NS_ERROR_ALREADY_INITIALIZED;
 
-  install_rust_panic_hook();
-
 #if !defined(DEBUG) || defined(MOZ_WIDGET_GONK)
   // In non-debug builds, enable the crash reporter by default, and allow
   // disabling it with the MOZ_CRASHREPORTER_DISABLE environment variable.
   // Also enable it by default in debug gonk builds as it is difficult to
   // set environment on startup.
   const char *envvar = PR_GetEnv("MOZ_CRASHREPORTER_DISABLE");
   if (envvar && *envvar && !force)
     return NS_OK;
@@ -1817,16 +1822,18 @@ nsresult SetExceptionHandler(nsIFile* aX
                                       library_mappings[i].file_offset);
   }
 #endif
 
   mozalloc_set_oom_abort_handler(AnnotateOOMAllocationSize);
 
   oldTerminateHandler = std::set_terminate(&TerminateHandler);
 
+  install_rust_panic_hook();
+
   InitThreadAnnotation();
 
   return NS_OK;
 }
 
 bool GetEnabled()
 {
   return gExceptionHandler != nullptr;
@@ -3775,16 +3782,18 @@ SetRemoteExceptionHandler(const nsACStri
 #ifdef _WIN64
   SetJitExceptionHandler();
 #endif
 
   mozalloc_set_oom_abort_handler(AnnotateOOMAllocationSize);
 
   oldTerminateHandler = std::set_terminate(&TerminateHandler);
 
+  install_rust_panic_hook();
+
   // we either do remote or nothing, no fallback to regular crash reporting
   return gExceptionHandler->IsOutOfProcess();
 }
 
 //--------------------------------------------------
 #elif defined(XP_LINUX)
 
 // Parent-side API for children
@@ -3822,16 +3831,18 @@ SetRemoteExceptionHandler()
                      true,       // install signal handlers
                      gMagicChildCrashReportFd);
   RunAndCleanUpDelayedNotes();
 
   mozalloc_set_oom_abort_handler(AnnotateOOMAllocationSize);
 
   oldTerminateHandler = std::set_terminate(&TerminateHandler);
 
+  install_rust_panic_hook();
+
   // we either do remote or nothing, no fallback to regular crash reporting
   return gExceptionHandler->IsOutOfProcess();
 }
 
 //--------------------------------------------------
 #elif defined(XP_MACOSX)
 // Child-side API
 bool
@@ -3851,16 +3862,18 @@ SetRemoteExceptionHandler(const nsACStri
                      true,       // install signal handlers
                      crashPipe.BeginReading());
   RunAndCleanUpDelayedNotes();
 
   mozalloc_set_oom_abort_handler(AnnotateOOMAllocationSize);
 
   oldTerminateHandler = std::set_terminate(&TerminateHandler);
 
+  install_rust_panic_hook();
+
   // we either do remote or nothing, no fallback to regular crash reporting
   return gExceptionHandler->IsOutOfProcess();
 }
 #endif  // XP_WIN
 
 
 bool
 TakeMinidumpForChild(uint32_t childPid, nsIFile** dump, uint32_t* aSequence)
--- a/toolkit/crashreporter/test/unit/head_crashreporter.js
+++ b/toolkit/crashreporter/test/unit/head_crashreporter.js
@@ -140,16 +140,23 @@ function handleMinidump(callback) {
   if (extrafile.exists()) {
     extrafile.remove(false);
   }
   if (memoryfile.exists()) {
     memoryfile.remove(false);
   }
 }
 
+/**
+ * Helper for testing a content process crash.
+ *
+ * This variant accepts a setup function which runs in the content process
+ * to set data as needed _before_ the crash.  The tail file triggers a generic
+ * crash after setup.
+ */
 function do_content_crash(setup, callback) {
   do_load_child_test_harness();
   do_test_pending();
 
   // Setting the minidump path won't work in the child, so we need to do
   // that here.
   let crashReporter =
       Components.classes["@mozilla.org/toolkit/crash-reporter;1"]
@@ -187,11 +194,60 @@ function do_content_crash(setup, callbac
         sendCommand("load(\"" + tailfile.path.replace(/\\/g, "/") + "\");", () =>
           do_execute_soon(handleCrash)
         )
       )
     );
   });
 }
 
+/**
+ * Helper for testing a content process crash.
+ *
+ * This variant accepts a trigger function which runs in the content process
+ * and does something to _trigger_ the crash.
+ */
+function do_triggered_content_crash(trigger, callback) {
+  do_load_child_test_harness();
+  do_test_pending();
+
+  // Setting the minidump path won't work in the child, so we need to do
+  // that here.
+  let crashReporter =
+      Components.classes["@mozilla.org/toolkit/crash-reporter;1"]
+                .getService(Components.interfaces.nsICrashReporter);
+  crashReporter.minidumpPath = do_get_tempdir();
+
+  /* import-globals-from ../unit/crasher_subprocess_head.js */
+
+  let headfile = do_get_file("../unit/crasher_subprocess_head.js");
+  if (trigger) {
+    if (typeof(trigger) == "function") {
+      // funky, but convenient
+      trigger = "(" + trigger.toSource() + ")();";
+    }
+  }
+
+  let handleCrash = function() {
+    let id = getMinidump().leafName.slice(0, -4);
+    Services.crashmanager.ensureCrashIsPresent(id).then(() => {
+      try {
+        handleMinidump(callback);
+      } catch (x) {
+        do_report_unexpected_exception(x);
+      }
+      do_test_finished();
+    });
+  };
+
+  do_get_profile();
+  makeFakeAppDir().then(() => {
+    sendCommand("load(\"" + headfile.path.replace(/\\/g, "/") + "\");", () =>
+      sendCommand(trigger, () =>
+        do_execute_soon(handleCrash)
+      )
+    );
+  });
+}
+
 // Import binary APIs via js-ctypes.
 Components.utils.import("resource://test/CrashTestUtils.jsm");
 Components.utils.import("resource://gre/modules/KeyValueParser.jsm");
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/test/unit_ipc/test_content_rust_panic.js
@@ -0,0 +1,21 @@
+/* import-globals-from ../unit/head_crashreporter.js */
+load("../unit/head_crashreporter.js");
+
+function run_test() {
+  if (!("@mozilla.org/toolkit/crash-reporter;1" in Components.classes)) {
+    dump("INFO | test_content_rust_panic.js | Can't test crashreporter in a non-libxul build.\n");
+    return;
+  }
+
+  // Try crashing with a Rust panic
+  do_triggered_content_crash(
+    function() {
+      Components.classes["@mozilla.org/xpcom/debug;1"]
+                .getService(Components.interfaces.nsIDebug2)
+                .rustPanic("OH NO");
+    },
+    function(mdump, extra) {
+      do_check_eq(extra.MozCrashReason, "OH NO");
+    }
+  );
+}
--- a/toolkit/crashreporter/test/unit_ipc/xpcshell.ini
+++ b/toolkit/crashreporter/test/unit_ipc/xpcshell.ini
@@ -7,8 +7,9 @@ support-files =
   !/toolkit/crashreporter/test/unit/head_crashreporter.js
 
 [test_content_annotation.js]
 [test_content_exception_time_annotation.js]
 [test_content_oom_annotation_windows.js]
 skip-if = os != 'win'
 [test_content_memory_list.js]
 skip-if = os != 'win'
+[test_content_rust_panic.js]
\ No newline at end of file