Bug 1379857 - Record Rust panics for child process crashes. r=dmajor, a=jcristau
authorJ. Ryan Stinnett <jryans@gmail.com>
Wed, 12 Jul 2017 14:41:19 -0500
changeset 414383 ed5cfbc47793cd5fa9e42efb3b99d427926ef386
parent 414382 2e92dd3f6f9530d98f3064428fef90d1a8cd28e0
child 414384 74382cf3e203d15085bc28f4467b6fbc469defb0
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdmajor, jcristau
bugs1379857
milestone55.0
Bug 1379857 - Record Rust panics for child process crashes. r=dmajor, a=jcristau 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
@@ -1382,17 +1382,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);
@@ -1585,18 +1592,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;
@@ -1810,16 +1815,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;
@@ -3747,16 +3754,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
@@ -3794,16 +3803,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
@@ -3823,16 +3834,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