Bug 1441430 - Provide more detail about atoms memory usage. r=froydnj
authorNicholas Nethercote <nnethercote@mozilla.com>
Wed, 28 Feb 2018 11:05:07 +1100
changeset 405820 2e2934676a8b1132723db6be553646d568b1dffc
parent 405819 3fa481af8f5b761082671b482079f0a6c6929d51
child 405821 23ed1a55d8768db3509ff7c291c0dd38a71e935a
push id100303
push usernnethercote@mozilla.com
push dateWed, 28 Feb 2018 22:04:13 +0000
treeherdermozilla-inbound@2e2934676a8b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1441430
milestone60.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 1441430 - Provide more detail about atoms memory usage. r=froydnj The old output had a single value: "atoms-table". The new output looks like this: > 649,904 B (00.39%) -- atoms > ├──350,256 B (00.21%) -- dynamic > │ ├──235,056 B (00.14%) ── unshared-buffers > │ └──115,200 B (00.07%) ── atom-objects > ├──212,992 B (00.13%) ── table > └───86,656 B (00.05%) ── static/atom-objects MozReview-Commit-ID: 924vUmxHAlh
toolkit/components/aboutmemory/tests/test_memoryReporters.xul
xpcom/base/nsMemoryReporterManager.cpp
xpcom/ds/nsAtom.h
xpcom/ds/nsAtomTable.cpp
xpcom/ds/nsAtomTable.h
--- a/toolkit/components/aboutmemory/tests/test_memoryReporters.xul
+++ b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul
@@ -98,18 +98,18 @@
     } else if (aPath.search(/^explicit\/window-objects\/top\(.*\/js-compartment\(/) >= 0) {
       present.windowObjectsJsCompartments = true;
     } else if (aPath.search(/^explicit\/storage\/sqlite\/places.sqlite/) >= 0) {
       present.places = true;
     } else if (aPath.search(/^explicit\/images/) >= 0) {
       present.images = true;
     } else if (aPath.search(/^explicit\/xpti-working-set$/) >= 0) {
       present.xptiWorkingSet = true;
-    } else if (aPath.search(/^explicit\/atom-table$/) >= 0) {
-      present.atomTablesMain = true;
+    } else if (aPath.search(/^explicit\/atoms\/static\/atom-objects$/) >= 0) {
+      present.staticAtomObjects = 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;
     } else if (aPath.includes(bigStringPrefix)) {
       present.bigString = true;
     } else if (aPath.includes("!)(*&")) {
       present.smallString1 = true;
@@ -256,17 +256,17 @@
     checkSizeReasonable("js-main-runtime-gc-heap-committed/used/gc-things",
                         jsGcHeapUsedGcThingsTotal);
 
     ok(present.jsNonWindowCompartments,     "js-non-window compartments are present");
     ok(present.windowObjectsJsCompartments, "window-objects/.../js compartments are present");
     ok(present.places,                      "places is present");
     ok(present.images,                      "images is present");
     ok(present.xptiWorkingSet,              "xpti-working-set is present");
-    ok(present.atomTablesMain,              "atom-table is present");
+    ok(present.staticAtomObjects,           "static/atom-objects is present");
     ok(present.sandboxLocation,             "sandbox locations are present");
     ok(present.bigString,                   "large string is present");
     ok(present.smallString1,                "small string 1 is present");
     ok(present.smallString2,                "small string 2 is present");
 
     ok(!present.anonymizedWhenUnnecessary,
        "anonymized paths are not present when unnecessary. Failed case: " +
        present.anonymizedWhenUnnecessary);
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -1390,21 +1390,38 @@ class AtomTablesReporter final : public 
   ~AtomTablesReporter() {}
 
 public:
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
                             nsISupports* aData, bool aAnonymize) override
   {
-    int64_t size = NS_SizeOfAtomTableIncludingThis(MallocSizeOf);
+    AtomsSizes sizes;
+    NS_AddSizeOfAtoms(MallocSizeOf, sizes);
+
+    MOZ_COLLECT_REPORT(
+      "explicit/atoms/table", KIND_HEAP, UNITS_BYTES, sizes.mTable,
+      "Memory used by the atom table.");
 
     MOZ_COLLECT_REPORT(
-      "explicit/atom-table", KIND_HEAP, UNITS_BYTES, size,
-      "Memory used by the atom table.");
+      "explicit/atoms/static/atom-objects", KIND_HEAP, UNITS_BYTES,
+      sizes.mStaticAtomObjects,
+      "Memory used by static atom objects.");
+
+    MOZ_COLLECT_REPORT(
+      "explicit/atoms/dynamic/atom-objects", KIND_HEAP, UNITS_BYTES,
+      sizes.mDynamicAtomObjects,
+      "Memory used by dynamic atom objects.");
+
+    MOZ_COLLECT_REPORT(
+      "explicit/atoms/dynamic/unshared-buffers", KIND_HEAP, UNITS_BYTES,
+      sizes.mDynamicUnsharedBuffers,
+      "Memory used by unshared string buffers pointed to by dynamic atom "
+      "objects.");
 
     return NS_OK;
   }
 };
 NS_IMPL_ISUPPORTS(AtomTablesReporter, nsIMemoryReporter)
 
 #ifdef DEBUG
 
--- a/xpcom/ds/nsAtom.h
+++ b/xpcom/ds/nsAtom.h
@@ -6,20 +6,25 @@
 
 #ifndef nsAtom_h
 #define nsAtom_h
 
 #include "nsISupportsImpl.h"
 #include "nsString.h"
 #include "nsStringBuffer.h"
 
+namespace mozilla {
+struct AtomsSizes;
+}
+
 class nsAtom
 {
 public:
-  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+  void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+                              mozilla::AtomsSizes& aSizes) const;
 
   enum class AtomKind : uint8_t {
     DynamicAtom = 0,
     StaticAtom = 1,
     HTML5Atom = 2,
   };
 
   bool Equals(char16ptr_t aString, uint32_t aLength) const
--- a/xpcom/ds/nsAtomTable.cpp
+++ b/xpcom/ds/nsAtomTable.cpp
@@ -136,30 +136,33 @@ nsAtom::ToString(nsAString& aString) con
 
 void
 nsAtom::ToUTF8String(nsACString& aBuf) const
 {
   MOZ_ASSERT(!IsHTML5Atom(), "Called ToUTF8String() on an HTML5 atom");
   CopyUTF16toUTF8(nsDependentString(mString, mLength), aBuf);
 }
 
-size_t
-nsAtom::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+void
+nsAtom::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, AtomsSizes& aSizes)
+  const
 {
-  MOZ_ASSERT(!IsHTML5Atom(), "Called SizeOfIncludingThis() on an HTML5 atom");
-  size_t n = aMallocSizeOf(this);
-  // String buffers pointed to by static atoms are in static memory, and so
-  // are not measured here.
-  if (IsDynamicAtom()) {
-    n += nsStringBuffer::FromData(mString)->SizeOfIncludingThisIfUnshared(
-           aMallocSizeOf);
+  MOZ_ASSERT(!IsHTML5Atom(),
+             "Called AddSizeOfIncludingThis() on an HTML5 atom");
+  size_t thisSize = aMallocSizeOf(this);
+  if (IsStaticAtom()) {
+    // String buffers pointed to by static atoms are in static memory, and so
+    // are not measured here.
+    aSizes.mStaticAtomObjects += thisSize;
   } else {
-    MOZ_ASSERT(IsStaticAtom());
+    aSizes.mDynamicAtomObjects += thisSize;
+    aSizes.mDynamicUnsharedBuffers +=
+      nsStringBuffer::FromData(mString)->SizeOfIncludingThisIfUnshared(
+        aMallocSizeOf);
   }
-  return n;
 }
 
 //----------------------------------------------------------------------
 
 struct AtomTableKey
 {
   AtomTableKey(const char16_t* aUTF16String, uint32_t aLength,
                uint32_t* aHashOut)
@@ -213,17 +216,18 @@ static nsAtom*
 // ConcurrentHashTable.
 class nsAtomSubTable
 {
   friend class nsAtomTable;
   Mutex mLock;
   PLDHashTable mTable;
   nsAtomSubTable();
   void GCLocked(GCKind aKind);
-  size_t SizeOfExcludingThisLocked(MallocSizeOf aMallocSizeOf);
+  void AddSizeOfExcludingThisLocked(MallocSizeOf aMallocSizeOf,
+                                    AtomsSizes& aSizes);
 
   AtomTableEntry* Search(AtomTableKey& aKey)
   {
     mLock.AssertCurrentThreadOwns();
     return static_cast<AtomTableEntry*>(mTable.Search(&aKey));
   }
 
   AtomTableEntry* Add(AtomTableKey& aKey)
@@ -234,17 +238,17 @@ class nsAtomSubTable
 };
 
 // The outer atom table, which coordinates access to the inner array of
 // subtables.
 class nsAtomTable
 {
 public:
   nsAtomSubTable& SelectSubTable(AtomTableKey& aKey);
-  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf);
+  void AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, AtomsSizes& aSizes);
   void GC(GCKind aKind);
   already_AddRefed<nsAtom> Atomize(const nsAString& aUTF16String);
   already_AddRefed<nsAtom> Atomize(const nsACString& aUTF8String);
   already_AddRefed<nsAtom> AtomizeMainThread(const nsAString& aUTF16String);
   nsStaticAtom* GetStaticAtom(const nsAString& aUTF16String);
   void RegisterStaticAtoms(const nsStaticAtomSetup* aSetup, uint32_t aCount);
 
   // The result of this function may be imprecise if other threads are operating
@@ -380,27 +384,26 @@ nsAtomTable::SelectSubTable(AtomTableKey
   // means we should prefer the rightmost bits here.
   //
   // Note that the below is equivalent to mHash % kNumSubTables, a replacement
   // which an optimizing compiler should make, but let's avoid any doubt.
   static_assert((kNumSubTables & (kNumSubTables - 1)) == 0, "must be power of two");
   return mSubTables[aKey.mHash & (kNumSubTables - 1)];
 }
 
-size_t
-nsAtomTable::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
+void
+nsAtomTable::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
+                                    AtomsSizes& aSizes)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  size_t size = aMallocSizeOf(this);
+  aSizes.mTable += aMallocSizeOf(this);
   for (auto& table : mSubTables) {
     MutexAutoLock lock(table.mLock);
-    size += table.SizeOfExcludingThisLocked(aMallocSizeOf);
+    table.AddSizeOfExcludingThisLocked(aMallocSizeOf, aSizes);
   }
-
-  return size;
 }
 
 void nsAtomTable::GC(GCKind aKind)
 {
   MOZ_ASSERT(NS_IsMainThread());
   for (uint32_t i = 0; i < RECENTLY_USED_MAIN_THREAD_ATOM_CACHE_SIZE; ++i) {
     sRecentlyUsedMainThreadAtoms[i] = nullptr;
   }
@@ -603,35 +606,34 @@ NS_ShutdownAtomTable()
   // builds.
   gAtomTable->GC(GCKind::Shutdown);
 #endif
 
   delete gAtomTable;
   gAtomTable = nullptr;
 }
 
-size_t
-NS_SizeOfAtomTableIncludingThis(MallocSizeOf aMallocSizeOf)
+void
+NS_AddSizeOfAtoms(MallocSizeOf aMallocSizeOf, AtomsSizes& aSizes)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(gAtomTable);
-  return gAtomTable->SizeOfIncludingThis(aMallocSizeOf);
+  return gAtomTable->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
 }
 
-size_t
-nsAtomSubTable::SizeOfExcludingThisLocked(MallocSizeOf aMallocSizeOf)
+void
+nsAtomSubTable::AddSizeOfExcludingThisLocked(MallocSizeOf aMallocSizeOf,
+                                             AtomsSizes& aSizes)
 {
   mLock.AssertCurrentThreadOwns();
-  size_t size = mTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
+  aSizes.mTable += mTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
   for (auto iter = mTable.Iter(); !iter.Done(); iter.Next()) {
     auto entry = static_cast<AtomTableEntry*>(iter.Get());
-    size += entry->mAtom->SizeOfIncludingThis(aMallocSizeOf);
+    entry->mAtom->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
   }
-
-  return size;
 }
 
 void
 nsAtomTable::RegisterStaticAtoms(const nsStaticAtomSetup* aSetup,
                                  uint32_t aCount)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_RELEASE_ASSERT(!gStaticAtomsDone, "Static atom insertion is finished!");
--- a/xpcom/ds/nsAtomTable.h
+++ b/xpcom/ds/nsAtomTable.h
@@ -8,11 +8,29 @@
 #define nsAtomTable_h__
 
 #include "mozilla/MemoryReporting.h"
 #include <stddef.h>
 
 void NS_InitAtomTable();
 void NS_ShutdownAtomTable();
 
-size_t NS_SizeOfAtomTableIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
+namespace mozilla {
+struct AtomsSizes
+{
+  size_t mTable;
+  size_t mStaticAtomObjects;
+  size_t mDynamicAtomObjects;
+  size_t mDynamicUnsharedBuffers;
+
+  AtomsSizes()
+   : mTable(0)
+   , mStaticAtomObjects(0)
+   , mDynamicAtomObjects(0)
+   , mDynamicUnsharedBuffers(0)
+  {}
+};
+} // namespace mozilla
+
+void NS_AddSizeOfAtoms(mozilla::MallocSizeOf aMallocSizeOf,
+                       mozilla::AtomsSizes& aSizes);
 
 #endif // nsAtomTable_h__