Backed out 3 changesets (bug 913260)
authorPhil Ringnalda <philringnalda@gmail.com>
Tue, 24 Sep 2013 20:31:00 -0700
changeset 148629 1d72ff0f809ffa7477441729782a0be7ecb2a24c
parent 148628 f5f1e753652129dbdf3ba8605d780936fec8822d
child 148630 753597994318ad97681db5b904e0556763b0dad0
push id25349
push userryanvm@gmail.com
push dateWed, 25 Sep 2013 18:52:12 +0000
treeherdermozilla-central@39f30376058c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs913260
milestone27.0a1
backs out9195be8a50cbb0478e07c1b29d419b6fc78768a8
09c71a3e7b85211747236c168ec6524fe57c352b
8a8691a260121ee5863f44ce3883507e177db417
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 3 changesets (bug 913260) CLOSED TREE Backed out changeset 9195be8a50cb (bug 913260) Backed out changeset 09c71a3e7b85 (bug 913260) Backed out changeset 8a8691a26012 (bug 913260)
dom/base/nsWindowMemoryReporter.cpp
dom/base/nsWindowMemoryReporter.h
image/src/imgLoader.cpp
js/xpconnect/src/XPCJSRuntime.cpp
storage/src/mozStorageService.cpp
storage/src/mozStorageService.h
toolkit/components/aboutmemory/content/aboutMemory.js
toolkit/components/aboutmemory/tests/test_aboutmemory.xul
toolkit/components/aboutmemory/tests/test_memoryReporters.xul
toolkit/components/telemetry/TelemetryPing.js
xpcom/base/AvailableMemoryTracker.cpp
xpcom/base/nsIMemoryReporter.idl
xpcom/base/nsMemoryReporterManager.cpp
xpcom/base/nsMemoryReporterManager.h
--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -4,59 +4,59 @@
  * 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 "amIAddonManager.h"
 #include "nsWindowMemoryReporter.h"
 #include "nsGlobalWindow.h"
 #include "nsIDocument.h"
 #include "nsIEffectiveTLDService.h"
-#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/Services.h"
 #include "mozilla/Preferences.h"
-#include "mozilla/Services.h"
-#include "mozilla/StaticPtr.h"
 #include "nsNetCID.h"
 #include "nsPrintfCString.h"
 #include "XPCJSMemoryReporter.h"
 #include "js/MemoryMetrics.h"
 #include "nsServiceManagerUtils.h"
 
 using namespace mozilla;
 
-StaticRefPtr<nsWindowMemoryReporter> sWindowReporter;
-
 nsWindowMemoryReporter::nsWindowMemoryReporter()
   : mCheckForGhostWindowsCallbackPending(false)
 {
 }
 
 NS_IMPL_ISUPPORTS3(nsWindowMemoryReporter, nsIMemoryReporter, nsIObserver,
                    nsSupportsWeakReference)
 
 /* static */
 void
 nsWindowMemoryReporter::Init()
 {
-  MOZ_ASSERT(!sWindowReporter);
-  sWindowReporter = new nsWindowMemoryReporter();
-  ClearOnShutdown(&sWindowReporter);
-  NS_RegisterMemoryReporter(sWindowReporter);
+  // The memory reporter manager will own this object.
+  nsRefPtr<nsWindowMemoryReporter> windowReporter = new nsWindowMemoryReporter();
+  NS_RegisterMemoryReporter(windowReporter);
 
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   if (os) {
     // DOM_WINDOW_DESTROYED_TOPIC announces what we call window "detachment",
     // when a window's docshell is set to NULL.
-    os->AddObserver(sWindowReporter, DOM_WINDOW_DESTROYED_TOPIC,
+    os->AddObserver(windowReporter, DOM_WINDOW_DESTROYED_TOPIC,
                     /* weakRef = */ true);
-    os->AddObserver(sWindowReporter, "after-minimize-memory-usage",
+    os->AddObserver(windowReporter, "after-minimize-memory-usage",
                     /* weakRef = */ true);
   }
 
-  NS_RegisterMemoryReporter(new GhostWindowsReporter());
-  RegisterGhostWindowsDistinguishedAmount(GhostWindowsReporter::DistinguishedAmount);
+  nsRefPtr<GhostURLsReporter> ghostURLsReporter =
+    new GhostURLsReporter(windowReporter);
+  NS_RegisterMemoryReporter(ghostURLsReporter);
+
+  nsRefPtr<NumGhostsReporter> numGhostsReporter =
+    new NumGhostsReporter(windowReporter);
+  NS_RegisterMemoryReporter(numGhostsReporter);
 }
 
 static already_AddRefed<nsIURI>
 GetWindowURI(nsIDOMWindow *aWindow)
 {
   nsCOMPtr<nsPIDOMWindow> pWindow = do_QueryInterface(aWindow);
   NS_ENSURE_TRUE(pWindow, nullptr);
 
@@ -306,62 +306,16 @@ static
 PLDHashOperator
 GetWindows(const uint64_t& aId, nsGlobalWindow*& aWindow, void* aClosure)
 {
   ((WindowArray *)aClosure)->AppendElement(aWindow);
 
   return PL_DHASH_NEXT;
 }
 
-struct ReportGhostWindowsEnumeratorData
-{
-  nsIMemoryReporterCallback* callback;
-  nsISupports* closure;
-  nsresult rv;
-};
-
-static PLDHashOperator
-ReportGhostWindowsEnumerator(nsUint64HashKey* aIDHashKey, void* aClosure)
-{
-  ReportGhostWindowsEnumeratorData *data =
-    static_cast<ReportGhostWindowsEnumeratorData*>(aClosure);
-
-  nsGlobalWindow::WindowByIdTable* windowsById =
-    nsGlobalWindow::GetWindowsTable();
-  if (!windowsById) {
-    NS_WARNING("Couldn't get window-by-id hashtable?");
-    return PL_DHASH_NEXT;
-  }
-
-  nsGlobalWindow* window = windowsById->Get(aIDHashKey->GetKey());
-  if (!window) {
-    NS_WARNING("Could not look up window?");
-    return PL_DHASH_NEXT;
-  }
-
-  nsAutoCString path;
-  path.AppendLiteral("ghost-windows/");
-  AppendWindowURI(window, path);
-
-  nsresult rv = data->callback->Callback(
-    /* process = */ EmptyCString(),
-    path,
-    nsIMemoryReporter::KIND_OTHER,
-    nsIMemoryReporter::UNITS_COUNT,
-    /* amount = */ 1,
-    /* description = */ NS_LITERAL_CSTRING("A ghost window."),
-    data->closure);
-
-  if (NS_FAILED(rv) && NS_SUCCEEDED(data->rv)) {
-    data->rv = rv;
-  }
-
-  return PL_DHASH_NEXT;
-}
-
 NS_IMETHODIMP
 nsWindowMemoryReporter::GetName(nsACString &aName)
 {
   aName.AssignLiteral("window-objects");
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -372,46 +326,40 @@ nsWindowMemoryReporter::CollectReports(n
     nsGlobalWindow::GetWindowsTable();
   NS_ENSURE_TRUE(windowsById, NS_OK);
 
   // Hold on to every window in memory so that window objects can't be
   // destroyed while we're calling the memory reporter callback.
   WindowArray windows;
   windowsById->Enumerate(GetWindows, &windows);
 
-  // Get the IDs of all the "ghost" windows, and call aCb->Callback() for each
-  // one.
+  // Get the IDs of all the "ghost" windows.
   nsTHashtable<nsUint64HashKey> ghostWindows;
   CheckForGhostWindows(&ghostWindows);
-  ReportGhostWindowsEnumeratorData reportGhostWindowsEnumData =
-    { aCb, aClosure, NS_OK };
-  ghostWindows.EnumerateEntries(ReportGhostWindowsEnumerator,
-                                &reportGhostWindowsEnumData);
-  nsresult rv = reportGhostWindowsEnumData.rv;
-  NS_ENSURE_SUCCESS(rv, rv);
 
   WindowPaths windowPaths;
+
   WindowPaths topWindowPaths;
 
   // Collect window memory usage.
   nsWindowSizes windowTotalSizes(NULL);
   nsCOMPtr<amIAddonManager> addonManager =
     do_GetService("@mozilla.org/addons/integration;1");
   for (uint32_t i = 0; i < windows.Length(); i++) {
-    rv = CollectWindowReports(windows[i], addonManager,
-                              &windowTotalSizes, &ghostWindows,
-                              &windowPaths, &topWindowPaths, aCb,
-                              aClosure);
+    nsresult rv = CollectWindowReports(windows[i], addonManager,
+                                       &windowTotalSizes, &ghostWindows,
+                                       &windowPaths, &topWindowPaths, aCb,
+                                       aClosure);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Report JS memory usage.  We do this from here because the JS memory
   // reporter needs to be passed |windowPaths|.
-  rv = xpc::JSReporter::CollectReports(&windowPaths, &topWindowPaths,
-                                       aCb, aClosure);
+  nsresult rv = xpc::JSReporter::CollectReports(&windowPaths, &topWindowPaths,
+                                                aCb, aClosure);
   NS_ENSURE_SUCCESS(rv, rv);
 
 #define REPORT(_path, _amount, _desc)                                         \
   do {                                                                        \
     nsresult rv;                                                              \
     rv = aCb->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path),             \
                        nsIMemoryReporter::KIND_OTHER,                         \
                        nsIMemoryReporter::UNITS_BYTES, _amount,               \
@@ -709,16 +657,99 @@ nsWindowMemoryReporter::CheckForGhostWin
   // if it's not null.
   CheckForGhostWindowsEnumeratorData ghostEnumData =
     { &nonDetachedWindowDomains, aOutGhostIDs, tldService,
       GetGhostTimeout(), TimeStamp::Now() };
   mDetachedWindows.Enumerate(CheckForGhostWindowsEnumerator,
                              &ghostEnumData);
 }
 
-/* static */ int64_t
-nsWindowMemoryReporter::GhostWindowsReporter::DistinguishedAmount()
+NS_IMPL_ISUPPORTS1(nsWindowMemoryReporter::GhostURLsReporter,
+                   nsIMemoryReporter)
+
+nsWindowMemoryReporter::
+GhostURLsReporter::GhostURLsReporter(
+  nsWindowMemoryReporter* aWindowReporter)
+  : mWindowReporter(aWindowReporter)
+{
+}
+
+NS_IMETHODIMP
+nsWindowMemoryReporter::
+GhostURLsReporter::GetName(nsACString& aName)
+{
+  aName.AssignLiteral("ghost-windows-multi");
+  return NS_OK;
+}
+
+struct ReportGhostWindowsEnumeratorData
+{
+  nsIMemoryReporterCallback* callback;
+  nsISupports* closure;
+  nsresult rv;
+};
+
+static PLDHashOperator
+ReportGhostWindowsEnumerator(nsUint64HashKey* aIDHashKey, void* aClosure)
+{
+  ReportGhostWindowsEnumeratorData *data =
+    static_cast<ReportGhostWindowsEnumeratorData*>(aClosure);
+
+  nsGlobalWindow::WindowByIdTable* windowsById =
+    nsGlobalWindow::GetWindowsTable();
+  if (!windowsById) {
+    NS_WARNING("Couldn't get window-by-id hashtable?");
+    return PL_DHASH_NEXT;
+  }
+
+  nsGlobalWindow* window = windowsById->Get(aIDHashKey->GetKey());
+  if (!window) {
+    NS_WARNING("Could not look up window?");
+    return PL_DHASH_NEXT;
+  }
+
+  nsAutoCString path;
+  path.AppendLiteral("ghost-windows/");
+  AppendWindowURI(window, path);
+
+  nsresult rv = data->callback->Callback(
+    /* process = */ EmptyCString(),
+    path,
+    nsIMemoryReporter::KIND_OTHER,
+    nsIMemoryReporter::UNITS_COUNT,
+    /* amount = */ 1,
+    /* description = */ NS_LITERAL_CSTRING("A ghost window."),
+    data->closure);
+
+  if (NS_FAILED(rv) && NS_SUCCEEDED(data->rv)) {
+    data->rv = rv;
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+NS_IMETHODIMP
+nsWindowMemoryReporter::
+GhostURLsReporter::CollectReports(
+  nsIMemoryReporterCallback* aCb,
+  nsISupports* aClosure)
+{
+  // Get the IDs of all the ghost windows in existance.
+  nsTHashtable<nsUint64HashKey> ghostWindows;
+  mWindowReporter->CheckForGhostWindows(&ghostWindows);
+
+  ReportGhostWindowsEnumeratorData reportGhostWindowsEnumData =
+    { aCb, aClosure, NS_OK };
+
+  // Call aCb->Callback() for each ghost window.
+  ghostWindows.EnumerateEntries(ReportGhostWindowsEnumerator,
+                                &reportGhostWindowsEnumData);
+
+  return reportGhostWindowsEnumData.rv;
+}
+
+int64_t
+nsWindowMemoryReporter::NumGhostsReporter::Amount()
 {
   nsTHashtable<nsUint64HashKey> ghostWindows;
-  sWindowReporter->CheckForGhostWindows(&ghostWindows);
+  mWindowReporter->CheckForGhostWindows(&ghostWindows);
   return ghostWindows.Count();
 }
-
--- a/dom/base/nsWindowMemoryReporter.h
+++ b/dom/base/nsWindowMemoryReporter.h
@@ -116,38 +116,56 @@ public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIMEMORYREPORTER
   NS_DECL_NSIOBSERVER
 
   static void Init();
 
 private:
   /**
+   * GhostURLsReporter generates the list of all ghost windows' URLs.  If
+   * you're only interested in this list, running this report is faster than
+   * running nsWindowMemoryReporter.
+   */
+  class GhostURLsReporter MOZ_FINAL : public nsIMemoryReporter
+  {
+  public:
+    GhostURLsReporter(nsWindowMemoryReporter* aWindowReporter);
+
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIMEMORYREPORTER
+
+  private:
+    nsRefPtr<nsWindowMemoryReporter> mWindowReporter;
+  };
+
+  /**
    * nsGhostWindowReporter generates the "ghost-windows" report, which counts
    * the number of ghost windows present.
    */
-  class GhostWindowsReporter MOZ_FINAL : public mozilla::MemoryUniReporter
+  class NumGhostsReporter MOZ_FINAL : public mozilla::MemoryUniReporter
   {
   public:
-    GhostWindowsReporter()
+    NumGhostsReporter(nsWindowMemoryReporter* aWindowReporter)
       : MemoryUniReporter("ghost-windows", KIND_OTHER, UNITS_COUNT,
 "The number of ghost windows present (the number of nodes underneath "
 "explicit/window-objects/top(none)/ghost, modulo race conditions).  A ghost "
 "window is not shown in any tab, does not share a domain with any non-detached "
 "windows, and has met these criteria for at least "
 "memory.ghost_window_timeout_seconds, or has survived a round of "
 "about:memory's minimize memory usage button.\n\n"
 "Ghost windows can happen legitimately, but they are often indicative of "
 "leaks in the browser or add-ons.")
+      , mWindowReporter(aWindowReporter)
     {}
 
-    static int64_t DistinguishedAmount();
+  private:
+    int64_t Amount() MOZ_OVERRIDE;
 
-  private:
-    int64_t Amount() MOZ_OVERRIDE { return DistinguishedAmount(); }
+    nsRefPtr<nsWindowMemoryReporter> mWindowReporter;
   };
 
   // Protect ctor, use Init() instead.
   nsWindowMemoryReporter();
 
   /**
    * Get the number of seconds for which a window must satisfy ghost criteria
    * (1) and (2) before we deem that it satisfies criterion (3).
--- a/image/src/imgLoader.cpp
+++ b/image/src/imgLoader.cpp
@@ -132,17 +132,17 @@ public:
            nsIMemoryReporter::KIND_NONHEAP, content.mUnusedUncompressedNonheap,
            "Memory used by not in-use content images (uncompressed data).");
 
 #undef REPORT
 
     return NS_OK;
   }
 
-  static int64_t ImagesContentUsedUncompressedDistinguishedAmount()
+  static int64_t GetImagesContentUsedUncompressed()
   {
     size_t n = 0;
     for (uint32_t i = 0; i < imgLoader::sMemReporter->mKnownLoaders.Length(); i++) {
       imgLoader::sMemReporter->mKnownLoaders[i]->mCache.EnumerateRead(EntryUsedUncompressedSize, &n);
     }
     return n;
   }
 
@@ -218,16 +218,36 @@ private:
     }
 
     return PL_DHASH_NEXT;
   }
 };
 
 NS_IMPL_ISUPPORTS1(imgMemoryReporter, nsIMemoryReporter)
 
+// This is used by telemetry.
+class ImagesContentUsedUncompressedReporter MOZ_FINAL
+  : public MemoryUniReporter
+{
+public:
+  ImagesContentUsedUncompressedReporter()
+    : MemoryUniReporter("images-content-used-uncompressed",
+                         KIND_OTHER, UNITS_BYTES,
+"This is the sum of the 'explicit/images/content/used/uncompressed-heap' "
+"and 'explicit/images/content/used/uncompressed-nonheap' numbers.  However, "
+"it is measured at a different time and so may give slightly different "
+"results.")
+  {}
+private:
+  int64_t Amount() MOZ_OVERRIDE
+  {
+    return imgMemoryReporter::GetImagesContentUsedUncompressed();
+  }
+};
+
 NS_IMPL_ISUPPORTS3(nsProgressNotificationProxy,
                      nsIProgressEventSink,
                      nsIChannelEventSink,
                      nsIInterfaceRequestor)
 
 NS_IMETHODIMP
 nsProgressNotificationProxy::OnProgress(nsIRequest* request,
                                         nsISupports* ctxt,
@@ -814,17 +834,17 @@ void imgLoader::GlobalInit()
   rv = Preferences::GetInt("image.cache.size", &cachesize);
   if (NS_SUCCEEDED(rv))
     sCacheMaxSize = cachesize;
   else
     sCacheMaxSize = 5 * 1024 * 1024;
 
   sMemReporter = new imgMemoryReporter();
   NS_RegisterMemoryReporter(sMemReporter);
-  RegisterImagesContentUsedUncompressedDistinguishedAmount(imgMemoryReporter::ImagesContentUsedUncompressedDistinguishedAmount);
+  NS_RegisterMemoryReporter(new ImagesContentUsedUncompressedReporter());
 }
 
 nsresult imgLoader::InitCache()
 {
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (!os)
     return NS_ERROR_FAILURE;
 
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1570,58 +1570,91 @@ GetCompartmentName(JSCompartment *c, nsC
         // (such as about:memory) have to undo this change.
         if (replaceSlashes)
             name.ReplaceChar('/', '\\');
     } else {
         name.AssignLiteral("null-principal");
     }
 }
 
-static int64_t
-JSMainRuntimeGCHeapDistinguishedAmount()
+// Telemetry relies on this being a uni-reporter (rather than part of the "js"
+// reporter).
+class JSMainRuntimeGCHeapReporter MOZ_FINAL : public MemoryUniReporter
 {
-    JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
-    return int64_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) *
-           js::gc::ChunkSize;
-}
-
-static int64_t
-JSMainRuntimeTemporaryPeakDistinguishedAmount()
+public:
+    JSMainRuntimeGCHeapReporter()
+      : MemoryUniReporter("js-main-runtime-gc-heap", KIND_OTHER, UNITS_BYTES,
+"Memory used by the garbage-collected heap in the main JSRuntime.")
+    {}
+private:
+    int64_t Amount() MOZ_OVERRIDE
+    {
+        JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
+        return int64_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) *
+               js::gc::ChunkSize;
+    }
+};
+
+// Nb: js-main-runtime-compartments/system + js-main-runtime-compartments/user
+// could be different to the number of compartments reported by JSReporter if a
+// garbage collection occurred between them being consulted.  We could move
+// these reporters into JSReporter to avoid that problem, but then we couldn't
+// easily report them via telemetry, so we live with the small risk of
+// inconsistencies.
+
+class RedundantJSMainRuntimeCompartmentsSystemReporter MOZ_FINAL : public MemoryUniReporter
 {
-    JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
-    return JS::PeakSizeOfTemporary(rt);
-}
-
-static int64_t
-JSMainRuntimeCompartmentsSystemDistinguishedAmount()
+public:
+    // An empty description is ok because this is a "redundant/"-prefixed
+    // reporter and so is ignored by about:memory.
+    RedundantJSMainRuntimeCompartmentsSystemReporter()
+      : MemoryUniReporter("redundant/js-main-runtime-compartments/system",
+                          KIND_OTHER, UNITS_COUNT, "")
+    {}
+private:
+    int64_t Amount() MOZ_OVERRIDE
+    {
+        JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
+        return JS::SystemCompartmentCount(rt);
+    }
+};
+
+class RedundantJSMainRuntimeCompartmentsUserReporter MOZ_FINAL : public MemoryUniReporter
 {
-    JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
-    return JS::SystemCompartmentCount(rt);
-}
-
-static int64_t
-JSMainRuntimeCompartmentsUserDistinguishedAmount()
-{
-    JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
-    return JS::UserCompartmentCount(rt);
-}
-
+public:
+    // An empty description is ok because this is a "redundant/"-prefixed
+    // reporter and so is ignored by about:memory.
+    RedundantJSMainRuntimeCompartmentsUserReporter()
+      : MemoryUniReporter("redundant/js-main-runtime-compartments/user",
+                          KIND_OTHER, UNITS_COUNT,
+"The number of JavaScript compartments for user code in the main JSRuntime.")
+    {}
+private:
+    int64_t Amount() MOZ_OVERRIDE
+    {
+        JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
+        return JS::UserCompartmentCount(rt);
+    }
+};
+
+// This is also a single reporter so it can be used by telemetry.
 class JSMainRuntimeTemporaryPeakReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     JSMainRuntimeTemporaryPeakReporter()
       : MemoryUniReporter("js-main-runtime-temporary-peak",
                            KIND_OTHER, UNITS_BYTES,
 "The peak size of the transient storage in the main JSRuntime (the current "
 "size of which is reported as 'explicit/js-non-window/runtime/temporary').")
     {}
 private:
     int64_t Amount() MOZ_OVERRIDE
     {
-        return JSMainRuntimeTemporaryPeakDistinguishedAmount();
+        JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
+        return JS::PeakSizeOfTemporary(rt);
     }
 };
 
 // The REPORT* macros do an unconditional report.  The ZCREPORT* macros are for
 // compartments and zones; they aggregate any entries smaller than
 // SUNDRIES_THRESHOLD into the "sundries/gc-heap" and "sundries/malloc-heap"
 // entries for the compartment.
 
@@ -3018,23 +3051,21 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
     js::SetSourceHook(runtime, new XPCJSSourceHook);
 
     // Set up locale information and callbacks for the newly-created runtime so
     // that the various toLocaleString() methods, localeCompare(), and other
     // internationalization APIs work as desired.
     if (!xpc_LocalizeRuntime(runtime))
         NS_RUNTIMEABORT("xpc_LocalizeRuntime failed.");
 
-    // Register memory reporters and distinguished amount functions.
-    NS_RegisterMemoryReporter(new JSMainRuntimeCompartmentsReporter);
+    NS_RegisterMemoryReporter(new JSMainRuntimeGCHeapReporter());
+    NS_RegisterMemoryReporter(new RedundantJSMainRuntimeCompartmentsSystemReporter());
+    NS_RegisterMemoryReporter(new RedundantJSMainRuntimeCompartmentsUserReporter());
     NS_RegisterMemoryReporter(new JSMainRuntimeTemporaryPeakReporter());
-    RegisterJSMainRuntimeGCHeapDistinguishedAmount(JSMainRuntimeGCHeapDistinguishedAmount);
-    RegisterJSMainRuntimeTemporaryPeakDistinguishedAmount(JSMainRuntimeTemporaryPeakDistinguishedAmount);
-    RegisterJSMainRuntimeCompartmentsSystemDistinguishedAmount(JSMainRuntimeCompartmentsSystemDistinguishedAmount);
-    RegisterJSMainRuntimeCompartmentsUserDistinguishedAmount(JSMainRuntimeCompartmentsUserDistinguishedAmount);
+    NS_RegisterMemoryReporter(new JSMainRuntimeCompartmentsReporter);
 
     // Install a JavaScript 'debugger' keyword handler in debug builds only
 #ifdef DEBUG
     if (!JS_GetGlobalDebugHooks(runtime)->debuggerHandler)
         xpc_InstallJSDebuggerKeywordHandler(runtime);
 #endif
 }
 
--- a/storage/src/mozStorageService.cpp
+++ b/storage/src/mozStorageService.cpp
@@ -48,34 +48,43 @@
 #define PREF_TS_PAGESIZE_DEFAULT 32768
 
 namespace mozilla {
 namespace storage {
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Memory Reporting
 
-static int64_t
-StorageSQLiteDistinguishedAmount()
+// We don't need an "explicit" reporter for total SQLite memory usage, because
+// the multi-reporter provides reports that add up to the total.  But it's
+// useful to have the total in the "Other Measurements" list in about:memory,
+// and more importantly, we also gather the total via telemetry.
+class StorageSQLiteUniReporter MOZ_FINAL : public MemoryUniReporter
 {
-  return ::sqlite3_memory_used();
-}
+public:
+  StorageSQLiteUniReporter()
+    : MemoryUniReporter("storage-sqlite", KIND_OTHER, UNITS_BYTES,
+                         "Memory used by SQLite.")
+  {}
+private:
+  int64_t Amount() MOZ_OVERRIDE { return ::sqlite3_memory_used(); }
+};
 
-class StorageSQLiteReporter MOZ_FINAL : public nsIMemoryReporter
+class StorageSQLiteMultiReporter MOZ_FINAL : public nsIMemoryReporter
 {
 private:
   Service *mService;    // a weakref because Service contains a strongref to this
   nsCString mStmtDesc;
   nsCString mCacheDesc;
   nsCString mSchemaDesc;
 
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
 
-  StorageSQLiteReporter(Service *aService)
+  StorageSQLiteMultiReporter(Service *aService)
   : mService(aService)
   {
     mStmtDesc = NS_LITERAL_CSTRING(
       "Memory (approximate) used by all prepared statements used by "
       "connections to this database.");
 
     mCacheDesc = NS_LITERAL_CSTRING(
       "Memory (approximate) used by all pager caches used by connections "
@@ -200,17 +209,17 @@ private:
     NS_ENSURE_SUCCESS(rv, rv);
     *aTotal += curr;
 
     return NS_OK;
   }
 };
 
 NS_IMPL_ISUPPORTS1(
-  StorageSQLiteReporter,
+  StorageSQLiteMultiReporter,
   nsIMemoryReporter
 )
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Service
 
 NS_IMPL_ISUPPORTS2(
   Service,
@@ -293,18 +302,18 @@ Service::Service()
 , mSqliteVFS(nullptr)
 , mRegistrationMutex("Service::mRegistrationMutex")
 , mConnections()
 {
 }
 
 Service::~Service()
 {
-  (void)::NS_UnregisterMemoryReporter(mStorageSQLiteReporter);
-  mozilla::UnregisterStorageSQLiteDistinguishedAmount();
+  (void)::NS_UnregisterMemoryReporter(mStorageSQLiteUniReporter);
+  (void)::NS_UnregisterMemoryReporter(mStorageSQLiteMultiReporter);
 
   int rc = sqlite3_vfs_unregister(mSqliteVFS);
   if (rc != SQLITE_OK)
     NS_WARNING("Failed to unregister sqlite vfs wrapper.");
 
   // Shutdown the sqlite3 API.  Warn if shutdown did not turn out okay, but
   // there is nothing actionable we can do in that case.
   rc = ::sqlite3_shutdown();
@@ -524,22 +533,22 @@ Service::initialize()
     Preferences::GetInt(PREF_TS_SYNCHRONOUS, PREF_TS_SYNCHRONOUS_DEFAULT);
 
   // We need to obtain the toolkit.storage.pageSize preferences on the main
   // thread because the preference service can only be accessed there.  This
   // is cached in the service for all future Open[Unshared]Database calls.
   sDefaultPageSize =
       Preferences::GetInt(PREF_TS_PAGESIZE, PREF_TS_PAGESIZE_DEFAULT);
 
-  // Create and register our SQLite memory reporter and distinguished amount
-  // function.  Registration can only happen on the main thread (otherwise
-  // you'll get cryptic crashes).
-  mStorageSQLiteReporter = new StorageSQLiteReporter(this);
-  (void)::NS_RegisterMemoryReporter(mStorageSQLiteReporter);
-  mozilla::RegisterStorageSQLiteDistinguishedAmount(StorageSQLiteDistinguishedAmount);
+  // Create and register our SQLite memory reporters.  Registration can only
+  // happen on the main thread (otherwise you'll get cryptic crashes).
+  mStorageSQLiteUniReporter = new StorageSQLiteUniReporter();
+  mStorageSQLiteMultiReporter = new StorageSQLiteMultiReporter(this);
+  (void)::NS_RegisterMemoryReporter(mStorageSQLiteUniReporter);
+  (void)::NS_RegisterMemoryReporter(mStorageSQLiteMultiReporter);
 
   return NS_OK;
 }
 
 int
 Service::localeCompareStrings(const nsAString &aStr1,
                               const nsAString &aStr2,
                               int32_t aComparisonStrength)
--- a/storage/src/mozStorageService.h
+++ b/storage/src/mozStorageService.h
@@ -167,17 +167,18 @@ private:
    *
    * @note Collation implementations are platform-dependent and in general not
    *       thread-safe.  Access to this collation should be synchronized.
    */
   nsCOMPtr<nsICollation> mLocaleCollation;
 
   nsCOMPtr<nsIFile> mProfileStorageFile;
 
-  nsCOMPtr<nsIMemoryReporter> mStorageSQLiteReporter;
+  nsCOMPtr<nsIMemoryReporter> mStorageSQLiteUniReporter;
+  nsCOMPtr<nsIMemoryReporter> mStorageSQLiteMultiReporter;
 
   static Service *gService;
 
   static nsIXPConnect *sXPConnect;
 
   static int32_t sSynchronousPref;
   static int32_t sDefaultPageSize;
 };
--- a/toolkit/components/aboutmemory/content/aboutMemory.js
+++ b/toolkit/components/aboutmemory/content/aboutMemory.js
@@ -126,50 +126,66 @@ function onUnload()
                      "child-memory-reporter-update");
 }
 
 //---------------------------------------------------------------------------
 
 /**
  * Iterates over each reporter.
  *
+ * @param aIgnoreReporter
+ *        Function that indicates if we should skip an entire reporter, based
+ *        on its name.
+ * @param aIgnoreReport
+ *        Function that indicates if we should skip a single report from a
+ *        reporter, based on its path.
  * @param aHandleReport
  *        The function that's called for each non-skipped report.
  */
-function processMemoryReporters(aHandleReport)
+function processMemoryReporters(aIgnoreReporter, aIgnoreReport, aHandleReport)
 {
   let handleReport = function(aProcess, aUnsafePath, aKind, aUnits,
                               aAmount, aDescription) {
-    aHandleReport(aProcess, aUnsafePath, aKind, aUnits, aAmount,
-                  aDescription, /* presence = */ undefined);
+    if (!aIgnoreReport(aUnsafePath)) {
+      aHandleReport(aProcess, aUnsafePath, aKind, aUnits, aAmount,
+                    aDescription, /* presence = */ undefined);
+    }
   }
 
   let e = gMgr.enumerateReporters();
   while (e.hasMoreElements()) {
     let mr = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
-    mr.collectReports(handleReport, null);
+    if (!aIgnoreReporter(mr.name)) {
+      // |collectReports| never passes in a |presence| argument.
+      mr.collectReports(handleReport, null);
+    }
   }
 }
 
 /**
  * Iterates over each report.
  *
  * @param aReports
  *        Array of reports, read from a file or the clipboard.
+ * @param aIgnoreReport
+ *        Function that indicates if we should skip a single report, based
+ *        on its path.
  * @param aHandleReport
  *        The function that's called for each report.
  */
-function processMemoryReportsFromFile(aReports, aHandleReport)
+function processMemoryReportsFromFile(aReports, aIgnoreReport, aHandleReport)
 {
   // Process each memory reporter with aHandleReport.
 
   for (let i = 0; i < aReports.length; i++) {
     let r = aReports[i];
-    aHandleReport(r.process, r.path, r.kind, r.units, r.amount,
-                  r.description, r._presence);
+    if (!aIgnoreReport(r.path)) {
+      aHandleReport(r.process, r.path, r.kind, r.units, r.amount,
+                    r.description, r._presence);
+    }
   }
 }
 
 //---------------------------------------------------------------------------
 
 // The <div> holding everything but the header and footer (if they're present).
 // It's what is updated each time the page changes.
 let gMain;
@@ -178,16 +194,17 @@ let gMain;
 let gFooter;
 
 // The "verbose" checkbox.
 let gVerbose;
 
 // Values for the second argument to updateMainAndFooter.
 let HIDE_FOOTER = 0;
 let SHOW_FOOTER = 1;
+let IGNORE_FOOTER = 2;
 
 function updateMainAndFooter(aMsg, aFooterAction, aClassName)
 {
   // Clear gMain by replacing it with an empty node.
   let tmp = gMain.cloneNode(false);
   gMain.parentNode.replaceChild(tmp, gMain);
   gMain = tmp;
 
@@ -204,16 +221,17 @@ function updateMainAndFooter(aMsg, aFoot
       className = className + " " + aClassName;
     }
     appendElementWithText(gMain, 'div', className, aMsg);
   }
 
   switch (aFooterAction) {
    case HIDE_FOOTER:   gFooter.classList.add('hidden');    break;
    case SHOW_FOOTER:   gFooter.classList.remove('hidden'); break;
+   case IGNORE_FOOTER:                                     break;
    default: assertInput(false, "bad footer action in updateMainAndFooter");
   }
 }
 
 function appendTextNode(aP, aText)
 {
   let e = document.createTextNode(aText);
   aP.appendChild(e);
@@ -481,18 +499,18 @@ function updateAboutMemoryFromJSONObject
 {
   try {
     assertInput(aObj.version === gCurrentFileFormatVersion,
                 "data version number missing or doesn't match");
     assertInput(aObj.hasMozMallocUsableSize !== undefined,
                 "missing 'hasMozMallocUsableSize' property");
     assertInput(aObj.reports && aObj.reports instanceof Array,
                 "missing or non-array 'reports' property");
-    let process = function(aHandleReport) {
-      processMemoryReportsFromFile(aObj.reports, aHandleReport);
+    let process = function(aIgnoreReporter, aIgnoreReport, aHandleReport) {
+      processMemoryReportsFromFile(aObj.reports, aIgnoreReport, aHandleReport);
     }
     appendAboutMemoryMain(process, aObj.hasMozMallocUsableSize);
   } catch (ex) {
     handleException(ex);
   }
 }
 
 /**
@@ -914,16 +932,29 @@ function getPCollsByProcess(aProcessRepo
 {
   let pcollsByProcess = {};
 
   // This regexp matches sentences and sentence fragments, i.e. strings that
   // start with a capital letter and ends with a '.'.  (The final sentence may
   // be in parentheses, so a ')' might appear after the '.'.)
   const gSentenceRegExp = /^[A-Z].*\.\)?$/m;
 
+  // Ignore any "redundant/"-prefixed reporters and reports, which are only
+  // used by telemetry.
+
+  function ignoreReporter(aName)
+  {
+    return aName.startsWith("redundant/");
+  }
+
+  function ignoreReport(aUnsafePath)
+  {
+    return aUnsafePath.startsWith("redundant/");
+  }
+
   function handleReport(aProcess, aUnsafePath, aKind, aUnits, aAmount,
                         aDescription, aPresence)
   {
     if (aUnsafePath.startsWith("explicit/")) {
       assertInput(aKind === KIND_HEAP || aKind === KIND_NONHEAP,
                   "bad explicit kind");
       assertInput(aUnits === UNITS_BYTES, "bad explicit units");
       assertInput(gSentenceRegExp.test(aDescription),
@@ -952,18 +983,17 @@ function getPCollsByProcess(aProcessRepo
         assertInput(gSentenceRegExp.test(aDescription),
                     "non-sentence other description: " + aUnsafePath + ", " +
                     aDescription);
       }
     }
 
     assert(aPresence === undefined ||
            aPresence == DReport.PRESENT_IN_FIRST_ONLY ||
-           aPresence == DReport.PRESENT_IN_SECOND_ONLY,
-           "bad presence");
+           aPresence == DReport.PRESENT_IN_SECOND_ONLY);
 
     let process = aProcess === "" ? gUnnamedProcessStr : aProcess;
     let unsafeNames = aUnsafePath.split('/');
     let unsafeName0 = unsafeNames[0];
     let isDegenerate = unsafeNames.length === 1;
 
     // Get the PColl table for the process, creating it if necessary.
     let pcoll = pcollsByProcess[process];
@@ -1011,17 +1041,17 @@ function getPCollsByProcess(aProcessRepo
       t._amount = aAmount;
       t._description = aDescription;
       if (aPresence !== undefined) {
         t._presence = aPresence;
       }
     }
   }
 
-  aProcessReports(handleReport);
+  aProcessReports(ignoreReporter, ignoreReport, handleReport);
 
   return pcollsByProcess;
 }
 
 //---------------------------------------------------------------------------
 
 // There are two kinds of TreeNode.
 // - Leaf TreeNodes correspond to reports.
@@ -1337,21 +1367,16 @@ function appendProcessAboutMemoryElement
   // The explicit tree.
   let hasKnownHeapAllocated;
   {
     let treeName = "explicit";
     let pre = appendSectionHeader(aP, "Explicit Allocations");
     let t = aTrees[treeName];
     if (t) {
       fillInTree(t);
-      // Using the "heap-allocated" reporter here instead of
-      // nsMemoryReporterManager.heapAllocated goes against the usual pattern.
-      // But the "heap-allocated" node will go in the tree like the others, so
-      // we have to deal with it, and once we're dealing with it, it's easier
-      // to keep doing so rather than switching to the distinguished amount.
       hasKnownHeapAllocated =
         aDegenerates &&
         addHeapUnclassifiedNode(t, aDegenerates["heap-allocated"], aHeapTotal);
       sortTreeAndInsertAggregateNodes(t._amount, t);
       t._description = explicitTreeDescription;
       appendTreeElements(pre, t, aProcess, "");
       delete aTrees[treeName];
     }
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
@@ -145,32 +145,41 @@
     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 = [
+    { name: "redundant/foobar",
+      collectReports: function(aCbObj, aClosure) {
+        // Shouldn't reach here;  aboutMemory.js should skip the reporter.
+        // (Nb: this must come after |mgr.explicit| is accessed, otherwise it
+        // *will* be run by nsMemoryReporterManager::GetExplicit()).
+        ok(false, "'redundant/foobar' reporter was run");
+      }
+    },
     { name: "fake3",
       collectReports: function(aCbObj, aClosure) {
         function f(aP1, aP2, aK, aU, aA) {
           aCbObj.callback(aP1, aP2, aK, aU, aA, "Desc.", aClosure);
         }
         f("2nd", "heap-allocated",  OTHER,   BYTES,1000* MB);
         f("2nd", "heap-unallocated",OTHER,   BYTES,100 * MB);
         f("2nd", "explicit/a/b/c",  HEAP,    BYTES,497 * MB);
         f("2nd", "explicit/a/b/c",  HEAP,    BYTES,  1 * MB); // dup: merge
         f("2nd", "explicit/a/b/c",  HEAP,    BYTES,  1 * MB); // dup: merge
         f("2nd", "explicit/flip\\the\\backslashes",
                                     HEAP,    BYTES,200 * MB);
         f("2nd", "explicit/compartment(compartment-url)",
                                     HEAP,    BYTES,200 * MB);
         f("2nd", "other0",          OTHER,   BYTES,666 * MB);
         f("2nd", "other1",          OTHER,   BYTES,111 * MB);
+        f("2nd", "redundant/blah",  NONHEAP, BYTES,24*4*KB); // ignored!
 
         // Check that we can handle "heap-allocated" not being present.
         f("3rd", "explicit/a/b",    HEAP,    BYTES,333 * MB);
         f("3rd", "explicit/a/c",    HEAP,    BYTES,444 * MB);
         f("3rd", "other1",          OTHER,   BYTES,  1 * MB);
         f("3rd", "resident",        OTHER,   BYTES,100 * MB);
 
         // Invalid values (negative, too-big) should be identified.
--- a/toolkit/components/aboutmemory/tests/test_memoryReporters.xul
+++ b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul
@@ -40,17 +40,26 @@
   const PERCENTAGE = Ci.nsIMemoryReporter.UNITS_PERCENTAGE;
 
   let vsizeAmounts = [];
   let residentAmounts = [];
   let jsGcHeapAmounts = [];
   let heapAllocatedAmounts = [];
   let storageSqliteAmounts = [];
 
-  let present = {}
+  let areJsNonWindowCompartmentsPresent = false;
+  let areWindowObjectsJsCompartmentsPresent = false;
+  let isSandboxLocationShown = false;
+  let isPlacesPresent = false;
+  let isImagesPresent = false;
+  let isXptiWorkingSetPresent = false;
+  let isAtomTablePresent = false;
+  let isBigStringPresent = false;
+  let isSmallString1Present = false;
+  let isSmallString2Present = false;
 
   // Generate a long, random string.  We'll check that this string is
   // reported in at least one of the memory reporters.
   let bigString = "";
   while (bigString.length < 10000) {
     bigString += Math.random();
   }
   let bigStringPrefix = bigString.substring(0, 100);
@@ -78,87 +87,58 @@
       jsGcHeapAmounts.push(aAmount); 
     } else if (aPath === "heap-allocated") {
       heapAllocatedAmounts.push(aAmount);
     } else if (aPath === "storage-sqlite") {
       storageSqliteAmounts.push(aAmount);
 
     // Check the presence of some other notable reporters.
     } else if (aPath.search(/^explicit\/js-non-window\/.*compartment\(/) >= 0) {
-      present.jsNonWindowCompartments = true;
+      areJsNonWindowCompartmentsPresent = true;
     } else if (aPath.search(/^explicit\/window-objects\/top\(.*\/js-compartment\(/) >= 0) {
-      present.windowObjectsJsCompartments = true;
+      areWindowObjectsJsCompartmentsPresent = true;
     } else if (aPath.search(/^explicit\/storage\/sqlite\/places.sqlite/) >= 0) {
-      present.places = true;
+      isPlacesPresent = true;
     } else if (aPath.search(/^explicit\/images/) >= 0) {
-      present.images = true;
+      isImagesPresent = true;
     } else if (aPath.search(/^explicit\/xpti-working-set$/) >= 0) {
-      present.xptiWorkingSet = true;
+      isXptiWorkingSetPresent = true;
     } else if (aPath.search(/^explicit\/atom-tables$/) >= 0) {
-      present.atomTable = true;
+      isAtomTablePresent = true;
     } else if (/\[System Principal\].*this-is-a-sandbox-name/.test(aPath)) {
       // A system compartment with a location (such as a sandbox) should
       // show that location.
-      present.sandboxLocation = true;
+      isSandboxLocationShown = true;
     } else if (aPath.contains(bigStringPrefix)) {
-      present.bigString = true;
+      isBigStringPresent = true;
     } else if (aPath.contains("!)(*&")) {
-      present.smallString1 = true;
+      isSmallString1Present = true;
     } else if (aPath.contains("@)(*&")) {
-      present.smallString2 = true;
+      isSmallString2Present = true;
     }
   }
 
   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.
+  // Access mgr.explicit and mgr.resident 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
   // --enable-trace-malloc 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",
-    "resident",
-    "residentFast",
-    "heapAllocated",
-    "heapOverheadRatio",
-    "JSMainRuntimeGCHeap",
-    "JSMainRuntimeTemporaryPeak",
-    "JSMainRuntimeCompartmentsSystem",
-    "JSMainRuntimeCompartmentsUser",
-    "imagesContentUsedUncompressed",
-    "storageSQLite",
-    "lowMemoryEventsVirtual",
-    "lowMemoryEventsPhysical",
-    "ghostWindows",
-    "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.
-      dummy = mgr[amounts[i]];
-      ok(dummy !== undefined,
-         "accessed an unknown distinguished amount: " + amounts[i]);
-    } catch (ex) {
-    }
-  }
+  dummy = mgr.resident;
 
   let e = mgr.enumerateReporters();
   while (e.hasMoreElements()) {
     let r = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
     r.collectReports(handleReport, null);
 
     // Access |name| to make sure it doesn't crash or assert.
     dummy = r.name;
@@ -175,24 +155,25 @@
 
   // If mgr.explicit failed, we won't have "heap-allocated" either.
   if (haveExplicit) {
     checkSpecialReport("heap-allocated", heapAllocatedAmounts);
   }
   checkSpecialReport("vsize",          vsizeAmounts);
   checkSpecialReport("resident",       residentAmounts);
   checkSpecialReport("js-main-runtime-gc-heap-committed/used/gc-things", jsGcHeapAmounts);
+  checkSpecialReport("storage-sqlite", storageSqliteAmounts);
 
-  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.atomTable,                   "atom-table 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(areJsNonWindowCompartmentsPresent,     "js-non-window compartments are present");
+  ok(areWindowObjectsJsCompartmentsPresent, "window-objects/.../js compartments are present");
+  ok(isSandboxLocationShown,                "sandbox locations are present");
+  ok(isPlacesPresent,                       "places is present");
+  ok(isImagesPresent,                       "images is present");
+  ok(isXptiWorkingSetPresent,               "xpti-working-set is present");
+  ok(isAtomTablePresent,                    "atom-table is present");
+  ok(isBigStringPresent,                    "large string is present");
+  ok(isSmallString1Present,                 "small string 1 is present");
+  ok(isSmallString2Present,                 "small string 2 is present");
 
   ]]>
   </script>
 </window>
 
--- a/toolkit/components/telemetry/TelemetryPing.js
+++ b/toolkit/components/telemetry/TelemetryPing.js
@@ -34,16 +34,49 @@ const PREF_ENABLED = PREF_BRANCH + "enab
 #endif
 const PREF_PREVIOUS_BUILDID = PREF_BRANCH + "previousBuildID";
 
 // Do not gather data more than once a minute
 const TELEMETRY_INTERVAL = 60000;
 // Delay before intializing telemetry (ms)
 const TELEMETRY_DELAY = 60000;
 
+// MEM_HISTOGRAMS lists the memory reporters we turn into histograms.
+//
+// Note that we currently handle only vanilla memory reporters, not memory
+// multi-reporters.
+//
+// test_TelemetryPing.js relies on some of these memory reporters
+// being here.  If you remove any of the following histograms from
+// MEM_HISTOGRAMS, you'll have to modify test_TelemetryPing.js:
+//
+//   * MEMORY_JS_GC_HEAP, and
+//   * MEMORY_JS_COMPARTMENTS_SYSTEM.
+//
+// We used to measure "explicit" too, but it could cause hangs, and the data
+// was always really noisy anyway.  See bug 859657.
+const MEM_HISTOGRAMS = {
+  "js-main-runtime-gc-heap": "MEMORY_JS_GC_HEAP",
+  "redundant/js-main-runtime-compartments/system": "MEMORY_JS_COMPARTMENTS_SYSTEM",
+  "redundant/js-main-runtime-compartments/user": "MEMORY_JS_COMPARTMENTS_USER",
+  "js-main-runtime-temporary-peak": "MEMORY_JS_MAIN_RUNTIME_TEMPORARY_PEAK",
+  "redundant/resident-fast": "MEMORY_RESIDENT",
+  "vsize": "MEMORY_VSIZE",
+  "storage-sqlite": "MEMORY_STORAGE_SQLITE",
+  "images-content-used-uncompressed":
+    "MEMORY_IMAGES_CONTENT_USED_UNCOMPRESSED",
+  "heap-allocated": "MEMORY_HEAP_ALLOCATED",
+  "heap-overhead": "MEMORY_HEAP_COMMITTED_UNUSED",
+  "heap-overhead-ratio": "MEMORY_HEAP_COMMITTED_UNUSED_RATIO",
+  "page-faults-hard": "PAGE_FAULTS_HARD",
+  "low-memory-events/virtual": "LOW_MEMORY_EVENTS_VIRTUAL",
+  "low-memory-events/physical": "LOW_MEMORY_EVENTS_PHYSICAL",
+  "ghost-windows": "GHOST_WINDOWS"
+};
+
 // Seconds of idle time before pinging.
 // On idle-daily a gather-telemetry notification is fired, during it probes can
 // start asynchronous tasks to gather data.  On the next idle the data is sent.
 const IDLE_TIMEOUT_SECONDS = 5 * 60;
 
 var gLastMemoryPoll = null;
 
 let gWasDebuggerAttached = false;
@@ -409,67 +442,49 @@ TelemetryPing.prototype = {
             getService(Ci.nsIMemoryReporterManager);
     } catch (e) {
       // OK to skip memory reporters in xpcshell
       return;
     }
 
     let histogram = Telemetry.getHistogramById("TELEMETRY_MEMORY_REPORTER_MS");
     let startTime = new Date();
+    let e = mgr.enumerateReporters();
+    while (e.hasMoreElements()) {
+      let mr = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
+      let id = MEM_HISTOGRAMS[mr.name];
+      if (!id) {
+        continue;
+      }
 
-    // Get memory measurements from distinguished amount attributes.  We used
-    // to measure "explicit" too, but it could cause hangs, and the data was
-    // always really noisy anyway.  See bug 859657.
-    //
-    // test_TelemetryPing.js relies on some of these histograms being
-    // here.  If you remove any of the following histograms from here, you'll
-    // have to modify test_TelemetryPing.js:
-    //
-    //   * MEMORY_JS_GC_HEAP, and
-    //   * MEMORY_JS_COMPARTMENTS_SYSTEM.
-    //
-    // The distinguished amount attribute names don't match the telemetry id
-    // names in some cases due to a combination of (a) historical reasons, and
-    // (b) the fact that we can't change telemetry id names without breaking
-    // data continuity.
-    //
-    let boundHandleMemoryReport = this.handleMemoryReport.bind(this);
-    function h(id, units, amountName) {
+      // collectReports might throw an exception.  If so, just ignore that
+      // memory reporter; we're not getting useful data out of it.
       try {
-        // If mgr[amountName] 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 amount = mgr[amountName];
-        NS_ASSERT(amount !== undefined,
-                  "telemetry accessed an unknown distinguished amount");
-        boundHandleMemoryReport(id, units, amount);
-      } catch (e) {
-      };
-    }
-    let b = (id, n) => h(id, Ci.nsIMemoryReporter.UNITS_BYTES, n);
-    let c = (id, n) => h(id, Ci.nsIMemoryReporter.UNITS_COUNT, n);
-    let cc= (id, n) => h(id, Ci.nsIMemoryReporter.UNITS_COUNT_CUMULATIVE, n);
-    let p = (id, n) => h(id, Ci.nsIMemoryReporter.UNITS_PERCENTAGE, n);
+        // Bind handleMemoryReport() so it can be called inside the closure
+        // used as the callback.
+        let boundHandleMemoryReport = this.handleMemoryReport.bind(this);
+
+        // Reporters used for telemetry should be uni-reporters!  we assert if
+        // they make more than one report.
+        let hasReported = false;
 
-    b("MEMORY_VSIZE", "vsize");
-    b("MEMORY_RESIDENT", "residentFast");
-    b("MEMORY_HEAP_ALLOCATED", "heapAllocated");
-    p("MEMORY_HEAP_COMMITTED_UNUSED_RATIO", "heapOverheadRatio");
-    b("MEMORY_JS_GC_HEAP", "JSMainRuntimeGCHeap");
-    b("MEMORY_JS_MAIN_RUNTIME_TEMPORARY_PEAK", "JSMainRuntimeTemporaryPeak");
-    c("MEMORY_JS_COMPARTMENTS_SYSTEM", "JSMainRuntimeCompartmentsSystem");
-    c("MEMORY_JS_COMPARTMENTS_USER", "JSMainRuntimeCompartmentsUser");
-    b("MEMORY_IMAGES_CONTENT_USED_UNCOMPRESSED", "imagesContentUsedUncompressed");
-    b("MEMORY_STORAGE_SQLITE", "storageSQLite");
-    cc("MEMORY_EVENTS_VIRTUAL", "lowMemoryEventsVirtual");
-    cc("MEMORY_EVENTS_PHYSICAL", "lowMemoryEventsPhysical");
-    c("GHOST_WINDOWS", "ghostWindows");
-    cc("PAGE_FAULTS_HARD", "pageFaultsHard");
-
+        function h(process, path, kind, units, amount, desc) {
+          if (!hasReported) {
+            boundHandleMemoryReport(id, units, amount);
+            hasReported = true;
+          } else {
+            NS_ASSERT(false,
+                      "reporter " + mr.name + " has made more than one report");
+          }
+        }
+        mr.collectReports(h, null);
+      }
+      catch (e) {
+      }
+    }
     histogram.add(new Date() - startTime);
   },
 
   handleMemoryReport: function(id, units, amount) {
     let val;
     if (units == Ci.nsIMemoryReporter.UNITS_BYTES) {
       val = Math.floor(amount / 1024);
     }
--- a/xpcom/base/AvailableMemoryTracker.cpp
+++ b/xpcom/base/AvailableMemoryTracker.cpp
@@ -320,22 +320,16 @@ CreateDIBSectionHook(HDC aDC,
 
   if (doCheck) {
     CheckMemAvailable();
   }
 
   return result;
 }
 
-static int64_t
-LowMemoryEventsVirtualDistinguishedAmount()
-{
-  return sNumLowVirtualMemEvents;
-}
-
 class LowMemoryEventsVirtualReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
   LowMemoryEventsVirtualReporter()
     : MemoryUniReporter("low-memory-events/virtual",
                          KIND_OTHER, UNITS_COUNT_CUMULATIVE,
 "Number of low-virtual-memory events fired since startup. We fire such an "
 "event if we notice there is less than memory.low_virtual_mem_threshold_mb of "
@@ -346,17 +340,17 @@ public:
 
 private:
   int64_t Amount() MOZ_OVERRIDE
   {
     // This memory reporter shouldn't be installed on 64-bit machines, since we
     // force-disable virtual-memory tracking there.
     MOZ_ASSERT(sizeof(void*) == 4);
 
-    return LowMemoryEventsVirtualDistinguishedAmount();
+    return sNumLowVirtualMemEvents;
   }
 };
 
 class LowCommitSpaceEventsReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
   LowCommitSpaceEventsReporter()
     : MemoryUniReporter("low-commit-space-events",
@@ -367,37 +361,31 @@ public:
 "likely kill the process if it runs out of commit space, so this event is "
 "dire.")
   {}
 
 private:
   int64_t Amount() MOZ_OVERRIDE { return sNumLowCommitSpaceEvents; }
 };
 
-static int64_t
-LowMemoryEventsPhysicalDistinguishedAmount()
-{
-  return sNumLowPhysicalMemEvents;
-}
-
 class LowMemoryEventsPhysicalReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
   LowMemoryEventsPhysicalReporter()
     : MemoryUniReporter("low-memory-events/physical",
                          KIND_OTHER, UNITS_COUNT_CUMULATIVE,
 "Number of low-physical-memory events fired since startup. We fire such an "
 "event if we notice there is less than memory.low_physical_memory_threshold_mb "
 "of physical memory available (if zero, this behavior is disabled).  The "
 "machine will start to page if it runs out of physical memory.  This may "
 "cause it to run slowly, but it shouldn't cause it to crash.")
   {}
 
 private:
-  int64_t Amount() MOZ_OVERRIDE { return LowMemoryEventsPhysicalDistinguishedAmount(); }
+  int64_t Amount() MOZ_OVERRIDE { return sNumLowPhysicalMemEvents; }
 };
 
 #endif // defined(XP_WIN)
 
 /**
  * This runnable is executed in response to a memory-pressure event; we spin
  * the event-loop when receiving the memory-pressure event in the hope that
  * other observers will synchronously free some memory that we'll be able to
@@ -510,18 +498,16 @@ void Activate()
   Preferences::AddUintVarCache(&sLowMemoryNotificationIntervalMS,
       "memory.low_memory_notification_interval_ms", 10000);
 
   NS_RegisterMemoryReporter(new LowCommitSpaceEventsReporter());
   NS_RegisterMemoryReporter(new LowMemoryEventsPhysicalReporter());
   if (sizeof(void*) == 4) {
     NS_RegisterMemoryReporter(new LowMemoryEventsVirtualReporter());
   }
-  RegisterLowMemoryEventsVirtualDistinguishedAmount(LowMemoryEventsVirtualDistinguishedAmount);
-  RegisterLowMemoryEventsPhysicalDistinguishedAmount(LowMemoryEventsPhysicalDistinguishedAmount);
   sHooksActive = true;
 #endif
 
   // This object is held alive by the observer service.
   nsRefPtr<nsMemoryPressureWatcher> watcher = new nsMemoryPressureWatcher();
   watcher->Init();
 }
 
--- a/xpcom/base/nsIMemoryReporter.idl
+++ b/xpcom/base/nsIMemoryReporter.idl
@@ -138,16 +138,21 @@ interface nsIMemoryReporterCallback : ns
  * - There must be an "explicit" tree.  It represents non-overlapping
  *   regions of memory that have been explicitly allocated with an
  *   OS-level allocation (e.g. mmap/VirtualAlloc/vm_allocate) or a
  *   heap-level allocation (e.g. malloc/calloc/operator new).  Reporters
  *   in this tree must have kind HEAP or NONHEAP, units BYTES, and a
  *   description that is a sentence (i.e. starts with a capital letter and
  *   ends with a period, or similar).
  *
+ * - The "redundant" tree is optional, and can be used for reports that are
+ *   redundant w.r.t. other reports.  These are useful for telemetry, and are
+ *   not shown in about:memory.  Reports in this tree are entirely
+ *   unconstrained.
+ *
  * - All other reports are unconstrained except that they must have a
  *   description that is a sentence.
  */
 [scriptable, uuid(53248304-124b-43cd-99dc-6e5797b91618)]
 interface nsIMemoryReporter : nsISupports
 {
   /*
    * The name of the reporter.  Useful when only one reporter needs to be run.
@@ -179,128 +184,58 @@ interface nsIMemoryReporter : nsISupport
 };
 
 [scriptable, builtinclass, uuid(4db7040a-16f9-4249-879b-fe72729c7ef5)]
 interface nsIMemoryReporterManager : nsISupports
 {
   /*
    * Return an enumerator of nsIMemoryReporters that are currently registered.
    */
-  nsISimpleEnumerator enumerateReporters();
+  nsISimpleEnumerator enumerateReporters ();
 
   /*
    * Register the given nsIMemoryReporter.  After a reporter is registered,
    * it will be available via enumerateReporters().  The Manager service
    * will hold a strong reference to the given reporter.
    */
-  void registerReporter(in nsIMemoryReporter reporter);
+  void registerReporter (in nsIMemoryReporter reporter);
 
   /*
    * Unregister the given memory reporter.
    */
-  void unregisterReporter(in nsIMemoryReporter reporter);
+  void unregisterReporter (in nsIMemoryReporter reporter);
 
-  /*
+  /**
    * These functions should only be used for testing purposes.
    */
   void blockRegistration();
   void unblockRegistration();
   void registerReporterEvenIfBlocked(in nsIMemoryReporter aReporter);
 
   /*
    * Initialize.
    */
-  void init();
+  void init ();
 
   /*
-   * 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 enumerateReporters() does not
-   * look at them.  However, they 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.
-   *
-   * |resident| (UNITS_BYTES)  The resident size (a.k.a. RSS or physical memory
-   * used).
-   *
-   * |residentFast| (UNITS_BYTES)  This is like |resident|, but on Mac OS
-   * |resident| can purge pages, which is slow.  It also affects the result of
-   * |residentFast|, and so |resident| and |residentFast| should not be used
-   * together.
-   *
-   * |heapAllocated| (UNITS_BYTES)  Memory mapped by the heap allocator.
-   *
-   * |heapOverheadRatio| (UNITS_PERCENTAGE)  In the heap allocator, this is the
-   * ratio of committed, unused bytes to allocated bytes.  Like all
-   * UNITS_PERCENTAGE measurements, its amount is multiplied by 100x so it can
-   * be represented by an int64_t.
-   *
-   * |JSMainRuntimeGCHeap| (UNITS_BYTES)  Size of the main JS runtime's GC
-   * heap.
-   *
-   * |JSMainRuntimeTemporaryPeak| (UNITS_BYTES)  Peak size of the transient
-   * storage in the main JSRuntime.
-   *
-   * |JSMainRuntimeCompartments{System,User}| (UNITS_COUNT)  The number of
-   * {system,user} compartments in the main JS runtime.
-   *
-   * |imagesContentUsedUncompressed| (UNITS_BYTES)  Memory used for decoded
-   * images in content.
-   *
-   * |storageSQLite| (UNITS_BYTES)  Memory used by SQLite.
-   *
-   * |lowMemoryEvents{Virtual,Physical}| (UNITS_COUNT_CUMULATIVE)  The number
-   * 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.
+   * Get the resident size (aka. RSS, physical memory used).  This reporter
+   * is special-cased because it's interesting and is available on most
+   * platforms.  Accesses can fail.
+   */
+  readonly attribute int64_t resident;
+
+  /*
+   * Get 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).  (Nb: it covers all heap allocations, but will
+   * miss any OS-level ones not covered by memory reporters.)  This reporter
+   * is special-cased because it's interesting, and is difficult to compute
+   * from JavaScript code.  Accesses can fail.
    */
   readonly attribute int64_t explicit;
-  readonly attribute int64_t vsize;
-  readonly attribute int64_t resident;
-  readonly attribute int64_t residentFast;
-
-  readonly attribute int64_t heapAllocated;
-  readonly attribute int64_t heapOverheadRatio;
-
-  readonly attribute int64_t JSMainRuntimeGCHeap;
-  readonly attribute int64_t JSMainRuntimeTemporaryPeak;
-  readonly attribute int64_t JSMainRuntimeCompartmentsSystem;
-  readonly attribute int64_t JSMainRuntimeCompartmentsUser;
-
-  readonly attribute int64_t imagesContentUsedUncompressed;
-
-  readonly attribute int64_t storageSQLite;
-
-  readonly attribute int64_t lowMemoryEventsVirtual;
-  readonly attribute int64_t lowMemoryEventsPhysical;
-
-  readonly attribute int64_t ghostWindows;
-
-  readonly attribute int64_t pageFaultsHard;
 
   /*
    * This attribute indicates if moz_malloc_usable_size() works.
    */
   [infallible] readonly attribute boolean hasMozMallocUsableSize;
 
   /*
    * Run a series of GC/CC's in an attempt to minimize the application's memory
@@ -316,52 +251,16 @@ interface nsIMemoryReporterManager : nsI
 
 // Note that the memory reporters are held in an nsCOMArray, which means
 // that individual reporters should be referenced with |nsIMemoryReporter *|
 // instead of nsCOMPtr<nsIMemoryReporter>.
 
 XPCOM_API(nsresult) NS_RegisterMemoryReporter(nsIMemoryReporter* aReporter);
 XPCOM_API(nsresult) NS_UnregisterMemoryReporter(nsIMemoryReporter* aReporter);
 
-namespace mozilla {
-
-// The memory reporter manager provides access to several distinguished
-// amounts via attributes.  Some of these amounts are provided by Gecko
-// components that cannot be accessed directly from XPCOM code.  So we provide
-// the following functions for those components to be registered with the
-// manager.
-
-typedef int64_t (*InfallibleAmountFn)();
-typedef nsresult (*FallibleAmountFn)(int64_t* aAmount);
-
-#define DECL_REGISTER_DISTINGUISHED_AMOUNT(kind, name) \
-    nsresult Register##name##DistinguishedAmount(kind##AmountFn aAmountFn);
-#define DECL_UNREGISTER_DISTINGUISHED_AMOUNT(name) \
-    nsresult Unregister##name##DistinguishedAmount();
-
-DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeGCHeap)
-DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeTemporaryPeak)
-DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsSystem)
-DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsUser)
-
-DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, ImagesContentUsedUncompressed)
-
-DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, StorageSQLite)
-DECL_UNREGISTER_DISTINGUISHED_AMOUNT(StorageSQLite)
-
-DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsVirtual)
-DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsPhysical)
-
-DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, GhostWindows)
-
-#undef DECL_REGISTER_DISTINGUISHED_AMOUNT
-#undef DECL_UNREGISTER_DISTINGUISHED_AMOUNT
-
-}
-
 #if defined(MOZ_DMD)
 namespace mozilla {
 namespace dmd {
 // This runs all the memory reporters but does nothing with the results;  i.e.
 // it does the minimal amount of work possible for DMD to do its thing.
 void RunReporters();
 }
 }
@@ -489,9 +388,10 @@ protected:
   const nsCString mNameAndPath;
   const int32_t   mKind;
   const int32_t   mUnits;
   const nsCString mDescription;
 };
 
 } // namespace mozilla
 
+
 %}
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -11,20 +11,19 @@
 #include "nsServiceManagerUtils.h"
 #include "nsMemoryReporterManager.h"
 #include "nsISimpleEnumerator.h"
 #include "nsThreadUtils.h"
 #include "nsIObserverService.h"
 #if defined(XP_LINUX)
 #include "nsMemoryInfoDumper.h"
 #endif
+#include "mozilla/Telemetry.h"
 #include "mozilla/Attributes.h"
-#include "mozilla/PodOperations.h"
 #include "mozilla/Services.h"
-#include "mozilla/Telemetry.h"
 
 #ifndef XP_WIN
 #include <unistd.h>
 #endif
 
 using namespace mozilla;
 
 #if defined(MOZ_MEMORY)
@@ -422,16 +421,36 @@ public:
 "considering the memory resources used by the process, but it depends both on "
 "other processes being run and details of the OS kernel and so is best used "
 "for comparing the memory usage of a single process at different points in "
 "time.")
     {}
 
     NS_IMETHOD GetAmount(int64_t* aAmount) { return GetResident(aAmount); }
 };
+
+// This is a "redundant/"-prefixed reporter, which means it's ignored by
+// about:memory.  This is good because the "resident" reporter can purge pages
+// on MacOS, which affects the "resident-fast" results, and we don't want the
+// measurements shown in about:memory to be affected by the (arbitrary) order
+// of memory reporter execution.  This reporter is used by telemetry.
+class ResidentFastReporter MOZ_FINAL : public MemoryUniReporter
+{
+public:
+    ResidentFastReporter()
+      : MemoryUniReporter("redundant/resident-fast", KIND_OTHER, UNITS_BYTES,
+"This is the same measurement as 'resident', but it tries to be as fast as "
+"possible at the expense of accuracy.  On most platforms this is identical to "
+"the 'resident' measurement, but on Mac it may over-count.  You should use "
+"'resident-fast' where you care about latency of collection (e.g. in "
+"telemetry).  Otherwise you should use 'resident'.")
+    {}
+
+    NS_IMETHOD GetAmount(int64_t* aAmount) { return GetResidentFast(aAmount); }
+};
 #endif  // HAVE_VSIZE_AND_RESIDENT_REPORTERS
 
 #ifdef XP_UNIX
 
 #include <sys/resource.h>
 
 #define HAVE_PAGE_FAULT_REPORTERS 1
 
@@ -459,28 +478,16 @@ public:
         if (err != 0) {
             return NS_ERROR_FAILURE;
         }
         *aAmount = usage.ru_minflt;
         return NS_OK;
     }
 };
 
-static nsresult
-PageFaultsHardDistinguishedAmount(int64_t* aAmount)
-{
-    struct rusage usage;
-    int err = getrusage(RUSAGE_SELF, &usage);
-    if (err != 0) {
-        return NS_ERROR_FAILURE;
-    }
-    *aAmount = usage.ru_majflt;
-    return NS_OK;
-}
-
 class PageFaultsHardReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     PageFaultsHardReporter()
       : MemoryUniReporter("page-faults-hard", KIND_OTHER,
                            UNITS_COUNT_CUMULATIVE,
 "The number of hard page faults (also known as 'major page faults') that have "
 "occurred since the process started.  A hard page fault occurs when a process "
@@ -490,60 +497,52 @@ public:
 "the process tries to use more memory than your machine has available, you "
 "may see many thousands of hard page faults. Because accessing the disk is up "
 "to a million times slower than accessing RAM, the program may run very "
 "slowly when it is experiencing more than 100 or so hard page faults a second.")
     {}
 
     NS_IMETHOD GetAmount(int64_t* aAmount)
     {
-        return PageFaultsHardDistinguishedAmount(aAmount);
+        struct rusage usage;
+        int err = getrusage(RUSAGE_SELF, &usage);
+        if (err != 0) {
+            return NS_ERROR_FAILURE;
+        }
+        *aAmount = usage.ru_majflt;
+        return NS_OK;
     }
 };
 #endif  // HAVE_PAGE_FAULT_REPORTERS
 
 /**
  ** memory reporter implementation for jemalloc and OSX malloc,
  ** to obtain info on total memory in use (that we know about,
  ** at least -- on OSX, there are sometimes other zones in use).
  **/
 
 #ifdef HAVE_JEMALLOC_STATS
 
-static int64_t
-HeapAllocated()
-{
-    jemalloc_stats_t stats;
-    jemalloc_stats(&stats);
-    return (int64_t) stats.allocated;
-}
-
-// This has UNITS_PERCENTAGE, so it is multiplied by 100x.
-static int64_t
-HeapOverheadRatio()
-{
-    jemalloc_stats_t stats;
-    jemalloc_stats(&stats);
-    return (int64_t) 10000 *
-      (stats.waste + stats.bookkeeping + stats.page_cache) /
-      ((double)stats.allocated);
-}
-
 class HeapAllocatedReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     HeapAllocatedReporter()
       : MemoryUniReporter("heap-allocated", KIND_OTHER, UNITS_BYTES,
 "Memory mapped by the heap allocator that is currently allocated to the "
 "application.  This may exceed the amount of memory requested by the "
 "application because the allocator regularly rounds up request sizes. (The "
 "exact amount requested is not recorded.)")
     {}
 private:
-    int64_t Amount() MOZ_OVERRIDE { return HeapAllocated(); }
+    int64_t Amount() MOZ_OVERRIDE
+    {
+        jemalloc_stats_t stats;
+        jemalloc_stats(&stats);
+        return (int64_t) stats.allocated;
+    }
 };
 
 class HeapOverheadWasteReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     // We mark this and the other heap-overhead reporters as KIND_NONHEAP
     // because KIND_HEAP memory means "counted in heap-allocated", which this
     // is not.
@@ -552,34 +551,34 @@ public:
                            KIND_NONHEAP, UNITS_BYTES,
 "Committed bytes which do not correspond to an active allocation and which the "
 "allocator is not intentionally keeping alive (i.e., not 'heap-bookkeeping' or "
 "'heap-page-cache').  Although the allocator will waste some space under any "
 "circumstances, a large value here may indicate that the heap is highly "
 "fragmented, or that allocator is performing poorly for some other reason.")
     {}
 private:
-    int64_t Amount() MOZ_OVERRIDE
+    int64_t Amount()
     {
         jemalloc_stats_t stats;
         jemalloc_stats(&stats);
         return stats.waste;
     }
 };
 
 class HeapOverheadBookkeepingReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     HeapOverheadBookkeepingReporter()
       : MemoryUniReporter("explicit/heap-overhead/bookkeeping",
                            KIND_NONHEAP, UNITS_BYTES,
 "Committed bytes which the heap allocator uses for internal data structures.")
     {}
 private:
-    int64_t Amount() MOZ_OVERRIDE
+    int64_t Amount()
     {
         jemalloc_stats_t stats;
         jemalloc_stats(&stats);
         return stats.bookkeeping;
     }
 };
 
 class HeapOverheadPageCacheReporter MOZ_FINAL : public MemoryUniReporter
@@ -589,17 +588,17 @@ public:
       : MemoryUniReporter("explicit/heap-overhead/page-cache",
                            KIND_NONHEAP, UNITS_BYTES,
 "Memory which the allocator could return to the operating system, but hasn't. "
 "The allocator keeps this memory around as an optimization, so it doesn't "
 "have to ask the OS the next time it needs to fulfill a request. This value "
 "is typically not larger than a few megabytes.")
     {}
 private:
-    int64_t Amount() MOZ_OVERRIDE
+    int64_t Amount()
     {
         jemalloc_stats_t stats;
         jemalloc_stats(&stats);
         return (int64_t) stats.page_cache;
     }
 };
 
 class HeapCommittedReporter MOZ_FINAL : public MemoryUniReporter
@@ -609,17 +608,17 @@ public:
       : MemoryUniReporter("heap-committed", KIND_OTHER, UNITS_BYTES,
 "Memory mapped by the heap allocator that is committed, i.e. in physical "
 "memory or paged to disk.  This value corresponds to 'heap-allocated' + "
 "'heap-waste' + 'heap-bookkeeping' + 'heap-page-cache', but because "
 "these values are read at different times, the result probably won't match "
 "exactly.")
     {}
 private:
-    int64_t Amount() MOZ_OVERRIDE
+    int64_t Amount()
     {
         jemalloc_stats_t stats;
         jemalloc_stats(&stats);
         return (int64_t) (stats.allocated + stats.waste +
                           stats.bookkeeping + stats.page_cache);
     }
 };
 
@@ -629,37 +628,41 @@ public:
     HeapOverheadRatioReporter()
       : MemoryUniReporter("heap-overhead-ratio", KIND_OTHER,
                            UNITS_PERCENTAGE,
 "Ratio of committed, unused bytes to allocated bytes; i.e., "
 "'heap-overhead' / 'heap-allocated'.  This measures the overhead of "
 "the heap allocator relative to amount of memory allocated.")
     {}
 private:
-    int64_t Amount() MOZ_OVERRIDE { return HeapOverheadRatio(); }
+    int64_t Amount()
+    {
+        jemalloc_stats_t stats;
+        jemalloc_stats(&stats);
+        return (int64_t) 10000 *
+          (stats.waste + stats.bookkeeping + stats.page_cache) /
+          ((double)stats.allocated);
+    }
 };
 #endif  // HAVE_JEMALLOC_STATS
 
 // Why is this here?  At first glance, you'd think it could be defined and
 // registered with nsMemoryReporterManager entirely within nsAtomTable.cpp.
 // However, the obvious time to register it is when the table is initialized,
 // and that happens before XPCOM components are initialized, which means the
 // NS_RegisterMemoryReporter call fails.  So instead we do it here.
 class AtomTablesReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     AtomTablesReporter()
       : MemoryUniReporter("explicit/atom-tables", KIND_HEAP, UNITS_BYTES,
 "Memory used by the dynamic and static atoms tables.")
     {}
 private:
-    int64_t Amount() MOZ_OVERRIDE
-    {
-        return NS_SizeOfAtomTablesIncludingThis(MallocSizeOf);
-    }
+    int64_t Amount() { return NS_SizeOfAtomTablesIncludingThis(MallocSizeOf); }
 };
 
 #ifdef MOZ_DMD
 
 namespace mozilla {
 namespace dmd {
 
 class DMDReporter MOZ_FINAL : public nsIMemoryReporter
@@ -744,16 +747,17 @@ nsMemoryReporterManager::Init()
     RegisterReporter(new HeapOverheadPageCacheReporter);
     RegisterReporter(new HeapCommittedReporter);
     RegisterReporter(new HeapOverheadRatioReporter);
 #endif
 
 #ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
     RegisterReporter(new VsizeReporter);
     RegisterReporter(new ResidentReporter);
+    RegisterReporter(new ResidentFastReporter);
 #endif
 
 #ifdef HAVE_RESIDENT_UNIQUE_REPORTER
     RegisterReporter(new ResidentUniqueReporter);
 #endif
 
 #ifdef HAVE_PAGE_FAULT_REPORTERS
     RegisterReporter(new PageFaultsSoftReporter);
@@ -839,17 +843,16 @@ HashtableEnumerator::GetNext(nsISupports
 }
 
 } // anonymous namespace
 
 nsMemoryReporterManager::nsMemoryReporterManager()
   : mMutex("nsMemoryReporterManager::mMutex"),
     mIsRegistrationBlocked(false)
 {
-    PodZero(&mAmountFns);
 }
 
 nsMemoryReporterManager::~nsMemoryReporterManager()
 {
 }
 
 NS_IMETHODIMP
 nsMemoryReporterManager::EnumerateReporters(nsISimpleEnumerator** aResult)
@@ -956,16 +959,27 @@ nsMemoryReporterManager::UnblockRegistra
     mozilla::MutexAutoLock autoLock(mMutex);
     if (!mIsRegistrationBlocked) {
         return NS_ERROR_FAILURE;
     }
     mIsRegistrationBlocked = false;
     return NS_OK;
 }
 
+NS_IMETHODIMP
+nsMemoryReporterManager::GetResident(int64_t* aResident)
+{
+#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
+    return ::GetResident(aResident);
+#else
+    *aResident = 0;
+    return NS_ERROR_NOT_AVAILABLE;
+#endif
+}
+
 // This is just a wrapper for int64_t that implements nsISupports, so it can be
 // passed to nsIMemoryReporter::CollectReports.
 class Int64Wrapper MOZ_FINAL : public nsISupports {
 public:
     NS_DECL_ISUPPORTS
     Int64Wrapper() : mValue(0) { }
     int64_t mValue;
 };
@@ -976,40 +990,34 @@ class ExplicitCallback MOZ_FINAL : publi
 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)
     {
-        // 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.Equals("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_ISUPPORTS1(ExplicitCallback, nsIMemoryReporterCallback)
 
 NS_IMETHODIMP
-nsMemoryReporterManager::GetExplicit(int64_t* aAmount)
+nsMemoryReporterManager::GetExplicit(int64_t* aExplicit)
 {
-    NS_ENSURE_ARG_POINTER(aAmount);
-    *aAmount = 0;
+    NS_ENSURE_ARG_POINTER(aExplicit);
+    *aExplicit = 0;
 #ifndef HAVE_JEMALLOC_STATS
     return NS_ERROR_NOT_AVAILABLE;
 #else
     bool more;
 
     // 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()
@@ -1022,160 +1030,23 @@ nsMemoryReporterManager::GetExplicit(int
     nsCOMPtr<nsISimpleEnumerator> e;
     EnumerateReporters(getter_AddRefs(e));
     while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
         nsCOMPtr<nsIMemoryReporter> r;
         e->GetNext(getter_AddRefs(r));
         r->CollectReports(cb, wrappedExplicitSize);
     }
 
-    *aAmount = wrappedExplicitSize->mValue;
+    *aExplicit = wrappedExplicitSize->mValue;
 
     return NS_OK;
 #endif // HAVE_JEMALLOC_STATS
 }
 
 NS_IMETHODIMP
-nsMemoryReporterManager::GetVsize(int64_t* aVsize)
-{
-#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
-    return ::GetVsize(aVsize);
-#else
-    *aResident = 0;
-    return NS_ERROR_NOT_AVAILABLE;
-#endif
-}
-
-NS_IMETHODIMP
-nsMemoryReporterManager::GetResident(int64_t* aAmount)
-{
-#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
-    return ::GetResident(aAmount);
-#else
-    *aAmount = 0;
-    return NS_ERROR_NOT_AVAILABLE;
-#endif
-}
-
-NS_IMETHODIMP
-nsMemoryReporterManager::GetResidentFast(int64_t* aAmount)
-{
-#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
-    return ::GetResidentFast(aAmount);
-#else
-    *aAmount = 0;
-    return NS_ERROR_NOT_AVAILABLE;
-#endif
-}
-
-NS_IMETHODIMP
-nsMemoryReporterManager::GetHeapAllocated(int64_t* aAmount)
-{
-#ifdef HAVE_JEMALLOC_STATS
-    *aAmount = HeapAllocated();
-    return NS_OK;
-#else
-    *aAmount = 0;
-    return NS_ERROR_NOT_AVAILABLE;
-#endif
-}
-
-// This has UNITS_PERCENTAGE, so it is multiplied by 100x.
-NS_IMETHODIMP
-nsMemoryReporterManager::GetHeapOverheadRatio(int64_t* aAmount)
-{
-#ifdef HAVE_JEMALLOC_STATS
-    *aAmount = HeapOverheadRatio();
-    return NS_OK;
-#else
-    *aAmount = 0;
-    return NS_ERROR_NOT_AVAILABLE;
-#endif
-}
-
-static nsresult
-GetInfallibleAmount(InfallibleAmountFn aAmountFn, int64_t* aAmount)
-{
-    if (aAmountFn) {
-        *aAmount = aAmountFn();
-        return NS_OK;
-    }
-    *aAmount = 0;
-    return NS_ERROR_NOT_AVAILABLE;
-}
-
-NS_IMETHODIMP
-nsMemoryReporterManager::GetJSMainRuntimeGCHeap(int64_t* aAmount)
-{
-    return GetInfallibleAmount(mAmountFns.mJSMainRuntimeGCHeap, aAmount);
-}
-
-NS_IMETHODIMP
-nsMemoryReporterManager::GetJSMainRuntimeTemporaryPeak(int64_t* aAmount)
-{
-    return GetInfallibleAmount(mAmountFns.mJSMainRuntimeTemporaryPeak, aAmount);
-}
-
-NS_IMETHODIMP
-nsMemoryReporterManager::GetJSMainRuntimeCompartmentsSystem(int64_t* aAmount)
-{
-    return GetInfallibleAmount(mAmountFns.mJSMainRuntimeCompartmentsSystem,
-                               aAmount);
-}
-
-NS_IMETHODIMP
-nsMemoryReporterManager::GetJSMainRuntimeCompartmentsUser(int64_t* aAmount)
-{
-    return GetInfallibleAmount(mAmountFns.mJSMainRuntimeCompartmentsUser,
-                               aAmount);
-}
-
-NS_IMETHODIMP
-nsMemoryReporterManager::GetImagesContentUsedUncompressed(int64_t* aAmount)
-{
-    return GetInfallibleAmount(mAmountFns.mImagesContentUsedUncompressed,
-                               aAmount);
-}
-
-NS_IMETHODIMP
-nsMemoryReporterManager::GetStorageSQLite(int64_t* aAmount)
-{
-    return GetInfallibleAmount(mAmountFns.mStorageSQLite, aAmount);
-}
-
-NS_IMETHODIMP
-nsMemoryReporterManager::GetLowMemoryEventsVirtual(int64_t* aAmount)
-{
-    return GetInfallibleAmount(mAmountFns.mLowMemoryEventsVirtual, aAmount);
-}
-
-NS_IMETHODIMP
-nsMemoryReporterManager::GetLowMemoryEventsPhysical(int64_t* aAmount)
-{
-    return GetInfallibleAmount(mAmountFns.mLowMemoryEventsPhysical, aAmount);
-}
-
-NS_IMETHODIMP
-nsMemoryReporterManager::GetGhostWindows(int64_t* aAmount)
-{
-    return GetInfallibleAmount(mAmountFns.mGhostWindows, aAmount);
-}
-
-NS_IMETHODIMP
-nsMemoryReporterManager::GetPageFaultsHard(int64_t* aAmount)
-{
-#ifdef HAVE_PAGE_FAULT_REPORTERS
-    return PageFaultsHardDistinguishedAmount(aAmount);
-#else
-    *aAmount = 0;
-    return NS_ERROR_NOT_AVAILABLE;
-#endif
-}
-
-NS_IMETHODIMP
 nsMemoryReporterManager::GetHasMozMallocUsableSize(bool* aHas)
 {
     void* p = malloc(16);
     if (!p) {
         return NS_ERROR_OUT_OF_MEMORY;
     }
     size_t usable = moz_malloc_usable_size(p);
     free(p);
@@ -1287,70 +1158,16 @@ NS_UnregisterMemoryReporter(nsIMemoryRep
 {
     nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
     if (!mgr) {
         return NS_ERROR_FAILURE;
     }
     return mgr->UnregisterReporter(aReporter);
 }
 
-namespace mozilla {
-
-// 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)             \
-    {                                                                         \
-        nsCOMPtr<nsIMemoryReporterManager> imgr =                             \
-            do_GetService("@mozilla.org/memory-reporter-manager;1");          \
-        nsRefPtr<nsMemoryReporterManager> mgr =                               \
-            static_cast<nsMemoryReporterManager*>(imgr.get());                \
-        if (!mgr) {                                                           \
-            return NS_ERROR_FAILURE;                                          \
-        }                                                                     \
-        mgr->mAmountFns.m##name = aAmountFn;                                  \
-        return NS_OK;                                                         \
-    }
-
-#define DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT(name)                          \
-    nsresult                                                                  \
-    Unregister##name##DistinguishedAmount()                                   \
-    {                                                                         \
-        nsCOMPtr<nsIMemoryReporterManager> imgr =                             \
-            do_GetService("@mozilla.org/memory-reporter-manager;1");          \
-        nsRefPtr<nsMemoryReporterManager> mgr =                               \
-            static_cast<nsMemoryReporterManager*>(imgr.get());                \
-        if (!mgr) {                                                           \
-            return NS_ERROR_FAILURE;                                          \
-        }                                                                     \
-        mgr->mAmountFns.m##name = nullptr;                                    \
-        return NS_OK;                                                         \
-    }
-
-DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeGCHeap)
-DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeTemporaryPeak)
-DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsSystem)
-DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsUser)
-
-DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, ImagesContentUsedUncompressed)
-
-DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, StorageSQLite)
-DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT(StorageSQLite)
-
-DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsVirtual)
-DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsPhysical)
-
-DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, GhostWindows)
-
-#undef DEFINE_REGISTER_DISTINGUISHED_AMOUNT
-#undef DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT
-
-}
-
 #if defined(MOZ_DMD)
 
 namespace mozilla {
 namespace dmd {
 
 class NullReporterCallback : public nsIMemoryReporterCallback
 {
 public:
--- a/xpcom/base/nsMemoryReporterManager.h
+++ b/xpcom/base/nsMemoryReporterManager.h
@@ -15,37 +15,18 @@ class nsMemoryReporterManager : public n
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIMEMORYREPORTERMANAGER
 
   nsMemoryReporterManager();
   virtual ~nsMemoryReporterManager();
 
-  // Functions that (a) implement distinguished amounts, and (b) are outside of
-  // this module.
-  struct AmountFns {
-    mozilla::InfallibleAmountFn mJSMainRuntimeGCHeap;
-    mozilla::InfallibleAmountFn mJSMainRuntimeTemporaryPeak;
-    mozilla::InfallibleAmountFn mJSMainRuntimeCompartmentsSystem;
-    mozilla::InfallibleAmountFn mJSMainRuntimeCompartmentsUser;
-
-    mozilla::InfallibleAmountFn mImagesContentUsedUncompressed;
-
-    mozilla::InfallibleAmountFn mStorageSQLite;
-
-    mozilla::InfallibleAmountFn mLowMemoryEventsVirtual;
-    mozilla::InfallibleAmountFn mLowMemoryEventsPhysical;
-
-    mozilla::InfallibleAmountFn mGhostWindows;
-  };
-  AmountFns mAmountFns;
-
 private:
-  nsresult RegisterReporterHelper(nsIMemoryReporter *aReporter, bool aForce);
+  nsresult RegisterReporterHelper(nsIMemoryReporter *reporter, bool aForce);
 
   nsTHashtable<nsISupportsHashKey> mReporters;
   Mutex mMutex;
   bool mIsRegistrationBlocked;
 };
 
 #define NS_MEMORY_REPORTER_MANAGER_CID \
 { 0xfb97e4f5, 0x32dd, 0x497a, \