Bug 1471025: Part 1 - Store preference access counts in a separate hashtable. r=njn
Once the majority of preferences are stored in a read-only shared map, it
won't be possible to modify the access counts in their entries. Which means we
need a separate map for the access counts.
Fortunately, this code is only active in debug builds, so it shouldn't affect
release users. And the net impact on memory usage of this patchset will still
be decidedly downward.
MozReview-Commit-ID: EuLXlMQJP1M
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -962,38 +962,31 @@ private:
PrefValue mDefaultValue;
PrefValue mUserValue;
};
class PrefEntry : public PLDHashEntryHdr
{
public:
-#ifdef DEBUG
- // This field is before mPref to minimize sizeof(PrefEntry) on 64-bit.
- uint32_t mAccessCount;
-#endif
Pref* mPref; // Note: this is never null in a live entry.
static bool MatchEntry(const PLDHashEntryHdr* aEntry, const void* aKey)
{
auto entry = static_cast<const PrefEntry*>(aEntry);
auto prefName = static_cast<const char*>(aKey);
return entry->mPref->MatchEntry(prefName);
}
static void InitEntry(PLDHashEntryHdr* aEntry, const void* aKey)
{
auto entry = static_cast<PrefEntry*>(aEntry);
auto prefName = static_cast<const char*>(aKey);
-#ifdef DEBUG
- entry->mAccessCount = 0;
-#endif
entry->mPref = new Pref(prefName);
}
static void ClearEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
{
auto entry = static_cast<PrefEntry*>(aEntry);
delete entry->mPref;
@@ -1071,16 +1064,48 @@ private:
static PLDHashTable* gHashTable;
// The callback list contains all the priority callbacks followed by the
// non-priority callbacks. gLastPriorityNode records where the first part ends.
static CallbackNode* gFirstCallback = nullptr;
static CallbackNode* gLastPriorityNode = nullptr;
+#ifdef DEBUG
+#define ACCESS_COUNTS
+#endif
+
+#ifdef ACCESS_COUNTS
+using AccessCountsHashTable = nsDataHashtable<nsCStringHashKey, uint32_t>;
+static AccessCountsHashTable* gAccessCounts;
+
+static void
+AddAccessCount(const nsACString& aPrefName)
+{
+ uint32_t& count = gAccessCounts->GetOrInsert(aPrefName);
+ count++;
+}
+
+static void
+AddAccessCount(const char* aPrefName)
+{
+ AddAccessCount(nsDependentCString(aPrefName));
+}
+#else
+static void MOZ_MAYBE_UNUSED
+AddAccessCount(const nsACString& aPrefName)
+{
+}
+
+static void
+AddAccessCount(const char* aPrefName)
+{
+}
+#endif
+
// These are only used during the call to NotifyCallbacks().
static bool gCallbacksInProgress = false;
static bool gShouldCleanupDeadNodes = false;
static PLDHashTableOps pref_HashTableOps = {
PLDHashTable::HashStringKey, PrefEntry::MatchEntry,
PLDHashTable::MoveEntryStub, PrefEntry::ClearEntry,
PrefEntry::InitEntry,
@@ -1142,19 +1167,17 @@ pref_HashTableLookupInner(const char* aP
static Pref*
pref_HashTableLookup(const char* aPrefName)
{
PrefEntry* entry = pref_HashTableLookupInner(aPrefName);
if (!entry) {
return nullptr;
}
-#ifdef DEBUG
- entry->mAccessCount += 1;
-#endif
+ AddAccessCount(aPrefName);
return entry->mPref;
}
static nsresult
pref_SetPref(const char* aPrefName,
PrefType aType,
PrefValueKind aKind,
@@ -3270,16 +3293,21 @@ Preferences::GetInstanceForService()
MOZ_ASSERT(!gHashTable);
gHashTable = new PLDHashTable(
&pref_HashTableOps, sizeof(PrefEntry), PREF_HASHTABLE_INITIAL_LENGTH);
gTelemetryLoadData =
new nsDataHashtable<nsCStringHashKey, TelemetryLoadData>();
+#ifdef ACCESS_COUNTS
+ MOZ_ASSERT(!gAccessCounts);
+ gAccessCounts = new AccessCountsHashTable();
+#endif
+
gCacheData = new nsTArray<nsAutoPtr<CacheData>>();
gCacheDataDesc = "set by GetInstanceForService() (1)";
Result<Ok, const char*> res = InitInitialObjects(/* isStartup */ true);
if (res.isErr()) {
sPreferences = nullptr;
gCacheDataDesc = res.unwrapErr();
return nullptr;
@@ -3403,16 +3431,20 @@ Preferences::~Preferences()
gLastPriorityNode = gFirstCallback = nullptr;
delete gHashTable;
gHashTable = nullptr;
delete gTelemetryLoadData;
gTelemetryLoadData = nullptr;
+#ifdef ACCESS_COUNTS
+ delete gAccessCounts;
+#endif
+
gPrefNameArena.Clear();
}
NS_IMPL_ISUPPORTS(Preferences,
nsIPrefService,
nsIObserver,
nsIPrefBranch,
nsISupportsWeakReference)
@@ -3739,35 +3771,32 @@ Preferences::GetDefaultBranch(const char
prefBranch.forget(aRetVal);
return NS_OK;
}
NS_IMETHODIMP
Preferences::ReadStats(nsIPrefStatsCallback* aCallback)
{
-#ifdef DEBUG
- for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
- PrefEntry* entry = static_cast<PrefEntry*>(iter.Get());
- aCallback->Visit(entry->mPref->Name(), entry->mAccessCount);
+#ifdef ACCESS_COUNTS
+ for (auto iter = gAccessCounts->Iter(); !iter.Done(); iter.Next()) {
+ aCallback->Visit(iter.Key(), iter.Data());
}
return NS_OK;
#else
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
NS_IMETHODIMP
Preferences::ResetStats()
{
-#ifdef DEBUG
- for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
- static_cast<PrefEntry*>(iter.Get())->mAccessCount = 0;
- }
+#ifdef ACCESS_COUNTS
+ gAccessCounts->Clear();
return NS_OK;
#else
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
NS_IMETHODIMP
Preferences::GetDirty(bool* aRetVal)
--- a/modules/libpref/nsIPrefService.idl
+++ b/modules/libpref/nsIPrefService.idl
@@ -10,17 +10,17 @@ interface nsIFile;
/**
* A helper function for reading access statistics for preferences.
* See nsIPrefService.readStats for more details.
*/
[function, scriptable, uuid(c3f0cedc-e244-4316-b33a-80306a1c35a1)]
interface nsIPrefStatsCallback : nsISupports
{
- void visit(in string prefName, in unsigned long accessCount);
+ void visit(in ACString prefName, in unsigned long accessCount);
};
/**
* The nsIPrefService interface is the main entry point into the back end
* preferences management library. The preference service is directly
* responsible for the management of the preferences files and also facilitates
* access to the preference branch object which allows the direct manipulation
* of the preferences themselves.