Bug 1359326 - Run the minidump analyzer directly from the CrashService code; r=bsmedberg
authorGabriele Svelto <gsvelto@mozilla.com>
Thu, 11 May 2017 14:03:50 +0200
changeset 412268 001d49708a355c9b127fc338722959874cfc7552
parent 412267 e6f613b7ec08ca91240e1c28e687bad0ff38f2e4
child 412269 f4448d4bcf9828c099be8a6c40c28b48d1aa6698
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)
reviewersbsmedberg
bugs1359326, 1322611, 1280477
milestone55.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 1359326 - Run the minidump analyzer directly from the CrashService code; r=bsmedberg This patch removes the C++ code used to run the minidump analyzer when a content process crashes, and replaces it with JS code within the CrashService object. This removes the need for a separate shutdown blocker in C++ code and allows end-to-end testing of the crash service functionality. Additionally the exception handler code can be simplified since it's now only used to run the crash reporter client. The test added to test_crash_service.js covers computing the minidump SHA256 hash (bug 1322611) and of the minidump analyzer itself (bug 1280477). MozReview-Commit-ID: LO5w839NHev
browser/base/content/test/general/browser_restore_isAppTab.js
browser/base/content/test/plugins/browser_pluginCrashReportNonDeterminism.js
browser/base/content/test/social/social_crash_content_helper.js
dom/ipc/tests/process_error.xul
dom/ipc/tests/test_process_error.xul
dom/plugins/test/mochitest/hang_test.js
dom/plugins/test/mochitest/test_busy_hang.xul
dom/plugins/test/mochitest/test_crash_notify.xul
dom/plugins/test/mochitest/test_crash_notify_no_report.xul
dom/plugins/test/mochitest/test_hangui.xul
dom/plugins/test/mochitest/test_idle_hang.xul
ipc/glue/CrashReporterHost.cpp
netwerk/test/browser/browser_child_resource.js
netwerk/test/browser/dummy.html
toolkit/components/crashes/CrashManagerTest.jsm
toolkit/components/crashes/CrashService.js
toolkit/components/crashes/nsICrashService.idl
toolkit/components/crashes/tests/xpcshell/crash.dmp
toolkit/components/crashes/tests/xpcshell/crash.extra
toolkit/components/crashes/tests/xpcshell/test_crash_service.js
toolkit/components/crashes/tests/xpcshell/xpcshell.ini
toolkit/components/thumbnails/test/browser.ini
toolkit/components/thumbnails/test/browser_thumbnails_bg_crash_during_capture.js
toolkit/components/thumbnails/test/browser_thumbnails_bg_crash_while_idle.js
toolkit/components/thumbnails/test/head.js
toolkit/components/thumbnails/test/thumbnails_crash_content_helper.js
toolkit/content/tests/browser/browser_crash_previous_frameloader.js
toolkit/crashreporter/nsExceptionHandler.cpp
toolkit/crashreporter/nsExceptionHandler.h
toolkit/crashreporter/test/unit/head_crashreporter.js
--- a/browser/base/content/test/general/browser_restore_isAppTab.js
+++ b/browser/base/content/test/general/browser_restore_isAppTab.js
@@ -2,71 +2,20 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /* eslint-env mozilla/frame-script */
 
 const {TabStateFlusher} = Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
 
 const DUMMY = "http://example.com/browser/browser/base/content/test/general/dummy_page.html";
 
-function getMinidumpDirectory() {
-  let dir = Services.dirsvc.get("ProfD", Ci.nsIFile);
-  dir.append("minidumps");
-  return dir;
-}
-
-// This observer is needed so we can clean up all evidence of the crash so
-// the testrunner thinks things are peachy.
-var CrashObserver = {
-  observe(subject, topic, data) {
-    is(topic, "ipc:content-shutdown", "Received correct observer topic.");
-    ok(subject instanceof Ci.nsIPropertyBag2,
-       "Subject implements nsIPropertyBag2.");
-    // we might see this called as the process terminates due to previous tests.
-    // We are only looking for "abnormal" exits...
-    if (!subject.hasKey("abnormal")) {
-      info("This is a normal termination and isn't the one we are looking for...");
-      return;
-    }
-
-    let dumpID;
-    if ("nsICrashReporter" in Ci) {
-      dumpID = subject.getPropertyAsAString("dumpID");
-      ok(dumpID, "dumpID is present and not an empty string");
-    }
-
-    if (dumpID) {
-      let minidumpDirectory = getMinidumpDirectory();
-      let file = minidumpDirectory.clone();
-      file.append(dumpID + ".dmp");
-      file.remove(true);
-      file = minidumpDirectory.clone();
-      file.append(dumpID + ".extra");
-      file.remove(true);
-    }
-  }
-}
-Services.obs.addObserver(CrashObserver, "ipc:content-shutdown");
-
-registerCleanupFunction(() => {
-  Services.obs.removeObserver(CrashObserver, "ipc:content-shutdown");
-});
-
 function frameScript() {
   addMessageListener("Test:GetIsAppTab", function() {
     sendAsyncMessage("Test:IsAppTab", { isAppTab: docShell.isAppTab });
   });
-
-  addMessageListener("Test:Crash", function() {
-    privateNoteIntentionalCrash();
-    Components.utils.import("resource://gre/modules/ctypes.jsm");
-    let zero = new ctypes.intptr_t(8);
-    let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
-    badptr.contents
-  });
 }
 
 function loadFrameScript(browser) {
   browser.messageManager.loadFrameScript("data:,(" + frameScript.toString() + ")();", true);
 }
 
 function isBrowserAppTab(browser) {
   return new Promise(resolve => {
@@ -87,18 +36,17 @@ function isBrowserAppTab(browser) {
 var restart = async function(browser) {
   // If the tab isn't remote this would crash the main process so skip it
   if (!browser.isRemoteBrowser)
     return;
 
   // Make sure the main process has all of the current tab state before crashing
   await TabStateFlusher.flush(browser);
 
-  browser.messageManager.sendAsyncMessage("Test:Crash");
-  await promiseWaitForEvent(browser, "AboutTabCrashedLoad", false, true);
+  await BrowserTestUtils.crashBrowser(browser);
 
   let tab = gBrowser.getTabForBrowser(browser);
   SessionStore.reviveCrashedTab(tab);
 
   await promiseTabLoaded(tab);
 };
 
 add_task(async function navigate() {
--- a/browser/base/content/test/plugins/browser_pluginCrashReportNonDeterminism.js
+++ b/browser/base/content/test/plugins/browser_pluginCrashReportNonDeterminism.js
@@ -88,30 +88,32 @@ add_task(async function setup() {
   let crashObserver = (subject, topic, data) => {
     if (topic != "plugin-crashed") {
       return;
     }
 
     let propBag = subject.QueryInterface(Ci.nsIPropertyBag2);
     let minidumpID = propBag.getPropertyAsAString("pluginDumpID");
 
-    let minidumpDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
-    minidumpDir.append("minidumps");
+    Services.crashmanager.ensureCrashIsPresent(minidumpID).then(() => {
+      let minidumpDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
+      minidumpDir.append("minidumps");
 
-    let pluginDumpFile = minidumpDir.clone();
-    pluginDumpFile.append(minidumpID + ".dmp");
+      let pluginDumpFile = minidumpDir.clone();
+      pluginDumpFile.append(minidumpID + ".dmp");
 
-    let extraFile = minidumpDir.clone();
-    extraFile.append(minidumpID + ".extra");
+      let extraFile = minidumpDir.clone();
+      extraFile.append(minidumpID + ".extra");
 
-    ok(pluginDumpFile.exists(), "Found minidump");
-    ok(extraFile.exists(), "Found extra file");
+      ok(pluginDumpFile.exists(), "Found minidump");
+      ok(extraFile.exists(), "Found extra file");
 
-    pluginDumpFile.remove(false);
-    extraFile.remove(false);
+      pluginDumpFile.remove(false);
+      extraFile.remove(false);
+    });
   };
 
   Services.obs.addObserver(crashObserver, "plugin-crashed");
   // plugins.testmode will make BrowserPlugins:Test:ClearCrashData work.
   Services.prefs.setBoolPref("plugins.testmode", true);
   registerCleanupFunction(() => {
     Services.prefs.clearUserPref("plugins.testmode");
     Services.obs.removeObserver(crashObserver, "plugin-crashed");
deleted file mode 100644
--- a/browser/base/content/test/social/social_crash_content_helper.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-* http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/* eslint-env mozilla/frame-script */
-
-var Cu = Components.utils;
-
-// Ideally we would use CrashTestUtils.jsm, but that's only available for
-// xpcshell tests - so we just copy a ctypes crasher from it.
-Cu.import("resource://gre/modules/ctypes.jsm");
-var crash = function() { // this will crash when called.
-  let zero = new ctypes.intptr_t(8);
-  let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
-  badptr.contents
-};
-
-
-var TestHelper = {
-  init() {
-    addMessageListener("social-test:crash", this);
-  },
-
-  receiveMessage(msg) {
-    switch (msg.name) {
-      case "social-test:crash":
-        privateNoteIntentionalCrash();
-        crash();
-      break;
-    }
-  },
-}
-
-TestHelper.init();
--- a/dom/ipc/tests/process_error.xul
+++ b/dom/ipc/tests/process_error.xul
@@ -8,54 +8,33 @@
   <script type="application/javascript"><![CDATA[
     Components.utils.import("resource://gre/modules/Services.jsm");
 
     const ok = window.opener.wrappedJSObject.ok;
     const is = window.opener.wrappedJSObject.is;
     const done = window.opener.wrappedJSObject.done;
     const SimpleTest = window.opener.wrappedJSObject.SimpleTest;
 
-    function getMinidumpDirectory() {
-      var dir = Services.dirsvc.get('ProfD', Components.interfaces.nsIFile);
-      dir.append("minidumps");
-      return dir;
-    }
-
-    function removeFile(directory, filename) {
-      var file = directory.clone();
-      file.append(filename);
-      if (file.exists()) {
-        file.remove(false);
-      }
-    }
-
     function crashObserver(subject, topic, data) {
       is(topic, 'ipc:content-shutdown', 'Received correct observer topic.');
       ok(subject instanceof Components.interfaces.nsIPropertyBag2,
          'Subject implements nsIPropertyBag2.');
 
+      var waitCrash = Promise.resolve();
       var dumpID;
       if ('nsICrashReporter' in Components.interfaces) {
         dumpID = subject.getPropertyAsAString('dumpID');
         ok(dumpID, "dumpID is present and not an empty string");
-      }
-
-      let p = Promise.resolve();
-
-      if (dumpID) {
-        var minidumpDirectory = getMinidumpDirectory();
-        p = Services.crashmanager.ensureCrashIsPresent(dumpID).then(() => {
-          removeFile(minidumpDirectory, dumpID + '.dmp');
-          removeFile(minidumpDirectory, dumpID + '.extra');
-        });
+        waitCrash = Services.crashmanager.ensureCrashIsPresent(dumpID);
       }
 
       Services.obs.removeObserver(crashObserver, 'ipc:content-shutdown');
-      p.then(done);
+      waitCrash.then(done);
     }
+
     Services.obs.addObserver(crashObserver, 'ipc:content-shutdown');
 
     document.getElementById('thebrowser')
             .QueryInterface(Components.interfaces.nsIFrameLoaderOwner)
             .frameLoader.messageManager
             .loadFrameScript('chrome://mochitests/content/chrome/dom/ipc/tests/process_error_contentscript.js', true);
   ]]></script>
 
--- a/dom/ipc/tests/test_process_error.xul
+++ b/dom/ipc/tests/test_process_error.xul
@@ -3,16 +3,17 @@
 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script type="application/javascript"
 	  src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
 
   <script>
   SimpleTest.waitForExplicitFinish();
+  SimpleTest.expectChildProcessCrash();
 
   var w = window.open('process_error.xul', '_blank', 'chrome,resizable=yes,width=400,height=600');
 
   function done()
   {
     w.close();
     SimpleTest.finish();
   }
--- a/dom/plugins/test/mochitest/hang_test.js
+++ b/dom/plugins/test/mochitest/hang_test.js
@@ -60,23 +60,16 @@ var testObserver = {
       ok(cpuUsage == 0, "plugin cpu usage is 0%");
     } else {
       ok(cpuUsage > 0, "plugin cpu usage is >0%");
     }
 
     // check processor count field
     ok("NumberOfProcessors" in extraData, "got extra field for processor count");
     ok(parseInt(extraData["NumberOfProcessors"]) > 0, "number of processors is >0");
-
-    // cleanup, to be nice
-    pluginDumpFile.remove(false);
-    pluginExtraFile.remove(false);
-    for (let file of additionalDumpFiles) {
-      file.remove(false);
-    }
   },
 
   QueryInterface: function(iid) {
     if (iid.equals(Ci.nsIObserver) ||
         iid.equals(Ci.nsISupportsWeakReference) ||
         iid.equals(Ci.nsISupports))
       return this;
     throw Components.results.NS_NOINTERFACE;
@@ -105,10 +98,12 @@ function onPluginCrashed(aEvent) {
   // allow either true or false here.
   ok("submittedCrashReport" in aEvent, "submittedCrashReport is a property of event");
   is(typeof aEvent.submittedCrashReport, "boolean", "submittedCrashReport is correct type");
 
   var os = Cc["@mozilla.org/observer-service;1"].
            getService(Ci.nsIObserverService);
   os.removeObserver(testObserver, "plugin-crashed");
 
-  SimpleTest.finish();
+  Services.crashmanager.ensureCrashIsPresent(aEvent.pluginDumpID).then(() => {
+    SimpleTest.finish();
+  });
 }
--- a/dom/plugins/test/mochitest/test_busy_hang.xul
+++ b/dom/plugins/test/mochitest/test_busy_hang.xul
@@ -14,17 +14,20 @@
   <script type="application/javascript">
     setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
   </script>
   <body xmlns="http://www.w3.org/1999/xhtml" onload="runTests()">
     <embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
   </body>
   <script class="testbody" type="application/javascript">
     <![CDATA[
+Components.utils.import("resource://gre/modules/Services.jsm");
+
 SimpleTest.waitForExplicitFinish();
+SimpleTest.expectChildProcessCrash();
 
 function runTests() {
   // Default plugin hang timeout is too high for mochitests
   var prefs = Cc["@mozilla.org/preferences-service;1"]
                     .getService(Ci.nsIPrefBranch);
   var timeoutPref = "dom.ipc.plugins.timeoutSecs";
   prefs.setIntPref(timeoutPref, 5);
 
--- a/dom/plugins/test/mochitest/test_crash_notify.xul
+++ b/dom/plugins/test/mochitest/test_crash_notify.xul
@@ -10,17 +10,20 @@
   <script type="application/javascript">
     setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
   </script>
 <body xmlns="http://www.w3.org/1999/xhtml" onload="runTests()">
 <embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
 </body>
 <script class="testbody" type="application/javascript">
 <![CDATA[
+Components.utils.import("resource://gre/modules/Services.jsm");
+
 SimpleTest.waitForExplicitFinish();
+SimpleTest.expectChildProcessCrash();
 
 var success = false;
 
 var observerFired = false;
 
 var testObserver = {
   observe: function(subject, topic, data) {
     observerFired = true;
@@ -38,19 +41,16 @@ var testObserver = {
     let profD = directoryService.get("ProfD", Components.interfaces.nsIFile);
     profD.append("minidumps");
     let dumpFile = profD.clone();
     dumpFile.append(id + ".dmp");
     ok(dumpFile.exists(), "minidump exists");
     let extraFile = profD.clone();
     extraFile.append(id + ".extra");
     ok(extraFile.exists(), "extra file exists");
-    // cleanup, to be nice
-    dumpFile.remove(false);
-    extraFile.remove(false);
   },
 
   QueryInterface: function(iid) {
     if (iid.equals(Components.interfaces.nsIObserver) ||
         iid.equals(Components.interfaces.nsISupportsWeakReference) ||
         iid.equals(Components.interfaces.nsISupports))
       return this;
     throw Components.results.NS_NOINTERFACE;
@@ -79,17 +79,19 @@ function onPluginCrashed(aEvent) {
   // allow either true or false here.
   ok("submittedCrashReport" in aEvent, "submittedCrashReport is a property of event");
   is(typeof aEvent.submittedCrashReport, "boolean", "submittedCrashReport is correct type");
 
   var os = Components.classes["@mozilla.org/observer-service;1"].
            getService(Components.interfaces.nsIObserverService);
   os.removeObserver(testObserver, "plugin-crashed");
 
-  SimpleTest.finish();
+  Services.crashmanager.ensureCrashIsPresent(aEvent.pluginDumpID).then(() => {
+    SimpleTest.finish();
+  });
 }
 
 function runTests() {
   var os = Components.classes["@mozilla.org/observer-service;1"].
            getService(Components.interfaces.nsIObserverService);
   os.addObserver(testObserver, "plugin-crashed", true);
 
   document.addEventListener("PluginCrashed", onPluginCrashed, false);
--- a/dom/plugins/test/mochitest/test_crash_notify_no_report.xul
+++ b/dom/plugins/test/mochitest/test_crash_notify_no_report.xul
@@ -10,16 +10,18 @@
   <script type="application/javascript">
     setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
   </script>
 <body xmlns="http://www.w3.org/1999/xhtml" onload="runTests()">
 <embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
 </body>
 <script class="testbody" type="application/javascript">
 <![CDATA[
+Components.utils.import("resource://gre/modules/Services.jsm");
+
 SimpleTest.waitForExplicitFinish();
 
 var success = false;
 
 var observerFired = false;
 
 var testObserver = {
   observe: function(subject, topic, data) {
@@ -35,25 +37,27 @@ var testObserver = {
     isnot(id, "", "got a non-empty crash id");
     let directoryService =
       Components.classes["@mozilla.org/file/directory_service;1"].
       getService(Components.interfaces.nsIProperties);
     let pendingD = directoryService.get("UAppData",
                                         Components.interfaces.nsIFile);
     pendingD.append("Crash Reports");
     pendingD.append("pending");
-    let dumpFile = pendingD.clone();    
+    let dumpFile = pendingD.clone();
     dumpFile.append(id + ".dmp");
     ok(dumpFile.exists(), "minidump exists");
     let extraFile = pendingD.clone();
     extraFile.append(id + ".extra");
     ok(extraFile.exists(), "extra file exists");
-    // cleanup, to be nice
-    dumpFile.remove(false);
-    extraFile.remove(false);
+    Services.crashmanager.ensureCrashIsPresent(id).then(() => {
+      // cleanup, to be nice, this will execute before SimpleTest.finish()
+      dumpFile.remove(false);
+      extraFile.remove(false);
+    });
   },
 
   QueryInterface: function(iid) {
     if (iid.equals(Components.interfaces.nsIObserver) ||
         iid.equals(Components.interfaces.nsISupportsWeakReference) ||
         iid.equals(Components.interfaces.nsISupports))
       return this;
     throw Components.results.NS_NOINTERFACE;
@@ -82,17 +86,19 @@ function onPluginCrashed(aEvent) {
   var os = Components.classes["@mozilla.org/observer-service;1"].
            getService(Components.interfaces.nsIObserverService);
   os.removeObserver(testObserver, "plugin-crashed");
 
   // re-set MOZ_CRASHREPORTER_NO_REPORT
   let env = Components.classes["@mozilla.org/process/environment;1"]
                       .getService(Components.interfaces.nsIEnvironment);
   env.set("MOZ_CRASHREPORTER_NO_REPORT", "1");
-  SimpleTest.finish();
+  Services.crashmanager.ensureCrashIsPresent(aEvent.pluginDumpID).then(() => {
+    SimpleTest.finish();
+  });
 }
 
 function runTests() {
   // the test harness will have set MOZ_CRASHREPORTER_NO_REPORT,
   // ensure that we can change the setting and have our minidumps
   // wind up in Crash Reports/pending
   let env = Components.classes["@mozilla.org/process/environment;1"]
                       .getService(Components.interfaces.nsIEnvironment);
--- a/dom/plugins/test/mochitest/test_hangui.xul
+++ b/dom/plugins/test/mochitest/test_hangui.xul
@@ -16,16 +16,17 @@
           src="http://mochi.test:8888/chrome/dom/plugins/test/mochitest/hangui_common.js" />
 
 <body xmlns="http://www.w3.org/1999/xhtml">
   <iframe id="iframe1" src="hangui_subpage.html" width="400" height="400"></iframe>
 </body>
 <script class="testbody" type="application/javascript">
 <![CDATA[
 SimpleTest.waitForExplicitFinish();
+SimpleTest.expectChildProcessCrash();
 setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 const hangUITimeoutPref = "dom.ipc.plugins.hangUITimeoutSecs";
 const hangUIMinDisplayPref = "dom.ipc.plugins.hangUIMinDisplaySecs";
 const timeoutPref = "dom.ipc.plugins.timeoutSecs";
 
--- a/dom/plugins/test/mochitest/test_idle_hang.xul
+++ b/dom/plugins/test/mochitest/test_idle_hang.xul
@@ -14,17 +14,20 @@
   <script type="application/javascript">
     getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_ENABLED;
   </script>
 <body xmlns="http://www.w3.org/1999/xhtml" onload="runTests()">
 <embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
 </body>
 <script class="testbody" type="application/javascript">
 <![CDATA[
+Components.utils.import("resource://gre/modules/Services.jsm");
+
 SimpleTest.waitForExplicitFinish();
+SimpleTest.expectChildProcessCrash();
 
 function runTests() {
   // Default plugin hang timeout is too high for mochitests
   var prefs = Cc["@mozilla.org/preferences-service;1"]
                     .getService(Ci.nsIPrefBranch);
   var timeoutPref = "dom.ipc.plugins.timeoutSecs";
   prefs.setIntPref(timeoutPref, 5);
 
--- a/ipc/glue/CrashReporterHost.cpp
+++ b/ipc/glue/CrashReporterHost.cpp
@@ -1,22 +1,20 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CrashReporterHost.h"
 #include "CrashReporterMetadataShmem.h"
-#include "mozilla/LazyIdleThread.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/SyncRunnable.h"
 #include "mozilla/Telemetry.h"
 #ifdef MOZ_CRASHREPORTER
-# include "nsIAsyncShutdown.h"
 # include "nsICrashService.h"
 #endif
 
 namespace mozilla {
 namespace ipc {
 
 CrashReporterHost::CrashReporterHost(GeckoProcessType aProcessType,
                                      const Shmem& aShmem,
@@ -100,177 +98,38 @@ CrashReporterHost::FinalizeCrashReport()
   // Use mExtraNotes, since NotifyCrashService looks for "PluginHang" which is
   // set in the parent process.
   NotifyCrashService(mProcessType, mDumpID, &mExtraNotes);
 
   mFinalized = true;
   return true;
 }
 
-/**
- * Runnable used to execute the minidump analyzer program asynchronously after
- * a crash. This should run on a background thread not to block the main thread
- * over the potentially long minidump analysis. Once analysis is over, the
- * crash information will be relayed to the crash manager via another runnable
- * sent to the main thread. Shutdown will be blocked for the duration of the
- * entire process to ensure this information is sent.
- */
-class AsyncMinidumpAnalyzer final : public nsIRunnable
-                                  , public nsIAsyncShutdownBlocker
-{
-public:
-  /**
-   * Create a new minidump analyzer runnable, this will also block shutdown
-   * until the associated crash has been added to the crash manager.
-   */
-  AsyncMinidumpAnalyzer(int32_t aProcessType,
-                        int32_t aCrashType,
-                        const nsString& aChildDumpID)
-    : mProcessType(aProcessType)
-    , mCrashType(aCrashType)
-    , mChildDumpID(aChildDumpID)
-    , mName(NS_LITERAL_STRING("Crash reporter: blocking on minidump analysis"))
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    nsresult rv = GetShutdownBarrier()->AddBlocker(
-      this, NS_LITERAL_STRING(__FILE__), __LINE__,
-      NS_LITERAL_STRING("Minidump analysis"));
-    Unused << NS_WARN_IF(NS_FAILED(rv));
-  }
-
-  NS_IMETHOD Run() override
-  {
-    MOZ_ASSERT(!NS_IsMainThread());
-
-    if (mProcessType == nsICrashService::PROCESS_TYPE_CONTENT ||
-        mProcessType == nsICrashService::PROCESS_TYPE_GPU) {
-      CrashReporter::RunMinidumpAnalyzer(mChildDumpID);
-    }
-
-    // Make a copy of these so we can copy them into the runnable lambda
-    int32_t processType = mProcessType;
-    int32_t crashType = mCrashType;
-    nsString childDumpID(mChildDumpID);
-    nsCOMPtr<nsIAsyncShutdownBlocker> self(this);
-
-    NS_DispatchToMainThread(NS_NewRunnableFunction([
-      self, processType, crashType, childDumpID
-    ] {
-      nsCOMPtr<nsICrashService> crashService =
-        do_GetService("@mozilla.org/crashservice;1");
-      if (crashService) {
-        crashService->AddCrash(processType, crashType, childDumpID);
-      }
-
-      nsCOMPtr<nsIAsyncShutdownClient> barrier = GetShutdownBarrier();
-
-      if (barrier) {
-        barrier->RemoveBlocker(self);
-      }
-    }));
-
-    return NS_OK;
-  }
-
-  NS_IMETHOD BlockShutdown(nsIAsyncShutdownClient* aBarrierClient) override
-  {
-    return NS_OK;
-  }
-
-  NS_IMETHOD GetName(nsAString& aName) override
-  {
-    aName = mName;
-    return NS_OK;
-  }
-
-  NS_IMETHOD GetState(nsIPropertyBag**) override
-  {
-    return NS_OK;
-  }
-
-  NS_DECL_THREADSAFE_ISUPPORTS
-
-private:
-  ~AsyncMinidumpAnalyzer() {}
-
-  // Returns the profile-before-change shutdown barrier
-  static nsCOMPtr<nsIAsyncShutdownClient> GetShutdownBarrier()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdown();
-    nsCOMPtr<nsIAsyncShutdownClient> barrier;
-    nsresult rv = svc->GetProfileBeforeChange(getter_AddRefs(barrier));
-
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return nullptr;
-    }
-
-    return barrier.forget();
-  }
-
-  int32_t mProcessType;
-  int32_t mCrashType;
-  const nsString mChildDumpID;
-  const nsString mName;
-};
-
-NS_IMPL_ISUPPORTS(AsyncMinidumpAnalyzer, nsIRunnable, nsIAsyncShutdownBlocker)
-
-/**
- * Add information about a crash to the crash manager. This method runs the
- * various activities required to gather additional information and notify the
- * crash manager asynchronously, since many of them involve I/O or potentially
- * long processing.
- *
- * @param aProcessType The type of process that crashed
- * @param aCrashType The type of crash (crash or hang)
- * @param aChildDumpID A string holding the ID of the dump associated with this
- *        crash
- */
-/* static */ void
-CrashReporterHost::AsyncAddCrash(int32_t aProcessType,
-                                 int32_t aCrashType,
-                                 const nsString& aChildDumpID)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  static StaticRefPtr<LazyIdleThread> sBackgroundThread;
-
-  if (!sBackgroundThread) {
-    // Background thread used for running minidump analysis. It will be
-    // destroyed immediately after it's done with the task.
-    sBackgroundThread =
-      new LazyIdleThread(0, NS_LITERAL_CSTRING("CrashReporterHost"));
-    ClearOnShutdown(&sBackgroundThread);
-  }
-
-  RefPtr<AsyncMinidumpAnalyzer> task =
-    new AsyncMinidumpAnalyzer(aProcessType, aCrashType, aChildDumpID);
-
-  Unused << sBackgroundThread->Dispatch(task, NS_DISPATCH_NORMAL);
-}
-
 /* static */ void
 CrashReporterHost::NotifyCrashService(GeckoProcessType aProcessType,
                                       const nsString& aChildDumpID,
                                       const AnnotationTable* aNotes)
 {
   if (!NS_IsMainThread()) {
     RefPtr<Runnable> runnable = NS_NewRunnableFunction([=] () -> void {
       CrashReporterHost::NotifyCrashService(aProcessType, aChildDumpID, aNotes);
     });
     RefPtr<nsIThread> mainThread = do_GetMainThread();
     SyncRunnable::DispatchToThread(mainThread, runnable);
     return;
   }
 
   MOZ_ASSERT(!aChildDumpID.IsEmpty());
 
+  nsCOMPtr<nsICrashService> crashService =
+    do_GetService("@mozilla.org/crashservice;1");
+  if (!crashService) {
+    return;
+  }
+
   int32_t processType;
   int32_t crashType = nsICrashService::CRASH_TYPE_CRASH;
 
   nsCString telemetryKey;
 
   switch (aProcessType) {
     case GeckoProcessType_Content:
       processType = nsICrashService::PROCESS_TYPE_CONTENT;
@@ -295,17 +154,18 @@ CrashReporterHost::NotifyCrashService(Ge
       processType = nsICrashService::PROCESS_TYPE_GPU;
       telemetryKey.AssignLiteral("gpu");
       break;
     default:
       NS_ERROR("unknown process type");
       return;
   }
 
-  AsyncAddCrash(processType, crashType, aChildDumpID);
+  nsCOMPtr<nsISupports> promise;
+  crashService->AddCrash(processType, crashType, aChildDumpID, getter_AddRefs(promise));
   Telemetry::Accumulate(Telemetry::SUBPROCESS_CRASHES_WITH_DUMP, telemetryKey, 1);
 }
 
 void
 CrashReporterHost::AddNote(const nsCString& aKey, const nsCString& aValue)
 {
   mExtraNotes.Put(aKey, aValue);
 }
--- a/netwerk/test/browser/browser_child_resource.js
+++ b/netwerk/test/browser/browser_child_resource.js
@@ -7,83 +7,31 @@ http://creativecommons.org/publicdomain/
 const TEST_URL = "http://example.com/browser/netwerk/test/browser/dummy.html";
 
 const expectedRemote = gMultiProcessBrowser ? "true" : "";
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 const resProtocol = Cc["@mozilla.org/network/protocol;1?name=resource"]
                         .getService(Ci.nsIResProtocolHandler);
 
-function getMinidumpDirectory() {
-  var dir = Services.dirsvc.get('ProfD', Ci.nsIFile);
-  dir.append("minidumps");
-  return dir;
-}
-
-// This observer is needed so we can clean up all evidence of the crash so
-// the testrunner thinks things are peachy.
-var CrashObserver = {
-  observe: function(subject, topic, data) {
-    is(topic, 'ipc:content-shutdown', 'Received correct observer topic.');
-    ok(subject instanceof Ci.nsIPropertyBag2,
-       'Subject implements nsIPropertyBag2.');
-    // we might see this called as the process terminates due to previous tests.
-    // We are only looking for "abnormal" exits...
-    if (!subject.hasKey("abnormal")) {
-      info("This is a normal termination and isn't the one we are looking for...");
-      return;
-    }
-
-    var dumpID;
-    if ('nsICrashReporter' in Ci) {
-      dumpID = subject.getPropertyAsAString('dumpID');
-      ok(dumpID, "dumpID is present and not an empty string");
-    }
-
-    if (dumpID) {
-      var minidumpDirectory = getMinidumpDirectory();
-      let file = minidumpDirectory.clone();
-      file.append(dumpID + '.dmp');
-      file.remove(true);
-      file = minidumpDirectory.clone();
-      file.append(dumpID + '.extra');
-      file.remove(true);
-    }
-  }
-}
-Services.obs.addObserver(CrashObserver, 'ipc:content-shutdown');
-
-registerCleanupFunction(() => {
-  Services.obs.removeObserver(CrashObserver, 'ipc:content-shutdown');
-});
-
 function frameScript() {
   Components.utils.import("resource://gre/modules/Services.jsm");
   let resProtocol = Components.classes["@mozilla.org/network/protocol;1?name=resource"]
                               .getService(Components.interfaces.nsIResProtocolHandler);
 
   addMessageListener("Test:ResolveURI", function({ data: uri }) {
     uri = Services.io.newURI(uri);
     try {
       let resolved = resProtocol.resolveURI(uri);
       sendAsyncMessage("Test:ResolvedURI", resolved);
     }
     catch (e) {
       sendAsyncMessage("Test:ResolvedURI", null);
     }
   });
-
-  addMessageListener("Test:Crash", function() {
-    dump("Crashing\n");
-    privateNoteIntentionalCrash();
-    Components.utils.import("resource://gre/modules/ctypes.jsm");
-    let zero = new ctypes.intptr_t(8);
-    let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
-    badptr.contents
-  });
 }
 
 function waitForEvent(obj, name, capturing, chromeEvent) {
   info("Waiting for " + name);
   return new Promise((resolve) => {
     function listener(event) {
       info("Saw " + name);
       obj.removeEventListener(name, listener, capturing, chromeEvent);
@@ -128,18 +76,17 @@ var loadTestTab = Task.async(function*()
 
 // Restarts the child process by crashing it then reloading the tab
 var restart = Task.async(function*() {
   let browser = gBrowser.selectedBrowser;
   // If the tab isn't remote this would crash the main process so skip it
   if (browser.getAttribute("remote") != "true")
     return browser;
 
-  browser.messageManager.sendAsyncMessage("Test:Crash");
-  yield waitForEvent(browser, "AboutTabCrashedLoad", false, true);
+  yield BrowserTestUtils.crashBrowser(browser);
 
   browser.reload();
 
   yield BrowserTestUtils.browserLoaded(browser);
   is(browser.getAttribute("remote"), expectedRemote, "Browser should be in the right process");
   browser.messageManager.loadFrameScript("data:,(" + frameScript.toString() + ")();", true);
   return browser;
 });
--- a/netwerk/test/browser/dummy.html
+++ b/netwerk/test/browser/dummy.html
@@ -1,7 +1,11 @@
 <!DOCTYPE html>
 
+<head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8">
+</head>
+
 <html>
 <body>
   <p>Dummy Page</p>
 </body>
 </html>
--- a/toolkit/components/crashes/CrashManagerTest.jsm
+++ b/toolkit/components/crashes/CrashManagerTest.jsm
@@ -173,13 +173,14 @@ this.getManager = function() {
     // paths are triggered.
     let storeD = await makeDir(false);
 
     let m = new TestingCrashManager({
       pendingDumpsDir: pendingD,
       submittedDumpsDir: submittedD,
       eventsDirs: [eventsD1, eventsD2],
       storeDir: storeD,
+      telemetryStoreSizeKey: "CRASH_STORE_COMPRESSED_BYTES",
     });
 
     return m;
   })();
 };
--- a/toolkit/components/crashes/CrashService.js
+++ b/toolkit/components/crashes/CrashService.js
@@ -1,85 +1,109 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
+Cu.import("resource://gre/modules/AppConstants.jsm", this);
 Cu.import("resource://gre/modules/AsyncShutdown.jsm", this);
 Cu.import("resource://gre/modules/KeyValueParser.jsm");
 Cu.import("resource://gre/modules/osfile.jsm", this);
 Cu.import("resource://gre/modules/Promise.jsm", this);
 Cu.import("resource://gre/modules/Services.jsm", this);
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 
 /**
- * Computes the SHA256 hash of the minidump file associated with a crash
+ * Run the minidump analyzer tool to gather stack traces from the minidump. The
+ * stack traces will be stored in the .extra file under the StackTraces= entry.
+ *
+ * @param minidumpPath {string} The path to the minidump file
  *
- * @param crashID {string} Crash ID. Likely a UUID.
+ * @returns {Promise} A promise that gets resolved once minidump analysis has
+ *          finished.
+ */
+function runMinidumpAnalyzer(minidumpPath) {
+  return new Promise((resolve, reject) => {
+    const binSuffix = AppConstants.platform === "win" ? ".exe" : "";
+    const exeName = "minidump-analyzer" + binSuffix;
+
+    let exe = Services.dirsvc.get("GreBinD", Ci.nsIFile);
+
+    if (AppConstants.platform === "macosx") {
+        exe.append("crashreporter.app");
+        exe.append("Contents");
+        exe.append("MacOS");
+    }
+
+    exe.append(exeName);
+
+    let args = [ minidumpPath ];
+    let process = Cc["@mozilla.org/process/util;1"]
+                    .createInstance(Ci.nsIProcess);
+    process.init(exe);
+    process.startHidden = true;
+    process.runAsync(args, args.length, (subject, topic, data) => {
+      switch (topic) {
+        case "process-finished":
+          resolve();
+          break;
+        default:
+          reject(topic);
+          break;
+      }
+    });
+  });
+}
+
+/**
+ * Computes the SHA256 hash of a minidump file
+ *
+ * @param minidumpPath {string} The path to the minidump file
  *
  * @returns {Promise} A promise that resolves to the hash value of the
- *          minidump. If the hash could not be computed then null is returned
- *          instead.
+ *          minidump.
  */
-function computeMinidumpHash(id) {
-  let cr = Cc["@mozilla.org/toolkit/crash-reporter;1"]
-             .getService(Components.interfaces.nsICrashReporter);
-
+function computeMinidumpHash(minidumpPath) {
   return (async function() {
-    try {
-      let minidumpFile = cr.getMinidumpForID(id);
-      let minidumpData = await OS.File.read(minidumpFile.path);
-      let hasher = Cc["@mozilla.org/security/hash;1"]
-                     .createInstance(Ci.nsICryptoHash);
-      hasher.init(hasher.SHA256);
-      hasher.update(minidumpData, minidumpData.length);
-
-      let hashBin = hasher.finish(false);
-      let hash = "";
+    let minidumpData = await OS.File.read(minidumpPath);
+    let hasher = Cc["@mozilla.org/security/hash;1"]
+                   .createInstance(Ci.nsICryptoHash);
+    hasher.init(hasher.SHA256);
+    hasher.update(minidumpData, minidumpData.length);
 
-      for (let i = 0; i < hashBin.length; i++) {
-        // Every character in the hash string contains a byte of the hash data
-        hash += ("0" + hashBin.charCodeAt(i).toString(16)).slice(-2);
-      }
+    let hashBin = hasher.finish(false);
+    let hash = "";
 
-      return hash;
-    } catch (e) {
-      Cu.reportError(e);
-      return null;
+    for (let i = 0; i < hashBin.length; i++) {
+      // Every character in the hash string contains a byte of the hash data
+      hash += ("0" + hashBin.charCodeAt(i).toString(16)).slice(-2);
     }
+
+    return hash;
   })();
 }
 
 /**
- * Process the .extra file associated with the crash id and return the
- * annotations it contains in an object.
+ * Process the given .extra file and return the annotations it contains in an
+ * object.
  *
- * @param crashID {string} Crash ID. Likely a UUID.
+ * @param extraPath {string} The path to the .extra file
  *
  * @return {Promise} A promise that resolves to an object holding the crash
- *         annotations, this object may be empty if no annotations were found.
+ *         annotations.
  */
-function processExtraFile(id) {
-  let cr = Cc["@mozilla.org/toolkit/crash-reporter;1"]
-             .getService(Components.interfaces.nsICrashReporter);
-
+function processExtraFile(extraPath) {
   return (async function() {
-    try {
-      let extraFile = cr.getExtraFileForID(id);
-      let decoder = new TextDecoder();
-      let extraData = await OS.File.read(extraFile.path);
+    let decoder = new TextDecoder();
+    let extraData = await OS.File.read(extraPath);
 
-      return parseKeyValuePairs(decoder.decode(extraData));
-    } catch (e) {
-      Cu.reportError(e);
-      return {};
-    }
+    return parseKeyValuePairs(decoder.decode(extraData));
   })();
 }
 
 /**
  * This component makes crash data available throughout the application.
  *
  * It is a service because some background activity will eventually occur.
  */
@@ -120,32 +144,47 @@ CrashService.prototype = Object.freeze({
     case Ci.nsICrashService.CRASH_TYPE_HANG:
       crashType = Services.crashmanager.CRASH_TYPE_HANG;
       break;
     default:
       throw new Error("Unrecognized CRASH_TYPE: " + crashType);
     }
 
     let blocker = (async function() {
-      let metadata = await processExtraFile(id);
-      let hash = await computeMinidumpHash(id);
+      let metadata = {};
+      let hash = null;
+
+      try {
+        let cr = Cc["@mozilla.org/toolkit/crash-reporter;1"]
+                   .getService(Components.interfaces.nsICrashReporter);
+        let minidumpPath = cr.getMinidumpForID(id).path;
+        let extraPath = cr.getExtraFileForID(id).path;
+
+        await runMinidumpAnalyzer(minidumpPath);
+        metadata = await processExtraFile(extraPath);
+        hash = await computeMinidumpHash(minidumpPath);
+      } catch (e) {
+        Cu.reportError(e);
+      }
 
       if (hash) {
         metadata.MinidumpSha256Hash = hash;
       }
 
       await Services.crashmanager.addCrash(processType, crashType, id,
                                            new Date(), metadata);
     })();
 
     AsyncShutdown.profileBeforeChange.addBlocker(
       "CrashService waiting for content crash ping to be sent", blocker
     );
 
     blocker.then(AsyncShutdown.profileBeforeChange.removeBlocker(blocker));
+
+    return blocker;
   },
 
   observe(subject, topic, data) {
     switch (topic) {
       case "profile-after-change":
         // Side-effect is the singleton is instantiated.
         Services.crashmanager;
         break;
--- a/toolkit/components/crashes/nsICrashService.idl
+++ b/toolkit/components/crashes/nsICrashService.idl
@@ -14,17 +14,17 @@ interface nsICrashService : nsISupports
    *        One of the PROCESS_TYPE constants defined below.
    * @param crashType
    *        One of the CRASH_TYPE constants defined below.
    * @param id
    *        Crash ID. Likely a UUID.
    *
    * @return {Promise} A promise that resolves after the crash has been stored
    */
-  void addCrash(in long processType, in long crashType, in AString id);
+  nsISupports addCrash(in long processType, in long crashType, in AString id);
 
   const long PROCESS_TYPE_MAIN = 0;
   const long PROCESS_TYPE_CONTENT = 1;
   const long PROCESS_TYPE_PLUGIN = 2;
   const long PROCESS_TYPE_GMPLUGIN = 3;
   const long PROCESS_TYPE_GPU = 4;
 
   const long CRASH_TYPE_CRASH = 0;
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1a6feee7cc1f6a988c3810c325dc43df70562799
GIT binary patch
literal 11317
zc%1E83v`s#o&V3|!6ZBq!a!4bG~iGo%OixS3AiwMA+`yL5|0c<!y{9Yl7wjz)3E)H
znUIi7jNpt?lA}?h#V}T-S)mdv;vnhD*`?=XD|-lC+|8k=AS_}<z`SPvzk9!LW)d})
zv^}S1*E{FVef{6}fA9Cd_xm!_GP9rB8+&gU5yg;9WqI$Vt5pi(oX@#dM-<GZJ%s2y
z*SVks&LNYDLb-HvKErto=S0qrbB^E~&AF4ajyByrj>|8MG(rj`!+4!8M8Ffph7;+z
zEEpp@?zWaZQdnZWr=;9k)Xhx2eHVV^<rJ;TnHw=w_N!xM$@DydT%JoYi&C$)b!w?0
zF8B4^T&mK0MPkW@BtOKgA@C<yfa9u;nykKbWuOYhw9leM^Bfy5X^O<zHJ%lkI1Ii=
z!k@p^B%0AYq0?`b@arOp#*1FFgzb`=7qP7#8#5|ZDVFoRNh3ix6<Lm@>wgNT1G^)W
z_c5=xf<GCA#dSO@wT9Smtz52R1@lioj1sJtGa@g$<YkVa%+d&|efpKO%fon8kCnVo
zj>Ky==ZX<|t@^1bx+c-)Gn444BOjzB7>4p{3zxi5ev@9$jL7SK$*Vn#&W#VJy==pB
z!{}@Xz1|v;*GH09WEgGh45NL_>&;Q}Vz;BSBl7xG^0Ea}S647y%-WyYKa7`NoiF`2
z%ol7JGUA?cQu3lO%FPZVi|+d=cZCh*wM?C_W#2Gg&}+_!ypBp<yh*;NgwTCWk;#t+
z@WSpWZ5Ybsk(q(KhVMAw^@9<4B~^%`OGl<wy_hdg*yz_+@rb<IETV`ECzB(bo@QPY
zK000|&QFZU%cI(msB*fF4l=K_zkt{N5qZ5WZP?E1@I|73WnPX^@`4RdjlgRf=hbAT
zQmUXr+Q_vS%BJ;P+sw6i9-YB?BQ2p^D(BHM{?Fra8~+w?yOJ~UC{e0_d4TSrRg^|q
zTwBfUGTJI|tW->knO-iHa4VmR__shU<^KQY;%^qxmhgX(=<!pz87a0fhIJVJ%}BAH
zu_}Ze3Wrg->n5m_GX6&XFXxzLbb4%JtiMEpN4O1pj-Em>w<}aFMyEp|*NTTpu<R!A
z^JxRyp+I<qZIm=BgcZtImM!dCqtnC2ZRrty{E=cd#ARAc=3lKyvK@Vp+dA?8S4&Gt
z?%~K<9DDQ?yRznR3hf$<60xR(OT?m6xxPZLqgrm?D&!56(Vy(!(<Ihp4ygY>OzB}R
zWfkYdH)io(uP4)iS;EfFWS2s=fvS*5r*h(TX`7b)vncwqQ?W6;=UkJrWtmIaz}dhE
zXBhtg=NVtYW5EH5kFyMD&%i0Ef1qmN#;u$3OA9Nk3pXxI%SydJGyTy;3o|p*SLI}J
zB_k^}Ib$_fa;(KRjg&4<(n{4jW-uL1hqb4P4Ev=Vf6u;*)WNZ}{Z5C{?P*d<4j}e6
zQ!SrUc3pHT(wF%x`PsL=d^71=Uq-4|N4(zJKAd}v6hEeNj8WA1jLiFE>p&Fi-p>Dt
zlU>U0$qC*lLgw?!Pv%C<<uZp(FmA!UW?GY(!|k~OQ{y?rmmf5ed##bWIKy}4QP@rW
ze1;D6<xAZ5`?4mZKf#y5(=W42!5xNQa-v)w^ULp4<})q77B1yts7vwd!e{m&8p-6>
ztDX}2qsXsa6y4xFZ=oD}nESMGr1n9{+rv3;rdf;uZ)wHc2p6~G7R4`yUWrLdmfrP`
z%a)syQ^dT&9!vjy#;xX1BXz8aobhl_k97e*f8+T%yojI2`TSq0Gtzwe5%(l=Y&Bo-
zOA@sW+;cnU`3x0D&8v*uvT|*)_`ic&czPETe9Ea9NP2=J-lZJjJq2{9)77z+j2Fx<
zl*YEo=Ioha5<K5JWLH{wOtHTs&I<+}L;oe#?F#1)c^vdGjxogFCN3|`ty9o<V68*J
z9)@`!LF4F8?I4sL#OEA&7Y2H{JReh5)+%9BUCMo{(^h{F-@LXo_Gog=&eILX3kSXM
z?u|=w%dKVQdFySq1-ASR1h`-4-FruB*SU1pwbI@Nv6UXMDJjn{D)K?(R-Qk6;DPj%
zw!((ZKmP8a96)TgmX=tH7RUR5UbikSxVoY~dH3%x7iZjmaUy`W6j=ix@`@fx&y4;}
z`mcZcS;V%cdyRltpI@=wRu}+Ox^~Zz7pDIq`4?~2?SC<7;%Weu+BTM!C-^{){ODs(
z8=d;$zU$AWEpgSg0Ay2sAd$?U{KMs6)I6Kv&{xcyv#{p-fOw?LS{eXy?6b<Y{~5nI
zrN$Y5Xm|6YiLg#_Sp_rlfwb)T&b+C!cV-+q``V;ar$3$oDuu<x0Yu7vet*)`vwvE#
zGq&*7BhUS~0z~pRY$~u8`OVwu43Dl2TDD@_`<)NYd+cBds8m=>%L=!Y_+Y+x;n+*h
z7j&n-HtVsl+Y%dWfU%XaVFUy&3~`Bui#ODb7u5?ZW|uVi%NK9MFV?&@otGMdP4A|e
z;GWcwYcg;>5+FI};gatd$cc1+<E5HQ*$ru?arrOcrn1q#hY?g%fOBb3OBEd#sExs<
z#t$Ak=G?Id$5`5NkEGUkG9p1#j%fq=CONVr$7J~3f@93CrTP+X_!_pK8Sr`=c69T9
zW4fU+Gq_>pxE<T)q0`X;nVa|E--)~1hW1wcceH?H=C}q2E^Qk#sljnTRQekn9<Ds@
zIE?@1W?>7@1*h_Fv+YVpz$GF!Fl;zjTt=cH+dtHs)8kYYB=JKnd!S0Y<b)X#*xrSA
zzw0>3rTgg`<;HAzsn^Wyc$J6tpg6~U-sxu2`rN&e--KyqLLJGwb#6rd7c9$EGa*2M
zOk~!RjmHh<6aixg9_LkYb&7`POFUP47Wj{!NWa*6T6V9z`t_T943D9|HP0wE`CjfT
zzb5HwoBS1hv(F}pSw>$!HXFEsISaJfEX{ecEkt2j&cVH84s8|>jA(`dj?h6sv{@Y2
zo+sz$QS?%wOS!>voX)Lx*twm`@~;fuE-vgb?qE9ZnI@l_fH485Z=eeO;Q6^GU*>k+
z1e=9TF-OpA_esgOT`dhPQ*@TpKT<7a8+K<^+2-GLt%0<V<fSrjD|Oq&<{Rsi(kKE<
z_)que@&S5<^~c=94j+Ekr8qv^OL4rAb)3CZAzL%qxzEn>!ERbRFrjVWc?Rw&%aaOp
zHK_9r_#4a<hqp7=zRc6W@PL86)`+@PSGz1*yFo7;zF;s&^#1o?kUFkopB=0cH4h#<
z+Xd!)k9?37{NPs>&@kQNQtTXHYqI_>_er^C1bv<0o1QJ#k1uf>aG2|pEb~>a+c`kg
zUZ`L^`g#;;f#Y`;R4d;3)hdrR!B^<c>k>HUnFi)J|At(X1b8IIIc_r1`#bkqG+)kO
zx_1~}7BsXzl<T<v+?bawMc1Ql$|s6LT#7SK`cv9?dEK^-eFHC%;2TH?0J=HgF^ATR
z09sowq;dkBJE&f)DFo2k82X#JAMnVqMHkb#al1pTLztlTqkR_M_9>2ohWDOvDQR5a
zpxP<>!ahs)$NMax4IPcaetjY74TkZkW1#nQ1N^bgf*=CpVXAyDGVK=5kPmgpkgxLx
zm}m>1=MsAb_@V8AD{;V9-Y0R32EXi|t<RcC3szO}uvaekFs&2dn`svPy}WihRK6Gs
zO_2R~RH2Q154!X&5_2c|kTrd=uWq}|?CaBDR&xL2gL3aXYci8rZDS1j0I!a!l^ot%
zy1$TcQ<!Vc))e1fJLUn{O;3AxT+4d$zqspE@c?gRTVyaTl(d%dxZcWj$Y|yEo{(y#
z{AiQ%QHb>St89bjK8fF|mQGRH#WNx>X>w^g4^NAEe#QC_FQV+qdCv2g#`n%Tm5IIe
zVorLtNWMkAG8n}n41G`h3okds8tZsX^~!Z-OR5t%SNK`_hdXM;vnlME8l_#8t9_V!
zEi88j=abz3_MJ`z?H;e}vvF?cdN=o@4~LY-uYdtxok|P+>nfl6N*L^i=*Jw0=Tf}%
zIRx&D^Ib}TuwlJ;4g%)PB9{`yW7V7igM%r8cpO#}878MsJ~KP}qI~p-Cb9EJ5g!D^
zC0Xysa_vG6P1nGey0}Est8;^kwZG>Jly5Hs|2FCW>IDjX!-h+N6pnZ2)Qj~b5HiGm
z8>8**S_p1tIG?;~IgasvmYS@-f+VWtI`JNiI2`K$(IE016&{xep!?6e%w%FdxGX??
zh6@DXtr2^Do2C7b+oF5OOp|{pc^+WmIIs%^Y3N4{hsVf^L-hG#?G`gxIPZGhOj(c2
zjqtFXmsXnvZd_75Wpnm&Kcs7oF^TvE>mK7FKFTJlWxQ8=<zCgb#Y}t0{u8E%uz#Ks
z`|eQu<J=D!#My!OG;MFzQfiLW$4-&<nBFFO{*>H*R$gynz7d2A5iteazVndXhr1|9
z#_>q&&p4G&JLDMn*seGE=;v@fZky4!O9j46RCp|gX-**ZxxEg5ZePq}V7w>)!>Ker
zV^><(o?SPZ#2D&{JZ7&lQp|t3Es?#lznS|TeNEI;C1H>PX$2<qzZpbdn>4(@<QWDJ
zns|v58UR21B@Qu6K_D$|pRAIy+gP9Vm)({g@kx=JrmbRs<-WIu_3h!lyieQ~`RS4T
z2&t3%0JCneiH>u9?b}1g@!Zw;;EY|w=U0b%QKZ3PERJngb<AyP?Q~n(*><2;z;RYK
zFUAaAuy|3diQYpKW{%a32^Rs~6501Df;4f4P+u^ku7}U+MAp_UUOc0Lm(Muk#NH=u
zlAz8VO4kJ5m)PGB4<G0ASnjIrLKcC(k^~N5dw3q=?)&n5cA|fq)c5NW{AMLDj7q)y
zcnk7g;-cej-zP%>$>_7w(4^D7%>S^}8L!M(Z<VY=4(HdNbt#W?`A+DTstGE_G+qE0
zhh7C-Zr^8DD*Kzr#JT1_-4@h=V`tc%+%~Eoo?+eWW?4IUJn#Y)ypcNxny9<CiL`V2
zOz1LBp3`BYb{@l=UuAqe2M~v#4cJ#gC9M-|V{P7h*b3(u$Gy>|l=rDP28n~%@4<Q1
zO8q=FuIvj~=K~TJ`$hhMlocOpfj;QM?qZFwonH0fN$+qDs`pLYpS_xWuhA&tFE9J-
zwei)8TV2aqJMip-w1#o+f_>CAK4n6+0=qybjKw@*q%}-)MTASqR@;yzPKAeU(MB2Q
zgP^x=(61xrAx_l;vS8dDu+M<kntrK6q>2MsD%p=Ak0zgqC-urWxm42nCH;3fe*S2t
zT%Wjq11#>}ntA+H&c8qFwhSNZRCU(;-~_i<{m~Dn`8!~Hcnm)9)aze8uGtGO2`ah%
z(tfu^ql@_qyc0wccgX`yK0M(MH~C@T@C2H#!WY>v%}n><gKi7pkst%?h*Zn#?IiR3
z*`c8_Cf;KdW7@OEz;(!^iYzX=k?|58A8lg&Blf}lA3o^Bu85C6@eybTmvJON0M$MO
zEl}qgrB=sBq4+rTw#o7?wSS7dGsXBi-jDFnAwGOu6fQp^#7Chx*WW%*;$trGF(>u~
z=;u&%)KUiX?N5^TEU=I7euK7)_?qiF*b|qTFn>0wt12>p$gm?q0^6nXO}7R1(s%>J
zq3*Y^TfSG$Dg0Z!R2eJJLP#Bb{xjLW80pi%h5Vp}cw=IW*{7RtjReqr?QOdfGuy6Q
zQs)cv7I;q`7&~bn)D|(!;efl*qFc<;=X_^GGvO`(^47dzSHwMNK%e+<G_<A8!*sgq
Vme*-9k@F4vkeDd$78Rb;{{lqd;6nfa
new file mode 100644
--- /dev/null
+++ b/toolkit/components/crashes/tests/xpcshell/crash.extra
@@ -0,0 +1,32 @@
+E10SCohort=unsupportedChannel
+ContentSandboxLevel=2
+TelemetryEnvironment={}
+EMCheckCompatibility=true
+ProductName=Firefox
+ContentSandboxCapabilities=119
+TelemetryClientId=
+Vendor=Mozilla
+InstallTime=1000000000
+Theme=classic/1.0
+ReleaseChannel=default
+AddonsShouldHaveBlockedE10s=1
+ServerURL=https://crash-reports.mozilla.com
+SafeMode=0
+ContentSandboxCapable=1
+useragent_locale=en-US
+Version=55.0a1
+BuildID=20170512114708
+ProductID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
+TelemetryServerURL=
+DOMIPCEnabled=1
+Add-ons=
+CrashTime=1494582646
+UptimeTS=14.9179586
+ThreadIdNameMapping=
+ContentSandboxLevel=2
+ContentSandboxEnabled=1
+ProcessType=content
+DOMIPCEnabled=1
+StartupTime=1000000000
+URL=about:home
+ContentSandboxCapabilities=119
--- a/toolkit/components/crashes/tests/xpcshell/test_crash_service.js
+++ b/toolkit/components/crashes/tests/xpcshell/test_crash_service.js
@@ -1,17 +1,19 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
+Cu.import("resource://gre/modules/osfile.jsm", this);
 Cu.import("resource://gre/modules/Services.jsm", this);
 Cu.import("resource://testing-common/AppData.jsm", this);
+Cu.import("resource://testing-common/CrashManagerTest.jsm", this);
 var bsp = Cu.import("resource://gre/modules/CrashManager.jsm", {});
 
 function run_test() {
   run_next_test();
 }
 
 add_task(async function test_instantiation() {
   Assert.ok(!bsp.gCrashManager, "CrashManager global instance not initially defined.");
@@ -24,8 +26,56 @@ add_task(async function test_instantiati
     .getService(Ci.nsIObserver)
     .observe(null, "profile-after-change", null);
 
   Assert.ok(bsp.gCrashManager, "Profile creation makes it available.");
   Assert.ok(Services.crashmanager, "CrashManager available via Services.");
   Assert.strictEqual(bsp.gCrashManager, Services.crashmanager,
                      "The objects are the same.");
 });
+
+add_task(async function test_addCrash() {
+  const crashId = "56cd87bc-bb26-339b-3a8e-f00c0f11380e";
+
+  let cwd = await OS.File.getCurrentDirectory();
+  let minidump = OS.Path.join(cwd, "crash.dmp");
+  let extra = OS.Path.join(cwd, "crash.extra");
+  let dir = do_get_tempdir();
+
+  // Make a copy of the files because the .extra file will be modified
+  await OS.File.copy(minidump, OS.Path.join(dir.path, crashId + ".dmp"));
+  await OS.File.copy(extra, OS.Path.join(dir.path, crashId + ".extra"));
+
+  // Ensure that the nsICrashReporter methods can find the dump
+  let crashReporter =
+      Components.classes["@mozilla.org/toolkit/crash-reporter;1"]
+                .getService(Components.interfaces.nsICrashReporter);
+  crashReporter.minidumpPath = dir;
+
+  let cs = Cc["@mozilla.org/crashservice;1"].getService(Ci.nsICrashService);
+  await cs.addCrash(Ci.nsICrashService.PROCESS_TYPE_CONTENT,
+                    Ci.nsICrashService.CRASH_TYPE_CRASH, crashId);
+  let crashes = await Services.crashmanager.getCrashes();
+  let crash = crashes.find(c => { return c.id === crashId; });
+  Assert.ok(crash, "Crash " + crashId + " has been stored successfully.");
+  Assert.equal(crash.metadata.ProcessType, "content");
+  Assert.equal(crash.metadata.MinidumpSha256Hash,
+    "24b0ea7794b2d2523c46c9aea72c03ccbb0ab88ad76d8258d3752c7b71d233ff");
+  Assert.ok(crash.metadata.StackTraces, "The StackTraces field is present.\n")
+
+  try {
+    let stackTraces = JSON.parse(crash.metadata.StackTraces);
+    Assert.equal(stackTraces.status, "OK");
+    Assert.ok(stackTraces.crash_info, "The crash_info field is populated.");
+    Assert.ok(stackTraces.modules && (stackTraces.modules.length > 0),
+              "The module list is populated.");
+    Assert.ok(stackTraces.threads && (stackTraces.modules.length > 0),
+              "The thread list is populated.");
+
+    let frames = stackTraces.threads[0].frames;
+    Assert.ok(frames && (frames.length > 0), "The stack trace is present.\n");
+  } catch (e) {
+    Assert.ok(false, "StackTraces does not contain valid JSON.");
+  }
+
+  // Remove the minidumps to prevent the test harness from thinking we crashed
+  await OS.File.removeDir(dir.path);
+});
--- a/toolkit/components/crashes/tests/xpcshell/xpcshell.ini
+++ b/toolkit/components/crashes/tests/xpcshell/xpcshell.ini
@@ -1,7 +1,10 @@
 [DEFAULT]
 head =
 skip-if = toolkit == 'android'
+support-files =
+  crash.dmp
+  crash.extra
 
 [test_crash_manager.js]
 [test_crash_service.js]
 [test_crash_store.js]
--- a/toolkit/components/thumbnails/test/browser.ini
+++ b/toolkit/components/thumbnails/test/browser.ini
@@ -2,17 +2,16 @@
 support-files =
   authenticate.sjs
   background_red.html
   background_red_redirect.sjs
   background_red_scroll.html
   head.js
   privacy_cache_control.sjs
   thumbnails_background.sjs
-  thumbnails_crash_content_helper.js
   thumbnails_update.sjs
 
 [browser_thumbnails_bg_bad_url.js]
 [browser_thumbnails_bg_crash_during_capture.js]
 skip-if = !crashreporter
 [browser_thumbnails_bg_crash_while_idle.js]
 skip-if = !crashreporter
 [browser_thumbnails_bg_basic.js]
--- a/toolkit/components/thumbnails/test/browser_thumbnails_bg_crash_during_capture.js
+++ b/toolkit/components/thumbnails/test/browser_thumbnails_bg_crash_during_capture.js
@@ -1,49 +1,45 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function* runTests() {
-  let crashObserver = bgAddCrashObserver();
-
   // make a good capture first - this ensures we have the <browser>
   let goodUrl = bgTestPageURL();
   yield bgCapture(goodUrl);
   ok(thumbnailExists(goodUrl), "Thumbnail should be cached after capture");
   removeThumbnail(goodUrl);
 
-  // inject our content script.
-  let mm = bgInjectCrashContentScript();
-
   // queue up 2 captures - the first has a wait, so this is the one that
   // will die.  The second one should immediately capture after the crash.
   let waitUrl = bgTestPageURL({ wait: 30000 });
   let sawWaitUrlCapture = false;
   bgCapture(waitUrl, { onDone: () => {
     sawWaitUrlCapture = true;
     ok(!thumbnailExists(waitUrl), "Thumbnail should not have been saved due to the crash");
   }});
   bgCapture(goodUrl, { onDone: () => {
     ok(sawWaitUrlCapture, "waitUrl capture should have finished first");
     ok(thumbnailExists(goodUrl), "We should have recovered and completed the 2nd capture after the crash");
     removeThumbnail(goodUrl);
     // Test done.
-    ok(crashObserver.crashed, "Saw a crash from this test");
     next();
   }});
   let crashPromise = new Promise(resolve => {
     bgAddPageThumbObserver(waitUrl).catch(function(err) {
       ok(true, `page-thumbnail error thrown for ${waitUrl}`);
       resolve();
     });
   });
   let capturePromise = new Promise(resolve => {
     bgAddPageThumbObserver(goodUrl).then(() => {
       ok(true, `page-thumbnail created for ${goodUrl}`);
       resolve();
     });
   });
 
   info("Crashing the thumbnail content process.");
-  mm.sendAsyncMessage("thumbnails-test:crash");
+  let crash = yield BrowserTestUtils.crashBrowser(BackgroundPageThumbs._thumbBrowser, false);
+  ok(crash.CrashTime, "Saw a crash from this test");
+
   yield crashPromise;
   yield capturePromise;
 }
--- a/toolkit/components/thumbnails/test/browser_thumbnails_bg_crash_while_idle.js
+++ b/toolkit/components/thumbnails/test/browser_thumbnails_bg_crash_while_idle.js
@@ -1,38 +1,25 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function* runTests() {
-  let crashObserver = bgAddCrashObserver();
-
   // make a good capture first - this ensures we have the <browser>
   let goodUrl = bgTestPageURL();
   yield bgCapture(goodUrl);
   ok(thumbnailExists(goodUrl), "Thumbnail should be cached after capture");
   removeThumbnail(goodUrl);
 
-  // inject our content script.
-  let mm = bgInjectCrashContentScript();
-
-  // the observer for the crashing process is basically async, so it's hard
-  // to know when the <browser> has actually seen it.  Easist is to just add
-  // our own observer.
-  Services.obs.addObserver(function onCrash() {
-    Services.obs.removeObserver(onCrash, "oop-frameloader-crashed");
-    // spin the event loop to ensure the BPT observer was called.
-    executeSoon(function() {
-      // Now queue another capture and ensure it recovers.
-      bgCapture(goodUrl, { onDone: () => {
-        ok(thumbnailExists(goodUrl), "We should have recovered and handled new capture requests");
-        removeThumbnail(goodUrl);
-        // Test done.
-        ok(crashObserver.crashed, "Saw a crash from this test");
-        next();
-      }});
-    });
-  }, "oop-frameloader-crashed");
-
   // Nothing is pending - crash the process.
   info("Crashing the thumbnail content process.");
-  mm.sendAsyncMessage("thumbnails-test:crash");
+  let crash = yield BrowserTestUtils.crashBrowser(BackgroundPageThumbs._thumbBrowser, false);
+  ok(crash.CrashTime, "Saw a crash from this test");
+
+  // Now queue another capture and ensure it recovers.
+  bgCapture(goodUrl, { onDone: () => {
+    ok(thumbnailExists(goodUrl), "We should have recovered and handled new capture requests");
+    removeThumbnail(goodUrl);
+    // Test done.
+    next();
+  }});
+
   yield true;
 }
--- a/toolkit/components/thumbnails/test/head.js
+++ b/toolkit/components/thumbnails/test/head.js
@@ -296,64 +296,8 @@ function bgAddPageThumbObserver(url) {
         Services.obs.removeObserver(observe, "page-thumbnail:create");
         Services.obs.removeObserver(observe, "page-thumbnail:error");
       }
     }
     Services.obs.addObserver(observe, "page-thumbnail:create");
     Services.obs.addObserver(observe, "page-thumbnail:error");
   });
 }
-
-function bgAddCrashObserver() {
-  let crashed = false;
-  Services.obs.addObserver(function crashObserver(subject, topic, data) {
-    is(topic, "ipc:content-shutdown", "Received correct observer topic.");
-    ok(subject instanceof Components.interfaces.nsIPropertyBag2,
-       "Subject implements nsIPropertyBag2.");
-    // we might see this called as the process terminates due to previous tests.
-    // We are only looking for "abnormal" exits...
-    if (!subject.hasKey("abnormal")) {
-      info("This is a normal termination and isn't the one we are looking for...");
-      return;
-    }
-    Services.obs.removeObserver(crashObserver, "ipc:content-shutdown");
-    crashed = true;
-
-    var dumpID;
-    if ("nsICrashReporter" in Components.interfaces) {
-      dumpID = subject.getPropertyAsAString("dumpID");
-      ok(dumpID, "dumpID is present and not an empty string");
-    }
-
-    if (dumpID) {
-      var minidumpDirectory = getMinidumpDirectory();
-      removeFile(minidumpDirectory, dumpID + ".dmp");
-      removeFile(minidumpDirectory, dumpID + ".extra");
-    }
-  }, "ipc:content-shutdown");
-  return {
-    get crashed() {
-      return crashed;
-    }
-  };
-}
-
-function bgInjectCrashContentScript() {
-  const TEST_CONTENT_HELPER = "chrome://mochitests/content/browser/toolkit/components/thumbnails/test/thumbnails_crash_content_helper.js";
-  let thumbnailBrowser = BackgroundPageThumbs._thumbBrowser;
-  let mm = thumbnailBrowser.messageManager;
-  mm.loadFrameScript(TEST_CONTENT_HELPER, false);
-  return mm;
-}
-
-function getMinidumpDirectory() {
-  var dir = Services.dirsvc.get("ProfD", Components.interfaces.nsIFile);
-  dir.append("minidumps");
-  return dir;
-}
-
-function removeFile(directory, filename) {
-  var file = directory.clone();
-  file.append(filename);
-  if (file.exists()) {
-    file.remove(false);
-  }
-}
deleted file mode 100644
--- a/toolkit/components/thumbnails/test/thumbnails_crash_content_helper.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-* http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/* eslint-env mozilla/frame-script */
-
-var Cu = Components.utils;
-
-// Ideally we would use CrashTestUtils.jsm, but that's only available for
-// xpcshell tests - so we just copy a ctypes crasher from it.
-Cu.import("resource://gre/modules/ctypes.jsm");
-var crash = function() { // this will crash when called.
-  let zero = new ctypes.intptr_t(8);
-  let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
-  badptr.contents
-};
-
-
-var TestHelper = {
-  init() {
-    addMessageListener("thumbnails-test:crash", this);
-  },
-
-  receiveMessage(msg) {
-    switch (msg.name) {
-      case "thumbnails-test:crash":
-        privateNoteIntentionalCrash();
-        crash();
-      break;
-    }
-  },
-}
-
-TestHelper.init();
--- a/toolkit/content/tests/browser/browser_crash_previous_frameloader.js
+++ b/toolkit/content/tests/browser/browser_crash_previous_frameloader.js
@@ -17,28 +17,26 @@ function getCrashDumpId(subject) {
 }
 
 /**
  * Cleans up the .dmp and .extra file from a crash.
  *
  * @param id {String} The crash dump id.
  */
 function cleanUpMinidump(id) {
-  if (id) {
-    let dir = Services.dirsvc.get("ProfD", Ci.nsIFile);
-    dir.append("minidumps");
+  let dir = Services.dirsvc.get("ProfD", Ci.nsIFile);
+  dir.append("minidumps");
 
-    let file = dir.clone();
-    file.append(id + ".dmp");
-    file.remove(true);
+  let file = dir.clone();
+  file.append(id + ".dmp");
+  file.remove(true);
 
-    file = dir.clone();
-    file.append(id + ".extra");
-    file.remove(true);
-  }
+  file = dir.clone();
+  file.append(id + ".extra");
+  file.remove(true);
 }
 
 /**
  * This test ensures that if a remote frameloader crashes after
  * the frameloader owner swaps it out for a new frameloader,
  * that a oop-browser-crashed event is not sent to the new
  * frameloader's browser element.
  */
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -192,22 +192,19 @@ static xpstring *defaultMemoryReportPath
 
 static const char kCrashMainID[] = "crash.main.2\n";
 
 static google_breakpad::ExceptionHandler* gExceptionHandler = nullptr;
 
 static XP_CHAR* pendingDirectory;
 static XP_CHAR* crashReporterPath;
 static XP_CHAR* memoryReportPath;
-#if !defined(MOZ_WIDGET_ANDROID)
-static XP_CHAR* minidumpAnalyzerPath;
 #ifdef XP_MACOSX
 static XP_CHAR* libraryPath; // Path where the NSS library is
 #endif // XP_MACOSX
-#endif // !defined(MOZ_WIDGET_ANDROID)
 
 // Where crash events should go.
 static XP_CHAR* eventsDirectory;
 static char* eventsEnv = nullptr;
 
 // The current telemetry session ID to write to the event file
 static char* currentSessionId = nullptr;
 
@@ -814,21 +811,19 @@ WriteGlobalMemoryStatus(PlatformWriter* 
 
 /**
  * Launches the program specified in aProgramPath with aMinidumpPath as its
  * sole argument.
  *
  * @param aProgramPath The path of the program to be launched
  * @param aMinidumpPath The path of the minidump file, passed as an argument
  *        to the launched program
- * @param aWait If true wait for the program termination
  */
 static bool
-LaunchProgram(const XP_CHAR* aProgramPath, const XP_CHAR* aMinidumpPath,
-              bool aWait = false)
+LaunchProgram(const XP_CHAR* aProgramPath, const XP_CHAR* aMinidumpPath)
 {
 #ifdef XP_WIN
   XP_CHAR cmdLine[CMDLINE_SIZE];
   XP_CHAR* p;
 
   size_t size = CMDLINE_SIZE;
   p = Concat(cmdLine, L"\"", &size);
   p = Concat(p, aProgramPath, &size);
@@ -839,20 +834,16 @@ LaunchProgram(const XP_CHAR* aProgramPat
   PROCESS_INFORMATION pi = {};
   STARTUPINFO si = {};
   si.cb = sizeof(si);
 
   // If CreateProcess() fails don't do anything
   if (CreateProcess(nullptr, (LPWSTR)cmdLine, nullptr, nullptr, FALSE,
                     NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW,
                     nullptr, nullptr, &si, &pi)) {
-    if (aWait) {
-      WaitForSingleObject(pi.hProcess, INFINITE);
-    }
-
     CloseHandle(pi.hProcess);
     CloseHandle(pi.hThread);
   }
 #elif defined(XP_UNIX)
   pid_t pid = sys_fork();
 
   if (pid == -1) {
     return false;
@@ -863,20 +854,16 @@ LaunchProgram(const XP_CHAR* aProgramPat
     unsetenv("LD_LIBRARY_PATH");
 #else // XP_MACOSX
     // Needed to locate NSS and its dependencies
     setenv("DYLD_LIBRARY_PATH", libraryPath, /* overwrite */ 1);
 #endif
     Unused << execl(aProgramPath,
                     aProgramPath, aMinidumpPath, (char*)0);
     _exit(1);
-  } else {
-    if (aWait) {
-      waitpid(pid, nullptr, 0);
-    }
   }
 #endif // XP_UNIX
 
   return true;
 }
 
 #else
 
@@ -1692,32 +1679,21 @@ nsresult SetExceptionHandler(nsIFile* aX
 
     nsAutoString libraryPath_temp;
     rv = libPath->GetPath(libraryPath_temp);
     if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
     }
 #endif // XP_MACOSX
 
-    nsAutoString minidumpAnalyzerPath_temp;
-    rv = LocateExecutable(aXREDirectory,
-                          NS_LITERAL_CSTRING(MINIDUMP_ANALYZER_FILENAME),
-                          minidumpAnalyzerPath_temp);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
 #ifdef XP_WIN32
   crashReporterPath =
     reinterpret_cast<wchar_t*>(ToNewUnicode(crashReporterPath_temp));
-  minidumpAnalyzerPath =
-    reinterpret_cast<wchar_t*>(ToNewUnicode(minidumpAnalyzerPath_temp));
 #else
   crashReporterPath = ToNewCString(crashReporterPath_temp);
-  minidumpAnalyzerPath = ToNewCString(minidumpAnalyzerPath_temp);
 #ifdef XP_MACOSX
   libraryPath = ToNewCString(libraryPath_temp);
 #endif
 #endif // XP_WIN32
 #else
     // On Android, we launch using the application package name instead of a
     // filename, so use the dynamically set MOZ_ANDROID_PACKAGE_NAME, or fall
     // back to the static ANDROID_PACKAGE_NAME.
@@ -2202,28 +2178,22 @@ nsresult UnsetExceptionHandler()
     pendingDirectory = nullptr;
   }
 
   if (crashReporterPath) {
     free(crashReporterPath);
     crashReporterPath = nullptr;
   }
 
-#if !defined(MOZ_WIDGET_ANDROID)
-  if (minidumpAnalyzerPath) {
-    free(minidumpAnalyzerPath);
-    minidumpAnalyzerPath = nullptr;
-  }
 #ifdef XP_MACOSX
   if (libraryPath) {
     free(libraryPath);
     libraryPath = nullptr;
   }
 #endif // XP_MACOSX
-#endif // !defined(MOZ_WIDGET_ANDROID)
 
   if (eventsDirectory) {
     free(eventsDirectory);
     eventsDirectory = nullptr;
   }
 
   if (currentSessionId) {
     free(currentSessionId);
@@ -3126,44 +3096,16 @@ bool
 AppendExtraData(const nsAString& id, const AnnotationTable& data)
 {
   nsCOMPtr<nsIFile> extraFile;
   if (!GetExtraFileForID(id, getter_AddRefs(extraFile)))
     return false;
   return AppendExtraData(extraFile, data);
 }
 
-/**
- * Runs the minidump analyzer program on the specified crash dump. The analyzer
- * will extract the stack traces from the dump and store them in JSON format as
- * an annotation in the extra file associated with the crash.
- *
- * This method waits synchronously for the program to have finished executing,
- * do not call it from the main thread!
- */
-void
-RunMinidumpAnalyzer(const nsAString& id)
-{
-#if !defined(MOZ_WIDGET_ANDROID)
-  nsCOMPtr<nsIFile> file;
-
-  if (CrashReporter::GetMinidumpForID(id, getter_AddRefs(file))) {
-#ifdef XP_WIN
-    nsAutoString path;
-    file->GetPath(path);
-#else
-    nsAutoCString path;
-    file->GetNativePath(path);
-#endif
-
-    LaunchProgram(minidumpAnalyzerPath, path.get(), /* aWait */ true);
-  }
-#endif // !defined(MOZ_WIDGET_ANDROID)
-}
-
 //-----------------------------------------------------------------------------
 // Helpers for AppendExtraData()
 //
 struct Blacklist {
   Blacklist() : mItems(nullptr), mLen(0) { }
   Blacklist(const char** items, int len) : mItems(items), mLen(len) { }
 
   bool Contains(const nsACString& key) const {
--- a/toolkit/crashreporter/nsExceptionHandler.h
+++ b/toolkit/crashreporter/nsExceptionHandler.h
@@ -98,17 +98,16 @@ typedef nsDataHashtable<nsCStringHashKey
 
 void DeleteMinidumpFilesForID(const nsAString& id);
 bool GetMinidumpForID(const nsAString& id, nsIFile** minidump);
 bool GetIDFromMinidump(nsIFile* minidump, nsAString& id);
 bool GetExtraFileForID(const nsAString& id, nsIFile** extraFile);
 bool GetExtraFileForMinidump(nsIFile* minidump, nsIFile** extraFile);
 bool AppendExtraData(const nsAString& id, const AnnotationTable& data);
 bool AppendExtraData(nsIFile* extraFile, const AnnotationTable& data);
-void RunMinidumpAnalyzer(const nsAString& id);
 
 /*
  * Renames the stand alone dump file aDumpFile to:
  *  |aOwnerDumpFile-aDumpFileProcessType.dmp|
  * and moves it into the same directory as aOwnerDumpFile. Does not
  * modify aOwnerDumpFile in any way.
  *
  * @param aDumpFile - the dump file to associate with aOwnerDumpFile.
--- a/toolkit/crashreporter/test/unit/head_crashreporter.js
+++ b/toolkit/crashreporter/test/unit/head_crashreporter.js
@@ -164,34 +164,34 @@ function do_content_crash(setup, callbac
   if (setup) {
     if (typeof(setup) == "function") {
       // funky, but convenient
       setup = "(" + setup.toSource() + ")();";
     }
   }
 
   let handleCrash = function() {
-    do_get_profile();
-    makeFakeAppDir().then(() => {
-      let id = getMinidump().leafName.slice(0, -4);
-      return Services.crashmanager.ensureCrashIsPresent(id);
-    }).then(() => {
+    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();
     });
   };
 
-  sendCommand("load(\"" + headfile.path.replace(/\\/g, "/") + "\");", () =>
-    sendCommand(setup, () =>
-      sendCommand("load(\"" + tailfile.path.replace(/\\/g, "/") + "\");", () =>
-        do_execute_soon(handleCrash)
+  do_get_profile();
+  makeFakeAppDir().then(() => {
+    sendCommand("load(\"" + headfile.path.replace(/\\/g, "/") + "\");", () =>
+      sendCommand(setup, () =>
+        sendCommand("load(\"" + tailfile.path.replace(/\\/g, "/") + "\");", () =>
+          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");