Backed out 3 changesets (bug 1293656) for unexpected crash dump failures in test_process_error.xul
authorPhil Ringnalda <philringnalda@gmail.com>
Mon, 05 Dec 2016 20:34:55 -0800
changeset 325301 93d785448a69cd60c7105bfe722cfd94b6d59b37
parent 325300 17f5bc695acc81a98f34c95eef31454c1706aa56
child 325302 46c2e215ec343322c7828ce7eab7a6a5cb820f0e
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
bugs1293656
milestone53.0a1
backs outc8545ffbd0cb18f7ae3a5810f5b98ec0ba8be0e0
f59861923f8169c003f0f7f8bd50e3fae52e0b51
b997bb5bccb4da93ce1f5d3453ca562324ab1d5f
Backed out 3 changesets (bug 1293656) for unexpected crash dump failures in test_process_error.xul Backed out changeset c8545ffbd0cb (bug 1293656) Backed out changeset f59861923f81 (bug 1293656) Backed out changeset b997bb5bccb4 (bug 1293656) MozReview-Commit-ID: GextDZheo78
ipc/glue/CrashReporterHost.cpp
ipc/glue/CrashReporterHost.h
toolkit/components/crashes/CrashManager.jsm
toolkit/components/crashes/CrashService.js
toolkit/components/crashes/docs/index.rst
toolkit/components/crashes/tests/xpcshell/test_crash_manager.js
toolkit/crashreporter/docs/index.rst
toolkit/crashreporter/nsExceptionHandler.cpp
toolkit/crashreporter/nsExceptionHandler.h
--- 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)
@@ -60,173 +58,38 @@ CrashReporterHost::GenerateCrashReport(R
   notes.Put(NS_LITERAL_CSTRING("StartupTime"), nsDependentCString(startTime));
 
   CrashReporterMetadataShmem::ReadAppNotes(mShmem, &notes);
 
   CrashReporter::AppendExtraData(dumpID, notes);
   NotifyCrashService(mProcessType, dumpID, &notes);
 }
 
-/**
- * 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"));
-    MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
-  }
-
-  NS_IMETHOD Run() override
-  {
-    MOZ_ASSERT(!NS_IsMainThread());
-
-    if (mProcessType == nsICrashService::PROCESS_TYPE_CONTENT) {
-      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);
-
-    NS_DispatchToMainThread(NS_NewRunnableFunction([=] () -> void {
-      nsCOMPtr<nsICrashService> crashService =
-        do_GetService("@mozilla.org/crashservice;1");
-      if (crashService) {
-        crashService->AddCrash(processType, crashType, childDumpID);
-      }
-
-      nsCOMPtr<nsIAsyncShutdownClient> barrier = GetShutdownBarrier();
-
-      if (barrier) {
-        barrier->RemoveBlocker(this);
-      }
-    }));
-
-    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;
@@ -251,15 +114,15 @@ CrashReporterHost::NotifyCrashService(Ge
       processType = nsICrashService::PROCESS_TYPE_GPU;
       telemetryKey.AssignLiteral("gpu");
       break;
     default:
       NS_ERROR("unknown process type");
       return;
   }
 
-  AsyncAddCrash(processType, crashType, aChildDumpID);
+  crashService->AddCrash(processType, crashType, aChildDumpID);
   Telemetry::Accumulate(Telemetry::SUBPROCESS_CRASHES_WITH_DUMP, telemetryKey, 1);
 }
 #endif
 
 } // namespace ipc
 } // namespace mozilla
--- a/ipc/glue/CrashReporterHost.h
+++ b/ipc/glue/CrashReporterHost.h
@@ -45,18 +45,16 @@ public:
   static void NotifyCrashService(
     GeckoProcessType aProcessType,
     const nsString& aChildDumpID,
     const AnnotationTable* aNotes);
 #endif
 
 private:
   void GenerateCrashReport(RefPtr<nsIFile> aCrashDump);
-  static void AsyncAddCrash(int32_t aProcessType, int32_t aCrashType,
-                            const nsString& aChildDumpID);
 
 private:
   GeckoProcessType mProcessType;
   Shmem mShmem;
   time_t mStartTime;
 };
 
 } // namespace ipc
--- a/toolkit/components/crashes/CrashManager.jsm
+++ b/toolkit/components/crashes/CrashManager.jsm
@@ -34,46 +34,16 @@ const MILLISECONDS_IN_DAY = 24 * 60 * 60
 
 // Converts Date to days since UNIX epoch.
 // This was copied from /services/metrics.storage.jsm. The implementation
 // does not account for leap seconds.
 function dateToDays(date) {
   return Math.floor(date.getTime() / MILLISECONDS_IN_DAY);
 }
 
-/**
- * Parse the string stored in the specified field as JSON and then remove the
- * field from the object. The string might also be returned without parsing.
- *
- * @param obj {Object} The object holding the field
- * @param field {String} The name of the field to be parsed and removed
- * @param [parseAsJson=true] {Boolean} If true parse the field's contents as if
- *        it were JSON code, otherwise return the rew string.
- *
- * @returns {Object|String} the parsed object or the raw string
- */
-function parseAndRemoveField(obj, field, parseAsJson = true) {
-  let value = null;
-
-  if (field in obj) {
-    if (!parseAsJson) {
-      value = obj[field];
-    } else {
-      try {
-        value = JSON.parse(obj[field]);
-      } catch (e) {
-        Cu.reportError(e);
-      }
-    }
-
-    delete obj[field];
-  }
-
-  return value;
-}
 
 /**
  * A gateway to crash-related data.
  *
  * This type is generic and can be instantiated any number of times.
  * However, most applications will typically only have one instance
  * instantiated and that instance will point to profile and user appdata
  * directories.
@@ -139,19 +109,16 @@ this.CrashManager = function(options) {
 
   // Promise for in-progress aggregation operation. We store it on the
   // object so it can be returned for in-progress operations.
   this._aggregatePromise = null;
 
   // Map of crash ID / promise tuples used to track adding new crashes.
   this._crashPromises = new Map();
 
-  // Promise for the crash ping used only for testing.
-  this._pingPromise = null;
-
   // The CrashStore currently attached to this object.
   this._store = null;
 
   // A Task to retrieve the store. This is needed to avoid races when
   // _getStore() is called multiple times in a short interval.
   this._getStoreTask = null;
 
   // The timer controlling the expiration of the CrashStore instance.
@@ -202,51 +169,16 @@ this.CrashManager.prototype = Object.fre
   // The following are return codes for individual event file processing.
   // File processed OK.
   EVENT_FILE_SUCCESS: "ok",
   // The event appears to be malformed.
   EVENT_FILE_ERROR_MALFORMED: "malformed",
   // The type of event is unknown.
   EVENT_FILE_ERROR_UNKNOWN_EVENT: "unknown-event",
 
-  // A whitelist of crash annotations which do not contain sensitive data
-  // and are saved in the crash record and sent with Firefox Health Report.
-  ANNOTATION_WHITELIST: [
-    "AsyncShutdownTimeout",
-    "BuildID",
-    "ProductID",
-    "ProductName",
-    "ReleaseChannel",
-    "SecondsSinceLastCrash",
-    "ShutdownProgress",
-    "StartupCrash",
-    "TelemetryEnvironment",
-    "Version",
-    // The following entries are not normal annotations that can be found in
-    // the .extra file but are included in the crash record/FHR:
-    "AvailablePageFile",
-    "AvailablePhysicalMemory",
-    "AvailableVirtualMemory",
-    "BlockedDllList",
-    "BlocklistInitFailed",
-    "ContainsMemoryReport",
-    "CrashTime",
-    "EventLoopNestingLevel",
-    "IsGarbageCollecting",
-    "MozCrashReason",
-    "OOMAllocationSize",
-    "SystemMemoryUsePercentage",
-    "TextureUsage",
-    "TotalPageFile",
-    "TotalPhysicalMemory",
-    "TotalVirtualMemory",
-    "UptimeTS",
-    "User32BeforeBlocklist",
-  ],
-
   /**
    * Obtain a list of all dumps pending upload.
    *
    * The returned value is a promise that resolves to an array of objects
    * on success. Each element in the array has the following properties:
    *
    *   id (string)
    *      The ID of the crash (a UUID).
@@ -447,22 +379,17 @@ this.CrashManager.prototype = Object.fre
       }
 
       let deferred = this._crashPromises.get(id);
 
       if (deferred) {
         this._crashPromises.delete(id);
         deferred.resolve();
       }
-
-      // Send a telemetry ping for each content process crash
-      if (processType === this.PROCESS_TYPE_CONTENT) {
-        this._sendCrashPing(id, processType, date, metadata);
-      }
-   }.bind(this));
+    }.bind(this));
 
     return promise;
   },
 
   /**
    * Returns a promise that is resolved only the crash with the specified id
    * has been fully recorded.
    *
@@ -612,61 +539,16 @@ this.CrashManager.prototype = Object.fre
       }
       let date = new Date(time * 1000);
       let payload = data.substring(start);
 
       return this._handleEventFilePayload(store, entry, type, date, payload);
     }.bind(this));
   },
 
-  _filterAnnotations: function(annotations) {
-    let filteredAnnotations = {};
-
-    for (let line in annotations) {
-      if (this.ANNOTATION_WHITELIST.includes(line)) {
-        filteredAnnotations[line] = annotations[line];
-      }
-    }
-
-    return filteredAnnotations;
-  },
-
-  _sendCrashPing: function(crashId, type, date, metadata = {}) {
-    // If we have a saved environment, use it. Otherwise report
-    // the current environment.
-    let reportMeta = Cu.cloneInto(metadata, myScope);
-    let crashEnvironment = parseAndRemoveField(reportMeta,
-                                               "TelemetryEnvironment");
-    let sessionId = parseAndRemoveField(reportMeta, "TelemetrySessionId",
-                                        /* parseAsJson */ false);
-    let stackTraces = parseAndRemoveField(reportMeta, "StackTraces");
-
-    // Filter the remaining annotations to remove privacy-sensitive ones
-    reportMeta = this._filterAnnotations(reportMeta);
-
-    this._pingPromise = TelemetryController.submitExternalPing("crash",
-      {
-        version: 1,
-        crashDate: date.toISOString().slice(0, 10), // YYYY-MM-DD
-        sessionId: sessionId,
-        crashId: crashId,
-        processType: type,
-        stackTraces: stackTraces,
-        metadata: reportMeta,
-        hasCrashEnvironment: (crashEnvironment !== null),
-      },
-      {
-        retentionDays: 180,
-        addClientId: true,
-        addEnvironment: true,
-        overrideEnvironment: crashEnvironment,
-      }
-    );
-  },
-
   _handleEventFilePayload: function(store, entry, type, date, payload) {
       // The payload types and formats are documented in docs/crash-events.rst.
       // Do not change the format of an existing type. Instead, invent a new
       // type.
       // DO NOT ADD NEW TYPES WITHOUT DOCUMENTING!
       let lines = payload.split("\n");
 
       switch (type) {
@@ -678,17 +560,58 @@ this.CrashManager.prototype = Object.fre
           }
           // fall-through
         case "crash.main.2":
           let crashID = lines[0];
           let metadata = parseKeyValuePairsFromLines(lines.slice(1));
           store.addCrash(this.PROCESS_TYPE_MAIN, this.CRASH_TYPE_CRASH,
                          crashID, date, metadata);
 
-          this._sendCrashPing(crashID, this.PROCESS_TYPE_MAIN, date, metadata);
+          // If we have a saved environment, use it. Otherwise report
+          // the current environment.
+          let crashEnvironment = null;
+          let sessionId = null;
+          let stackTraces = null;
+          let reportMeta = Cu.cloneInto(metadata, myScope);
+          if ('TelemetryEnvironment' in reportMeta) {
+            try {
+              crashEnvironment = JSON.parse(reportMeta.TelemetryEnvironment);
+            } catch (e) {
+              Cu.reportError(e);
+            }
+            delete reportMeta.TelemetryEnvironment;
+          }
+          if ('TelemetrySessionId' in reportMeta) {
+            sessionId = reportMeta.TelemetrySessionId;
+            delete reportMeta.TelemetrySessionId;
+          }
+          if ('StackTraces' in reportMeta) {
+            try {
+              stackTraces = JSON.parse(reportMeta.StackTraces);
+            } catch (e) {
+              Cu.reportError(e);
+            }
+            delete reportMeta.StackTraces;
+          }
+          TelemetryController.submitExternalPing("crash",
+            {
+              version: 1,
+              crashDate: date.toISOString().slice(0, 10), // YYYY-MM-DD
+              sessionId: sessionId,
+              crashId: entry.id,
+              stackTraces: stackTraces,
+              metadata: reportMeta,
+              hasCrashEnvironment: (crashEnvironment !== null),
+            },
+            {
+              retentionDays: 180,
+              addClientId: true,
+              addEnvironment: true,
+              overrideEnvironment: crashEnvironment,
+            });
           break;
 
         case "crash.submission.1":
           if (lines.length == 3) {
             let [crashID, result, remoteID] = lines;
             store.addCrash(this.PROCESS_TYPE_MAIN, this.CRASH_TYPE_CRASH,
                            crashID, date);
 
--- a/toolkit/components/crashes/CrashService.js
+++ b/toolkit/components/crashes/CrashService.js
@@ -1,55 +1,20 @@
 /* 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/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/Task.jsm", this);
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 
 /**
- * Process the .extra file associated with the crash id and return the
- * annotations it contains in an object.
- *
- * @param crashID {string} Crash ID. Likely a UUID.
- *
- * @return {Promise} A promise that resolves to an object holding the crash
- *         annotations, this object may be empty if no annotations were found.
- */
-function processExtraFile(id) {
-  let crDir = OS.Path.join(OS.Constants.Path.userApplicationDataDir,
-                           "Crash Reports");
-  let pendingDumpsDir = OS.Path.join(crDir, "pending");
-  let extraPath = OS.Path.join(pendingDumpsDir, id + ".extra");
-
-  return Task.spawn(function* () {
-    try {
-      let decoder = new TextDecoder();
-      let extraFile = yield OS.File.read(extraPath);
-      let extraData = decoder.decode(extraFile);
-
-      return parseKeyValuePairs(extraData);
-    } catch (e) {
-      Cu.reportError(e);
-    }
-
-    return {};
-  });
-}
-
-/**
  * This component makes crash data available throughout the application.
  *
  * It is a service because some background activity will eventually occur.
  */
 this.CrashService = function() {};
 
 CrashService.prototype = Object.freeze({
   classID: Components.ID("{92668367-1b17-4190-86b2-1061b2179744}"),
@@ -85,23 +50,17 @@ CrashService.prototype = Object.freeze({
       break;
     case Ci.nsICrashService.CRASH_TYPE_HANG:
       crashType = Services.crashmanager.CRASH_TYPE_HANG;
       break;
     default:
       throw new Error("Unrecognized CRASH_TYPE: " + crashType);
     }
 
-    AsyncShutdown.profileBeforeChange.addBlocker(
-      "CrashService waiting for content crash ping to be sent",
-      processExtraFile(id).then(metadata => {
-        return Services.crashmanager.addCrash(processType, crashType, id,
-                                              new Date(), metadata)
-      })
-    );
+    Services.crashmanager.addCrash(processType, crashType, id, new Date());
   },
 
   observe: function(subject, topic, data) {
     switch (topic) {
       case "profile-after-change":
         // Side-effect is the singleton is instantiated.
         Services.crashmanager;
         break;
--- a/toolkit/components/crashes/docs/index.rst
+++ b/toolkit/components/crashes/docs/index.rst
@@ -10,21 +10,15 @@ data within the Gecko application.
 From JavaScript, the service can be accessed via::
 
    Cu.import("resource://gre/modules/Services.jsm");
    let crashManager = Services.crashmanager;
 
 That will give you an instance of ``CrashManager`` from ``CrashManager.jsm``.
 From there, you can access and manipulate crash data.
 
-The crash manager stores statistical information about crashes as well as
-detailed information for both browser and content crashes. The crash manager
-automatically detects new browser crashes at startup by scanning for
-:ref:`crash-events`. Content process crash information on the other hand is
-provided externally.
-
 Other Documents
 ===============
 
 .. toctree::
    :maxdepth: 1
 
    crash-events
--- a/toolkit/components/crashes/tests/xpcshell/test_crash_manager.js
+++ b/toolkit/components/crashes/tests/xpcshell/test_crash_manager.js
@@ -204,100 +204,82 @@ add_task(function* test_schedule_mainten
   yield m.createEventsFile("2", "crash.main.2", oldDate, "id2");
 
   yield m.scheduleMaintenance(25);
   let crashes = yield m.getCrashes();
   Assert.equal(crashes.length, 1);
   Assert.equal(crashes[0].id, "id1");
 });
 
-const crashId = "3cb67eba-0dc7-6f78-6a569a0e-172287ec";
-const productName = "Firefox";
-const productId = "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}";
-const stackTraces = "{\"status\":\"OK\"}";
-
 add_task(function* test_main_crash_event_file() {
   let ac = new TelemetryArchiveTesting.Checker();
   yield ac.promiseInit();
   let theEnvironment = TelemetryEnvironment.currentEnvironment;
-  const sessionId = "be66af2f-2ee5-4330-ae95-44462dfbdf0c";
+  let sessionId = "be66af2f-2ee5-4330-ae95-44462dfbdf0c";
+  let stackTraces = { status: "OK" };
 
   // To test proper escaping, add data to the environment with an embedded
   // double-quote
   theEnvironment.testValue = "MyValue\"";
 
   let m = yield getManager();
-  const fileContent = crashId + "\n" +
-    "ProductName=" + productName + "\n" +
-    "ProductID=" + productId + "\n" +
+  const fileContent = "id1\nk1=v1\nk2=v2\n" +
     "TelemetryEnvironment=" + JSON.stringify(theEnvironment) + "\n" +
     "TelemetrySessionId=" + sessionId + "\n" +
-    "StackTraces=" + stackTraces + "\n" +
-    "ThisShouldNot=end-up-in-the-ping\n";
+    "StackTraces=" + JSON.stringify(stackTraces) + "\n";
 
-  yield m.createEventsFile(crashId, "crash.main.2", DUMMY_DATE, fileContent);
+  yield m.createEventsFile("1", "crash.main.2", DUMMY_DATE, fileContent);
   let count = yield m.aggregateEventsFiles();
   Assert.equal(count, 1);
 
   let crashes = yield m.getCrashes();
   Assert.equal(crashes.length, 1);
-  Assert.equal(crashes[0].id, crashId);
+  Assert.equal(crashes[0].id, "id1");
   Assert.equal(crashes[0].type, "main-crash");
-  Assert.equal(crashes[0].metadata.ProductName, productName);
-  Assert.equal(crashes[0].metadata.ProductID, productId);
+  Assert.equal(crashes[0].metadata.k1, "v1");
+  Assert.equal(crashes[0].metadata.k2, "v2");
   Assert.ok(crashes[0].metadata.TelemetryEnvironment);
-  Assert.equal(Object.getOwnPropertyNames(crashes[0].metadata).length, 6);
+  Assert.equal(Object.getOwnPropertyNames(crashes[0].metadata).length, 5);
   Assert.equal(crashes[0].metadata.TelemetrySessionId, sessionId);
   Assert.ok(crashes[0].metadata.StackTraces);
   Assert.deepEqual(crashes[0].crashDate, DUMMY_DATE);
 
   let found = yield ac.promiseFindPing("crash", [
     [["payload", "hasCrashEnvironment"], true],
-    [["payload", "metadata", "ProductName"], productName],
-    [["payload", "metadata", "ProductID"], productId],
-    [["payload", "crashId"], crashId],
+    [["payload", "metadata", "k1"], "v1"],
+    [["payload", "crashId"], "1"],
     [["payload", "stackTraces", "status"], "OK"],
     [["payload", "sessionId"], sessionId],
   ]);
   Assert.ok(found, "Telemetry ping submitted for found crash");
-  Assert.deepEqual(found.environment, theEnvironment,
-                   "The saved environment should be present");
-  Assert.equal(found.payload.metadata.ThisShouldNot, undefined,
-               "Non-whitelisted fields should be filtered out");
+  Assert.deepEqual(found.environment, theEnvironment, "The saved environment should be present");
 
   count = yield m.aggregateEventsFiles();
   Assert.equal(count, 0);
 });
 
 add_task(function* test_main_crash_event_file_noenv() {
   let ac = new TelemetryArchiveTesting.Checker();
   yield ac.promiseInit();
-  const fileContent = crashId + "\n" +
-    "ProductName=" + productName + "\n" +
-    "ProductID=" + productId + "\n";
 
   let m = yield getManager();
-  yield m.createEventsFile(crashId, "crash.main.2", DUMMY_DATE, fileContent);
+  yield m.createEventsFile("1", "crash.main.2", DUMMY_DATE, "id1\nk1=v3\nk2=v2");
   let count = yield m.aggregateEventsFiles();
   Assert.equal(count, 1);
 
   let crashes = yield m.getCrashes();
   Assert.equal(crashes.length, 1);
-  Assert.equal(crashes[0].id, crashId);
+  Assert.equal(crashes[0].id, "id1");
   Assert.equal(crashes[0].type, "main-crash");
-  Assert.deepEqual(crashes[0].metadata, {
-    ProductName: productName,
-    ProductID: productId
-  });
+  Assert.deepEqual(crashes[0].metadata, { k1: "v3", k2: "v2"});
   Assert.deepEqual(crashes[0].crashDate, DUMMY_DATE);
 
   let found = yield ac.promiseFindPing("crash", [
     [["payload", "hasCrashEnvironment"], false],
-    [["payload", "metadata", "ProductName"], productName],
-    [["payload", "metadata", "ProductID"], productId],
+    [["payload", "metadata", "k1"], "v3"],
   ]);
   Assert.ok(found, "Telemetry ping submitted for found crash");
   Assert.ok(found.environment, "There is an environment");
 
   count = yield m.aggregateEventsFiles();
   Assert.equal(count, 0);
 });
 
@@ -453,38 +435,16 @@ add_task(function* test_addCrash() {
 
   crash = map.get("changing-item");
   Assert.ok(!!crash);
   Assert.equal(crash.crashDate, DUMMY_DATE_2);
   Assert.equal(crash.type, m.PROCESS_TYPE_CONTENT + "-" + m.CRASH_TYPE_HANG);
   Assert.ok(crash.isOfType(m.PROCESS_TYPE_CONTENT, m.CRASH_TYPE_HANG));
 });
 
-add_task(function* test_content_crash_ping() {
-  let ac = new TelemetryArchiveTesting.Checker();
-  yield ac.promiseInit();
-
-  let m = yield getManager();
-  let id = yield m.createDummyDump();
-  yield m.addCrash(m.PROCESS_TYPE_CONTENT, m.CRASH_TYPE_CRASH, id, DUMMY_DATE, {
-    StackTraces: stackTraces,
-    ThisShouldNot: "end-up-in-the-ping"
-  });
-  yield m._pingPromise;
-
-  let found = yield ac.promiseFindPing("crash", [
-    [["payload", "crashId"], id],
-    [["payload", "processType"], m.PROCESS_TYPE_CONTENT],
-    [["payload", "stackTraces", "status"], "OK"],
-  ]);
-  Assert.ok(found, "Telemetry ping submitted for content crash");
-  Assert.equal(found.payload.metadata.ThisShouldNot, undefined,
-               "Non-whitelisted fields should be filtered out");
-});
-
 add_task(function* test_generateSubmissionID() {
   let m = yield getManager();
 
   const SUBMISSION_ID_REGEX =
     /^(sub-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i;
   let id = m.generateSubmissionID();
   Assert.ok(SUBMISSION_ID_REGEX.test(id));
 });
--- a/toolkit/crashreporter/docs/index.rst
+++ b/toolkit/crashreporter/docs/index.rst
@@ -125,25 +125,16 @@ actor should check for it by calling Tak
 Method: see ``mozilla::plugins::PluginModuleParent::ActorDestroy`` and
 ``mozilla::plugins::PluginModuleParent::ProcessFirstMinidump``. That method
 is responsible for calling
 ``mozilla::dom::CrashReporterParent::GenerateCrashReportForMinidump`` with
 appropriate crash annotations specific to the crash. All child-process
 crashes are annotated with a ``ProcessType`` annotation, such as "content" or
 "plugin".
 
-Once the minidump file has been generated the
-``mozilla::dom::CrashReporterHost`` is notified of the crash. It will first
-try to extract the stack traces from the minidump file using the
-*minidump analyzer*. Then the stack traces will be stored in the extra file
-together with the rest of the crash annotations and finally the crash will be
-recorded by calling ```CrashService.addCrash()```. This last step adds the
-crash to the ```CrashManager``` database and automatically sends a crash ping
-with information about the crash.
-
 Submission of child process crashes is handled by application code. This
 code prompts the user to submit crashes in context-appropriate UI and then
 submits the crashes using ``CrashSubmit.jsm``.
 
 Memory Reports
 ==============
 
 When a process detects that it is running low on memory, a memory report is
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -3,17 +3,16 @@
 /* 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 "nsExceptionHandler.h"
 
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsDirectoryServiceDefs.h"
-#include "nsDirectoryService.h"
 #include "nsDataHashtable.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/dom/CrashReporterChild.h"
 #include "mozilla/ipc/CrashReporterClient.h"
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
 #include "mozilla/Unused.h"
 #include "mozilla/Sprintf.h"
@@ -125,17 +124,16 @@ namespace CrashReporter {
 #ifdef XP_WIN32
 typedef wchar_t XP_CHAR;
 typedef std::wstring xpstring;
 #define XP_TEXT(x) L##x
 #define CONVERT_XP_CHAR_TO_UTF16(x) x
 #define XP_STRLEN(x) wcslen(x)
 #define my_strlen strlen
 #define CRASH_REPORTER_FILENAME "crashreporter.exe"
-#define MINIDUMP_ANALYZER_FILENAME "minidump-analyzer.exe"
 #define PATH_SEPARATOR "\\"
 #define XP_PATH_SEPARATOR L"\\"
 #define XP_PATH_SEPARATOR_CHAR L'\\'
 #define XP_PATH_MAX (MAX_PATH + 1)
 // "<reporter path>" "<minidump path>"
 #define CMDLINE_SIZE ((XP_PATH_MAX * 2) + 6)
 #ifdef _USE_32BIT_TIME_T
 #define XP_TTOA(time, buffer, base) ltoa(time, buffer, base)
@@ -144,17 +142,16 @@ typedef std::wstring xpstring;
 #endif
 #define XP_STOA(size, buffer, base) _ui64toa(size, buffer, base)
 #else
 typedef char XP_CHAR;
 typedef std::string xpstring;
 #define XP_TEXT(x) x
 #define CONVERT_XP_CHAR_TO_UTF16(x) NS_ConvertUTF8toUTF16(x)
 #define CRASH_REPORTER_FILENAME "crashreporter"
-#define MINIDUMP_ANALYZER_FILENAME "minidump-analyzer"
 #define PATH_SEPARATOR "/"
 #define XP_PATH_SEPARATOR "/"
 #define XP_PATH_SEPARATOR_CHAR '/'
 #define XP_PATH_MAX PATH_MAX
 #ifdef XP_LINUX
 #define XP_STRLEN(x) my_strlen(x)
 #define XP_TTOA(time, buffer, base) my_inttostring(time, buffer, sizeof(buffer))
 #define XP_STOA(size, buffer, base) my_inttostring(size, buffer, sizeof(buffer))
@@ -181,26 +178,51 @@ typedef std::string xpstring;
 static const XP_CHAR dumpFileExtension[] = XP_TEXT(".dmp");
 #endif
 
 static const XP_CHAR childCrashAnnotationBaseName[] = XP_TEXT("GeckoChildCrash");
 static const XP_CHAR extraFileExtension[] = XP_TEXT(".extra");
 static const XP_CHAR memoryReportExtension[] = XP_TEXT(".memory.json.gz");
 static xpstring *defaultMemoryReportPath = nullptr;
 
+// A whitelist of crash annotations which do not contain sensitive data
+// and are saved in the crash record and sent with Firefox Health Report.
+static char const * const kCrashEventAnnotations[] = {
+  "AsyncShutdownTimeout",
+  "BuildID",
+  "ProductID",
+  "ProductName",
+  "ReleaseChannel",
+  "SecondsSinceLastCrash",
+  "ShutdownProgress",
+  "StartupCrash",
+  "TelemetryEnvironment",
+  "Version",
+  // The following entries are not normal annotations but are included
+  // in the crash record/FHR:
+  // "ContainsMemoryReport"
+  // "EventLoopNestingLevel"
+  // "IsGarbageCollecting"
+  // "AvailablePageFile"
+  // "AvailableVirtualMemory"
+  // "SystemMemoryUsePercentage"
+  // "OOMAllocationSize"
+  // "TotalPageFile"
+  // "TotalPhysicalMemory"
+  // "TotalVirtualMemory"
+  // "MozCrashReason"
+};
+
 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;
-#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;
 
@@ -524,22 +546,20 @@ CreatePathFromFile(nsIFile* file)
   if (NS_FAILED(rv)) {
     return nullptr;
   }
   return new xpstring(path.get(), path.Length());
 }
 #endif
 
 static XP_CHAR*
-Concat(XP_CHAR* str, const XP_CHAR* toAppend, size_t* size)
+Concat(XP_CHAR* str, const XP_CHAR* toAppend, int* size)
 {
-  size_t appendLen = XP_STRLEN(toAppend);
-  if (appendLen >= *size) {
-    appendLen = *size - 1;
-  }
+  int appendLen = XP_STRLEN(toAppend);
+  if (appendLen >= *size) appendLen = *size - 1;
 
   memcpy(str, toAppend, appendLen * sizeof(XP_CHAR));
   str += appendLen;
   *str = '\0';
   *size -= appendLen;
 
   return str;
 }
@@ -749,17 +769,17 @@ WriteAnnotation(PlatformWriter& pw, cons
  * dump file path.
  */
 static void
 OpenAPIData(PlatformWriter& aWriter,
             const XP_CHAR* dump_path, const XP_CHAR* minidump_id = nullptr
            )
 {
   static XP_CHAR extraDataPath[XP_PATH_MAX];
-  size_t size = XP_PATH_MAX;
+  int size = XP_PATH_MAX;
   XP_CHAR* p;
   if (minidump_id) {
     p = Concat(extraDataPath, dump_path, &size);
     p = Concat(p, XP_PATH_SEPARATOR, &size);
     p = Concat(p, minidump_id, &size);
   } else {
     p = Concat(extraDataPath, dump_path, &size);
     // Skip back past the .dmp extension, if any.
@@ -800,146 +820,16 @@ WriteGlobalMemoryStatus(PlatformWriter* 
     WRITE_STATEX_FIELD(ullTotalPhys, "TotalPhysicalMemory", _ui64toa);
     WRITE_STATEX_FIELD(ullAvailPhys, "AvailablePhysicalMemory", _ui64toa);
 
 #undef WRITE_STATEX_FIELD
   }
 }
 #endif
 
-#if !defined(MOZ_WIDGET_ANDROID)
-
-/**
- * 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
- */
-static bool
-LaunchProgram(XP_CHAR* aProgramPath, 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);
-  p = Concat(p, L"\" \"", &size);
-  p = Concat(p, aMinidumpPath, &size);
-  Concat(p, L"\"", &size);
-
-  STARTUPINFO si;
-  PROCESS_INFORMATION pi;
-  ZeroMemory(&si, sizeof(si));
-  si.cb = sizeof(si);
-  ZeroMemory(&pi, sizeof(pi));
-
-  // 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)) {
-    CloseHandle(pi.hProcess);
-    CloseHandle(pi.hThread);
-  }
-#elif defined(XP_UNIX)
-#ifdef XP_MACOSX
-  pid_t pid = 0;
-  char* const my_argv[] = {
-    aProgramPath,
-    aMinidumpPath,
-    nullptr
-  };
-
-  char **env = nullptr;
-  char ***nsEnv = _NSGetEnviron();
-  if (nsEnv)
-    env = *nsEnv;
-
-  int rv = posix_spawnp(&pid, my_argv[0], nullptr, &spawnattr, my_argv, env);
-
-  if (rv != 0) {
-    return false;
-  }
-
-#else // !XP_MACOSX
-  pid_t pid = sys_fork();
-
-  if (pid == -1) {
-    return false;
-  } else if (pid == 0) {
-    // need to clobber this, as libcurl might load NSS,
-    // and we want it to load the system NSS.
-    unsetenv("LD_LIBRARY_PATH");
-    Unused << execl(aProgramPath,
-                    aProgramPath, aMinidumpPath, (char*)0);
-    _exit(1);
-  }
-#endif // XP_MACOSX
-#endif // XP_UNIX
-
-  return true;
-}
-
-#else
-
-/**
- * Launch the crash reporter activity on Android
- *
- * @param aProgramPath The path of the program to be launched
- * @param aMinidumpPath The path to the crash minidump file
- * @param aSucceeded True if the minidump was obtained successfully
- */
-
-static bool
-LaunchCrashReporterActivity(XP_CHAR* aProgramPath, XP_CHAR* aMinidumpPath,
-                            bool aSucceeded)
-{
-  pid_t pid = sys_fork();
-
-  if (pid == -1)
-    return false;
-  else if (pid == 0) {
-    // Invoke the reportCrash activity using am
-    if (androidUserSerial) {
-      Unused << execlp("/system/bin/am",
-                       "/system/bin/am",
-                       "start",
-                       "--user", androidUserSerial,
-                       "-a", "org.mozilla.gecko.reportCrash",
-                       "-n", aProgramPath,
-                       "--es", "minidumpPath", aMinidumpPath,
-                       "--ez", "minidumpSuccess", aSucceeded ? "true" : "false",
-                       (char*)0);
-    } else {
-      Unused << execlp("/system/bin/am",
-                       "/system/bin/am",
-                       "start",
-                       "-a", "org.mozilla.gecko.reportCrash",
-                       "-n", aProgramPath,
-                       "--es", "minidumpPath", aMinidumpPath,
-                       "--ez", "minidumpSuccess", aSucceeded ? "true" : "false",
-                       (char*)0);
-    }
-    _exit(1);
-
-  } else {
-    // We need to wait on the 'am start' command above to finish, otherwise everything will
-    // be killed by the ActivityManager as soon as the signal handler exits
-    int status;
-    Unused << HANDLE_EINTR(sys_waitpid(pid, &status, __WALL));
-  }
-
-  return true;
-}
-
-#endif
-
 bool MinidumpCallback(
 #ifdef XP_LINUX
                       const MinidumpDescriptor& descriptor,
 #else
                       const XP_CHAR* dump_path,
                       const XP_CHAR* minidump_id,
 #endif
                       void* context,
@@ -947,17 +837,17 @@ bool MinidumpCallback(
                       EXCEPTION_POINTERS* exinfo,
                       MDRawAssertionInfo* assertion,
 #endif
                       bool succeeded)
 {
   bool returnValue = showOSCrashReporter ? false : succeeded;
 
   static XP_CHAR minidumpPath[XP_PATH_MAX];
-  size_t size = XP_PATH_MAX;
+  int size = XP_PATH_MAX;
   XP_CHAR* p;
 #ifndef XP_LINUX
   p = Concat(minidumpPath, dump_path, &size);
   p = Concat(p, XP_PATH_SEPARATOR, &size);
   p = Concat(p, minidump_id, &size);
   Concat(p, dumpFileExtension, &size);
 #else
   Concat(minidumpPath, descriptor.path(), &size);
@@ -1069,17 +959,17 @@ bool MinidumpCallback(
 #endif
 
   {
     PlatformWriter apiData;
     PlatformWriter eventFile;
 
     if (eventsDirectory) {
       static XP_CHAR crashEventPath[XP_PATH_MAX];
-      size_t size = XP_PATH_MAX;
+      int size = XP_PATH_MAX;
       XP_CHAR* p;
       p = Concat(crashEventPath, eventsDirectory, &size);
       p = Concat(p, XP_PATH_SEPARATOR, &size);
 #ifdef XP_LINUX
       p = Concat(p, id_ascii, &size);
 #else
       p = Concat(p, minidump_id, &size);
 #endif
@@ -1115,18 +1005,20 @@ bool MinidumpCallback(
 
     if (timeSinceLastCrash != 0) {
       WriteAnnotation(apiData, "SecondsSinceLastCrash",
                       timeSinceLastCrashString);
       WriteAnnotation(eventFile, "SecondsSinceLastCrash",
                       timeSinceLastCrashString);
     }
     if (isGarbageCollecting) {
-      WriteAnnotation(apiData, "IsGarbageCollecting", "1");
-      WriteAnnotation(eventFile, "IsGarbageCollecting", "1");
+      WriteAnnotation(apiData, "IsGarbageCollecting",
+                      isGarbageCollecting ? "1" : "0");
+      WriteAnnotation(eventFile, "IsGarbageCollecting",
+                      isGarbageCollecting ? "1" : "0");
     }
 
     char buffer[128];
 
     if (eventloopNestingLevel > 0) {
       XP_STOA(eventloopNestingLevel, buffer, 10);
       WriteAnnotation(apiData, "EventLoopNestingLevel", buffer);
       WriteAnnotation(eventFile, "EventLoopNestingLevel", buffer);
@@ -1182,32 +1074,118 @@ bool MinidumpCallback(
     }
 
     if (memoryReportPath) {
       WriteLiteral(apiData, "ContainsMemoryReport=1\n");
       WriteLiteral(eventFile, "ContainsMemoryReport=1\n");
     }
   }
 
+#ifdef XP_WIN
   if (!doReport) {
-#ifdef XP_WIN
     TerminateProcess(GetCurrentProcess(), 1);
-#endif // XP_WIN
+    return returnValue;
+  }
+
+  XP_CHAR cmdLine[CMDLINE_SIZE];
+  size = CMDLINE_SIZE;
+  p = Concat(cmdLine, L"\"", &size);
+  p = Concat(p, crashReporterPath, &size);
+  p = Concat(p, L"\" \"", &size);
+  p = Concat(p, minidumpPath, &size);
+  Concat(p, L"\"", &size);
+
+  STARTUPINFO si;
+  PROCESS_INFORMATION pi;
+
+  ZeroMemory(&si, sizeof(si));
+  si.cb = sizeof(si);
+  si.dwFlags = STARTF_USESHOWWINDOW;
+  si.wShowWindow = SW_SHOWNORMAL;
+  ZeroMemory(&pi, sizeof(pi));
+
+  if (CreateProcess(nullptr, (LPWSTR)cmdLine, nullptr, nullptr, FALSE, 0,
+                    nullptr, nullptr, &si, &pi)) {
+    CloseHandle( pi.hProcess );
+    CloseHandle( pi.hThread );
+  }
+  // we're not really in a position to do anything if the CreateProcess fails
+  TerminateProcess(GetCurrentProcess(), 1);
+#elif defined(XP_UNIX)
+  if (!doReport) {
     return returnValue;
   }
 
-#if defined(MOZ_WIDGET_ANDROID) // Android
-  returnValue = LaunchCrashReporterActivity(crashReporterPath, minidumpPath,
-                                            succeeded);
-#else // Windows, Mac, Linux, etc...
-  returnValue = LaunchProgram(crashReporterPath, minidumpPath);
-#ifdef XP_WIN
-  TerminateProcess(GetCurrentProcess(), 1);
+#ifdef XP_MACOSX
+  char* const my_argv[] = {
+    crashReporterPath,
+    minidumpPath,
+    nullptr
+  };
+
+  char **env = nullptr;
+  char ***nsEnv = _NSGetEnviron();
+  if (nsEnv)
+    env = *nsEnv;
+  int result = posix_spawnp(nullptr,
+                            my_argv[0],
+                            nullptr,
+                            &spawnattr,
+                            my_argv,
+                            env);
+
+  if (result != 0)
+    return false;
+
+#else // !XP_MACOSX
+  pid_t pid = sys_fork();
+
+  if (pid == -1)
+    return false;
+  else if (pid == 0) {
+#if !defined(MOZ_WIDGET_ANDROID)
+    // need to clobber this, as libcurl might load NSS,
+    // and we want it to load the system NSS.
+    unsetenv("LD_LIBRARY_PATH");
+    Unused << execl(crashReporterPath,
+                    crashReporterPath, minidumpPath, (char*)0);
+#else
+    // Invoke the reportCrash activity using am
+    if (androidUserSerial) {
+      Unused << execlp("/system/bin/am",
+                       "/system/bin/am",
+                       "start",
+                       "--user", androidUserSerial,
+                       "-a", "org.mozilla.gecko.reportCrash",
+                       "-n", crashReporterPath,
+                       "--es", "minidumpPath", minidumpPath,
+                       "--ez", "minidumpSuccess", succeeded ? "true" : "false",
+                       (char*)0);
+    } else {
+      Unused << execlp("/system/bin/am",
+                       "/system/bin/am",
+                       "start",
+                       "-a", "org.mozilla.gecko.reportCrash",
+                       "-n", crashReporterPath,
+                       "--es", "minidumpPath", minidumpPath,
+                       "--ez", "minidumpSuccess", succeeded ? "true" : "false",
+                       (char*)0);
+    }
 #endif
+    _exit(1);
+#ifdef MOZ_WIDGET_ANDROID
+  } else {
+    // We need to wait on the 'am start' command above to finish, otherwise everything will
+    // be killed by the ActivityManager as soon as the signal handler exits
+    int status;
+    Unused << HANDLE_EINTR(sys_waitpid(pid, &status, __WALL));
 #endif
+  }
+#endif // XP_MACOSX
+#endif // XP_UNIX
 
   return returnValue;
 }
 
 #if defined(XP_MACOSX) || defined(__ANDROID__) || defined(XP_LINUX)
 static size_t
 EnsureTrailingSlash(XP_CHAR* aBuf, size_t aBufLen)
 {
@@ -1272,32 +1250,32 @@ BuildTempPath(char* aBuf, size_t aBufLen
 static size_t
 BuildTempPath(char* aBuf, size_t aBufLen)
 {
   // GeckoAppShell or Gonk's init.rc sets this in the environment
   const char *tempenv = PR_GetEnv("TMPDIR");
   if (!tempenv) {
     return false;
   }
-  size_t size = aBufLen;
+  int size = (int)aBufLen;
   Concat(aBuf, tempenv, &size);
   return EnsureTrailingSlash(aBuf, aBufLen);
 }
 
 #elif defined(XP_UNIX)
 
 static size_t
 BuildTempPath(char* aBuf, size_t aBufLen)
 {
   const char *tempenv = PR_GetEnv("TMPDIR");
   const char *tmpPath = "/tmp/";
   if (!tempenv) {
     tempenv = tmpPath;
   }
-  size_t size = aBufLen;
+  int size = (int)aBufLen;
   Concat(aBuf, tempenv, &size);
   return EnsureTrailingSlash(aBuf, aBufLen);
 }
 
 #else
 #error "Implement this for your platform"
 #endif
 
@@ -1324,17 +1302,17 @@ BuildTempPath(PathStringT& aResult)
 
 static void
 PrepareChildExceptionTimeAnnotations()
 {
   MOZ_ASSERT(!XRE_IsParentProcess());
   static XP_CHAR tempPath[XP_PATH_MAX] = {0};
 
   // Get the temp path
-  size_t charsAvailable = XP_PATH_MAX;
+  int charsAvailable = XP_PATH_MAX;
   XP_CHAR* p = tempPath;
 #if (defined(XP_MACOSX) || defined(XP_WIN))
   if (!childProcessTmpDir || childProcessTmpDir->empty()) {
     return;
   }
   p = Concat(p, childProcessTmpDir->c_str(), &charsAvailable);
   // Ensure that this path ends with a path separator
   if (p > tempPath && *(p - 1) != XP_PATH_SEPARATOR_CHAR) {
@@ -1537,42 +1515,16 @@ ChildFilter(void* context)
 {
   bool result = Filter(context);
   if (result) {
     PrepareChildExceptionTimeAnnotations();
   }
   return result;
 }
 
-#if !defined(MOZ_WIDGET_ANDROID)
-
-// Locate the specified executable and store its path as a native string in
-// the |aPathPtr| so we can later invoke it from within the exception handler.
-static nsresult
-LocateExecutable(nsIFile* aXREDirectory, const nsACString& aName,
-                 nsAString& aPath)
-{
-  nsCOMPtr<nsIFile> exePath;
-  nsresult rv = aXREDirectory->Clone(getter_AddRefs(exePath));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-#ifdef XP_MACOSX
-  exePath->SetNativeLeafName(NS_LITERAL_CSTRING("MacOS"));
-  exePath->Append(NS_LITERAL_STRING("crashreporter.app"));
-  exePath->Append(NS_LITERAL_STRING("Contents"));
-  exePath->Append(NS_LITERAL_STRING("MacOS"));
-#endif
-
-  exePath->AppendNative(aName);
-  exePath->GetPath(aPath);
-  return NS_OK;
-}
-
-#endif // !defined(MOZ_WIDGET_ANDROID)
-
 nsresult SetExceptionHandler(nsIFile* aXREDirectory,
                              bool force/*=false*/)
 {
   if (gExceptionHandler)
     return NS_ERROR_ALREADY_INITIALIZED;
 
 #if !defined(DEBUG) || defined(MOZ_WIDGET_GONK)
   // In non-debug builds, enable the crash reporter by default, and allow
@@ -1613,57 +1565,54 @@ nsresult SetExceptionHandler(nsIFile* aX
   crashReporterAPIData_Hash =
     new nsDataHashtable<nsCStringHashKey,nsCString>();
   NS_ENSURE_TRUE(crashReporterAPIData_Hash, NS_ERROR_OUT_OF_MEMORY);
 
   notesField = new nsCString();
   NS_ENSURE_TRUE(notesField, NS_ERROR_OUT_OF_MEMORY);
 
   if (!headlessClient) {
-#if !defined(MOZ_WIDGET_ANDROID)
-    // Locate the crash reporter executable
-    nsAutoString crashReporterPath_temp;
-    nsresult rv = LocateExecutable(aXREDirectory,
-                                   NS_LITERAL_CSTRING(CRASH_REPORTER_FILENAME),
-                                   crashReporterPath_temp);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    nsAutoString minidumpAnalyzerPath_temp;
-    rv = LocateExecutable(aXREDirectory,
-                          NS_LITERAL_CSTRING(MINIDUMP_ANALYZER_FILENAME),
-                          minidumpAnalyzerPath_temp);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
+    // locate crashreporter executable
+    nsCOMPtr<nsIFile> exePath;
+    nsresult rv = aXREDirectory->Clone(getter_AddRefs(exePath));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+#if defined(XP_MACOSX)
+    exePath->SetNativeLeafName(NS_LITERAL_CSTRING("MacOS"));
+    exePath->Append(NS_LITERAL_STRING("crashreporter.app"));
+    exePath->Append(NS_LITERAL_STRING("Contents"));
+    exePath->Append(NS_LITERAL_STRING("MacOS"));
+#endif
+
+    exePath->AppendNative(NS_LITERAL_CSTRING(CRASH_REPORTER_FILENAME));
 
 #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);
-#endif // XP_WIN32
+    nsString crashReporterPath_temp;
+
+    exePath->GetPath(crashReporterPath_temp);
+    crashReporterPath = reinterpret_cast<wchar_t*>(ToNewUnicode(crashReporterPath_temp));
+#elif !defined(__ANDROID__)
+    nsCString crashReporterPath_temp;
+
+    exePath->GetNativePath(crashReporterPath_temp);
+    crashReporterPath = ToNewCString(crashReporterPath_temp);
 #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.
     const char* androidPackageName = PR_GetEnv("MOZ_ANDROID_PACKAGE_NAME");
     if (androidPackageName != nullptr) {
       nsCString package(androidPackageName);
       package.Append("/org.mozilla.gecko.CrashReporter");
       crashReporterPath = ToNewCString(package);
     } else {
       nsCString package(ANDROID_PACKAGE_NAME "/org.mozilla.gecko.CrashReporter");
       crashReporterPath = ToNewCString(package);
     }
-#endif // !defined(MOZ_WIDGET_ANDROID)
+#endif
   }
 
   // get temp path to use for minidump path
 #if defined(XP_WIN32)
   nsString tempPath;
 #else
   nsCString tempPath;
 #endif
@@ -2165,16 +2114,27 @@ static void ReplaceChar(nsCString& str, 
     str.Replace(pos - 1, 1, replacement);
 
     str.BeginReading(iter);
     iter.advance(pos + replacement.Length() - 1);
     str.EndReading(end);
   }
 }
 
+static bool
+IsInWhitelist(const nsACString& key)
+{
+  for (size_t i = 0; i < ArrayLength(kCrashEventAnnotations); ++i) {
+    if (key.EqualsASCII(kCrashEventAnnotations[i])) {
+      return true;
+    }
+  }
+  return false;
+}
+
 // This function is miscompiled with MSVC 2005/2008 when PGO is on.
 #ifdef _MSC_VER
 #pragma optimize("", off)
 #endif
 static nsresult
 EscapeAnnotation(const nsACString& key, const nsACString& data, nsCString& escapedData)
 {
   if (FindInReadable(NS_LITERAL_CSTRING("="), key) ||
@@ -2301,17 +2261,19 @@ nsresult AnnotateCrashReport(const nsACS
     const nsACString& key = it.Key();
     nsCString entry = it.Data();
     if (!entry.IsEmpty()) {
       NS_NAMED_LITERAL_CSTRING(kEquals, "=");
       NS_NAMED_LITERAL_CSTRING(kNewline, "\n");
       nsAutoCString line = key + kEquals + entry + kNewline;
 
       crashReporterAPIData->Append(line);
-      crashEventAPIData->Append(line);
+      if (IsInWhitelist(key)) {
+        crashEventAPIData->Append(line);
+      }
     }
   }
 
   return NS_OK;
 }
 
 nsresult RemoveCrashReportAnnotation(const nsACString& key)
 {
@@ -3063,40 +3025,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)) && file) {
-    nsAutoString path;
-
-    file->GetPath(path);
-    LaunchProgram(minidumpAnalyzerPath,
-                  (XP_CHAR*)(NS_ConvertUTF16toUTF8(path).get()));
-  }
-#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.