Bug 1417746 - Overhaul libpref's memory reporter. r=glandium draft
authorNicholas Nethercote <nnethercote@mozilla.com>
Thu, 16 Nov 2017 13:15:23 +1100
changeset 698738 6655875f2f06098d8c33313df6c41c620b6cb53c
parent 698737 57337aeb927c0e317367f1f6f8831ef44a0197d7
child 740427 455fab4f1231e10de8cbbda016038b4ca64bd37a
push id89343
push usernnethercote@mozilla.com
push dateThu, 16 Nov 2017 02:15:55 +0000
reviewersglandium
bugs1417746
milestone59.0a1
Bug 1417746 - Overhaul libpref's memory reporter. r=glandium This splits the measurements into several buckets, like so: > 718,528 B (00.40%) -- preferences > ├──262,176 B (00.14%) ── hash-table > ├──181,952 B (00.10%) ── callbacks > ├──122,880 B (00.07%) ── pref-name-arena > ├───91,872 B (00.05%) ── root-branches > ├───38,296 B (00.02%) ── string-values > ├───21,272 B (00.01%) ── cache-data > └───────80 B (00.00%) ── misc The patch also measures some things that were previously overlooked. - String pref values. (The old code had a comment that incorrectly claimed they were allocated out of an arena.) - The PrefCallback objects pointed to by entries in nsPrefBranch::mObservers. And it makes the code more like typical reporters. - It removes the "AndOtherStuff" from Preferences' measuring method, and measuring those global structures in PreferenceServiceReporter::CollectReports(). - It adds `const` where appropriate. MozReview-Commit-ID: 5laHM8Cwidx
modules/libpref/Preferences.cpp
modules/libpref/Preferences.h
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -248,16 +248,31 @@ private:
 };
 
 struct PrefHashEntry : PLDHashEntryHdr
 {
   PrefTypeFlags mPrefFlags; // this field first to minimize 64-bit struct size
   const char* mKey;
   PrefValue mDefaultPref;
   PrefValue mUserPref;
+
+  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
+  {
+    // Note: mKey is allocated in gPrefNameArena, measured elsewhere.
+    size_t n = 0;
+    if (mPrefFlags.IsTypeString()) {
+      if (mPrefFlags.HasDefault()) {
+        n += aMallocSizeOf(mDefaultPref.mStringVal);
+      }
+      if (mPrefFlags.HasUserValue()) {
+        n += aMallocSizeOf(mUserPref.mStringVal);
+      }
+    }
+    return n;
+  }
 };
 
 static void
 ClearPrefEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
 {
   auto pref = static_cast<PrefHashEntry*>(aEntry);
   if (pref->mPrefFlags.IsTypeString()) {
     free(const_cast<char*>(pref->mDefaultPref.mStringVal));
@@ -1951,16 +1966,26 @@ public:
   {
     if (!IsWeak())
       return false;
 
     nsCOMPtr<nsIObserver> observer(do_QueryReferent(mWeakRef));
     return !observer;
   }
 
+  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    size_t n = aMallocSizeOf(this);
+    n += mDomain.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+
+    // All the other fields are non-owning pointers, so we don't measure them.
+
+    return n;
+  }
+
   enum
   {
     ALLOW_MEMMOVE = true
   };
 
 private:
   nsCString mDomain;
   nsPrefBranch* mBranch;
@@ -1989,17 +2014,17 @@ public:
 
   nsPrefBranch(const char* aPrefRoot, bool aDefaultBranch);
   nsPrefBranch() = delete;
 
   int32_t GetRootLength() const { return mPrefRoot.Length(); }
 
   static void NotifyObserver(const char* aNewpref, void* aData);
 
-  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
+  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
 
   static void ReportToConsole(const nsAString& aMessage);
 
 private:
   // Helper class for either returning a raw cstring or nsCString.
   typedef mozilla::Variant<const char*, const nsCString> PrefNameBase;
   class PrefName : public PrefNameBase
   {
@@ -2916,21 +2941,28 @@ nsPrefBranch::NotifyObserver(const char*
   nsAutoCString suffix(aNewPref + len);
 
   observer->Observe(static_cast<nsIPrefBranch*>(pCallback->GetPrefBranch()),
                     NS_PREFBRANCH_PREFCHANGE_TOPIC_ID,
                     NS_ConvertASCIItoUTF16(suffix).get());
 }
 
 size_t
-nsPrefBranch::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
+nsPrefBranch::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t n = aMallocSizeOf(this);
+
   n += mPrefRoot.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+
   n += mObservers.ShallowSizeOfExcludingThis(aMallocSizeOf);
+  for (auto iter = mObservers.ConstIter(); !iter.Done(); iter.Next()) {
+    const PrefCallback* data = iter.UserData();
+    n += data->SizeOfIncludingThis(aMallocSizeOf);
+  }
+
   return n;
 }
 
 void
 nsPrefBranch::FreeObserverList()
 {
   // We need to prevent anyone from modifying mObservers while we're iterating
   // over it. In particular, some clients will call RemoveObserver() when
@@ -3342,55 +3374,55 @@ ReportToConsole(const char* aMessage, in
 {
   nsPrintfCString message("** Preference parsing %s (line %d) = %s **\n",
                           (aError ? "error" : "warning"),
                           aLine,
                           aMessage);
   nsPrefBranch::ReportToConsole(NS_ConvertUTF8toUTF16(message.get()));
 }
 
+struct PrefsSizes
+{
+  PrefsSizes()
+    : mHashTable(0)
+    , mStringValues(0)
+    , mCacheData(0)
+    , mRootBranches(0)
+    , mPrefNameArena(0)
+    , mCallbacks(0)
+    , mMisc(0)
+  {
+  }
+
+  size_t mHashTable;
+  size_t mStringValues;
+  size_t mCacheData;
+  size_t mRootBranches;
+  size_t mPrefNameArena;
+  size_t mCallbacks;
+  size_t mMisc;
+};
+
 // Although this is a member of Preferences, it measures sPreferences and
 // several other global structures.
-/* static */ int64_t
-Preferences::SizeOfIncludingThisAndOtherStuff(
-  mozilla::MallocSizeOf aMallocSizeOf)
-{
-  NS_ENSURE_TRUE(InitStaticMembers(), 0);
-
-  size_t n = aMallocSizeOf(sPreferences);
-  if (gHashTable) {
-    // Pref keys are allocated in a private arena, which we count elsewhere.
-    // Pref stringvals are allocated out of the same private arena.
-    n += gHashTable->ShallowSizeOfIncludingThis(aMallocSizeOf);
-  }
-
-  if (gCacheData) {
-    n += gCacheData->ShallowSizeOfIncludingThis(aMallocSizeOf);
-    for (uint32_t i = 0, count = gCacheData->Length(); i < count; ++i) {
-      n += aMallocSizeOf((*gCacheData)[i]);
-    }
-  }
-
-  if (sPreferences->mRootBranch) {
-    n += static_cast<nsPrefBranch*>(sPreferences->mRootBranch.get())
-           ->SizeOfIncludingThis(aMallocSizeOf);
-  }
-
-  if (sPreferences->mDefaultRootBranch) {
-    n += static_cast<nsPrefBranch*>(sPreferences->mDefaultRootBranch.get())
-           ->SizeOfIncludingThis(aMallocSizeOf);
-  }
-
-  n += gPrefNameArena.SizeOfExcludingThis(aMallocSizeOf);
-  for (CallbackNode* node = gFirstCallback; node; node = node->mNext) {
-    n += aMallocSizeOf(node);
-    n += aMallocSizeOf(node->mDomain);
-  }
-
-  return n;
+/* static */ void
+Preferences::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
+                                    PrefsSizes& aSizes)
+{
+  if (!sPreferences) {
+    return;
+  }
+
+  aSizes.mMisc += aMallocSizeOf(sPreferences.get());
+
+  aSizes.mRootBranches +=
+    static_cast<nsPrefBranch*>(sPreferences->mRootBranch.get())
+      ->SizeOfIncludingThis(aMallocSizeOf) +
+    static_cast<nsPrefBranch*>(sPreferences->mDefaultRootBranch.get())
+      ->SizeOfIncludingThis(aMallocSizeOf);
 }
 
 class PreferenceServiceReporter final : public nsIMemoryReporter
 {
   ~PreferenceServiceReporter() {}
 
 public:
   NS_DECL_ISUPPORTS
@@ -3405,22 +3437,87 @@ NS_IMPL_ISUPPORTS(PreferenceServiceRepor
 MOZ_DEFINE_MALLOC_SIZE_OF(PreferenceServiceMallocSizeOf)
 
 NS_IMETHODIMP
 PreferenceServiceReporter::CollectReports(
   nsIHandleReportCallback* aHandleReport,
   nsISupports* aData,
   bool aAnonymize)
 {
-  MOZ_COLLECT_REPORT("explicit/preferences",
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MallocSizeOf mallocSizeOf = PreferenceServiceMallocSizeOf;
+  PrefsSizes sizes;
+
+  Preferences::AddSizeOfIncludingThis(mallocSizeOf, sizes);
+
+  if (gHashTable) {
+    sizes.mHashTable += gHashTable->ShallowSizeOfIncludingThis(mallocSizeOf);
+    for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
+      auto entry = static_cast<PrefHashEntry*>(iter.Get());
+      sizes.mStringValues += entry->SizeOfExcludingThis(mallocSizeOf);
+    }
+  }
+
+  if (gCacheData) {
+    sizes.mCacheData += gCacheData->ShallowSizeOfIncludingThis(mallocSizeOf);
+    for (uint32_t i = 0, count = gCacheData->Length(); i < count; ++i) {
+      sizes.mCacheData += mallocSizeOf((*gCacheData)[i]);
+    }
+  }
+
+  sizes.mPrefNameArena += gPrefNameArena.SizeOfExcludingThis(mallocSizeOf);
+
+  for (CallbackNode* node = gFirstCallback; node; node = node->mNext) {
+    sizes.mCallbacks += mallocSizeOf(node);
+    sizes.mCallbacks += mallocSizeOf(node->mDomain);
+  }
+
+  MOZ_COLLECT_REPORT("explicit/preferences/hash-table",
                      KIND_HEAP,
                      UNITS_BYTES,
-                     Preferences::SizeOfIncludingThisAndOtherStuff(
-                       PreferenceServiceMallocSizeOf),
-                     "Memory used by the preferences system.");
+                     sizes.mHashTable,
+                     "Memory used by libpref's hash table.");
+
+  MOZ_COLLECT_REPORT("explicit/preferences/string-values",
+                     KIND_HEAP,
+                     UNITS_BYTES,
+                     sizes.mStringValues,
+                     "Memory used by libpref's string pref values.");
+
+  MOZ_COLLECT_REPORT("explicit/preferences/cache-data",
+                     KIND_HEAP,
+                     UNITS_BYTES,
+                     sizes.mCacheData,
+                     "Memory used by libpref's VarCaches.");
+
+  MOZ_COLLECT_REPORT("explicit/preferences/root-branches",
+                     KIND_HEAP,
+                     UNITS_BYTES,
+                     sizes.mRootBranches,
+                     "Memory used by libpref's root branches.");
+
+  MOZ_COLLECT_REPORT("explicit/preferences/pref-name-arena",
+                     KIND_HEAP,
+                     UNITS_BYTES,
+                     sizes.mPrefNameArena,
+                     "Memory used by libpref's arena for pref names.");
+
+  MOZ_COLLECT_REPORT("explicit/preferences/callbacks",
+                     KIND_HEAP,
+                     UNITS_BYTES,
+                     sizes.mCallbacks,
+                     "Memory used by libpref's callbacks list, including "
+                     "pref names and prefixes.");
+
+  MOZ_COLLECT_REPORT("explicit/preferences/misc",
+                     KIND_HEAP,
+                     UNITS_BYTES,
+                     sizes.mMisc,
+                     "Miscellaneous memory used by libpref.");
 
   nsPrefBranch* rootBranch =
     static_cast<nsPrefBranch*>(Preferences::GetRootBranch());
   if (!rootBranch) {
     return NS_OK;
   }
 
   size_t numStrong = 0;
--- a/modules/libpref/Preferences.h
+++ b/modules/libpref/Preferences.h
@@ -44,16 +44,18 @@ enum pref_initPhase
 #endif
 
 namespace mozilla {
 
 namespace dom {
 class PrefSetting;
 } // namespace dom
 
+struct PrefsSizes;
+
 class Preferences final
   : public nsIPrefService
   , public nsIObserver
   , public nsIPrefBranch
   , public nsSupportsWeakReference
 {
 public:
   typedef mozilla::dom::PrefSetting PrefSetting;
@@ -299,18 +301,18 @@ public:
   static void GetPreference(PrefSetting* aPref);
   static void SetPreference(const PrefSetting& aPref);
 
 #ifdef DEBUG
   static void SetInitPhase(pref_initPhase phase);
   static pref_initPhase InitPhase();
 #endif
 
-  static int64_t SizeOfIncludingThisAndOtherStuff(
-    mozilla::MallocSizeOf aMallocSizeOf);
+  static void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+                                     PrefsSizes& aSizes);
 
   static void HandleDirty();
 
   // Explicitly choosing synchronous or asynchronous (if allowed) preferences
   // file write. Only for the default file.  The guarantee for the "blocking"
   // is that when it returns, the file on disk reflect the current state of
   // preferences.
   nsresult SavePrefFileBlocking();