Bug 1441430 - Provide more detail about atoms memory usage. r=froydnj draft
authorNicholas Nethercote <nnethercote@mozilla.com>
Wed, 28 Feb 2018 10:52:50 +1100
changeset 760706 e46364b382d1b8b615599bea8e3f86a54cd596b0
parent 760705 882314e5c51e0b820f93e62828f5323b4891ae24
push id100726
push usernnethercote@mozilla.com
push dateTue, 27 Feb 2018 23:53:22 +0000
reviewersfroydnj
bugs1441430
milestone60.0a1
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__