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
--- 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__