Backed out 7 changesets (bug 1194555) for xpcshell failures
authorWes Kocher <wkocher@mozilla.com>
Tue, 06 Oct 2015 14:40:51 -0700
changeset 299844 dc6be2cf0c5d89a1e75497a83559972a8fbf6b20
parent 299843 a319d7355228b7174fe7b94671f10f7508b482aa
child 299845 ac69d6a3ff2236452b9033d75e00121dc0729cb5
push id5392
push userraliiev@mozilla.com
push dateMon, 14 Dec 2015 20:08:23 +0000
treeherdermozilla-beta@16ce8562a975 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1194555
milestone44.0a1
backs oute77be333f4fb32b1556c2e01308ebef44acc2d16
11951462a37c71976aac60a9094a9fa3e6cef11c
99b27aa952c57741df91fc047c2d973437e9c285
70a8ed3b6a450fdf309e13bbbfd4409456bd8d66
748bfebe81e7f6abfbbacab8268f1be7fc19b8ad
cbaac05a29345a9793802decc27d67f51954c3da
fcbfd1379fcd3bbe55d6b1feff5610eb6475f36b
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
Backed out 7 changesets (bug 1194555) for xpcshell failures Backed out changeset e77be333f4fb (bug 1194555) Backed out changeset 11951462a37c (bug 1194555) Backed out changeset 99b27aa952c5 (bug 1194555) Backed out changeset 70a8ed3b6a45 (bug 1194555) Backed out changeset 748bfebe81e7 (bug 1194555) Backed out changeset cbaac05a2934 (bug 1194555) Backed out changeset fcbfd1379fcd (bug 1194555)
dom/ipc/ContentChild.cpp
image/test/mochitest/test_bug601470.html
toolkit/components/aboutmemory/tests/test_aboutmemory.xul
toolkit/components/aboutmemory/tests/test_aboutmemory2.xul
toolkit/components/aboutmemory/tests/test_memoryReporters.xul
toolkit/components/aboutmemory/tests/test_sqliteMultiReporter.xul
xpcom/base/nsIMemoryReporter.idl
xpcom/base/nsMemoryReporterManager.cpp
xpcom/base/nsMemoryReporterManager.h
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -890,42 +890,16 @@ private:
     nsRefPtr<MemoryReportRequestChild> mActor;
     const nsCString mProcess;
 };
 NS_IMPL_ISUPPORTS(
   MemoryReportCallback
 , nsIMemoryReporterCallback
 )
 
-class MemoryReportFinishedCallback final : public nsIFinishReportingCallback
-{
-public:
-    NS_DECL_ISUPPORTS
-
-    explicit MemoryReportFinishedCallback(MemoryReportRequestChild* aActor)
-    : mActor(aActor)
-    {
-    }
-
-    NS_IMETHOD Callback(nsISupports* aUnused) override
-    {
-        bool sent = PMemoryReportRequestChild::Send__delete__(mActor);
-        return sent ? NS_OK : NS_ERROR_FAILURE;
-    }
-
-private:
-    ~MemoryReportFinishedCallback() {}
-
-    nsRefPtr<MemoryReportRequestChild> mActor;
-};
-NS_IMPL_ISUPPORTS(
-  MemoryReportFinishedCallback
-, nsIFinishReportingCallback
-)
-
 bool
 ContentChild::RecvPMemoryReportRequestConstructor(
     PMemoryReportRequestChild* aChild,
     const uint32_t& aGeneration,
     const bool& aAnonymize,
     const bool& aMinimizeMemoryUsage,
     const MaybeFileDesc& aDMDFile)
 {
@@ -952,22 +926,21 @@ NS_IMETHODIMP MemoryReportRequestChild::
     nsCString process;
     child->GetProcessName(process);
     child->AppendProcessId(process);
 
     // Run the reporters.  The callback will turn each measurement into a
     // MemoryReport.
     nsRefPtr<MemoryReportCallback> cb =
         new MemoryReportCallback(this, process);
-    nsRefPtr<MemoryReportFinishedCallback> finished =
-        new MemoryReportFinishedCallback(this);
-
-    return mgr->GetReportsForThisProcessExtended(cb, nullptr, mAnonymize,
-                                                 FileDescriptorToFILE(mDMDFile, "wb"),
-                                                 finished, nullptr);
+    mgr->GetReportsForThisProcessExtended(cb, nullptr, mAnonymize,
+                                          FileDescriptorToFILE(mDMDFile, "wb"));
+
+    bool sent = Send__delete__(this);
+    return sent ? NS_OK : NS_ERROR_FAILURE;
 }
 
 bool
 ContentChild::RecvDataStoreNotify(const uint32_t& aAppId,
                                   const nsString& aName,
                                   const nsString& aManifestURL)
 {
   nsRefPtr<DataStoreService> service = DataStoreService::GetOrCreate();
--- a/image/test/mochitest/test_bug601470.html
+++ b/image/test/mochitest/test_bug601470.html
@@ -25,21 +25,20 @@ window.onload = function() {
   var mgr = SpecialPowers.Cc["@mozilla.org/memory-reporter-manager;1"]
     .getService(SpecialPowers.Ci.nsIMemoryReporterManager);
 
   var amount = 0;
   var handleReport = function(aProcess, aPath, aKind, aUnits, aAmount, aDesc) {
     amount += aAmount;
   }
 
-  var finished = function() {
-        ok(amount > 0, "we should be using a nonzero amount of memory");
-        ok(true, "yay, didn't crash!");
-        SimpleTest.finish();
-  }
+  mgr.getReportsForThisProcess(handleReport, null, /* anonymize = */ false);
 
-  mgr.getReports(handleReport, null, finished, null, /* anonymize = */ false);
+  ok(amount > 0, "we should be using a nonzero amount of memory");
+  ok(true, "yay, didn't crash!");
+
+  SimpleTest.finish();
 }
 
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
@@ -114,16 +114,31 @@
         f("compartments/system/bar");
       }
     }
   ];
   for (let i = 0; i < fakeReporters.length; i++) {
     mgr.registerStrongReporterEvenIfBlocked(fakeReporters[i]);
   }
 
+  // mgr.explicit sums "heap-allocated" and all the appropriate NONHEAP ones:
+  // - "explicit/c", "explicit/cc" x 2, "explicit/d", "explicit/e"
+  // - but *not* "explicit/c/d" x 2
+  // Check explicit now before we add the fake reporters for the fake 2nd
+  // and subsequent processes.
+  //
+  // Nb: mgr.explicit will throw NS_ERROR_NOT_AVAILABLE if this is a
+  // --disable-jemalloc build. Allow for that exception, but *only* that
+  // exception.
+  try {
+    is(mgr.explicit, 500*MB + (100 + 13 + 10)*MB + 599*KB, "mgr.explicit");
+  } catch (ex) {
+    is(ex.result, Cr.NS_ERROR_NOT_AVAILABLE, "mgr.explicit exception");
+  }
+
   // The main process always comes first when we display about:memory.  The
   // remaining processes are sorted by their |resident| values (starting with
   // the largest).  Processes without a |resident| memory reporter are saved
   // for the end.
   let fakeReporters2 = [
     { collectReports: function(aCbObj, aClosure, aAnonymize) {
         function f(aP1, aP2, aK, aU, aA) {
           aCbObj.callback(aP1, aP2, aK, aU, aA, "Desc.", aClosure);
@@ -547,24 +562,17 @@ End of 5th\n\
       let measureButton = doc.getElementById("measureButton");
       let verbose = doc.getElementById("verbose");
       verbose.checked = aVerbose;
       measureButton.click();
 
       SimpleTest.waitForClipboard(
         function(aActual) {
           mostRecentActual = aActual;
-          let rslt = aActual.trim() === aExpected.trim();
-          if (!rslt) {
-            // Try copying again.
-            synthesizeKey("A", {accelKey: true});
-            synthesizeKey("C", {accelKey: true});
-          }
-
-          return rslt;
+          return aActual.trim() === aExpected.trim();
         },
         function() {
           synthesizeKey("A", {accelKey: true});
           synthesizeKey("C", {accelKey: true});
         },
         aNext,
         function() {
           ok(false, "pasted text doesn't match for " + aFrameId);
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory2.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory2.xul
@@ -101,24 +101,17 @@
     }
 
     SimpleTest.executeSoon(function() {
       let mostRecentActual;
       document.getElementById("amFrame").focus();
       SimpleTest.waitForClipboard(
         function(aActual) {
           mostRecentActual = aActual;
-          let rslt = aActual.trim() === aExpected.trim();
-          if (!rslt) {
-            // Try copying again.
-            synthesizeKey("A", {accelKey: true});
-            synthesizeKey("C", {accelKey: true});
-          }
-
-          return rslt;
+          return aActual.trim() === aExpected.trim();
         },
         function() {
           synthesizeKey("A", {accelKey: true});
           synthesizeKey("C", {accelKey: true});
         },
         aNext,
         function() {
           ok(false, "pasted text doesn't match");
--- a/toolkit/components/aboutmemory/tests/test_memoryReporters.xul
+++ b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul
@@ -39,18 +39,16 @@
   const COUNT_CUMULATIVE = Ci.nsIMemoryReporter.UNITS_COUNT_CUMULATIVE;
   const PERCENTAGE = Ci.nsIMemoryReporter.UNITS_PERCENTAGE;
 
   // Use backslashes instead of forward slashes due to memory reporting's hacky
   // handling of URLs.
   const XUL_NS =
     "http:\\\\www.mozilla.org\\keymaster\\gatekeeper\\there.is.only.xul";
 
-  SimpleTest.waitForExplicitFinish();
-
   let vsizeAmounts = [];
   let residentAmounts = [];
   let heapAllocatedAmounts = [];
   let storageSqliteAmounts = [];
 
   let jsGcHeapUsedGcThingsTotal = 0;
   let jsGcHeapUsedGcThings = {};
 
@@ -138,16 +136,31 @@
     if (reducedPath.search('file:..[^<]') !== -1) {
         present.unanonymizedFilePathWhenAnonymized = aPath;
     }
   }
 
   let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
             getService(Ci.nsIMemoryReporterManager);
 
+  // Access the distinguished amounts (mgr.explicit et al.) just to make sure
+  // they don't crash.  We can't check their actual values because they're
+  // non-deterministic.
+  //
+  // Nb: mgr.explicit will throw NS_ERROR_NOT_AVAILABLE if this is a
+  // --disable-jemalloc build.  Allow for that exception, but *only* that
+  // exception.
+  let dummy;
+  let haveExplicit = true;
+  try {
+    dummy = mgr.explicit;
+  } catch (ex) {
+    is(ex.result, Cr.NS_ERROR_NOT_AVAILABLE, "mgr.explicit exception");
+    haveExplicit = false;
+  }
   let amounts = [
     "vsize",
     "vsizeMaxContiguous",
     "resident",
     "residentFast",
     "residentPeak",
     "residentUnique",
     "heapAllocated",
@@ -164,17 +177,17 @@
     "pageFaultsHard",
   ];
   for (let i = 0; i < amounts.length; i++) {
     try {
       // If mgr[amounts[i]] throws an exception, just move on -- some amounts
       // aren't available on all platforms.  But if the attribute simply
       // isn't present, that indicates the distinguished amounts have changed
       // and this file hasn't been updated appropriately.
-      let dummy = mgr[amounts[i]];
+      dummy = mgr[amounts[i]];
       ok(dummy !== undefined,
          "accessed an unknown distinguished amount: " + amounts[i]);
     } catch (ex) {
     }
   }
 
   // Run sizeOfTab() to make sure it doesn't crash.  We can't check the result
   // values because they're non-deterministic.
@@ -186,43 +199,21 @@
   let otherSize = {};
   let totalSize = {};
   let jsMilliseconds = {};
   let nonJSMilliseconds = {};
   mgr.sizeOfTab(window, jsObjectsSize, jsStringsSize, jsOtherSize,
                 domSize, styleSize, otherSize, totalSize,
                 jsMilliseconds, nonJSMilliseconds);
 
-  let asyncSteps = [
-    getReportsNormal,
-    getReportsAnonymized,
-    checkResults,
-    test_register_strong,
-    test_register_strong, // Make sure re-registering works
-    test_register_weak,
-    SimpleTest.finish
-  ];
-
-  function runNext() {
-    setTimeout(asyncSteps.shift(), 0);
-  }
+  mgr.getReportsForThisProcess(handleReportNormal, null,
+                               /* anonymize = */ false);
 
-  function getReportsNormal()
-  {
-    mgr.getReports(handleReportNormal, null,
-                   runNext, null,
-                   /* anonymize = */ false);
-  }
-
-  function getReportsAnonymized()
-  {
-    mgr.getReports(handleReportAnonymized, null,
-                   runNext, null,
-                   /* anonymize = */ true);
-  }
+  mgr.getReportsForThisProcess(handleReportAnonymized, null,
+                               /* anonymize = */ true);
 
   function checkSizeReasonable(aName, aAmount)
   {
     // Check the size is reasonable -- i.e. not ridiculously large or small.
     ok(100 * 1000 <= aAmount && aAmount <= 10 * 1000 * 1000 * 1000,
        aName + "'s size is reasonable");
   }
 
@@ -230,60 +221,50 @@
   {
     ok(aAmounts.length == 1, aName + " has " + aAmounts.length + " report");
     let n = aAmounts[0];
     if (!aCanBeUnreasonable) {
       checkSizeReasonable(aName, n);
     }
   }
 
-  function checkResults()
-  {
-    try {
-      // Nb: mgr.heapAllocated will throw NS_ERROR_NOT_AVAILABLE if this is a
-      // --disable-jemalloc build.  Allow for skipping this test on that
-      // exception, but *only* that exception.
-      let dummy = mgr.heapAllocated;
-      checkSpecialReport("heap-allocated", heapAllocatedAmounts);
-    } catch (ex) {
-      is(ex.result, Cr.NS_ERROR_NOT_AVAILABLE, "mgr.heapAllocated exception");
-    }
-    // vsize may be unreasonable if ASAN is enabled
-    checkSpecialReport("vsize",          vsizeAmounts, /*canBeUnreasonable*/true);
-    checkSpecialReport("resident",       residentAmounts);
+  // If mgr.explicit failed, we won't have "heap-allocated" either.
+  if (haveExplicit) {
+    checkSpecialReport("heap-allocated", heapAllocatedAmounts);
+  }
+  // vsize may be unreasonable if ASAN is enabled
+  checkSpecialReport("vsize",          vsizeAmounts, /*canBeUnreasonable*/true);
+  checkSpecialReport("resident",       residentAmounts);
 
-    for (var reporter in jsGcHeapUsedGcThings) {
-      ok(jsGcHeapUsedGcThings[reporter] == 1);
-    }
-    checkSizeReasonable("js-main-runtime-gc-heap-committed/used/gc-things",
-                        jsGcHeapUsedGcThingsTotal);
+  for (var reporter in jsGcHeapUsedGcThings) {
+    ok(jsGcHeapUsedGcThings[reporter] == 1);
+  }
+  checkSizeReasonable("js-main-runtime-gc-heap-committed/used/gc-things",
+                      jsGcHeapUsedGcThingsTotal);
 
-    ok(present.jsNonWindowCompartments,     "js-non-window compartments are present");
-    ok(present.windowObjectsJsCompartments, "window-objects/.../js compartments are present");
-    ok(present.places,                      "places is present");
-    ok(present.images,                      "images is present");
-    ok(present.xptiWorkingSet,              "xpti-working-set is present");
-    ok(present.atomTablesMain,              "atom-tables/main is present");
-    ok(present.sandboxLocation,             "sandbox locations are present");
-    ok(present.bigString,                   "large string is present");
-    ok(present.smallString1,                "small string 1 is present");
-    ok(present.smallString2,                "small string 2 is present");
+  ok(present.jsNonWindowCompartments,     "js-non-window compartments are present");
+  ok(present.windowObjectsJsCompartments, "window-objects/.../js compartments are present");
+  ok(present.places,                      "places is present");
+  ok(present.images,                      "images is present");
+  ok(present.xptiWorkingSet,              "xpti-working-set is present");
+  ok(present.atomTablesMain,              "atom-tables/main is present");
+  ok(present.sandboxLocation,             "sandbox locations are present");
+  ok(present.bigString,                   "large string is present");
+  ok(present.smallString1,                "small string 1 is present");
+  ok(present.smallString2,                "small string 2 is present");
 
-    ok(!present.anonymizedWhenUnnecessary,
-       "anonymized paths are not present when unnecessary. Failed case: " +
-       present.anonymizedWhenUnnecessary);
-    ok(!present.httpWhenAnonymized,
-       "http URLs are anonymized when necessary. Failed case: " +
-       present.httpWhenAnonymized);
-    ok(!present.unanonymizedFilePathWhenAnonymized,
-       "file URLs are anonymized when necessary. Failed case: " +
-       present.unanonymizedFilePathWhenAnonymized);
-
-    runNext();
-  }
+  ok(!present.anonymizedWhenUnnecessary,
+     "anonymized paths are not present when unnecessary. Failed case: " +
+     present.anonymizedWhenUnnecessary);
+  ok(!present.httpWhenAnonymized,
+     "http URLs are anonymized when necessary. Failed case: " +
+     present.httpWhenAnonymized);
+  ok(!present.unanonymizedFilePathWhenAnonymized,
+     "file URLs are anonymized when necessary. Failed case: " +
+     present.unanonymizedFilePathWhenAnonymized);
 
   // Reporter registration tests
 
   // collectReports() calls to the test reporter.
   let called = 0;
 
   // The test memory reporter, testing the various report units.
   // Also acts as a report collector, verifying the reported values match the
@@ -366,38 +347,37 @@
 
   // General memory reporter + registerStrongReporter tests.
   function test_register_strong() {
     let reporterAndCallback = new MemoryReporterAndCallback();
     // Registration works.
     mgr.registerStrongReporter(reporterAndCallback);
 
     // Check the generated reports.
-    mgr.getReports(reporterAndCallback, null,
-      () => {
-        reporterAndCallback.finish();
-        window.setTimeout(test_unregister_strong, 0, reporterAndCallback);
-      }, null,
-      /* anonymize = */ false);
-  }
+    mgr.getReportsForThisProcess(reporterAndCallback, null,
+                                 /* anonymize = */ false);
+    reporterAndCallback.finish();
 
-  function test_unregister_strong(aReporterAndCallback)
-  {
-    mgr.unregisterStrongReporter(aReporterAndCallback);
+    // Unregistration works.
+    mgr.unregisterStrongReporter(reporterAndCallback);
 
     // The reporter was unregistered, hence there shouldn't be any reports from
     // the test reporter.
-    mgr.getReports(aReporterAndCallback, null,
-      () => {
-        aReporterAndCallback.finish(0);
-        runNext();
-      }, null,
-      /* anonymize = */ false);
+    mgr.getReportsForThisProcess(reporterAndCallback, null,
+                                 /* anonymize = */ false);
+    reporterAndCallback.finish(0);
   }
 
+  test_register_strong();
+
+  // Check strong reporters a second time, to make sure a reporter can be
+  // re-registered.
+  test_register_strong();
+
+
   // Check that you cannot register JS components as weak reporters.
   function test_register_weak() {
     let reporterAndCallback = new MemoryReporterAndCallback();
     try {
       // Should fail! nsMemoryReporterManager will only hold a raw pointer to
       // "weak" reporters.  When registering a weak reporter, XPConnect will
       // create a WrappedJS for JS components.  This WrappedJS would be
       // successfully registered with the manager, only to be destroyed
@@ -407,18 +387,15 @@
       // See bug 950391 comment #0.
       mgr.registerWeakReporter(reporterAndCallback);
       ok(false, "Shouldn't be allowed to register a JS component (WrappedJS)");
     }
     catch (ex) {
       ok(ex.message.indexOf("NS_ERROR_") >= 0,
          "WrappedJS reporter got rejected: " + ex);
     }
-
-    runNext();
   }
 
-  // Kick-off the async tests.
-  runNext();
+  test_register_weak();
 
   ]]>
   </script>
 </window>
--- a/toolkit/components/aboutmemory/tests/test_sqliteMultiReporter.xul
+++ b/toolkit/components/aboutmemory/tests/test_sqliteMultiReporter.xul
@@ -19,36 +19,33 @@
   // Nb: this test is all JS and chould be done with an xpcshell test,
   // but all the other memory reporter tests are mochitests, so it's easier
   // if this one is too.
 
   const Cc = Components.classes;
   const Ci = Components.interfaces;
   const Cu = Components.utils;
 
-  SimpleTest.waitForExplicitFinish();
-
   // Make a fake DB file.
   let file = Cc["@mozilla.org/file/directory_service;1"].
              getService(Ci.nsIProperties).
              get("ProfD", Ci.nsIFile);
   file.append("test_sqliteMultiReporter-fake-DB-tmp.sqlite");
 
   // Open and close the DB.
   let storage = Cc["@mozilla.org/storage/service;1"].
                 getService(Ci.mozIStorageService);
   let db = storage.openDatabase(file);
   db.close();
 
   // Invoke all the reporters.  The SQLite multi-reporter is among
   // them.  It shouldn't crash.
   let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
             getService(Ci.nsIMemoryReporterManager);
-  mgr.getReports(function(){}, null,
-    () => {
-      ok(true, "didn't crash");
-      SimpleTest.finish();
-    }, null,
-    /* anonymize = */ false);
+  mgr.getReportsForThisProcess(function(){}, null, /* anonymize = */ false);
+
+  // If we haven't crashed, we've passed, but the test harness requires that
+  // we explicitly check something.
+  ok(true, "didn't crash");
 
   ]]>
   </script>
 </window>
--- a/xpcom/base/nsIMemoryReporter.idl
+++ b/xpcom/base/nsIMemoryReporter.idl
@@ -200,42 +200,40 @@ interface nsIMemoryReporter : nsISupport
 };
 
 [scriptable, function, uuid(548b3909-c04d-4ca6-8466-b8bee3837457)]
 interface nsIFinishReportingCallback : nsISupports
 {
   void callback(in nsISupports data);
 };
 
-[scriptable, builtinclass, uuid(61de6dc7-ed11-4104-a577-79941f22f434)]
+[scriptable, builtinclass, uuid(5e4eaa5a-4808-4b97-8005-e7cdc4d73693)]
 interface nsIMemoryReporterManager : nsISupports
 {
   /*
    * Initialize.
    */
   void init();
 
   /*
    * Register the given nsIMemoryReporter.  The Manager service will hold a
    * strong reference to the given reporter, and will be responsible for freeing
    * the reporter at shutdown.  You may manually unregister the reporter with
    * unregisterStrongReporter() at any point.
    */
   void registerStrongReporter(in nsIMemoryReporter reporter);
-  void registerStrongAsyncReporter(in nsIMemoryReporter reporter);
 
   /*
    * Like registerReporter, but the Manager service will hold a weak reference
    * via a raw pointer to the given reporter.  The reporter should be
    * unregistered before shutdown.
    * You cannot register JavaScript components with this function!  Always
    * register your JavaScript components with registerStrongReporter().
    */
   void registerWeakReporter(in nsIMemoryReporter reporter);
-  void registerWeakAsyncReporter(in nsIMemoryReporter reporter);
 
   /*
    * Unregister the given memory reporter, which must have been registered with
    * registerStrongReporter().  You normally don't need to unregister your
    * strong reporters, as nsIMemoryReporterManager will take care of that at
    * shutdown.
    */
   void unregisterStrongReporter(in nsIMemoryReporter reporter);
@@ -287,49 +285,56 @@ interface nsIMemoryReporterManager : nsI
                        in nsISupports handleReportData,
                        in nsIFinishReportingCallback finishReporting,
                        in nsISupports finishReportingData,
                        in boolean anonymize,
                        in boolean minimizeMemoryUsage,
                        in AString DMDDumpIdent);
 
   /*
+   * Get memory reports in the current process only.  |handleReport| is called
+   * for each report.
+   */
+  void getReportsForThisProcess(in nsIMemoryReporterCallback handleReport,
+                                in nsISupports handleReportData,
+                                in boolean anonymize);
+
+  /*
    * As above, but if DMD is enabled and |DMDFile| is non-null then
    * write a DMD report to that file and close it.
    */
   [noscript] void
     getReportsForThisProcessExtended(in nsIMemoryReporterCallback handleReport,
                                      in nsISupports handleReportData,
                                      in boolean anonymize,
-                                     in FILE DMDFile,
-                                     in nsIFinishReportingCallback finishReporting,
-                                     in nsISupports finishReportingData);
-
-  /*
-   * Called by an asynchronous memory reporter upon completion.
-   */
-  [noscript] void endReport();
+                                     in FILE DMDFile);
 
   /*
    * The memory reporter manager, for the most part, treats reporters
    * registered with it as a black box.  However, there are some
    * "distinguished" amounts (as could be reported by a memory reporter) that
    * the manager provides as attributes, because they are sufficiently
    * interesting that we want external code (e.g. telemetry) to be able to rely
    * on them.
    *
-   * Note that these are not reporters and so getReports() does not look at
-   * them.  However, distinguished amounts can be embedded in a reporter.
+   * Note that these are not reporters and so getReports() and
+   * getReportsForThisProcess() do not look at them.  However, distinguished
+   * amounts can be embedded in a reporter.
    *
    * Access to these attributes can fail.  In particular, some of them are not
    * available on all platforms.
    *
    * If you add a new distinguished amount, please update
    * toolkit/components/aboutmemory/tests/test_memoryReporters.xul.
    *
+   * |explicit| (UNITS_BYTES)  The total size of explicit memory allocations,
+   * both at the OS-level (eg. via mmap, VirtualAlloc) and at the heap level
+   * (eg. via malloc, calloc, operator new).  It covers all heap allocations,
+   * but will miss any OS-level ones not covered by memory reporters.
+   *
    * |vsize| (UNITS_BYTES)  The virtual size, i.e. the amount of address space
    * taken up.
    *
    * |vsizeMaxContiguous| (UNITS_BYTES)  The size of the largest contiguous
    * block of virtual memory.
    *
    * |resident| (UNITS_BYTES)  The resident size (a.k.a. RSS or physical memory
    * used).
@@ -368,16 +373,17 @@ interface nsIMemoryReporterManager : nsI
    * of low-{virtual,physical}-memory events that have occurred since the
    * process started.
    *
    * |ghostWindows| (UNITS_COUNT)  The number of ghost windows.
    *
    * |pageFaultsHard| (UNITS_COUNT_CUMULATIVE)  The number of hard (a.k.a.
    * major) page faults that have occurred since the process started.
    */
+  readonly attribute int64_t explicit;
   readonly attribute int64_t vsize;
   readonly attribute int64_t vsizeMaxContiguous;
   readonly attribute int64_t resident;
   readonly attribute int64_t residentFast;
   readonly attribute int64_t residentPeak;
   readonly attribute int64_t residentUnique;
 
   readonly attribute int64_t heapAllocated;
@@ -448,22 +454,20 @@ class nsPIDOMWindow;
 // around for backwards compatibility.
 typedef nsIMemoryReporterCallback nsIHandleReportCallback;
 
 namespace mozilla {
 
 // Register a memory reporter.  The manager service will hold a strong
 // reference to this reporter.
 XPCOM_API(nsresult) RegisterStrongMemoryReporter(nsIMemoryReporter* aReporter);
-XPCOM_API(nsresult) RegisterStrongAsyncMemoryReporter(nsIMemoryReporter* aReporter);
 
 // Register a memory reporter.  The manager service will hold a weak reference
 // to this reporter.
 XPCOM_API(nsresult) RegisterWeakMemoryReporter(nsIMemoryReporter* aReporter);
-XPCOM_API(nsresult) RegisterWeakAsyncMemoryReporter(nsIMemoryReporter* aReporter);
 
 // Unregister a strong memory reporter.
 XPCOM_API(nsresult) UnregisterStrongMemoryReporter(nsIMemoryReporter* aReporter);
 
 // Unregister a weak memory reporter.
 XPCOM_API(nsresult) UnregisterWeakMemoryReporter(nsIMemoryReporter* aReporter);
 
 // The memory reporter manager provides access to several distinguished
@@ -511,16 +515,25 @@ typedef nsresult (*NonJSSizeOfTabFn)(nsP
                                      size_t* aOtherSize);
 
 nsresult RegisterJSSizeOfTab(JSSizeOfTabFn aSizeOfTabFn);
 nsresult RegisterNonJSSizeOfTab(NonJSSizeOfTabFn aSizeOfTabFn);
 
 }
 
 #if defined(MOZ_DMD) && !defined(MOZILLA_XPCOMRT_API)
+namespace mozilla {
+namespace dmd {
+// This runs all the memory reporters in the current process but does nothing
+// with the results;  i.e. it does the minimal amount of work possible for DMD
+// to do its thing.  It does nothing with child processes.
+void RunReportersForThisProcess();
+}
+}
+
 #if !defined(MOZ_MEMORY)
 #error "MOZ_DMD requires MOZ_MEMORY"
 #endif
 
 #include "DMD.h"
 
 #define MOZ_REPORT(ptr)          mozilla::dmd::Report(ptr)
 #define MOZ_REPORT_ON_ALLOC(ptr) mozilla::dmd::ReportOnAlloc(ptr)
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -1351,18 +1351,17 @@ nsMemoryReporterManager::Init()
 nsMemoryReporterManager::nsMemoryReporterManager()
   : mMutex("nsMemoryReporterManager::mMutex")
   , mIsRegistrationBlocked(false)
   , mStrongReporters(new StrongReportersTable())
   , mWeakReporters(new WeakReportersTable())
   , mSavedStrongReporters(nullptr)
   , mSavedWeakReporters(nullptr)
   , mNextGeneration(1)
-  , mPendingProcessesState(nullptr)
-  , mPendingReportersState(nullptr)
+  , mGetReportsState(nullptr)
 {
 }
 
 nsMemoryReporterManager::~nsMemoryReporterManager()
 {
   delete mStrongReporters;
   delete mWeakReporters;
   NS_ASSERTION(!mSavedStrongReporters, "failed to restore strong reporters");
@@ -1410,84 +1409,82 @@ nsMemoryReporterManager::GetReportsExten
   // Memory reporters are not necessarily threadsafe, so this function must
   // be called from the main thread.
   if (!NS_IsMainThread()) {
     MOZ_CRASH();
   }
 
   uint32_t generation = mNextGeneration++;
 
-  if (mPendingProcessesState) {
+  if (mGetReportsState) {
     // A request is in flight.  Don't start another one.  And don't report
     // an error;  just ignore it, and let the in-flight request finish.
     MEMORY_REPORTING_LOG("GetReports (gen=%u, s->gen=%u): abort\n",
-                         generation, mPendingProcessesState->mGeneration);
+                         generation, mGetReportsState->mGeneration);
     return NS_OK;
   }
 
   MEMORY_REPORTING_LOG("GetReports (gen=%u)\n", generation);
 
   uint32_t concurrency = Preferences::GetUint("memory.report_concurrency", 1);
   MOZ_ASSERT(concurrency >= 1);
   if (concurrency < 1) {
     concurrency = 1;
   }
-  mPendingProcessesState = new PendingProcessesState(generation,
-                                                     aAnonymize,
-                                                     aMinimize,
-                                                     concurrency,
-                                                     aHandleReport,
-                                                     aHandleReportData,
-                                                     aFinishReporting,
-                                                     aFinishReportingData,
-                                                     aDMDDumpIdent);
+  mGetReportsState = new GetReportsState(generation,
+                                         aAnonymize,
+                                         aMinimize,
+                                         concurrency,
+                                         aHandleReport,
+                                         aHandleReportData,
+                                         aFinishReporting,
+                                         aFinishReportingData,
+                                         aDMDDumpIdent);
+  mGetReportsState->mChildrenPending = new nsTArray<nsRefPtr<mozilla::dom::ContentParent>>();
 
   if (aMinimize) {
     rv = MinimizeMemoryUsage(NS_NewRunnableMethod(
       this, &nsMemoryReporterManager::StartGettingReports));
   } else {
     rv = StartGettingReports();
   }
   return rv;
 }
 
 nsresult
 nsMemoryReporterManager::StartGettingReports()
 {
-  PendingProcessesState* s = mPendingProcessesState;
+  GetReportsState* s = mGetReportsState;
   nsresult rv;
 
   // Get reports for this process.
   FILE* parentDMDFile = nullptr;
 #ifdef MOZ_DMD
   if (!s->mDMDDumpIdent.IsEmpty()) {
     rv = nsMemoryInfoDumper::OpenDMDFile(s->mDMDDumpIdent, getpid(),
                                                   &parentDMDFile);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       // Proceed with the memory report as if DMD were disabled.
       parentDMDFile = nullptr;
     }
   }
 #endif
-
-  // This is async.
   GetReportsForThisProcessExtended(s->mHandleReport, s->mHandleReportData,
-                                   s->mAnonymize, parentDMDFile,
-                                   s->mFinishReporting, s->mFinishReportingData);
+                                   s->mAnonymize, parentDMDFile);
 
   nsTArray<ContentParent*> childWeakRefs;
   ContentParent::GetAll(childWeakRefs);
   if (!childWeakRefs.IsEmpty()) {
     // Request memory reports from child processes.  This happens
     // after the parent report so that the parent's main thread will
     // be free to process the child reports, instead of causing them
     // to be buffered and consume (possibly scarce) memory.
 
     for (size_t i = 0; i < childWeakRefs.Length(); ++i) {
-      s->mChildrenPending.AppendElement(childWeakRefs[i]);
+      s->mChildrenPending->AppendElement(childWeakRefs[i]);
     }
 
     nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
     // Don't use NS_ENSURE_* here; can't return until the report is finished.
     if (NS_WARN_IF(!timer)) {
       FinishReporting();
       return NS_ERROR_FAILURE;
     }
@@ -1498,128 +1495,85 @@ nsMemoryReporterManager::StartGettingRep
       FinishReporting();
       return rv;
     }
 
     MOZ_ASSERT(!s->mTimer);
     s->mTimer.swap(timer);
   }
 
+  // The parent's report is done; make note of that, and start
+  // launching child process reports (if any).
+  EndProcessReport(s->mGeneration, true);
   return NS_OK;
 }
 
-void
-nsMemoryReporterManager::DispatchReporter(
-  nsIMemoryReporter* aReporter, bool aIsAsync,
+NS_IMETHODIMP
+nsMemoryReporterManager::GetReportsForThisProcess(
   nsIHandleReportCallback* aHandleReport,
-  nsISupports* aHandleReportData,
-  bool aAnonymize)
+  nsISupports* aHandleReportData, bool aAnonymize)
 {
-  MOZ_ASSERT(mPendingReportersState);
-
-  // Grab refs to everything used in the lambda function.
-  nsRefPtr<nsMemoryReporterManager> self = this;
-  nsCOMPtr<nsIMemoryReporter> reporter = aReporter;
-  nsCOMPtr<nsIHandleReportCallback> handleReport = aHandleReport;
-  nsCOMPtr<nsISupports> handleReportData = aHandleReportData;
-
-  nsCOMPtr<nsIRunnable> event = NS_NewRunnableFunction(
-    [self, reporter, aIsAsync, handleReport, handleReportData, aAnonymize] () {
-      reporter->CollectReports(handleReport,
-                               handleReportData,
-                               aAnonymize);
-      if (!aIsAsync) {
-        self->EndReport();
-      }
-    });
-
-  NS_DispatchToMainThread(event);
-  mPendingReportersState->mReportsPending++;
+  return GetReportsForThisProcessExtended(aHandleReport, aHandleReportData,
+                                          aAnonymize, nullptr);
 }
 
 NS_IMETHODIMP
 nsMemoryReporterManager::GetReportsForThisProcessExtended(
   nsIHandleReportCallback* aHandleReport, nsISupports* aHandleReportData,
-  bool aAnonymize, FILE* aDMDFile,
-  nsIFinishReportingCallback* aFinishReporting,
-  nsISupports* aFinishReportingData)
+  bool aAnonymize, FILE* aDMDFile)
 {
   // Memory reporters are not necessarily threadsafe, so this function must
   // be called from the main thread.
   if (!NS_IsMainThread()) {
     MOZ_CRASH();
   }
 
-  if (NS_WARN_IF(mPendingReportersState)) {
-    // Report is already in progress.
-    return NS_ERROR_IN_PROGRESS;
-  }
-
 #ifdef MOZ_DMD
   if (aDMDFile) {
     // Clear DMD's reportedness state before running the memory
     // reporters, to avoid spurious twice-reported warnings.
     dmd::ClearReports();
   }
 #else
   MOZ_ASSERT(!aDMDFile);
 #endif
 
-  mPendingReportersState = new PendingReportersState(
-      aFinishReporting, aFinishReportingData, aDMDFile);
-
+  nsCOMArray<nsIMemoryReporter> allReporters;
   {
     mozilla::MutexAutoLock autoLock(mMutex);
-
     for (auto iter = mStrongReporters->Iter(); !iter.Done(); iter.Next()) {
-      DispatchReporter(iter.Key(), iter.Data(),
-                       aHandleReport, aHandleReportData, aAnonymize);
+      nsRefPtrHashKey<nsIMemoryReporter>* entry = iter.Get();
+      allReporters.AppendElement(entry->GetKey());
     }
-
     for (auto iter = mWeakReporters->Iter(); !iter.Done(); iter.Next()) {
-      nsCOMPtr<nsIMemoryReporter> reporter = iter.Key();
-      DispatchReporter(reporter, iter.Data(),
-                       aHandleReport, aHandleReportData, aAnonymize);
+      nsPtrHashKey<nsIMemoryReporter>* entry = iter.Get();
+      allReporters.AppendElement(entry->GetKey());
     }
   }
+  for (uint32_t i = 0; i < allReporters.Length(); i++) {
+    allReporters[i]->CollectReports(aHandleReport, aHandleReportData,
+                                    aAnonymize);
+  }
+
+#ifdef MOZ_DMD
+  if (aDMDFile) {
+    return nsMemoryInfoDumper::DumpDMDToFile(aDMDFile);
+  }
+#endif
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsMemoryReporterManager::EndReport()
-{
-  if (--mPendingReportersState->mReportsPending == 0) {
-#ifdef MOZ_DMD
-    if (mPendingReportersState->mDMDFile) {
-      nsMemoryInfoDumper::DumpDMDToFile(mPendingReportersState->mDMDFile);
-    }
-#endif
-    if (mPendingProcessesState) {
-      // This is the parent process.
-      EndProcessReport(mPendingProcessesState->mGeneration, true);
-    } else {
-      mPendingReportersState->mFinishReporting->Callback(
-          mPendingReportersState->mFinishReportingData);
-    }
-
-    delete mPendingReportersState;
-    mPendingReportersState = nullptr;
-  }
-
-  return NS_OK;
-}
-
-nsMemoryReporterManager::PendingProcessesState*
+nsMemoryReporterManager::GetReportsState*
 nsMemoryReporterManager::GetStateForGeneration(uint32_t aGeneration)
 {
   // Memory reporting only happens on the main thread.
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
-  PendingProcessesState* s = mPendingProcessesState;
+  GetReportsState* s = mGetReportsState;
 
   if (!s) {
     // If we reach here, then:
     //
     // - A child process reported back too late, and no subsequent request
     //   is in flight.
     //
     // So there's nothing to be done.  Just ignore it.
@@ -1646,17 +1600,17 @@ nsMemoryReporterManager::GetStateForGene
 // This function has no return value.  If something goes wrong, there's no
 // clear place to report the problem to, but that's ok -- we will end up
 // hitting the timeout and executing TimeoutCallback().
 void
 nsMemoryReporterManager::HandleChildReport(
   uint32_t aGeneration,
   const dom::MemoryReport& aChildReport)
 {
-  PendingProcessesState* s = GetStateForGeneration(aGeneration);
+  GetReportsState* s = GetStateForGeneration(aGeneration);
   if (!s) {
     return;
   }
 
   // Child reports should have a non-empty process.
   MOZ_ASSERT(!aChildReport.process().IsEmpty());
 
   // If the call fails, ignore and continue.
@@ -1666,17 +1620,17 @@ nsMemoryReporterManager::HandleChildRepo
                              aChildReport.units(),
                              aChildReport.amount(),
                              aChildReport.desc(),
                              s->mHandleReportData);
 }
 
 /* static */ bool
 nsMemoryReporterManager::StartChildReport(mozilla::dom::ContentParent* aChild,
-                                          const PendingProcessesState* aState)
+                                          const GetReportsState* aState)
 {
 #ifdef MOZ_NUWA_PROCESS
   if (aChild->IsNuwaProcess()) {
     return false;
   }
 #endif
 
   if (!aChild->IsAlive()) {
@@ -1704,139 +1658,121 @@ nsMemoryReporterManager::StartChildRepor
 #endif
   return aChild->SendPMemoryReportRequestConstructor(
     aState->mGeneration, aState->mAnonymize, aState->mMinimize, dmdFileDesc);
 }
 
 void
 nsMemoryReporterManager::EndProcessReport(uint32_t aGeneration, bool aSuccess)
 {
-  PendingProcessesState* s = GetStateForGeneration(aGeneration);
+  GetReportsState* s = GetStateForGeneration(aGeneration);
   if (!s) {
     return;
   }
 
   MOZ_ASSERT(s->mNumProcessesRunning > 0);
   s->mNumProcessesRunning--;
   s->mNumProcessesCompleted++;
   MEMORY_REPORTING_LOG("HandleChildReports (aGen=%u): process %u %s"
                        " (%u running, %u pending)\n",
                        aGeneration, s->mNumProcessesCompleted,
                        aSuccess ? "completed" : "exited during report",
                        s->mNumProcessesRunning,
-                       static_cast<unsigned>(s->mChildrenPending.Length()));
+                       static_cast<unsigned>(s->mChildrenPending->Length()));
 
   // Start pending children up to the concurrency limit.
   while (s->mNumProcessesRunning < s->mConcurrencyLimit &&
-         !s->mChildrenPending.IsEmpty()) {
+         !s->mChildrenPending->IsEmpty()) {
     // Pop last element from s->mChildrenPending
     nsRefPtr<ContentParent> nextChild;
-    nextChild.swap(s->mChildrenPending.LastElement());
-    s->mChildrenPending.TruncateLength(s->mChildrenPending.Length() - 1);
+    nextChild.swap(s->mChildrenPending->LastElement());
+    s->mChildrenPending->TruncateLength(s->mChildrenPending->Length() - 1);
     // Start report (if the child is still alive and not Nuwa).
     if (StartChildReport(nextChild, s)) {
       ++s->mNumProcessesRunning;
       MEMORY_REPORTING_LOG("HandleChildReports (aGen=%u): started child report"
                            " (%u running, %u pending)\n",
                            aGeneration, s->mNumProcessesRunning,
-                           static_cast<unsigned>(s->mChildrenPending.Length()));
+                           static_cast<unsigned>(s->mChildrenPending->Length()));
     }
   }
 
   // If all the child processes (if any) have reported, we can cancel
   // the timer (if started) and finish up.  Otherwise, just return.
   if (s->mNumProcessesRunning == 0) {
-    MOZ_ASSERT(s->mChildrenPending.IsEmpty());
+    MOZ_ASSERT(s->mChildrenPending->IsEmpty());
     if (s->mTimer) {
       s->mTimer->Cancel();
     }
     FinishReporting();
   }
 }
 
 /* static */ void
 nsMemoryReporterManager::TimeoutCallback(nsITimer* aTimer, void* aData)
 {
   nsMemoryReporterManager* mgr = static_cast<nsMemoryReporterManager*>(aData);
-  PendingProcessesState* s = mgr->mPendingProcessesState;
+  GetReportsState* s = mgr->mGetReportsState;
 
   // Release assert because: if the pointer is null we're about to
   // crash regardless of DEBUG, and this way the compiler doesn't
   // complain about unused variables.
-  MOZ_RELEASE_ASSERT(s, "mgr->mPendingProcessesState");
+  MOZ_RELEASE_ASSERT(s, "mgr->mGetReportsState");
   MEMORY_REPORTING_LOG("TimeoutCallback (s->gen=%u; %u running, %u pending)\n",
                        s->mGeneration, s->mNumProcessesRunning,
-                       static_cast<unsigned>(s->mChildrenPending.Length()));
+                       static_cast<unsigned>(s->mChildrenPending->Length()));
 
   // We don't bother sending any kind of cancellation message to the child
   // processes that haven't reported back.
   mgr->FinishReporting();
 }
 
 nsresult
 nsMemoryReporterManager::FinishReporting()
 {
   // Memory reporting only happens on the main thread.
   if (!NS_IsMainThread()) {
     MOZ_CRASH();
   }
 
-  MOZ_ASSERT(mPendingProcessesState);
+  MOZ_ASSERT(mGetReportsState);
   MEMORY_REPORTING_LOG("FinishReporting (s->gen=%u; %u processes reported)\n",
-                       mPendingProcessesState->mGeneration,
-                       mPendingProcessesState->mNumProcessesCompleted);
+                       mGetReportsState->mGeneration,
+                       mGetReportsState->mNumProcessesCompleted);
 
-  // Call this before deleting |mPendingProcessesState|.  That way, if
+  // Call this before deleting |mGetReportsState|.  That way, if
   // |mFinishReportData| calls GetReports(), it will silently abort, as
   // required.
-  nsresult rv = mPendingProcessesState->mFinishReporting->Callback(
-    mPendingProcessesState->mFinishReportingData);
+  nsresult rv = mGetReportsState->mFinishReporting->Callback(
+    mGetReportsState->mFinishReportingData);
 
-  delete mPendingProcessesState;
-  mPendingProcessesState = nullptr;
+  delete mGetReportsState;
+  mGetReportsState = nullptr;
   return rv;
 }
 
-nsMemoryReporterManager::PendingProcessesState::PendingProcessesState(
-    uint32_t aGeneration, bool aAnonymize, bool aMinimize,
-    uint32_t aConcurrencyLimit,
-    nsIHandleReportCallback* aHandleReport,
-    nsISupports* aHandleReportData,
-    nsIFinishReportingCallback* aFinishReporting,
-    nsISupports* aFinishReportingData,
-    const nsAString& aDMDDumpIdent)
-  : mGeneration(aGeneration)
-  , mAnonymize(aAnonymize)
-  , mMinimize(aMinimize)
-  , mChildrenPending()
-  , mNumProcessesRunning(1) // reporting starts with the parent
-  , mNumProcessesCompleted(0)
-  , mConcurrencyLimit(aConcurrencyLimit)
-  , mHandleReport(aHandleReport)
-  , mHandleReportData(aHandleReportData)
-  , mFinishReporting(aFinishReporting)
-  , mFinishReportingData(aFinishReportingData)
-  , mDMDDumpIdent(aDMDDumpIdent)
+nsMemoryReporterManager::GetReportsState::~GetReportsState()
 {
+  delete mChildrenPending;
 }
 
 static void
 CrashIfRefcountIsZero(nsISupports* aObj)
 {
   // This will probably crash if the object's refcount is 0.
   uint32_t refcnt = NS_ADDREF(aObj);
   if (refcnt <= 1) {
     MOZ_CRASH("CrashIfRefcountIsZero: refcount is zero");
   }
   NS_RELEASE(aObj);
 }
 
 nsresult
 nsMemoryReporterManager::RegisterReporterHelper(
-  nsIMemoryReporter* aReporter, bool aForce, bool aStrong, bool aIsAsync)
+  nsIMemoryReporter* aReporter, bool aForce, bool aStrong)
 {
   // This method is thread-safe.
   mozilla::MutexAutoLock autoLock(mMutex);
 
   if (mIsRegistrationBlocked && !aForce) {
     return NS_ERROR_FAILURE;
   }
 
@@ -1853,120 +1789,101 @@ nsMemoryReporterManager::RegisterReporte
   // death grip goes out of scope, we would delete the reporter).  In debug
   // mode, we check that this doesn't happen.
   //
   // If |aStrong| is false, we require that |aReporter| have a non-zero
   // refcnt.
   //
   if (aStrong) {
     nsCOMPtr<nsIMemoryReporter> kungFuDeathGrip = aReporter;
-    mStrongReporters->Put(aReporter, aIsAsync);
+    mStrongReporters->PutEntry(aReporter);
     CrashIfRefcountIsZero(aReporter);
   } else {
     CrashIfRefcountIsZero(aReporter);
     nsCOMPtr<nsIXPConnectWrappedJS> jsComponent = do_QueryInterface(aReporter);
     if (jsComponent) {
       // We cannot allow non-native reporters (WrappedJS), since we'll be
       // holding onto a raw pointer, which would point to the wrapper,
       // and that wrapper is likely to go away as soon as this register
       // call finishes.  This would then lead to subsequent crashes in
       // CollectReports().
       return NS_ERROR_XPC_BAD_CONVERT_JS;
     }
-    mWeakReporters->Put(aReporter, aIsAsync);
+    mWeakReporters->PutEntry(aReporter);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsMemoryReporterManager::RegisterStrongReporter(nsIMemoryReporter* aReporter)
 {
   return RegisterReporterHelper(aReporter, /* force = */ false,
-                                /* strong = */ true,
-                                /* async = */ false);
-}
-
-NS_IMETHODIMP
-nsMemoryReporterManager::RegisterStrongAsyncReporter(nsIMemoryReporter* aReporter)
-{
-  return RegisterReporterHelper(aReporter, /* force = */ false,
-                                /* strong = */ true,
-                                /* async = */ true);
+                                /* strong = */ true);
 }
 
 NS_IMETHODIMP
 nsMemoryReporterManager::RegisterWeakReporter(nsIMemoryReporter* aReporter)
 {
   return RegisterReporterHelper(aReporter, /* force = */ false,
-                                /* strong = */ false,
-                                /* async = */ false);
-}
-
-NS_IMETHODIMP
-nsMemoryReporterManager::RegisterWeakAsyncReporter(nsIMemoryReporter* aReporter)
-{
-  return RegisterReporterHelper(aReporter, /* force = */ false,
-                                /* strong = */ false,
-                                /* async = */ true);
+                                /* strong = */ false);
 }
 
 NS_IMETHODIMP
 nsMemoryReporterManager::RegisterStrongReporterEvenIfBlocked(
   nsIMemoryReporter* aReporter)
 {
   return RegisterReporterHelper(aReporter, /* force = */ true,
-                                /* strong = */ true,
-                                /* async = */ false);
+                                /* strong = */ true);
 }
 
 NS_IMETHODIMP
 nsMemoryReporterManager::UnregisterStrongReporter(nsIMemoryReporter* aReporter)
 {
   // This method is thread-safe.
   mozilla::MutexAutoLock autoLock(mMutex);
 
   MOZ_ASSERT(!mWeakReporters->Contains(aReporter));
 
   if (mStrongReporters->Contains(aReporter)) {
-    mStrongReporters->Remove(aReporter);
+    mStrongReporters->RemoveEntry(aReporter);
     return NS_OK;
   }
 
   // We don't register new reporters when the block is in place, but we do
   // unregister existing reporters. This is so we don't keep holding strong
   // references that these reporters aren't expecting (which can keep them
   // alive longer than intended).
   if (mSavedStrongReporters && mSavedStrongReporters->Contains(aReporter)) {
-    mSavedStrongReporters->Remove(aReporter);
+    mSavedStrongReporters->RemoveEntry(aReporter);
     return NS_OK;
   }
 
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsMemoryReporterManager::UnregisterWeakReporter(nsIMemoryReporter* aReporter)
 {
   // This method is thread-safe.
   mozilla::MutexAutoLock autoLock(mMutex);
 
   MOZ_ASSERT(!mStrongReporters->Contains(aReporter));
 
   if (mWeakReporters->Contains(aReporter)) {
-    mWeakReporters->Remove(aReporter);
+    mWeakReporters->RemoveEntry(aReporter);
     return NS_OK;
   }
 
   // We don't register new reporters when the block is in place, but we do
   // unregister existing reporters. This is so we don't keep holding weak
   // references that the old reporters aren't expecting (which can end up as
   // dangling pointers that lead to use-after-frees).
   if (mSavedWeakReporters && mSavedWeakReporters->Contains(aReporter)) {
-    mSavedWeakReporters->Remove(aReporter);
+    mSavedWeakReporters->RemoveEntry(aReporter);
     return NS_OK;
   }
 
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsMemoryReporterManager::BlockRegistrationAndHideExistingReporters()
@@ -2005,16 +1922,94 @@ nsMemoryReporterManager::UnblockRegistra
   mWeakReporters = mSavedWeakReporters;
   mSavedStrongReporters = nullptr;
   mSavedWeakReporters = nullptr;
 
   mIsRegistrationBlocked = false;
   return NS_OK;
 }
 
+// This is just a wrapper for int64_t that implements nsISupports, so it can be
+// passed to nsIMemoryReporter::CollectReports.
+class Int64Wrapper final : public nsISupports
+{
+  ~Int64Wrapper() {}
+
+public:
+  NS_DECL_ISUPPORTS
+  Int64Wrapper() : mValue(0)
+  {
+  }
+  int64_t mValue;
+};
+
+NS_IMPL_ISUPPORTS0(Int64Wrapper)
+
+class ExplicitCallback final : public nsIHandleReportCallback
+{
+  ~ExplicitCallback() {}
+
+public:
+  NS_DECL_ISUPPORTS
+
+  NS_IMETHOD Callback(const nsACString& aProcess, const nsACString& aPath,
+                      int32_t aKind, int32_t aUnits, int64_t aAmount,
+                      const nsACString& aDescription,
+                      nsISupports* aWrappedExplicit) override
+  {
+    // Using the "heap-allocated" reporter here instead of
+    // nsMemoryReporterManager.heapAllocated goes against the usual
+    // pattern.  But it's for a good reason:  in tests, we can easily
+    // create artificial (i.e. deterministic) reporters -- which allows us
+    // to precisely test nsMemoryReporterManager.explicit -- but we can't
+    // do that for distinguished amounts.
+    if (aPath.EqualsLiteral("heap-allocated") ||
+        (aKind == nsIMemoryReporter::KIND_NONHEAP &&
+         PromiseFlatCString(aPath).Find("explicit") == 0)) {
+      Int64Wrapper* wrappedInt64 = static_cast<Int64Wrapper*>(aWrappedExplicit);
+      wrappedInt64->mValue += aAmount;
+    }
+    return NS_OK;
+  }
+};
+
+NS_IMPL_ISUPPORTS(ExplicitCallback, nsIHandleReportCallback)
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetExplicit(int64_t* aAmount)
+{
+  if (NS_WARN_IF(!aAmount)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  *aAmount = 0;
+#ifndef HAVE_JEMALLOC_STATS
+  return NS_ERROR_NOT_AVAILABLE;
+#else
+
+  // For each reporter we call CollectReports and filter out the
+  // non-explicit, non-NONHEAP measurements (except for "heap-allocated").
+  // That's lots of wasted work, and we used to have a GetExplicitNonHeap()
+  // method which did this more efficiently, but it ended up being more
+  // trouble than it was worth.
+
+  nsRefPtr<ExplicitCallback> handleReport = new ExplicitCallback();
+  nsRefPtr<Int64Wrapper> wrappedExplicitSize = new Int64Wrapper();
+
+  // Anonymization doesn't matter here, because we're only summing all the
+  // reported values. Enable it anyway because it's slightly faster, since it
+  // doesn't have to get URLs, find notable strings, etc.
+  GetReportsForThisProcess(handleReport, wrappedExplicitSize,
+                           /* anonymize = */ true);
+
+  *aAmount = wrappedExplicitSize->mValue;
+
+  return NS_OK;
+#endif // HAVE_JEMALLOC_STATS
+}
+
 NS_IMETHODIMP
 nsMemoryReporterManager::GetVsize(int64_t* aVsize)
 {
 #ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
   return VsizeDistinguishedAmount(aVsize);
 #else
   *aVsize = 0;
   return NS_ERROR_NOT_AVAILABLE;
@@ -2373,71 +2368,71 @@ nsMemoryReporterManager::SizeOfTab(nsIDO
   *aJSMilliseconds    = (t2 - t1).ToMilliseconds();
   *aNonJSMilliseconds = (t3 - t2).ToMilliseconds();
 
   return NS_OK;
 }
 
 namespace mozilla {
 
-#define GET_MEMORY_REPORTER_MANAGER(mgr)                                      \
-  nsRefPtr<nsMemoryReporterManager> mgr =                                     \
-    nsMemoryReporterManager::GetOrCreate();                                   \
-  if (!mgr) {                                                                 \
-    return NS_ERROR_FAILURE;                                                  \
-  }
-
 nsresult
 RegisterStrongMemoryReporter(nsIMemoryReporter* aReporter)
 {
   // Hold a strong reference to the argument to make sure it gets released if
   // we return early below.
   nsCOMPtr<nsIMemoryReporter> reporter = aReporter;
-  GET_MEMORY_REPORTER_MANAGER(mgr)
+
+  nsCOMPtr<nsIMemoryReporterManager> mgr =
+    do_GetService("@mozilla.org/memory-reporter-manager;1");
+  if (!mgr) {
+    return NS_ERROR_FAILURE;
+  }
   return mgr->RegisterStrongReporter(reporter);
 }
 
 nsresult
-RegisterStrongAsyncMemoryReporter(nsIMemoryReporter* aReporter)
-{
-  // Hold a strong reference to the argument to make sure it gets released if
-  // we return early below.
-  nsCOMPtr<nsIMemoryReporter> reporter = aReporter;
-  GET_MEMORY_REPORTER_MANAGER(mgr)
-  return mgr->RegisterStrongAsyncReporter(reporter);
-}
-
-nsresult
 RegisterWeakMemoryReporter(nsIMemoryReporter* aReporter)
 {
-  GET_MEMORY_REPORTER_MANAGER(mgr)
+  nsCOMPtr<nsIMemoryReporterManager> mgr =
+    do_GetService("@mozilla.org/memory-reporter-manager;1");
+  if (!mgr) {
+    return NS_ERROR_FAILURE;
+  }
   return mgr->RegisterWeakReporter(aReporter);
 }
 
 nsresult
-RegisterWeakAsyncMemoryReporter(nsIMemoryReporter* aReporter)
-{
-  GET_MEMORY_REPORTER_MANAGER(mgr)
-  return mgr->RegisterWeakAsyncReporter(aReporter);
-}
-
-nsresult
 UnregisterStrongMemoryReporter(nsIMemoryReporter* aReporter)
 {
-  GET_MEMORY_REPORTER_MANAGER(mgr)
+  nsCOMPtr<nsIMemoryReporterManager> mgr =
+    do_GetService("@mozilla.org/memory-reporter-manager;1");
+  if (!mgr) {
+    return NS_ERROR_FAILURE;
+  }
   return mgr->UnregisterStrongReporter(aReporter);
 }
 
 nsresult
 UnregisterWeakMemoryReporter(nsIMemoryReporter* aReporter)
 {
-  GET_MEMORY_REPORTER_MANAGER(mgr)
+  nsCOMPtr<nsIMemoryReporterManager> mgr =
+    do_GetService("@mozilla.org/memory-reporter-manager;1");
+  if (!mgr) {
+    return NS_ERROR_FAILURE;
+  }
   return mgr->UnregisterWeakReporter(aReporter);
 }
 
+#define GET_MEMORY_REPORTER_MANAGER(mgr)                                      \
+  nsRefPtr<nsMemoryReporterManager> mgr =                                     \
+    nsMemoryReporterManager::GetOrCreate();                                   \
+  if (!mgr) {                                                                 \
+    return NS_ERROR_FAILURE;                                                  \
+  }
+
 // Macro for generating functions that register distinguished amount functions
 // with the memory reporter manager.
 #define DEFINE_REGISTER_DISTINGUISHED_AMOUNT(kind, name)                      \
   nsresult                                                                    \
   Register##name##DistinguishedAmount(kind##AmountFn aAmountFn)               \
   {                                                                           \
     GET_MEMORY_REPORTER_MANAGER(mgr)                                          \
     mgr->mAmountFns.m##name = aAmountFn;                                      \
@@ -2483,8 +2478,50 @@ DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Inf
 DEFINE_REGISTER_SIZE_OF_TAB(JS);
 DEFINE_REGISTER_SIZE_OF_TAB(NonJS);
 
 #undef DEFINE_REGISTER_SIZE_OF_TAB
 
 #undef GET_MEMORY_REPORTER_MANAGER
 
 } // namespace mozilla
+
+#if defined(MOZ_DMD)
+
+namespace mozilla {
+namespace dmd {
+
+class DoNothingCallback final : public nsIHandleReportCallback
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  NS_IMETHOD Callback(const nsACString& aProcess, const nsACString& aPath,
+                      int32_t aKind, int32_t aUnits, int64_t aAmount,
+                      const nsACString& aDescription,
+                      nsISupports* aData) override
+  {
+    // Do nothing;  the reporter has already reported to DMD.
+    return NS_OK;
+  }
+
+private:
+  ~DoNothingCallback() {}
+};
+
+NS_IMPL_ISUPPORTS(DoNothingCallback, nsIHandleReportCallback)
+
+void
+RunReportersForThisProcess()
+{
+  nsCOMPtr<nsIMemoryReporterManager> mgr =
+    do_GetService("@mozilla.org/memory-reporter-manager;1");
+
+  nsRefPtr<DoNothingCallback> doNothing = new DoNothingCallback();
+
+  mgr->GetReportsForThisProcess(doNothing, nullptr, /* anonymize = */ false);
+}
+
+} // namespace dmd
+} // namespace mozilla
+
+#endif  // defined(MOZ_DMD)
+
--- a/xpcom/base/nsMemoryReporterManager.h
+++ b/xpcom/base/nsMemoryReporterManager.h
@@ -36,24 +36,24 @@ public:
   // Gets the memory reporter manager service.
   static nsMemoryReporterManager* GetOrCreate()
   {
     nsCOMPtr<nsIMemoryReporterManager> imgr =
       do_GetService("@mozilla.org/memory-reporter-manager;1");
     return static_cast<nsMemoryReporterManager*>(imgr.get());
   }
 
-  typedef nsDataHashtable<nsRefPtrHashKey<nsIMemoryReporter>, bool> StrongReportersTable;
-  typedef nsDataHashtable<nsPtrHashKey<nsIMemoryReporter>, bool> WeakReportersTable;
+  typedef nsTHashtable<nsRefPtrHashKey<nsIMemoryReporter>> StrongReportersTable;
+  typedef nsTHashtable<nsPtrHashKey<nsIMemoryReporter>> WeakReportersTable;
 
   // Inter-process memory reporting proceeds as follows.
   //
   // - GetReports() (declared within NS_DECL_NSIMEMORYREPORTERMANAGER)
   //   synchronously gets memory reports for the current process, sets up some
-  //   state (mPendingProcessesState) for when child processes report back --
+  //   state (mGetReportsState) for when child processes report back --
   //   including a timer -- and starts telling child processes to get memory
   //   reports.  Control then returns to the main event loop.
   //
   //   The number of concurrent child process reports is limited by the pref
   //   "memory.report_concurrency" in order to prevent the memory overhead of
   //   memory reporting from causing problems, especially on B2G when swapping
   //   to compressed RAM; see bug 1154053.
   //
@@ -103,17 +103,17 @@ public:
   //   this late stage.
   //
   // - If the time-out occurs after a child process has sent some reports but
   //   before it has signaled completion (see bug 1151597), then what it
   //   successfully sent will be included, with no explicit indication that it
   //   is incomplete.
   //
   // Now, what what happens if a child process is created/destroyed in the
-  // middle of a request?  Well, PendingProcessesState is initialized with an array
+  // middle of a request?  Well, GetReportsState is initialized with an array
   // of child process actors as of when the report started.  So...
   //
   // - If a process is created after reporting starts, it won't be sent a
   //   request for reports.  So the reported data will reflect how things were
   //   when the request began.
   //
   // - If a process is destroyed before it starts reporting back, the reported
   //   data will reflect how things are when the request ends.
@@ -179,25 +179,20 @@ public:
     {
       mozilla::PodZero(this);
     }
   };
   SizeOfTabFns mSizeOfTabFns;
 
 private:
   nsresult RegisterReporterHelper(nsIMemoryReporter* aReporter,
-                                  bool aForce, bool aStrongRef, bool aIsAsync);
+                                  bool aForce, bool aStrongRef);
   nsresult StartGettingReports();
   nsresult FinishReporting();
 
-  void DispatchReporter(nsIMemoryReporter* aReporter, bool aIsAsync,
-                        nsIHandleReportCallback* aHandleReport,
-                        nsISupports* aHandleReportData,
-                        bool aAnonymize);
-
   static void TimeoutCallback(nsITimer* aTimer, void* aData);
   // Note: this timeout needs to be long enough to allow for the
   // possibility of DMD reports and/or running on a low-end phone.
   static const uint32_t kTimeoutLengthMS = 50000;
 
   mozilla::Mutex mMutex;
   bool mIsRegistrationBlocked;
 
@@ -205,80 +200,67 @@ private:
   WeakReportersTable* mWeakReporters;
 
   // These two are only used for testing purposes.
   StrongReportersTable* mSavedStrongReporters;
   WeakReportersTable* mSavedWeakReporters;
 
   uint32_t mNextGeneration;
 
-  // Used to keep track of state of which processes are currently running and
-  // waiting to run memory reports. Holds references to parameters needed when
-  // requesting a memory report and finishing reporting.
-  struct PendingProcessesState
+  struct GetReportsState
   {
     uint32_t                             mGeneration;
     bool                                 mAnonymize;
     bool                                 mMinimize;
     nsCOMPtr<nsITimer>                   mTimer;
-    nsTArray<nsRefPtr<mozilla::dom::ContentParent>> mChildrenPending;
+    // This is a pointer to an nsTArray because otherwise C++ is
+    // unhappy unless this header includes ContentParent.h, which not
+    // everything that includes this header knows how to find.
+    nsTArray<nsRefPtr<mozilla::dom::ContentParent>>* mChildrenPending;
     uint32_t                             mNumProcessesRunning;
     uint32_t                             mNumProcessesCompleted;
     uint32_t                             mConcurrencyLimit;
     nsCOMPtr<nsIHandleReportCallback>    mHandleReport;
     nsCOMPtr<nsISupports>                mHandleReportData;
     nsCOMPtr<nsIFinishReportingCallback> mFinishReporting;
     nsCOMPtr<nsISupports>                mFinishReportingData;
     nsString                             mDMDDumpIdent;
 
-    PendingProcessesState(uint32_t aGeneration, bool aAnonymize, bool aMinimize,
-                          uint32_t aConcurrencyLimit,
-                          nsIHandleReportCallback* aHandleReport,
-                          nsISupports* aHandleReportData,
-                          nsIFinishReportingCallback* aFinishReporting,
-                          nsISupports* aFinishReportingData,
-                          const nsAString& aDMDDumpIdent);
-  };
-
-  // Used to keep track of the state of the asynchronously run memory
-  // reporters. The callback and file handle used when all memory reporters
-  // have finished are also stored here.
-  struct PendingReportersState
-  {
-    // Number of memory reporters currently running.
-    uint32_t mReportsPending;
-
-    // Callback for when all memory reporters have completed.
-    nsCOMPtr<nsIFinishReportingCallback> mFinishReporting;
-    nsCOMPtr<nsISupports> mFinishReportingData;
-
-    // File handle to write a DMD report to if requested.
-    FILE* mDMDFile;
-
-    PendingReportersState(nsIFinishReportingCallback* aFinishReporting,
-                        nsISupports* aFinishReportingData,
-                        FILE* aDMDFile)
-      : mReportsPending(0)
+    GetReportsState(uint32_t aGeneration, bool aAnonymize, bool aMinimize,
+                    uint32_t aConcurrencyLimit,
+                    nsIHandleReportCallback* aHandleReport,
+                    nsISupports* aHandleReportData,
+                    nsIFinishReportingCallback* aFinishReporting,
+                    nsISupports* aFinishReportingData,
+                    const nsAString& aDMDDumpIdent)
+      : mGeneration(aGeneration)
+      , mAnonymize(aAnonymize)
+      , mMinimize(aMinimize)
+      , mChildrenPending(nullptr)
+      , mNumProcessesRunning(1) // reporting starts with the parent
+      , mNumProcessesCompleted(0)
+      , mConcurrencyLimit(aConcurrencyLimit)
+      , mHandleReport(aHandleReport)
+      , mHandleReportData(aHandleReportData)
       , mFinishReporting(aFinishReporting)
       , mFinishReportingData(aFinishReportingData)
-      , mDMDFile(aDMDFile)
+      , mDMDDumpIdent(aDMDDumpIdent)
     {
     }
+
+    ~GetReportsState();
   };
 
   // When this is non-null, a request is in flight.  Note: We use manual
   // new/delete for this because its lifetime doesn't match block scope or
   // anything like that.
-  PendingProcessesState* mPendingProcessesState;
+  GetReportsState* mGetReportsState;
 
-  // This is reinitialized each time a call to GetReports is initiated.
-  PendingReportersState* mPendingReportersState;
-
-  PendingProcessesState* GetStateForGeneration(uint32_t aGeneration);
+  GetReportsState* GetStateForGeneration(uint32_t aGeneration);
   static bool StartChildReport(mozilla::dom::ContentParent* aChild,
-                               const PendingProcessesState* aState);
+                               const GetReportsState* aState);
 };
 
 #define NS_MEMORY_REPORTER_MANAGER_CID \
 { 0xfb97e4f5, 0x32dd, 0x497a, \
 { 0xba, 0xa2, 0x7d, 0x1e, 0x55, 0x7, 0x99, 0x10 } }
 
 #endif // nsMemoryReporterManager_h__