/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef js_MemoryMetrics_h #define js_MemoryMetrics_h // These declarations are highly likely to change in the future. Depend on them // at your own risk. #include "mozilla/Maybe.h" #include "mozilla/MemoryReporting.h" #include #include #include "jstypes.h" #include "js/AllocPolicy.h" #include "js/HashTable.h" #include "js/TraceKind.h" #include "js/TypeDecls.h" #include "js/Utility.h" #include "js/Vector.h" class nsISupports; // Needed for ObjectPrivateVisitor. namespace js { class SystemAllocPolicy; } namespace mozilla { struct CStringHasher; } namespace JS { class JS_PUBLIC_API AutoRequireNoGC; struct TabSizes { TabSizes() = default; enum Kind { Objects, Strings, Private, Other }; void add(Kind kind, size_t n) { switch (kind) { case Objects: objects_ += n; break; case Strings: strings_ += n; break; case Private: private_ += n; break; case Other: other_ += n; break; default: MOZ_CRASH("bad TabSizes kind"); } } size_t objects_ = 0; size_t strings_ = 0; size_t private_ = 0; size_t other_ = 0; }; /** These are the measurements used by Servo. */ struct ServoSizes { ServoSizes() = default; enum Kind { GCHeapUsed, GCHeapUnused, GCHeapAdmin, GCHeapDecommitted, MallocHeap, NonHeap, Ignore }; 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 = 0; size_t gcHeapUnused = 0; size_t gcHeapAdmin = 0; size_t gcHeapDecommitted = 0; size_t mallocHeap = 0; size_t nonHeap = 0; }; } // namespace JS namespace js { /** * In memory reporting, we have concept of "sundries", line items which are too * small to be worth reporting individually. Under some circumstances, a memory * reporter gets tossed into the sundries bucket if it's smaller than * MemoryReportingSundriesThreshold() bytes. * * We need to define this value here, rather than in the code which actually * generates the memory reports, because NotableStringInfo uses this value. */ JS_PUBLIC_API size_t MemoryReportingSundriesThreshold(); /** * This hash policy avoids flattening ropes (which perturbs the site being * measured and requires a JSContext) at the expense of doing a FULL ROPE COPY * on every hash and match! Beware. */ struct InefficientNonFlatteningStringHashPolicy { typedef JSString* Lookup; static HashNumber hash(const Lookup& l); static bool match(const JSString* const& k, const Lookup& l); }; // This file features many classes with numerous size_t fields, and each such // class has one or more methods that need to operate on all of these fields. // Writing these individually is error-prone -- it's easy to add a new field // without updating all the required methods. So we define a single macro list // in each class to name the fields (and notable characteristics of them), and // then use the following macros to transform those lists into the required // 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 '_' // for those. // #define DECL_SIZE_ZERO(tabKind, servoKind, mSize) size_t mSize = 0; #define ADD_OTHER_SIZE(tabKind, servoKind, mSize) mSize += other.mSize; #define SUB_OTHER_SIZE(tabKind, servoKind, mSize) \ MOZ_ASSERT(mSize >= other.mSize); \ mSize -= other.mSize; #define ADD_SIZE_TO_N(tabKind, servoKind, mSize) n += mSize; #define ADD_SIZE_TO_N_IF_LIVE_GC_THING(tabKind, servoKind, mSize) \ /* Avoid self-comparison warnings by comparing enums indirectly. */ \ n += (std::is_same_v) \ ? mSize \ : 0; #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); } // namespace js namespace JS { struct ClassInfo { #define FOR_EACH_SIZE(MACRO) \ MACRO(Objects, GCHeapUsed, objectsGCHeap) \ MACRO(Objects, MallocHeap, objectsMallocHeapSlots) \ MACRO(Objects, MallocHeap, objectsMallocHeapElementsNormal) \ MACRO(Objects, MallocHeap, objectsMallocHeapElementsAsmJS) \ MACRO(Objects, MallocHeap, objectsMallocHeapGlobalData) \ MACRO(Objects, MallocHeap, objectsMallocHeapGlobalVarNamesSet) \ MACRO(Objects, MallocHeap, objectsMallocHeapMisc) \ MACRO(Objects, NonHeap, objectsNonHeapElementsNormal) \ MACRO(Objects, NonHeap, objectsNonHeapElementsShared) \ MACRO(Objects, NonHeap, objectsNonHeapElementsWasm) \ MACRO(Objects, NonHeap, objectsNonHeapElementsWasmShared) \ MACRO(Objects, NonHeap, objectsNonHeapCodeWasm) ClassInfo() = default; void add(const ClassInfo& other) { FOR_EACH_SIZE(ADD_OTHER_SIZE); } void subtract(const ClassInfo& other) { FOR_EACH_SIZE(SUB_OTHER_SIZE); } size_t sizeOfAllThings() const { size_t n = 0; FOR_EACH_SIZE(ADD_SIZE_TO_N); return n; } bool isNotable() const { static const size_t NotabilityThreshold = 16 * 1024; return sizeOfAllThings() >= NotabilityThreshold; } size_t sizeOfLiveGCThings() const { size_t n = 0; FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING); return n; } void addToTabSizes(TabSizes* sizes) const { FOR_EACH_SIZE(ADD_TO_TAB_SIZES); } void addToServoSizes(ServoSizes* sizes) const { FOR_EACH_SIZE(ADD_TO_SERVO_SIZES); } FOR_EACH_SIZE(DECL_SIZE_ZERO); #undef FOR_EACH_SIZE }; struct ShapeInfo { #define FOR_EACH_SIZE(MACRO) \ MACRO(Other, GCHeapUsed, shapesGCHeapShared) \ MACRO(Other, GCHeapUsed, shapesGCHeapDict) \ MACRO(Other, GCHeapUsed, shapesGCHeapBase) \ MACRO(Other, MallocHeap, shapesMallocHeapCache) ShapeInfo() = default; void add(const ShapeInfo& other) { FOR_EACH_SIZE(ADD_OTHER_SIZE); } void subtract(const ShapeInfo& other) { FOR_EACH_SIZE(SUB_OTHER_SIZE); } size_t sizeOfAllThings() const { size_t n = 0; FOR_EACH_SIZE(ADD_SIZE_TO_N); return n; } size_t sizeOfLiveGCThings() const { size_t n = 0; FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING); return n; } void addToTabSizes(TabSizes* sizes) const { FOR_EACH_SIZE(ADD_TO_TAB_SIZES); } void addToServoSizes(ServoSizes* sizes) const { FOR_EACH_SIZE(ADD_TO_SERVO_SIZES); } FOR_EACH_SIZE(DECL_SIZE_ZERO); #undef FOR_EACH_SIZE }; /** * Holds data about a notable class (one whose combined object and shape * instances use more than a certain amount of memory) so we can report it * individually. * * The only difference between this class and ClassInfo is that this class * holds a copy of the filename. */ struct NotableClassInfo : public ClassInfo { NotableClassInfo() = default; NotableClassInfo(NotableClassInfo&&) = default; NotableClassInfo(const NotableClassInfo& info) = delete; NotableClassInfo(const char* className, const ClassInfo& info); UniqueChars className_ = nullptr; }; /** Data for tracking JIT-code memory usage. */ struct CodeSizes { #define FOR_EACH_SIZE(MACRO) \ MACRO(_, NonHeap, ion) \ MACRO(_, NonHeap, baseline) \ MACRO(_, NonHeap, regexp) \ MACRO(_, NonHeap, other) \ MACRO(_, NonHeap, unused) CodeSizes() = default; void addToServoSizes(ServoSizes* sizes) const { FOR_EACH_SIZE(ADD_TO_SERVO_SIZES); } FOR_EACH_SIZE(DECL_SIZE_ZERO); #undef FOR_EACH_SIZE }; /** Data for tracking GC memory usage. */ 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) \ MACRO(_, MallocHeap, marker) \ MACRO(_, NonHeap, nurseryCommitted) \ MACRO(_, MallocHeap, nurseryMallocedBuffers) \ MACRO(_, MallocHeap, nurseryMallocedBlockCache) \ MACRO(_, MallocHeap, nurseryTrailerBlockSets) \ MACRO(_, MallocHeap, storeBufferVals) \ MACRO(_, MallocHeap, storeBufferCells) \ MACRO(_, MallocHeap, storeBufferSlots) \ MACRO(_, MallocHeap, storeBufferWholeCells) \ MACRO(_, MallocHeap, storeBufferGenerics) GCSizes() = default; void addToServoSizes(ServoSizes* sizes) const { FOR_EACH_SIZE(ADD_TO_SERVO_SIZES); } FOR_EACH_SIZE(DECL_SIZE_ZERO); #undef FOR_EACH_SIZE }; /** * This class holds information about the memory taken up by identical copies of * a particular string. Multiple JSStrings may have their sizes aggregated * together into one StringInfo object. Note that two strings with identical * chars will not be aggregated together if one is a short string and the other * is not. */ struct StringInfo { #define FOR_EACH_SIZE(MACRO) \ MACRO(Strings, GCHeapUsed, gcHeapLatin1) \ MACRO(Strings, GCHeapUsed, gcHeapTwoByte) \ MACRO(Strings, MallocHeap, mallocHeapLatin1) \ MACRO(Strings, MallocHeap, mallocHeapTwoByte) StringInfo() = default; void add(const StringInfo& other) { FOR_EACH_SIZE(ADD_OTHER_SIZE); numCopies++; } void subtract(const StringInfo& other) { FOR_EACH_SIZE(SUB_OTHER_SIZE); numCopies--; } bool isNotable() const { static const size_t NotabilityThreshold = 16 * 1024; size_t n = 0; FOR_EACH_SIZE(ADD_SIZE_TO_N); return n >= NotabilityThreshold; } size_t sizeOfLiveGCThings() const { size_t n = 0; FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING); return n; } void addToTabSizes(TabSizes* sizes) const { FOR_EACH_SIZE(ADD_TO_TAB_SIZES); } void addToServoSizes(ServoSizes* sizes) const { FOR_EACH_SIZE(ADD_TO_SERVO_SIZES); } FOR_EACH_SIZE(DECL_SIZE_ZERO); uint32_t numCopies = 0; // How many copies of the string have we seen? #undef FOR_EACH_SIZE }; /** * Holds data about a notable string (one which, counting all duplicates, uses * more than a certain amount of memory) so we can report it individually. * * The only difference between this class and StringInfo is that * NotableStringInfo holds a copy of some or all of the string's chars. */ struct NotableStringInfo : public StringInfo { static const size_t MAX_SAVED_CHARS = 1024; NotableStringInfo() = default; NotableStringInfo(NotableStringInfo&&) = default; NotableStringInfo(const NotableStringInfo&) = delete; NotableStringInfo(JSString* str, const StringInfo& info); UniqueChars buffer = nullptr; size_t length = 0; }; /** * This class holds information about the memory taken up by script sources * from a particular file. */ struct ScriptSourceInfo { #define FOR_EACH_SIZE(MACRO) MACRO(_, MallocHeap, misc) ScriptSourceInfo() = default; void add(const ScriptSourceInfo& other) { FOR_EACH_SIZE(ADD_OTHER_SIZE); numScripts++; } void subtract(const ScriptSourceInfo& other) { FOR_EACH_SIZE(SUB_OTHER_SIZE); numScripts--; } void addToServoSizes(ServoSizes* sizes) const { FOR_EACH_SIZE(ADD_TO_SERVO_SIZES); } bool isNotable() const { static const size_t NotabilityThreshold = 16 * 1024; size_t n = 0; FOR_EACH_SIZE(ADD_SIZE_TO_N); return n >= NotabilityThreshold; } FOR_EACH_SIZE(DECL_SIZE_ZERO); uint32_t numScripts = 0; // How many ScriptSources come from this file? (It // can be more than one in XML files that have // multiple scripts in CDATA sections.) #undef FOR_EACH_SIZE }; /** * Holds data about a notable script source file (one whose combined * script sources use more than a certain amount of memory) so we can report it * individually. * * The only difference between this class and ScriptSourceInfo is that this * class holds a copy of the filename. */ struct NotableScriptSourceInfo : public ScriptSourceInfo { NotableScriptSourceInfo() = default; NotableScriptSourceInfo(NotableScriptSourceInfo&&) = default; NotableScriptSourceInfo(const NotableScriptSourceInfo&) = delete; NotableScriptSourceInfo(const char* filename, const ScriptSourceInfo& info); UniqueChars filename_ = nullptr; }; struct HelperThreadStats { #define FOR_EACH_SIZE(MACRO) \ MACRO(_, MallocHeap, stateData) \ MACRO(_, MallocHeap, parseTask) \ MACRO(_, MallocHeap, ionCompileTask) \ MACRO(_, MallocHeap, wasmCompile) \ MACRO(_, MallocHeap, contexts) HelperThreadStats() = default; FOR_EACH_SIZE(DECL_SIZE_ZERO); unsigned idleThreadCount = 0; unsigned activeThreadCount = 0; #undef FOR_EACH_SIZE }; /** * Measurements that not associated with any individual runtime. */ struct GlobalStats { explicit GlobalStats(mozilla::MallocSizeOf mallocSizeOf) : mallocSizeOf_(mallocSizeOf) {} HelperThreadStats helperThread; mozilla::MallocSizeOf mallocSizeOf_; }; /** * These measurements relate directly to the JSRuntime, and not to zones, * compartments, and realms within it. */ struct RuntimeSizes { #define FOR_EACH_SIZE(MACRO) \ MACRO(_, MallocHeap, object) \ MACRO(_, MallocHeap, atomsTable) \ MACRO(_, MallocHeap, atomsMarkBitmaps) \ MACRO(_, MallocHeap, selfHostStencil) \ MACRO(_, MallocHeap, contexts) \ MACRO(_, MallocHeap, temporary) \ MACRO(_, MallocHeap, interpreterStack) \ MACRO(_, MallocHeap, sharedImmutableStringsCache) \ MACRO(_, MallocHeap, sharedIntlData) \ MACRO(_, MallocHeap, uncompressedSourceCache) \ MACRO(_, MallocHeap, scriptData) \ MACRO(_, MallocHeap, wasmRuntime) \ MACRO(_, Ignore, wasmGuardPages) \ MACRO(_, MallocHeap, jitLazyLink) RuntimeSizes() { allScriptSources.emplace(); } void addToServoSizes(ServoSizes* sizes) const { FOR_EACH_SIZE(ADD_TO_SERVO_SIZES); scriptSourceInfo.addToServoSizes(sizes); gc.addToServoSizes(sizes); } FOR_EACH_SIZE(DECL_SIZE_ZERO); // The script source measurements in |scriptSourceInfo| are initially for // all script sources. At the end, if the measurement granularity is // FineGrained, we subtract the measurements of the notable script sources // and move them into |notableScriptSources|. ScriptSourceInfo scriptSourceInfo; GCSizes gc; typedef js::HashMap ScriptSourcesHashMap; // |allScriptSources| is only used transiently. During the reporting phase // it is filled with info about every script source in the runtime. It's // then used to fill in |notableScriptSources| (which actually gets // reported), and immediately discarded afterwards. mozilla::Maybe allScriptSources; js::Vector notableScriptSources; #undef FOR_EACH_SIZE }; struct UnusedGCThingSizes { #define FOR_EACH_SIZE(MACRO) \ MACRO(Other, GCHeapUnused, object) \ MACRO(Other, GCHeapUnused, script) \ MACRO(Other, GCHeapUnused, shape) \ MACRO(Other, GCHeapUnused, baseShape) \ MACRO(Other, GCHeapUnused, getterSetter) \ MACRO(Other, GCHeapUnused, propMap) \ MACRO(Other, GCHeapUnused, string) \ MACRO(Other, GCHeapUnused, symbol) \ MACRO(Other, GCHeapUnused, bigInt) \ MACRO(Other, GCHeapUnused, jitcode) \ MACRO(Other, GCHeapUnused, scope) \ MACRO(Other, GCHeapUnused, regExpShared) UnusedGCThingSizes() = default; UnusedGCThingSizes(UnusedGCThingSizes&& other) = default; void addToKind(JS::TraceKind kind, intptr_t n) { switch (kind) { case JS::TraceKind::Object: object += n; break; case JS::TraceKind::String: string += n; break; case JS::TraceKind::Symbol: symbol += n; break; case JS::TraceKind::BigInt: bigInt += n; break; case JS::TraceKind::Script: script += n; break; case JS::TraceKind::Shape: shape += n; break; case JS::TraceKind::BaseShape: baseShape += n; break; case JS::TraceKind::GetterSetter: getterSetter += n; break; case JS::TraceKind::PropMap: propMap += n; break; case JS::TraceKind::JitCode: jitcode += n; break; case JS::TraceKind::Scope: scope += n; break; case JS::TraceKind::RegExpShared: regExpShared += n; break; default: MOZ_CRASH("Bad trace kind for UnusedGCThingSizes"); } } void addSizes(const UnusedGCThingSizes& other) { FOR_EACH_SIZE(ADD_OTHER_SIZE); } size_t totalSize() const { size_t n = 0; FOR_EACH_SIZE(ADD_SIZE_TO_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_ZERO); #undef FOR_EACH_SIZE }; struct ZoneStats { #define FOR_EACH_SIZE(MACRO) \ MACRO(Other, GCHeapUsed, symbolsGCHeap) \ MACRO(Other, GCHeapUsed, bigIntsGCHeap) \ MACRO(Other, MallocHeap, bigIntsMallocHeap) \ MACRO(Other, GCHeapAdmin, gcHeapArenaAdmin) \ MACRO(Other, GCHeapUsed, jitCodesGCHeap) \ MACRO(Other, GCHeapUsed, getterSettersGCHeap) \ MACRO(Other, GCHeapUsed, compactPropMapsGCHeap) \ MACRO(Other, GCHeapUsed, normalPropMapsGCHeap) \ MACRO(Other, GCHeapUsed, dictPropMapsGCHeap) \ MACRO(Other, MallocHeap, propMapChildren) \ MACRO(Other, MallocHeap, propMapTables) \ MACRO(Other, GCHeapUsed, scopesGCHeap) \ MACRO(Other, MallocHeap, scopesMallocHeap) \ MACRO(Other, GCHeapUsed, regExpSharedsGCHeap) \ MACRO(Other, MallocHeap, regExpSharedsMallocHeap) \ MACRO(Other, MallocHeap, regexpZone) \ MACRO(Other, MallocHeap, jitZone) \ MACRO(Other, MallocHeap, baselineStubsOptimized) \ MACRO(Other, MallocHeap, uniqueIdMap) \ MACRO(Other, MallocHeap, initialPropMapTable) \ MACRO(Other, MallocHeap, shapeTables) \ MACRO(Other, MallocHeap, compartmentObjects) \ MACRO(Other, MallocHeap, crossCompartmentWrappersTables) \ MACRO(Other, MallocHeap, compartmentsPrivateData) \ MACRO(Other, MallocHeap, scriptCountsMap) ZoneStats() = default; ZoneStats(ZoneStats&& other) = default; void initStrings(); void addSizes(const ZoneStats& other) { MOZ_ASSERT(isTotals); FOR_EACH_SIZE(ADD_OTHER_SIZE); unusedGCThings.addSizes(other.unusedGCThings); stringInfo.add(other.stringInfo); shapeInfo.add(other.shapeInfo); } size_t sizeOfLiveGCThings() const { MOZ_ASSERT(isTotals); size_t n = 0; FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING); n += stringInfo.sizeOfLiveGCThings(); n += shapeInfo.sizeOfLiveGCThings(); return n; } void addToTabSizes(JS::TabSizes* sizes) const { MOZ_ASSERT(isTotals); FOR_EACH_SIZE(ADD_TO_TAB_SIZES); unusedGCThings.addToTabSizes(sizes); stringInfo.addToTabSizes(sizes); shapeInfo.addToTabSizes(sizes); } void addToServoSizes(JS::ServoSizes* sizes) const { MOZ_ASSERT(isTotals); FOR_EACH_SIZE(ADD_TO_SERVO_SIZES); unusedGCThings.addToServoSizes(sizes); stringInfo.addToServoSizes(sizes); shapeInfo.addToServoSizes(sizes); code.addToServoSizes(sizes); } FOR_EACH_SIZE(DECL_SIZE_ZERO); // These string measurements are initially for all strings. At the end, // if the measurement granularity is FineGrained, we subtract the // measurements of the notable script sources and move them into // |notableStrings|. UnusedGCThingSizes unusedGCThings; StringInfo stringInfo; ShapeInfo shapeInfo; CodeSizes code; void* extra = nullptr; // This field can be used by embedders. typedef js::HashMap StringsHashMap; // |allStrings| is only used transiently. During the zone traversal it is // filled with info about every string in the zone. It's then used to fill // in |notableStrings| (which actually gets reported), and immediately // discarded afterwards. mozilla::Maybe allStrings; js::Vector notableStrings; bool isTotals = true; #undef FOR_EACH_SIZE }; struct RealmStats { // 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) \ MACRO(Private, MallocHeap, objectsPrivate) \ MACRO(Other, GCHeapUsed, scriptsGCHeap) \ MACRO(Other, MallocHeap, scriptsMallocHeapData) \ MACRO(Other, MallocHeap, baselineData) \ MACRO(Other, MallocHeap, baselineStubsFallback) \ MACRO(Other, MallocHeap, ionData) \ MACRO(Other, MallocHeap, jitScripts) \ MACRO(Other, MallocHeap, realmObject) \ MACRO(Other, MallocHeap, realmTables) \ MACRO(Other, MallocHeap, innerViewsTable) \ MACRO(Other, MallocHeap, objectMetadataTable) \ MACRO(Other, MallocHeap, savedStacksSet) \ MACRO(Other, MallocHeap, nonSyntacticLexicalScopesTable) \ MACRO(Other, MallocHeap, jitRealm) RealmStats() = default; RealmStats(RealmStats&& other) = default; RealmStats(const RealmStats&) = delete; // disallow copying void initClasses(); void addSizes(const RealmStats& other) { MOZ_ASSERT(isTotals); FOR_EACH_SIZE(ADD_OTHER_SIZE); classInfo.add(other.classInfo); } size_t sizeOfLiveGCThings() const { MOZ_ASSERT(isTotals); size_t n = 0; FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING); n += classInfo.sizeOfLiveGCThings(); return n; } void addToTabSizes(TabSizes* sizes) const { MOZ_ASSERT(isTotals); FOR_EACH_SIZE(ADD_TO_TAB_SIZES); classInfo.addToTabSizes(sizes); } void addToServoSizes(ServoSizes* sizes) const { MOZ_ASSERT(isTotals); FOR_EACH_SIZE(ADD_TO_SERVO_SIZES); classInfo.addToServoSizes(sizes); } FOR_EACH_SIZE(DECL_SIZE_ZERO); // The class measurements in |classInfo| are initially for all classes. At // the end, if the measurement granularity is FineGrained, we subtract the // measurements of the notable classes and move them into |notableClasses|. ClassInfo classInfo; void* extra = nullptr; // This field can be used by embedders. typedef js::HashMap ClassesHashMap; // These are similar to |allStrings| and |notableStrings| in ZoneStats. mozilla::Maybe allClasses; js::Vector notableClasses; bool isTotals = true; #undef FOR_EACH_SIZE }; typedef js::Vector RealmStatsVector; typedef js::Vector ZoneStatsVector; 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) \ MACRO(_, Ignore, gcHeapChunkTotal) \ MACRO(_, GCHeapDecommitted, gcHeapDecommittedPages) \ MACRO(_, GCHeapUnused, gcHeapUnusedChunks) \ MACRO(_, GCHeapUnused, gcHeapUnusedArenas) \ MACRO(_, GCHeapAdmin, gcHeapChunkAdmin) \ MACRO(_, Ignore, gcHeapGCThings) explicit RuntimeStats(mozilla::MallocSizeOf mallocSizeOf) : mallocSizeOf_(mallocSizeOf) {} // Here's a useful breakdown of the GC heap. // // - rtStats.gcHeapChunkTotal // - decommitted bytes // - rtStats.gcHeapDecommittedPages // (decommitted pages in non-empty chunks) // - unused bytes // - rtStats.gcHeapUnusedChunks (empty chunks) // - rtStats.gcHeapUnusedArenas (empty arenas within non-empty chunks) // - rtStats.zTotals.unusedGCThings.totalSize() // (empty GC thing slots within non-empty arenas) // - used bytes // - rtStats.gcHeapChunkAdmin // - rtStats.zTotals.gcHeapArenaAdmin // - rtStats.gcHeapGCThings (in-use GC things) // == (rtStats.zTotals.sizeOfLiveGCThings() + // rtStats.cTotals.sizeOfLiveGCThings()) // // It's possible that some pages in empty chunks may be decommitted, but // we don't count those under rtStats.gcHeapDecommittedPages because (a) // it's rare, and (b) this means that rtStats.gcHeapUnusedChunks is a // 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_ZERO); RuntimeSizes runtime; RealmStats realmTotals; // The sum of this runtime's realms' measurements. ZoneStats zTotals; // The sum of this runtime's zones' measurements. RealmStatsVector realmStatsVector; ZoneStatsVector zoneStatsVector; ZoneStats* currZoneStats = nullptr; mozilla::MallocSizeOf mallocSizeOf_; virtual void initExtraRealmStats(JS::Realm* realm, RealmStats* rstats, const JS::AutoRequireNoGC& nogc) = 0; virtual void initExtraZoneStats(JS::Zone* zone, ZoneStats* zstats, const JS::AutoRequireNoGC& nogc) = 0; #undef FOR_EACH_SIZE }; class ObjectPrivateVisitor { public: // Within CollectRuntimeStats, this method is called for each JS object // that has an nsISupports pointer. virtual size_t sizeOfIncludingThis(nsISupports* aSupports) = 0; // A callback that gets a JSObject's nsISupports pointer, if it has one. // Note: this function does *not* addref |iface|. typedef bool (*GetISupportsFun)(JSObject* obj, nsISupports** iface); GetISupportsFun getISupports_; explicit ObjectPrivateVisitor(GetISupportsFun getISupports) : getISupports_(getISupports) {} }; extern JS_PUBLIC_API bool CollectGlobalStats(GlobalStats* gStats); extern JS_PUBLIC_API bool CollectRuntimeStats(JSContext* cx, RuntimeStats* rtStats, ObjectPrivateVisitor* opv, bool anonymize); extern JS_PUBLIC_API size_t SystemCompartmentCount(JSContext* cx); extern JS_PUBLIC_API size_t UserCompartmentCount(JSContext* cx); extern JS_PUBLIC_API size_t SystemRealmCount(JSContext* cx); extern JS_PUBLIC_API size_t UserRealmCount(JSContext* cx); extern JS_PUBLIC_API size_t PeakSizeOfTemporary(const JSContext* cx); extern JS_PUBLIC_API bool AddSizeOfTab(JSContext* cx, JS::HandleObject obj, mozilla::MallocSizeOf mallocSizeOf, ObjectPrivateVisitor* opv, TabSizes* sizes); extern JS_PUBLIC_API bool AddServoSizeOf(JSContext* cx, mozilla::MallocSizeOf mallocSizeOf, ObjectPrivateVisitor* opv, ServoSizes* sizes); } // namespace JS #undef DECL_SIZE_ZERO #undef ADD_OTHER_SIZE #undef SUB_OTHER_SIZE #undef ADD_SIZE_TO_N #undef ADD_SIZE_TO_N_IF_LIVE_GC_THING #undef ADD_TO_TAB_SIZES #endif /* js_MemoryMetrics_h */