Bug 1181452 - Add memory reporting infrastructure for Servo to SpiderMonkey. r=till.

Basically, this change is all about aggregating SpiderMonkey's fine-grained
measurements into the new set of coarse-grained measurements, called
ServoSizes (which is similar to the existing TabSizes). The change utilizes and
extends the existing macro machinery to do this in a way that has some chance
to maintaining correctness over the long-term despite the fact that this code
is so fiddly.
This commit is contained in:
Nicholas Nethercote 2015-07-07 23:18:44 -07:00
parent ffe648a61f
commit 3a776e8b2d
2 changed files with 288 additions and 141 deletions

View File

@ -54,6 +54,43 @@ struct TabSizes
size_t other; size_t other;
}; };
// These are the measurements used by Servo. It's important that this is a POD
// struct so that Servo can have a parallel |repr(C)| Rust equivalent.
struct ServoSizes
{
enum Kind {
GCHeapUsed,
GCHeapUnused,
GCHeapAdmin,
GCHeapDecommitted,
MallocHeap,
NonHeap,
Ignore
};
ServoSizes() { mozilla::PodZero(this); }
void add(Kind kind, size_t n) {
switch (kind) {
case GCHeapUsed: gcHeapUsed += n; break;
case GCHeapUnused: gcHeapUnused += n; break;
case GCHeapAdmin: gcHeapAdmin += n; break;
case GCHeapDecommitted: gcHeapDecommitted += n; break;
case MallocHeap: mallocHeap += n; break;
case NonHeap: nonHeap += n; break;
case Ignore: /* do nothing */ break;
default: MOZ_CRASH("bad ServoSizes kind");
}
}
size_t gcHeapUsed;
size_t gcHeapUnused;
size_t gcHeapAdmin;
size_t gcHeapDecommitted;
size_t mallocHeap;
size_t nonHeap;
};
} // namespace JS } // namespace JS
namespace js { namespace js {
@ -92,24 +129,24 @@ struct CStringHashPolicy
// then use the following macros to transform those lists into the required // then use the following macros to transform those lists into the required
// methods. // methods.
// //
// - The |tabKind| value is used when measuring TabSizes.
//
// - The |servoKind| value is used when measuring ServoSizes and also for
// the various sizeOfLiveGCThings() methods.
//
// In some classes, one or more of the macro arguments aren't used. We use '_' // In some classes, one or more of the macro arguments aren't used. We use '_'
// for those. // for those.
// //
#define DECL_SIZE(kind, gc, mSize) size_t mSize; #define DECL_SIZE(tabKind, servoKind, mSize) size_t mSize;
#define ZERO_SIZE(kind, gc, mSize) mSize(0), #define ZERO_SIZE(tabKind, servoKind, mSize) mSize(0),
#define COPY_OTHER_SIZE(kind, gc, mSize) mSize(other.mSize), #define COPY_OTHER_SIZE(tabKind, servoKind, mSize) mSize(other.mSize),
#define ADD_OTHER_SIZE(kind, gc, mSize) mSize += other.mSize; #define ADD_OTHER_SIZE(tabKind, servoKind, mSize) mSize += other.mSize;
#define SUB_OTHER_SIZE(kind, gc, mSize) MOZ_ASSERT(mSize >= other.mSize); \ #define SUB_OTHER_SIZE(tabKind, servoKind, mSize) MOZ_ASSERT(mSize >= other.mSize); \
mSize -= other.mSize; mSize -= other.mSize;
#define ADD_SIZE_TO_N(kind, gc, mSize) n += mSize; #define ADD_SIZE_TO_N(tabKind, servoKind, mSize) n += mSize;
#define ADD_SIZE_TO_N_IF_LIVE_GC_THING(kind, gc, mSize) n += (js::gc) ? mSize : 0; #define ADD_SIZE_TO_N_IF_LIVE_GC_THING(tabKind, servoKind, mSize) n += (ServoSizes::servoKind == ServoSizes::GCHeapUsed) ? mSize : 0;
#define ADD_TO_TAB_SIZES(kind, gc, mSize) sizes->add(JS::TabSizes::kind, mSize); #define ADD_TO_TAB_SIZES(tabKind, servoKind, mSize) sizes->add(JS::TabSizes::tabKind, mSize);
#define ADD_TO_SERVO_SIZES(tabKind, servoKind, mSize) sizes->add(JS::ServoSizes::servoKind, mSize);
// Used to annotate which size_t fields measure live GC things and which don't.
enum {
NotLiveGCThing = false,
IsLiveGCThing = true
};
} // namespace js } // namespace js
@ -118,21 +155,21 @@ namespace JS {
struct ClassInfo struct ClassInfo
{ {
#define FOR_EACH_SIZE(macro) \ #define FOR_EACH_SIZE(macro) \
macro(Objects, IsLiveGCThing, objectsGCHeap) \ macro(Objects, GCHeapUsed, objectsGCHeap) \
macro(Objects, NotLiveGCThing, objectsMallocHeapSlots) \ macro(Objects, MallocHeap, objectsMallocHeapSlots) \
macro(Objects, NotLiveGCThing, objectsMallocHeapElementsNonAsmJS) \ macro(Objects, MallocHeap, objectsMallocHeapElementsNonAsmJS) \
macro(Objects, NotLiveGCThing, objectsMallocHeapElementsAsmJS) \ macro(Objects, MallocHeap, objectsMallocHeapElementsAsmJS) \
macro(Objects, NotLiveGCThing, objectsNonHeapElementsAsmJS) \ macro(Objects, NonHeap, objectsNonHeapElementsAsmJS) \
macro(Objects, NotLiveGCThing, objectsNonHeapElementsMapped) \ macro(Objects, NonHeap, objectsNonHeapElementsMapped) \
macro(Objects, NotLiveGCThing, objectsNonHeapCodeAsmJS) \ macro(Objects, NonHeap, objectsNonHeapCodeAsmJS) \
macro(Objects, NotLiveGCThing, objectsMallocHeapMisc) \ macro(Objects, MallocHeap, objectsMallocHeapMisc) \
\ \
macro(Other, IsLiveGCThing, shapesGCHeapTree) \ macro(Other, GCHeapUsed, shapesGCHeapTree) \
macro(Other, IsLiveGCThing, shapesGCHeapDict) \ macro(Other, GCHeapUsed, shapesGCHeapDict) \
macro(Other, IsLiveGCThing, shapesGCHeapBase) \ macro(Other, GCHeapUsed, shapesGCHeapBase) \
macro(Other, NotLiveGCThing, shapesMallocHeapTreeTables) \ macro(Other, MallocHeap, shapesMallocHeapTreeTables) \
macro(Other, NotLiveGCThing, shapesMallocHeapDictTables) \ macro(Other, MallocHeap, shapesMallocHeapDictTables) \
macro(Other, NotLiveGCThing, shapesMallocHeapTreeKids) \ macro(Other, MallocHeap, shapesMallocHeapTreeKids)
ClassInfo() ClassInfo()
: FOR_EACH_SIZE(ZERO_SIZE) : FOR_EACH_SIZE(ZERO_SIZE)
@ -168,6 +205,10 @@ struct ClassInfo
FOR_EACH_SIZE(ADD_TO_TAB_SIZES) FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
} }
void addToServoSizes(ServoSizes *sizes) const {
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
}
FOR_EACH_SIZE(DECL_SIZE) FOR_EACH_SIZE(DECL_SIZE)
int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE) int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
@ -201,17 +242,21 @@ struct NotableClassInfo : public ClassInfo
struct CodeSizes struct CodeSizes
{ {
#define FOR_EACH_SIZE(macro) \ #define FOR_EACH_SIZE(macro) \
macro(_, _, ion) \ macro(_, NonHeap, ion) \
macro(_, _, baseline) \ macro(_, NonHeap, baseline) \
macro(_, _, regexp) \ macro(_, NonHeap, regexp) \
macro(_, _, other) \ macro(_, NonHeap, other) \
macro(_, _, unused) macro(_, NonHeap, unused)
CodeSizes() CodeSizes()
: FOR_EACH_SIZE(ZERO_SIZE) : FOR_EACH_SIZE(ZERO_SIZE)
dummy() dummy()
{} {}
void addToServoSizes(ServoSizes *sizes) const {
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
}
FOR_EACH_SIZE(DECL_SIZE) FOR_EACH_SIZE(DECL_SIZE)
int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE) int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
@ -221,24 +266,30 @@ struct CodeSizes
// Data for tracking GC memory usage. // Data for tracking GC memory usage.
struct GCSizes struct GCSizes
{ {
// |nurseryDecommitted| is marked as NonHeap rather than GCHeapDecommitted
// because we don't consider the nursery to be part of the GC heap.
#define FOR_EACH_SIZE(macro) \ #define FOR_EACH_SIZE(macro) \
macro(_, _, marker) \ macro(_, MallocHeap, marker) \
macro(_, _, nurseryCommitted) \ macro(_, NonHeap, nurseryCommitted) \
macro(_, _, nurseryDecommitted) \ macro(_, NonHeap, nurseryDecommitted) \
macro(_, _, nurseryMallocedBuffers) \ macro(_, MallocHeap, nurseryMallocedBuffers) \
macro(_, _, storeBufferVals) \ macro(_, MallocHeap, storeBufferVals) \
macro(_, _, storeBufferCells) \ macro(_, MallocHeap, storeBufferCells) \
macro(_, _, storeBufferSlots) \ macro(_, MallocHeap, storeBufferSlots) \
macro(_, _, storeBufferWholeCells) \ macro(_, MallocHeap, storeBufferWholeCells) \
macro(_, _, storeBufferRelocVals) \ macro(_, MallocHeap, storeBufferRelocVals) \
macro(_, _, storeBufferRelocCells) \ macro(_, MallocHeap, storeBufferRelocCells) \
macro(_, _, storeBufferGenerics) macro(_, MallocHeap, storeBufferGenerics)
GCSizes() GCSizes()
: FOR_EACH_SIZE(ZERO_SIZE) : FOR_EACH_SIZE(ZERO_SIZE)
dummy() dummy()
{} {}
void addToServoSizes(ServoSizes *sizes) const {
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
}
FOR_EACH_SIZE(DECL_SIZE) FOR_EACH_SIZE(DECL_SIZE)
int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE) int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
@ -253,10 +304,10 @@ struct GCSizes
struct StringInfo struct StringInfo
{ {
#define FOR_EACH_SIZE(macro) \ #define FOR_EACH_SIZE(macro) \
macro(Strings, IsLiveGCThing, gcHeapLatin1) \ macro(Strings, GCHeapUsed, gcHeapLatin1) \
macro(Strings, IsLiveGCThing, gcHeapTwoByte) \ macro(Strings, GCHeapUsed, gcHeapTwoByte) \
macro(Strings, NotLiveGCThing, mallocHeapLatin1) \ macro(Strings, MallocHeap, mallocHeapLatin1) \
macro(Strings, NotLiveGCThing, mallocHeapTwoByte) macro(Strings, MallocHeap, mallocHeapTwoByte)
StringInfo() StringInfo()
: FOR_EACH_SIZE(ZERO_SIZE) : FOR_EACH_SIZE(ZERO_SIZE)
@ -290,6 +341,10 @@ struct StringInfo
FOR_EACH_SIZE(ADD_TO_TAB_SIZES) FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
} }
void addToServoSizes(ServoSizes *sizes) const {
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
}
FOR_EACH_SIZE(DECL_SIZE) FOR_EACH_SIZE(DECL_SIZE)
uint32_t numCopies; // How many copies of the string have we seen? uint32_t numCopies; // How many copies of the string have we seen?
@ -326,9 +381,9 @@ struct NotableStringInfo : public StringInfo
struct ScriptSourceInfo struct ScriptSourceInfo
{ {
#define FOR_EACH_SIZE(macro) \ #define FOR_EACH_SIZE(macro) \
macro(_, _, compressed) \ macro(_, MallocHeap, compressed) \
macro(_, _, uncompressed) \ macro(_, MallocHeap, uncompressed) \
macro(_, _, misc) macro(_, MallocHeap, misc)
ScriptSourceInfo() ScriptSourceInfo()
: FOR_EACH_SIZE(ZERO_SIZE) : FOR_EACH_SIZE(ZERO_SIZE)
@ -345,6 +400,10 @@ struct ScriptSourceInfo
numScripts--; numScripts--;
} }
void addToServoSizes(ServoSizes *sizes) const {
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
}
bool isNotable() const { bool isNotable() const {
static const size_t NotabilityThreshold = 16 * 1024; static const size_t NotabilityThreshold = 16 * 1024;
size_t n = 0; size_t n = 0;
@ -387,16 +446,16 @@ struct NotableScriptSourceInfo : public ScriptSourceInfo
struct RuntimeSizes struct RuntimeSizes
{ {
#define FOR_EACH_SIZE(macro) \ #define FOR_EACH_SIZE(macro) \
macro(_, _, object) \ macro(_, MallocHeap, object) \
macro(_, _, atomsTable) \ macro(_, MallocHeap, atomsTable) \
macro(_, _, contexts) \ macro(_, MallocHeap, contexts) \
macro(_, _, dtoa) \ macro(_, MallocHeap, dtoa) \
macro(_, _, temporary) \ macro(_, MallocHeap, temporary) \
macro(_, _, interpreterStack) \ macro(_, MallocHeap, interpreterStack) \
macro(_, _, mathCache) \ macro(_, MallocHeap, mathCache) \
macro(_, _, uncompressedSourceCache) \ macro(_, MallocHeap, uncompressedSourceCache) \
macro(_, _, compressedSourceSet) \ macro(_, MallocHeap, compressedSourceSet) \
macro(_, _, scriptData) \ macro(_, MallocHeap, scriptData)
RuntimeSizes() RuntimeSizes()
: FOR_EACH_SIZE(ZERO_SIZE) : FOR_EACH_SIZE(ZERO_SIZE)
@ -417,14 +476,21 @@ struct RuntimeSizes
js_delete(allScriptSources); js_delete(allScriptSources);
} }
void addToServoSizes(ServoSizes *sizes) const {
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
scriptSourceInfo.addToServoSizes(sizes);
code.addToServoSizes(sizes);
gc.addToServoSizes(sizes);
}
// The script source measurements in |scriptSourceInfo| are initially for // The script source measurements in |scriptSourceInfo| are initially for
// all script sources. At the end, if the measurement granularity is // all script sources. At the end, if the measurement granularity is
// FineGrained, we subtract the measurements of the notable script sources // FineGrained, we subtract the measurements of the notable script sources
// and move them into |notableScriptSources|. // and move them into |notableScriptSources|.
FOR_EACH_SIZE(DECL_SIZE) FOR_EACH_SIZE(DECL_SIZE)
ScriptSourceInfo scriptSourceInfo; ScriptSourceInfo scriptSourceInfo;
CodeSizes code; CodeSizes code;
GCSizes gc; GCSizes gc;
typedef js::HashMap<const char*, ScriptSourceInfo, typedef js::HashMap<const char*, ScriptSourceInfo,
js::CStringHashPolicy, js::CStringHashPolicy,
@ -440,25 +506,25 @@ struct RuntimeSizes
#undef FOR_EACH_SIZE #undef FOR_EACH_SIZE
}; };
struct GCThingSizes struct UnusedGCThingSizes
{ {
#define FOR_EACH_SIZE(macro) \ #define FOR_EACH_SIZE(macro) \
macro(_, _, object) \ macro(Other, GCHeapUnused, object) \
macro(_, _, script) \ macro(Other, GCHeapUnused, script) \
macro(_, _, lazyScript) \ macro(Other, GCHeapUnused, lazyScript) \
macro(_, _, shape) \ macro(Other, GCHeapUnused, shape) \
macro(_, _, baseShape) \ macro(Other, GCHeapUnused, baseShape) \
macro(_, _, objectGroup) \ macro(Other, GCHeapUnused, objectGroup) \
macro(_, _, string) \ macro(Other, GCHeapUnused, string) \
macro(_, _, symbol) \ macro(Other, GCHeapUnused, symbol) \
macro(_, _, jitcode) \ macro(Other, GCHeapUnused, jitcode) \
GCThingSizes() UnusedGCThingSizes()
: FOR_EACH_SIZE(ZERO_SIZE) : FOR_EACH_SIZE(ZERO_SIZE)
dummy() dummy()
{} {}
GCThingSizes(GCThingSizes&& other) UnusedGCThingSizes(UnusedGCThingSizes&& other)
: FOR_EACH_SIZE(COPY_OTHER_SIZE) : FOR_EACH_SIZE(COPY_OTHER_SIZE)
dummy() dummy()
{} {}
@ -475,11 +541,11 @@ struct GCThingSizes
case JS::TraceKind::LazyScript: lazyScript += n; break; case JS::TraceKind::LazyScript: lazyScript += n; break;
case JS::TraceKind::ObjectGroup: objectGroup += n; break; case JS::TraceKind::ObjectGroup: objectGroup += n; break;
default: default:
MOZ_CRASH("Bad trace kind for GCThingSizes"); MOZ_CRASH("Bad trace kind for UnusedGCThingSizes");
} }
} }
void addSizes(const GCThingSizes& other) { void addSizes(const UnusedGCThingSizes& other) {
FOR_EACH_SIZE(ADD_OTHER_SIZE) FOR_EACH_SIZE(ADD_OTHER_SIZE)
} }
@ -489,6 +555,14 @@ struct GCThingSizes
return n; return n;
} }
void addToTabSizes(JS::TabSizes *sizes) const {
FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
}
void addToServoSizes(JS::ServoSizes *sizes) const {
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
}
FOR_EACH_SIZE(DECL_SIZE) FOR_EACH_SIZE(DECL_SIZE)
int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE) int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
@ -498,15 +572,15 @@ struct GCThingSizes
struct ZoneStats struct ZoneStats
{ {
#define FOR_EACH_SIZE(macro) \ #define FOR_EACH_SIZE(macro) \
macro(Other, IsLiveGCThing, symbolsGCHeap) \ macro(Other, GCHeapUsed, symbolsGCHeap) \
macro(Other, NotLiveGCThing, gcHeapArenaAdmin) \ macro(Other, GCHeapAdmin, gcHeapArenaAdmin) \
macro(Other, IsLiveGCThing, lazyScriptsGCHeap) \ macro(Other, GCHeapUsed, lazyScriptsGCHeap) \
macro(Other, NotLiveGCThing, lazyScriptsMallocHeap) \ macro(Other, MallocHeap, lazyScriptsMallocHeap) \
macro(Other, IsLiveGCThing, jitCodesGCHeap) \ macro(Other, GCHeapUsed, jitCodesGCHeap) \
macro(Other, IsLiveGCThing, objectGroupsGCHeap) \ macro(Other, GCHeapUsed, objectGroupsGCHeap) \
macro(Other, NotLiveGCThing, objectGroupsMallocHeap) \ macro(Other, MallocHeap, objectGroupsMallocHeap) \
macro(Other, NotLiveGCThing, typePool) \ macro(Other, MallocHeap, typePool) \
macro(Other, NotLiveGCThing, baselineStubsOptimized) \ macro(Other, MallocHeap, baselineStubsOptimized)
ZoneStats() ZoneStats()
: FOR_EACH_SIZE(ZERO_SIZE) : FOR_EACH_SIZE(ZERO_SIZE)
@ -558,16 +632,23 @@ struct ZoneStats
void addToTabSizes(JS::TabSizes* sizes) const { void addToTabSizes(JS::TabSizes* sizes) const {
MOZ_ASSERT(isTotals); MOZ_ASSERT(isTotals);
FOR_EACH_SIZE(ADD_TO_TAB_SIZES) FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
sizes->add(JS::TabSizes::Other, unusedGCThings.totalSize()); unusedGCThings.addToTabSizes(sizes);
stringInfo.addToTabSizes(sizes); stringInfo.addToTabSizes(sizes);
} }
void addToServoSizes(JS::ServoSizes *sizes) const {
MOZ_ASSERT(isTotals);
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
unusedGCThings.addToServoSizes(sizes);
stringInfo.addToServoSizes(sizes);
}
// These string measurements are initially for all strings. At the end, // These string measurements are initially for all strings. At the end,
// if the measurement granularity is FineGrained, we subtract the // if the measurement granularity is FineGrained, we subtract the
// measurements of the notable script sources and move them into // measurements of the notable script sources and move them into
// |notableStrings|. // |notableStrings|.
FOR_EACH_SIZE(DECL_SIZE) FOR_EACH_SIZE(DECL_SIZE)
GCThingSizes unusedGCThings; UnusedGCThingSizes unusedGCThings;
StringInfo stringInfo; StringInfo stringInfo;
void* extra; // This field can be used by embedders. void* extra; // This field can be used by embedders.
@ -588,25 +669,29 @@ struct ZoneStats
struct CompartmentStats struct CompartmentStats
{ {
// We assume that |objectsPrivate| is on the malloc heap, but it's not
// actually guaranteed. But for Servo, at least, it's a moot point because
// it doesn't provide an ObjectPrivateVisitor so the value will always be
// zero.
#define FOR_EACH_SIZE(macro) \ #define FOR_EACH_SIZE(macro) \
macro(Private, NotLiveGCThing, objectsPrivate) \ macro(Private, MallocHeap, objectsPrivate) \
macro(Other, IsLiveGCThing, scriptsGCHeap) \ macro(Other, GCHeapUsed, scriptsGCHeap) \
macro(Other, NotLiveGCThing, scriptsMallocHeapData) \ macro(Other, MallocHeap, scriptsMallocHeapData) \
macro(Other, NotLiveGCThing, baselineData) \ macro(Other, MallocHeap, baselineData) \
macro(Other, NotLiveGCThing, baselineStubsFallback) \ macro(Other, MallocHeap, baselineStubsFallback) \
macro(Other, NotLiveGCThing, ionData) \ macro(Other, MallocHeap, ionData) \
macro(Other, NotLiveGCThing, typeInferenceTypeScripts) \ macro(Other, MallocHeap, typeInferenceTypeScripts) \
macro(Other, NotLiveGCThing, typeInferenceAllocationSiteTables) \ macro(Other, MallocHeap, typeInferenceAllocationSiteTables) \
macro(Other, NotLiveGCThing, typeInferenceArrayTypeTables) \ macro(Other, MallocHeap, typeInferenceArrayTypeTables) \
macro(Other, NotLiveGCThing, typeInferenceObjectTypeTables) \ macro(Other, MallocHeap, typeInferenceObjectTypeTables) \
macro(Other, NotLiveGCThing, compartmentObject) \ macro(Other, MallocHeap, compartmentObject) \
macro(Other, NotLiveGCThing, compartmentTables) \ macro(Other, MallocHeap, compartmentTables) \
macro(Other, NotLiveGCThing, innerViewsTable) \ macro(Other, MallocHeap, innerViewsTable) \
macro(Other, NotLiveGCThing, lazyArrayBuffersTable) \ macro(Other, MallocHeap, lazyArrayBuffersTable) \
macro(Other, NotLiveGCThing, objectMetadataTable) \ macro(Other, MallocHeap, objectMetadataTable) \
macro(Other, NotLiveGCThing, crossCompartmentWrappersTable) \ macro(Other, MallocHeap, crossCompartmentWrappersTable) \
macro(Other, NotLiveGCThing, regexpCompartment) \ macro(Other, MallocHeap, regexpCompartment) \
macro(Other, NotLiveGCThing, savedStacksSet) macro(Other, MallocHeap, savedStacksSet)
CompartmentStats() CompartmentStats()
: FOR_EACH_SIZE(ZERO_SIZE) : FOR_EACH_SIZE(ZERO_SIZE)
@ -658,12 +743,18 @@ struct CompartmentStats
classInfo.addToTabSizes(sizes); classInfo.addToTabSizes(sizes);
} }
void addToServoSizes(ServoSizes *sizes) const {
MOZ_ASSERT(isTotals);
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
classInfo.addToServoSizes(sizes);
}
// The class measurements in |classInfo| are initially for all classes. At // The class measurements in |classInfo| are initially for all classes. At
// the end, if the measurement granularity is FineGrained, we subtract the // the end, if the measurement granularity is FineGrained, we subtract the
// measurements of the notable classes and move them into |notableClasses|. // measurements of the notable classes and move them into |notableClasses|.
FOR_EACH_SIZE(DECL_SIZE) FOR_EACH_SIZE(DECL_SIZE)
ClassInfo classInfo; ClassInfo classInfo;
void* extra; // This field can be used by embedders. void* extra; // This field can be used by embedders.
typedef js::HashMap<const char*, ClassInfo, typedef js::HashMap<const char*, ClassInfo,
js::CStringHashPolicy, js::CStringHashPolicy,
@ -682,13 +773,18 @@ typedef js::Vector<ZoneStats, 0, js::SystemAllocPolicy> ZoneStatsVector;
struct RuntimeStats struct RuntimeStats
{ {
// |gcHeapChunkTotal| is ignored because it's the sum of all the other
// values. |gcHeapGCThings| is ignored because it's the sum of some of the
// values from the zones and compartments. Both of those values are not
// reported directly, but are just present for sanity-checking other
// values.
#define FOR_EACH_SIZE(macro) \ #define FOR_EACH_SIZE(macro) \
macro(_, _, gcHeapChunkTotal) \ macro(_, Ignore, gcHeapChunkTotal) \
macro(_, _, gcHeapDecommittedArenas) \ macro(_, GCHeapDecommitted, gcHeapDecommittedArenas) \
macro(_, _, gcHeapUnusedChunks) \ macro(_, GCHeapUnused, gcHeapUnusedChunks) \
macro(_, _, gcHeapUnusedArenas) \ macro(_, GCHeapUnused, gcHeapUnusedArenas) \
macro(_, _, gcHeapChunkAdmin) \ macro(_, GCHeapAdmin, gcHeapChunkAdmin) \
macro(_, _, gcHeapGCThings) \ macro(_, Ignore, gcHeapGCThings)
explicit RuntimeStats(mozilla::MallocSizeOf mallocSizeOf) explicit RuntimeStats(mozilla::MallocSizeOf mallocSizeOf)
: FOR_EACH_SIZE(ZERO_SIZE) : FOR_EACH_SIZE(ZERO_SIZE)
@ -709,7 +805,7 @@ struct RuntimeStats
// - unused bytes // - unused bytes
// - rtStats.gcHeapUnusedChunks (empty chunks) // - rtStats.gcHeapUnusedChunks (empty chunks)
// - rtStats.gcHeapUnusedArenas (empty arenas within non-empty chunks) // - rtStats.gcHeapUnusedArenas (empty arenas within non-empty chunks)
// - rtStats.zTotals.unusedGCThings (empty GC thing slots within non-empty arenas) // - rtStats.zTotals.unusedGCThings.totalSize() (empty GC thing slots within non-empty arenas)
// - used bytes // - used bytes
// - rtStats.gcHeapChunkAdmin // - rtStats.gcHeapChunkAdmin
// - rtStats.zTotals.gcHeapArenaAdmin // - rtStats.zTotals.gcHeapArenaAdmin
@ -721,6 +817,11 @@ struct RuntimeStats
// it's rare, and (b) this means that rtStats.gcHeapUnusedChunks is a // it's rare, and (b) this means that rtStats.gcHeapUnusedChunks is a
// multiple of the chunk size, which is good. // multiple of the chunk size, which is good.
void addToServoSizes(ServoSizes *sizes) const {
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
runtime.addToServoSizes(sizes);
}
FOR_EACH_SIZE(DECL_SIZE) FOR_EACH_SIZE(DECL_SIZE)
RuntimeSizes runtime; RuntimeSizes runtime;
@ -774,6 +875,10 @@ extern JS_PUBLIC_API(bool)
AddSizeOfTab(JSRuntime* rt, JS::HandleObject obj, mozilla::MallocSizeOf mallocSizeOf, AddSizeOfTab(JSRuntime* rt, JS::HandleObject obj, mozilla::MallocSizeOf mallocSizeOf,
ObjectPrivateVisitor* opv, TabSizes* sizes); ObjectPrivateVisitor* opv, TabSizes* sizes);
extern JS_PUBLIC_API(bool)
AddServoSizeOf(JSRuntime *rt, mozilla::MallocSizeOf mallocSizeOf,
ObjectPrivateVisitor *opv, ServoSizes *sizes);
} // namespace JS } // namespace JS
#undef DECL_SIZE #undef DECL_SIZE

View File

@ -693,9 +693,9 @@ FindNotableScriptSources(JS::RuntimeSizes& runtime)
return true; return true;
} }
JS_PUBLIC_API(bool) static bool
JS::CollectRuntimeStats(JSRuntime* rt, RuntimeStats* rtStats, ObjectPrivateVisitor* opv, CollectRuntimeStatsHelper(JSRuntime* rt, RuntimeStats* rtStats, ObjectPrivateVisitor* opv,
bool anonymize) bool anonymize, IterateCellCallback statsCellCallback)
{ {
if (!rtStats->compartmentStatsVector.reserve(rt->numCompartments)) if (!rtStats->compartmentStatsVector.reserve(rt->numCompartments))
return false; return false;
@ -720,7 +720,7 @@ JS::CollectRuntimeStats(JSRuntime* rt, RuntimeStats* rtStats, ObjectPrivateVisit
StatsZoneCallback, StatsZoneCallback,
StatsCompartmentCallback, StatsCompartmentCallback,
StatsArenaCallback, StatsArenaCallback,
StatsCellCallback<FineGrained>); statsCellCallback);
// Take the "explicit/js/runtime/" measurements. // Take the "explicit/js/runtime/" measurements.
rt->addSizeOfIncludingThis(rtStats->mallocSizeOf_, &rtStats->runtime); rt->addSizeOfIncludingThis(rtStats->mallocSizeOf_, &rtStats->runtime);
@ -728,7 +728,7 @@ JS::CollectRuntimeStats(JSRuntime* rt, RuntimeStats* rtStats, ObjectPrivateVisit
if (!FindNotableScriptSources(rtStats->runtime)) if (!FindNotableScriptSources(rtStats->runtime))
return false; return false;
ZoneStatsVector& zs = rtStats->zoneStatsVector; JS::ZoneStatsVector& zs = rtStats->zoneStatsVector;
ZoneStats& zTotals = rtStats->zTotals; ZoneStats& zTotals = rtStats->zTotals;
// We don't look for notable strings for zTotals. So we first sum all the // We don't look for notable strings for zTotals. So we first sum all the
@ -743,7 +743,7 @@ JS::CollectRuntimeStats(JSRuntime* rt, RuntimeStats* rtStats, ObjectPrivateVisit
MOZ_ASSERT(!zTotals.allStrings); MOZ_ASSERT(!zTotals.allStrings);
CompartmentStatsVector& cs = rtStats->compartmentStatsVector; JS::CompartmentStatsVector& cs = rtStats->compartmentStatsVector;
CompartmentStats& cTotals = rtStats->cTotals; CompartmentStats& cTotals = rtStats->cTotals;
// As with the zones, we sum all compartments first, and then get the // As with the zones, we sum all compartments first, and then get the
@ -790,6 +790,13 @@ JS::CollectRuntimeStats(JSRuntime* rt, RuntimeStats* rtStats, ObjectPrivateVisit
return true; return true;
} }
JS_PUBLIC_API(bool)
JS::CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisitor *opv,
bool anonymize)
{
return CollectRuntimeStatsHelper(rt, rtStats, opv, anonymize, StatsCellCallback<FineGrained>);
}
JS_PUBLIC_API(size_t) JS_PUBLIC_API(size_t)
JS::SystemCompartmentCount(JSRuntime* rt) JS::SystemCompartmentCount(JSRuntime* rt)
{ {
@ -820,26 +827,26 @@ JS::PeakSizeOfTemporary(const JSRuntime* rt)
namespace JS { namespace JS {
class SimpleJSRuntimeStats : public JS::RuntimeStats
{
public:
explicit SimpleJSRuntimeStats(MallocSizeOf mallocSizeOf)
: JS::RuntimeStats(mallocSizeOf)
{}
virtual void initExtraZoneStats(JS::Zone* zone, JS::ZoneStats* zStats)
override
{}
virtual void initExtraCompartmentStats(
JSCompartment* c, JS::CompartmentStats* cStats) override
{}
};
JS_PUBLIC_API(bool) JS_PUBLIC_API(bool)
AddSizeOfTab(JSRuntime* rt, HandleObject obj, MallocSizeOf mallocSizeOf, ObjectPrivateVisitor* opv, AddSizeOfTab(JSRuntime* rt, HandleObject obj, MallocSizeOf mallocSizeOf, ObjectPrivateVisitor* opv,
TabSizes* sizes) TabSizes* sizes)
{ {
class SimpleJSRuntimeStats : public JS::RuntimeStats
{
public:
explicit SimpleJSRuntimeStats(MallocSizeOf mallocSizeOf)
: JS::RuntimeStats(mallocSizeOf)
{}
virtual void initExtraZoneStats(JS::Zone* zone, JS::ZoneStats* zStats)
override
{}
virtual void initExtraCompartmentStats(
JSCompartment* c, JS::CompartmentStats* cStats) override
{}
};
SimpleJSRuntimeStats rtStats(mallocSizeOf); SimpleJSRuntimeStats rtStats(mallocSizeOf);
JS::Zone* zone = GetObjectZone(obj); JS::Zone* zone = GetObjectZone(obj);
@ -855,8 +862,10 @@ AddSizeOfTab(JSRuntime* rt, HandleObject obj, MallocSizeOf mallocSizeOf, ObjectP
StatsClosure closure(&rtStats, opv, /* anonymize = */ false); StatsClosure closure(&rtStats, opv, /* anonymize = */ false);
if (!closure.init()) if (!closure.init())
return false; return false;
IterateZoneCompartmentsArenasCells(rt, zone, &closure, StatsZoneCallback, IterateZoneCompartmentsArenasCells(rt, zone, &closure,
StatsCompartmentCallback, StatsArenaCallback, StatsZoneCallback,
StatsCompartmentCallback,
StatsArenaCallback,
StatsCellCallback<CoarseGrained>); StatsCellCallback<CoarseGrained>);
MOZ_ASSERT(rtStats.zoneStatsVector.length() == 1); MOZ_ASSERT(rtStats.zoneStatsVector.length() == 1);
@ -874,5 +883,38 @@ AddSizeOfTab(JSRuntime* rt, HandleObject obj, MallocSizeOf mallocSizeOf, ObjectP
return true; return true;
} }
JS_PUBLIC_API(bool)
AddServoSizeOf(JSRuntime *rt, MallocSizeOf mallocSizeOf, ObjectPrivateVisitor *opv,
ServoSizes *sizes)
{
SimpleJSRuntimeStats rtStats(mallocSizeOf);
// No need to anonymize because the results will be aggregated.
if (!CollectRuntimeStatsHelper(rt, &rtStats, opv, /* anonymize = */ false,
StatsCellCallback<CoarseGrained>))
return false;
#ifdef DEBUG
size_t gcHeapTotalOriginal = sizes->gcHeapUsed +
sizes->gcHeapUnused +
sizes->gcHeapAdmin +
sizes->gcHeapDecommitted;
#endif
rtStats.addToServoSizes(sizes);
rtStats.zTotals.addToServoSizes(sizes);
rtStats.cTotals.addToServoSizes(sizes);
#ifdef DEBUG
size_t gcHeapTotal = sizes->gcHeapUsed +
sizes->gcHeapUnused +
sizes->gcHeapAdmin +
sizes->gcHeapDecommitted;
MOZ_ASSERT(rtStats.gcHeapChunkTotal == gcHeapTotal - gcHeapTotalOriginal);
#endif
return true;
}
} // namespace JS } // namespace JS