Bug 913260 (part 4) - Use distinguished amounts for all the memory measurements done by telemetry. r=mccr8.
☠☠ backed out by 1d72ff0f809f ☠ ☠
authorNicholas Nethercote <nnethercote@mozilla.com>
Thu, 19 Sep 2013 15:52:30 -0700
changeset 148609 9195be8a50cbb0478e07c1b29d419b6fc78768a8
parent 148608 09c71a3e7b85211747236c168ec6524fe57c352b
child 148610 016fa9293f661d15a21aedf683d2025aa827b0ab
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)
reviewersmccr8
bugs913260
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 913260 (part 4) - Use distinguished amounts for all the memory measurements done by telemetry. r=mccr8.
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_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
@@ -46,16 +46,17 @@ nsWindowMemoryReporter::Init()
     // when a window's docshell is set to NULL.
     os->AddObserver(sWindowReporter, DOM_WINDOW_DESTROYED_TOPIC,
                     /* weakRef = */ true);
     os->AddObserver(sWindowReporter, "after-minimize-memory-usage",
                     /* weakRef = */ true);
   }
 
   NS_RegisterMemoryReporter(new GhostWindowsReporter());
+  RegisterGhostWindowsDistinguishedAmount(GhostWindowsReporter::DistinguishedAmount);
 }
 
 static already_AddRefed<nsIURI>
 GetWindowURI(nsIDOMWindow *aWindow)
 {
   nsCOMPtr<nsPIDOMWindow> pWindow = do_QueryInterface(aWindow);
   NS_ENSURE_TRUE(pWindow, nullptr);
 
@@ -708,15 +709,16 @@ nsWindowMemoryReporter::CheckForGhostWin
   // if it's not null.
   CheckForGhostWindowsEnumeratorData ghostEnumData =
     { &nonDetachedWindowDomains, aOutGhostIDs, tldService,
       GetGhostTimeout(), TimeStamp::Now() };
   mDetachedWindows.Enumerate(CheckForGhostWindowsEnumerator,
                              &ghostEnumData);
 }
 
-int64_t
-nsWindowMemoryReporter::GhostWindowsReporter::Amount()
+/* static */ int64_t
+nsWindowMemoryReporter::GhostWindowsReporter::DistinguishedAmount()
 {
   nsTHashtable<nsUint64HashKey> ghostWindows;
   sWindowReporter->CheckForGhostWindows(&ghostWindows);
   return ghostWindows.Count();
 }
+
--- a/dom/base/nsWindowMemoryReporter.h
+++ b/dom/base/nsWindowMemoryReporter.h
@@ -134,18 +134,20 @@ private:
 "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.")
     {}
 
+    static int64_t DistinguishedAmount();
+
   private:
-    int64_t Amount() MOZ_OVERRIDE;
+    int64_t Amount() MOZ_OVERRIDE { return DistinguishedAmount(); }
   };
 
   // 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 GetImagesContentUsedUncompressed()
+  static int64_t ImagesContentUsedUncompressedDistinguishedAmount()
   {
     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,36 +218,16 @@ 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,
@@ -834,17 +814,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);
-  NS_RegisterMemoryReporter(new ImagesContentUsedUncompressedReporter());
+  RegisterImagesContentUsedUncompressedDistinguishedAmount(imgMemoryReporter::ImagesContentUsedUncompressedDistinguishedAmount);
 }
 
 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,63 +1570,58 @@ GetCompartmentName(JSCompartment *c, nsC
         // (such as about:memory) have to undo this change.
         if (replaceSlashes)
             name.ReplaceChar('/', '\\');
     } else {
         name.AssignLiteral("null-principal");
     }
 }
 
-// Telemetry relies on this being a uni-reporter (rather than part of the "js"
-// reporter).
-class JSMainRuntimeGCHeapReporter MOZ_FINAL : public MemoryUniReporter
+static int64_t
+JSMainRuntimeGCHeapDistinguishedAmount()
 {
-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;
-    }
-};
+    JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
+    return int64_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) *
+           js::gc::ChunkSize;
+}
+
+static int64_t
+JSMainRuntimeTemporaryPeakDistinguishedAmount()
+{
+    JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
+    return JS::PeakSizeOfTemporary(rt);
+}
 
 static int64_t
 JSMainRuntimeCompartmentsSystemDistinguishedAmount()
 {
     JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
     return JS::SystemCompartmentCount(rt);
 }
 
 static int64_t
 JSMainRuntimeCompartmentsUserDistinguishedAmount()
 {
     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
     {
-        JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
-        return JS::PeakSizeOfTemporary(rt);
+        return JSMainRuntimeTemporaryPeakDistinguishedAmount();
     }
 };
 
 // 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.
 
@@ -3024,19 +3019,20 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
 
     // 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 JSMainRuntimeGCHeapReporter());
+    NS_RegisterMemoryReporter(new JSMainRuntimeCompartmentsReporter);
     NS_RegisterMemoryReporter(new JSMainRuntimeTemporaryPeakReporter());
-    NS_RegisterMemoryReporter(new JSMainRuntimeCompartmentsReporter);
+    RegisterJSMainRuntimeGCHeapDistinguishedAmount(JSMainRuntimeGCHeapDistinguishedAmount);
+    RegisterJSMainRuntimeTemporaryPeakDistinguishedAmount(JSMainRuntimeTemporaryPeakDistinguishedAmount);
     RegisterJSMainRuntimeCompartmentsSystemDistinguishedAmount(JSMainRuntimeCompartmentsSystemDistinguishedAmount);
     RegisterJSMainRuntimeCompartmentsUserDistinguishedAmount(JSMainRuntimeCompartmentsUserDistinguishedAmount);
 
     // 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,43 +48,34 @@
 #define PREF_TS_PAGESIZE_DEFAULT 32768
 
 namespace mozilla {
 namespace storage {
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Memory Reporting
 
-// 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
+static int64_t
+StorageSQLiteDistinguishedAmount()
 {
-public:
-  StorageSQLiteUniReporter()
-    : MemoryUniReporter("storage-sqlite", KIND_OTHER, UNITS_BYTES,
-                         "Memory used by SQLite.")
-  {}
-private:
-  int64_t Amount() MOZ_OVERRIDE { return ::sqlite3_memory_used(); }
-};
+  return ::sqlite3_memory_used();
+}
 
-class StorageSQLiteMultiReporter MOZ_FINAL : public nsIMemoryReporter
+class StorageSQLiteReporter 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
 
-  StorageSQLiteMultiReporter(Service *aService)
+  StorageSQLiteReporter(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 "
@@ -209,17 +200,17 @@ private:
     NS_ENSURE_SUCCESS(rv, rv);
     *aTotal += curr;
 
     return NS_OK;
   }
 };
 
 NS_IMPL_ISUPPORTS1(
-  StorageSQLiteMultiReporter,
+  StorageSQLiteReporter,
   nsIMemoryReporter
 )
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Service
 
 NS_IMPL_ISUPPORTS2(
   Service,
@@ -302,18 +293,18 @@ Service::Service()
 , mSqliteVFS(nullptr)
 , mRegistrationMutex("Service::mRegistrationMutex")
 , mConnections()
 {
 }
 
 Service::~Service()
 {
-  (void)::NS_UnregisterMemoryReporter(mStorageSQLiteUniReporter);
-  (void)::NS_UnregisterMemoryReporter(mStorageSQLiteMultiReporter);
+  (void)::NS_UnregisterMemoryReporter(mStorageSQLiteReporter);
+  mozilla::UnregisterStorageSQLiteDistinguishedAmount();
 
   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();
@@ -533,22 +524,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 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);
+  // 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);
 
   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,18 +167,17 @@ 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> mStorageSQLiteUniReporter;
-  nsCOMPtr<nsIMemoryReporter> mStorageSQLiteMultiReporter;
+  nsCOMPtr<nsIMemoryReporter> mStorageSQLiteReporter;
 
   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
@@ -1337,16 +1337,21 @@ 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_memoryReporters.xul
+++ b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul
@@ -120,20 +120,45 @@
   let dummy;
   let haveExplicit = true;
   try {
     dummy = mgr.explicit;
   } catch (ex) {
     is(ex.result, Cr.NS_ERROR_NOT_AVAILABLE, "mgr.explicit exception");
     haveExplicit = false;
   }
-  dummy = mgr.resident;
-  dummy = mgr.residentFast;
-  dummy = mgr.JSMainRuntimeCompartmentsSystem;
-  dummy = mgr.JSMainRuntimeCompartmentsUser;
+  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) {
+    }
+  }
 
   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;
@@ -150,17 +175,16 @@
 
   // 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");
--- a/toolkit/components/telemetry/TelemetryPing.js
+++ b/toolkit/components/telemetry/TelemetryPing.js
@@ -34,43 +34,16 @@ 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.
-//
-// 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",
-  "js-main-runtime-temporary-peak": "MEMORY_JS_MAIN_RUNTIME_TEMPORARY_PEAK",
-  "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;
@@ -437,60 +410,65 @@ TelemetryPing.prototype = {
     } catch (e) {
       // OK to skip memory reporters in xpcshell
       return;
     }
 
     let histogram = Telemetry.getHistogramById("TELEMETRY_MEMORY_REPORTER_MS");
     let startTime = new Date();
 
-    // Get memory measurements from reporters.
-    let e = mgr.enumerateReporters();
-    while (e.hasMoreElements()) {
-      let mr = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
-      let id = MEM_HISTOGRAMS[mr.name];
-      if (!id) {
-        continue;
-      }
-
-      // collectReports might throw an exception.  If so, just ignore that
-      // memory reporter; we're not getting useful data out of it.
+    // 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) {
       try {
-        // 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;
-
-        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) {
-      }
+        // 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) {
+      };
     }
-
-    // Get memory measurements from distinguished amount attributes.
-    let h = this.handleMemoryReport.bind(this);
     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);
 
-    try { b("MEMORY_RESIDENT", mgr.residentFast); } catch (e) {}
-    try { c("MEMORY_JS_COMPARTMENTS_SYSTEM", mgr.JSMainRuntimeCompartmentsSystem); } catch (e) {}
-    try { c("MEMORY_JS_COMPARTMENTS_USER", mgr.JSMainRuntimeCompartmentsUser); } catch (e) {}
+    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");
 
     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,16 +320,22 @@ 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 "
@@ -340,17 +346,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 sNumLowVirtualMemEvents;
+    return LowMemoryEventsVirtualDistinguishedAmount();
   }
 };
 
 class LowCommitSpaceEventsReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
   LowCommitSpaceEventsReporter()
     : MemoryUniReporter("low-commit-space-events",
@@ -361,31 +367,37 @@ 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 sNumLowPhysicalMemEvents; }
+  int64_t Amount() MOZ_OVERRIDE { return LowMemoryEventsPhysicalDistinguishedAmount(); }
 };
 
 #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
@@ -498,16 +510,18 @@ 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
@@ -210,43 +210,98 @@ interface nsIMemoryReporterManager : nsI
    */
   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 to be able to rely on them.
+   * 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.
    *
-   * |explicit| (UNIT_BYTES)  The total size of explicit memory allocations,
+   * 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.
    *
-   * |resident| (UNIT_BYTES)  The resident size (a.k.a. RSS or physical memory
+   * |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| (UNIT_BYTES)  This is like |resident|, but on Mac OS
+   * |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.  It is used by telemetry.
+   * 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}| (UNIT_COUNTS)  The number of
+   * |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.
    */
   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
    * usage.  When we're finished, we invoke the given runnable if it's not
@@ -272,23 +327,38 @@ namespace mozilla {
 // 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 REGISTER_DISTINGUISHED_AMOUNT(kind, name) \
+#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)
 
-REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsSystem)
-REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsUser)
+DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, ImagesContentUsedUncompressed)
+
+DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, StorageSQLite)
+DECL_UNREGISTER_DISTINGUISHED_AMOUNT(StorageSQLite)
 
-#undef REGISTER_DISTINGUISHED_AMOUNT
+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.
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -459,16 +459,28 @@ 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 "
@@ -478,52 +490,60 @@ 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)
     {
-        struct rusage usage;
-        int err = getrusage(RUSAGE_SELF, &usage);
-        if (err != 0) {
-            return NS_ERROR_FAILURE;
-        }
-        *aAmount = usage.ru_majflt;
-        return NS_OK;
+        return PageFaultsHardDistinguishedAmount(aAmount);
     }
 };
 #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
-    {
-        jemalloc_stats_t stats;
-        jemalloc_stats(&stats);
-        return (int64_t) stats.allocated;
-    }
+    int64_t Amount() MOZ_OVERRIDE { return HeapAllocated(); }
 };
 
 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.
@@ -532,34 +552,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()
+    int64_t Amount() MOZ_OVERRIDE
     {
         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()
+    int64_t Amount() MOZ_OVERRIDE
     {
         jemalloc_stats_t stats;
         jemalloc_stats(&stats);
         return stats.bookkeeping;
     }
 };
 
 class HeapOverheadPageCacheReporter MOZ_FINAL : public MemoryUniReporter
@@ -569,17 +589,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()
+    int64_t Amount() MOZ_OVERRIDE
     {
         jemalloc_stats_t stats;
         jemalloc_stats(&stats);
         return (int64_t) stats.page_cache;
     }
 };
 
 class HeapCommittedReporter MOZ_FINAL : public MemoryUniReporter
@@ -589,17 +609,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()
+    int64_t Amount() MOZ_OVERRIDE
     {
         jemalloc_stats_t stats;
         jemalloc_stats(&stats);
         return (int64_t) (stats.allocated + stats.waste +
                           stats.bookkeeping + stats.page_cache);
     }
 };
 
@@ -609,41 +629,37 @@ 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()
-    {
-        jemalloc_stats_t stats;
-        jemalloc_stats(&stats);
-        return (int64_t) 10000 *
-          (stats.waste + stats.bookkeeping + stats.page_cache) /
-          ((double)stats.allocated);
-    }
+    int64_t Amount() MOZ_OVERRIDE { return HeapOverheadRatio(); }
 };
 #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() { return NS_SizeOfAtomTablesIncludingThis(MallocSizeOf); }
+    int64_t Amount() MOZ_OVERRIDE
+    {
+        return NS_SizeOfAtomTablesIncludingThis(MallocSizeOf);
+    }
 };
 
 #ifdef MOZ_DMD
 
 namespace mozilla {
 namespace dmd {
 
 class DMDReporter MOZ_FINAL : public nsIMemoryReporter
@@ -960,16 +976,22 @@ 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;
         }
@@ -1007,16 +1029,27 @@ nsMemoryReporterManager::GetExplicit(int
 
     *aAmount = 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
@@ -1028,42 +1061,121 @@ nsMemoryReporterManager::GetResidentFast
 #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);
@@ -1179,35 +1291,63 @@ NS_UnregisterMemoryReporter(nsIMemoryRep
     }
     return mgr->UnregisterReporter(aReporter);
 }
 
 namespace mozilla {
 
 // Macro for generating functions that register distinguished amount functions
 // with the memory reporter manager.
-#define REGISTER_DISTINGUISHED_AMOUNT(kind, name)                             \
+#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;                                                         \
     }
 
-REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsSystem)
-REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsUser)
+#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;                                                         \
+    }
 
-#undef REGISTER_DISTINGUISHED_AMOUNT
+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 {
 
--- a/xpcom/base/nsMemoryReporterManager.h
+++ b/xpcom/base/nsMemoryReporterManager.h
@@ -18,18 +18,29 @@ public:
   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);
 
   nsTHashtable<nsISupportsHashKey> mReporters;
   Mutex mMutex;