michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef js_MemoryMetrics_h michael@0: #define js_MemoryMetrics_h michael@0: michael@0: // These declarations are highly likely to change in the future. Depend on them michael@0: // at your own risk. michael@0: michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/NullPtr.h" michael@0: #include "mozilla/PodOperations.h" michael@0: michael@0: #include michael@0: michael@0: #include "jsalloc.h" michael@0: #include "jspubtd.h" michael@0: michael@0: #include "js/HashTable.h" michael@0: #include "js/Utility.h" michael@0: #include "js/Vector.h" michael@0: michael@0: class nsISupports; // Needed for ObjectPrivateVisitor. michael@0: michael@0: namespace JS { michael@0: michael@0: struct TabSizes michael@0: { michael@0: enum Kind { michael@0: Objects, michael@0: Strings, michael@0: Private, michael@0: Other michael@0: }; michael@0: michael@0: TabSizes() { mozilla::PodZero(this); } michael@0: michael@0: void add(Kind kind, size_t n) { michael@0: switch (kind) { michael@0: case Objects: objects += n; break; michael@0: case Strings: strings += n; break; michael@0: case Private: private_ += n; break; michael@0: case Other: other += n; break; michael@0: default: MOZ_CRASH("bad TabSizes kind"); michael@0: } michael@0: } michael@0: michael@0: size_t objects; michael@0: size_t strings; michael@0: size_t private_; michael@0: size_t other; michael@0: }; michael@0: michael@0: } // namespace JS michael@0: michael@0: namespace js { michael@0: michael@0: // In memory reporting, we have concept of "sundries", line items which are too michael@0: // small to be worth reporting individually. Under some circumstances, a memory michael@0: // reporter gets tossed into the sundries bucket if it's smaller than michael@0: // MemoryReportingSundriesThreshold() bytes. michael@0: // michael@0: // We need to define this value here, rather than in the code which actually michael@0: // generates the memory reports, because NotableStringInfo uses this value. michael@0: JS_FRIEND_API(size_t) MemoryReportingSundriesThreshold(); michael@0: michael@0: // This hash policy avoids flattening ropes (which perturbs the site being michael@0: // measured and requires a JSContext) at the expense of doing a FULL ROPE COPY michael@0: // on every hash and match! Beware. michael@0: struct InefficientNonFlatteningStringHashPolicy michael@0: { michael@0: typedef JSString *Lookup; michael@0: static HashNumber hash(const Lookup &l); michael@0: static bool match(const JSString *const &k, const Lookup &l); michael@0: }; michael@0: michael@0: struct CStringHashPolicy michael@0: { michael@0: typedef const char *Lookup; michael@0: static HashNumber hash(const Lookup &l); michael@0: static bool match(const char *const &k, const Lookup &l); michael@0: }; michael@0: michael@0: // This file features many classes with numerous size_t fields, and each such michael@0: // class has one or more methods that need to operate on all of these fields. michael@0: // Writing these individually is error-prone -- it's easy to add a new field michael@0: // without updating all the required methods. So we define a single macro list michael@0: // in each class to name the fields (and notable characteristics of them), and michael@0: // then use the following macros to transform those lists into the required michael@0: // methods. michael@0: // michael@0: // In some classes, one or more of the macro arguments aren't used. We use '_' michael@0: // for those. michael@0: // michael@0: #define DECL_SIZE(kind, gc, mSize) size_t mSize; michael@0: #define ZERO_SIZE(kind, gc, mSize) mSize(0), michael@0: #define COPY_OTHER_SIZE(kind, gc, mSize) mSize(other.mSize), michael@0: #define ADD_OTHER_SIZE(kind, gc, mSize) mSize += other.mSize; michael@0: #define SUB_OTHER_SIZE(kind, gc, mSize) MOZ_ASSERT(mSize >= other.mSize); \ michael@0: mSize -= other.mSize; michael@0: #define ADD_SIZE_TO_N(kind, gc, mSize) n += mSize; michael@0: #define ADD_SIZE_TO_N_IF_LIVE_GC_THING(kind, gc, mSize) n += (js::gc) ? mSize : 0; michael@0: #define ADD_TO_TAB_SIZES(kind, gc, mSize) sizes->add(JS::TabSizes::kind, mSize); michael@0: michael@0: // Used to annotate which size_t fields measure live GC things and which don't. michael@0: enum { michael@0: NotLiveGCThing = false, michael@0: IsLiveGCThing = true michael@0: }; michael@0: michael@0: } // namespace js michael@0: michael@0: namespace JS { michael@0: michael@0: // Data for tracking memory usage of things hanging off objects. michael@0: struct ObjectsExtraSizes michael@0: { michael@0: #define FOR_EACH_SIZE(macro) \ michael@0: macro(Objects, NotLiveGCThing, mallocHeapSlots) \ michael@0: macro(Objects, NotLiveGCThing, mallocHeapElementsNonAsmJS) \ michael@0: macro(Objects, NotLiveGCThing, mallocHeapElementsAsmJS) \ michael@0: macro(Objects, NotLiveGCThing, nonHeapElementsAsmJS) \ michael@0: macro(Objects, NotLiveGCThing, nonHeapElementsMapped) \ michael@0: macro(Objects, NotLiveGCThing, nonHeapCodeAsmJS) \ michael@0: macro(Objects, NotLiveGCThing, mallocHeapAsmJSModuleData) \ michael@0: macro(Objects, NotLiveGCThing, mallocHeapArgumentsData) \ michael@0: macro(Objects, NotLiveGCThing, mallocHeapRegExpStatics) \ michael@0: macro(Objects, NotLiveGCThing, mallocHeapPropertyIteratorData) \ michael@0: macro(Objects, NotLiveGCThing, mallocHeapCtypesData) michael@0: michael@0: ObjectsExtraSizes() michael@0: : FOR_EACH_SIZE(ZERO_SIZE) michael@0: dummy() michael@0: {} michael@0: michael@0: void add(const ObjectsExtraSizes &other) { michael@0: FOR_EACH_SIZE(ADD_OTHER_SIZE) michael@0: } michael@0: michael@0: size_t sizeOfLiveGCThings() const { michael@0: size_t n = 0; michael@0: FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING) michael@0: return n; michael@0: } michael@0: michael@0: void addToTabSizes(TabSizes *sizes) const { michael@0: FOR_EACH_SIZE(ADD_TO_TAB_SIZES) michael@0: } michael@0: michael@0: FOR_EACH_SIZE(DECL_SIZE) michael@0: int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE) michael@0: michael@0: #undef FOR_EACH_SIZE michael@0: }; michael@0: michael@0: // Data for tracking JIT-code memory usage. michael@0: struct CodeSizes michael@0: { michael@0: #define FOR_EACH_SIZE(macro) \ michael@0: macro(_, _, ion) \ michael@0: macro(_, _, baseline) \ michael@0: macro(_, _, regexp) \ michael@0: macro(_, _, other) \ michael@0: macro(_, _, unused) michael@0: michael@0: CodeSizes() michael@0: : FOR_EACH_SIZE(ZERO_SIZE) michael@0: dummy() michael@0: {} michael@0: michael@0: FOR_EACH_SIZE(DECL_SIZE) michael@0: int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE) michael@0: michael@0: #undef FOR_EACH_SIZE michael@0: }; michael@0: michael@0: // Data for tracking GC memory usage. michael@0: struct GCSizes michael@0: { michael@0: #define FOR_EACH_SIZE(macro) \ michael@0: macro(_, _, marker) \ michael@0: macro(_, _, nurseryCommitted) \ michael@0: macro(_, _, nurseryDecommitted) \ michael@0: macro(_, _, nurseryHugeSlots) \ michael@0: macro(_, _, storeBufferVals) \ michael@0: macro(_, _, storeBufferCells) \ michael@0: macro(_, _, storeBufferSlots) \ michael@0: macro(_, _, storeBufferWholeCells) \ michael@0: macro(_, _, storeBufferRelocVals) \ michael@0: macro(_, _, storeBufferRelocCells) \ michael@0: macro(_, _, storeBufferGenerics) michael@0: michael@0: GCSizes() michael@0: : FOR_EACH_SIZE(ZERO_SIZE) michael@0: dummy() michael@0: {} michael@0: michael@0: FOR_EACH_SIZE(DECL_SIZE) michael@0: int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE) michael@0: michael@0: #undef FOR_EACH_SIZE michael@0: }; michael@0: michael@0: // This class holds information about the memory taken up by identical copies of michael@0: // a particular string. Multiple JSStrings may have their sizes aggregated michael@0: // together into one StringInfo object. Note that two strings with identical michael@0: // chars will not be aggregated together if one is a short string and the other michael@0: // is not. michael@0: struct StringInfo michael@0: { michael@0: #define FOR_EACH_SIZE(macro) \ michael@0: macro(Strings, IsLiveGCThing, gcHeap) \ michael@0: macro(Strings, NotLiveGCThing, mallocHeap) \ michael@0: michael@0: StringInfo() michael@0: : FOR_EACH_SIZE(ZERO_SIZE) michael@0: numCopies(0) michael@0: {} michael@0: michael@0: void add(const StringInfo &other) { michael@0: FOR_EACH_SIZE(ADD_OTHER_SIZE); michael@0: numCopies++; michael@0: } michael@0: michael@0: void subtract(const StringInfo &other) { michael@0: FOR_EACH_SIZE(SUB_OTHER_SIZE); michael@0: numCopies--; michael@0: } michael@0: michael@0: bool isNotable() const { michael@0: static const size_t NotabilityThreshold = 16 * 1024; michael@0: size_t n = 0; michael@0: FOR_EACH_SIZE(ADD_SIZE_TO_N) michael@0: return n >= NotabilityThreshold; michael@0: } michael@0: michael@0: size_t sizeOfLiveGCThings() const { michael@0: size_t n = 0; michael@0: FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING) michael@0: return n; michael@0: } michael@0: michael@0: void addToTabSizes(TabSizes *sizes) const { michael@0: FOR_EACH_SIZE(ADD_TO_TAB_SIZES) michael@0: } michael@0: michael@0: FOR_EACH_SIZE(DECL_SIZE) michael@0: uint32_t numCopies; // How many copies of the string have we seen? michael@0: michael@0: #undef FOR_EACH_SIZE michael@0: }; michael@0: michael@0: // Holds data about a notable string (one which, counting all duplicates, uses michael@0: // more than a certain amount of memory) so we can report it individually. michael@0: // michael@0: // The only difference between this class and StringInfo is that michael@0: // NotableStringInfo holds a copy of some or all of the string's chars. michael@0: struct NotableStringInfo : public StringInfo michael@0: { michael@0: static const size_t MAX_SAVED_CHARS = 1024; michael@0: michael@0: NotableStringInfo(); michael@0: NotableStringInfo(JSString *str, const StringInfo &info); michael@0: NotableStringInfo(NotableStringInfo &&info); michael@0: NotableStringInfo &operator=(NotableStringInfo &&info); michael@0: michael@0: ~NotableStringInfo() { michael@0: js_free(buffer); michael@0: } michael@0: michael@0: char *buffer; michael@0: size_t length; michael@0: michael@0: private: michael@0: NotableStringInfo(const NotableStringInfo& info) MOZ_DELETE; michael@0: }; michael@0: michael@0: // This class holds information about the memory taken up by script sources michael@0: // from a particular file. michael@0: struct ScriptSourceInfo michael@0: { michael@0: #define FOR_EACH_SIZE(macro) \ michael@0: macro(_, _, compressed) \ michael@0: macro(_, _, uncompressed) \ michael@0: macro(_, _, misc) michael@0: michael@0: ScriptSourceInfo() michael@0: : FOR_EACH_SIZE(ZERO_SIZE) michael@0: numScripts(0) michael@0: {} michael@0: michael@0: void add(const ScriptSourceInfo &other) { michael@0: FOR_EACH_SIZE(ADD_OTHER_SIZE) michael@0: numScripts++; michael@0: } michael@0: michael@0: void subtract(const ScriptSourceInfo &other) { michael@0: FOR_EACH_SIZE(SUB_OTHER_SIZE) michael@0: numScripts--; michael@0: } michael@0: michael@0: bool isNotable() const { michael@0: static const size_t NotabilityThreshold = 16 * 1024; michael@0: size_t n = 0; michael@0: FOR_EACH_SIZE(ADD_SIZE_TO_N) michael@0: return n >= NotabilityThreshold; michael@0: } michael@0: michael@0: FOR_EACH_SIZE(DECL_SIZE) michael@0: uint32_t numScripts; // How many ScriptSources come from this file? (It michael@0: // can be more than one in XML files that have michael@0: // multiple scripts in CDATA sections.) michael@0: #undef FOR_EACH_SIZE michael@0: }; michael@0: michael@0: // Holds data about a notable script source file (one whose combined michael@0: // script sources use more than a certain amount of memory) so we can report it michael@0: // individually. michael@0: // michael@0: // The only difference between this class and ScriptSourceInfo is that this michael@0: // class holds a copy of the filename. michael@0: struct NotableScriptSourceInfo : public ScriptSourceInfo michael@0: { michael@0: NotableScriptSourceInfo(); michael@0: NotableScriptSourceInfo(const char *filename, const ScriptSourceInfo &info); michael@0: NotableScriptSourceInfo(NotableScriptSourceInfo &&info); michael@0: NotableScriptSourceInfo &operator=(NotableScriptSourceInfo &&info); michael@0: michael@0: ~NotableScriptSourceInfo() { michael@0: js_free(filename_); michael@0: } michael@0: michael@0: char *filename_; michael@0: michael@0: private: michael@0: NotableScriptSourceInfo(const NotableScriptSourceInfo& info) MOZ_DELETE; michael@0: }; michael@0: michael@0: // These measurements relate directly to the JSRuntime, and not to zones and michael@0: // compartments within it. michael@0: struct RuntimeSizes michael@0: { michael@0: #define FOR_EACH_SIZE(macro) \ michael@0: macro(_, _, object) \ michael@0: macro(_, _, atomsTable) \ michael@0: macro(_, _, contexts) \ michael@0: macro(_, _, dtoa) \ michael@0: macro(_, _, temporary) \ michael@0: macro(_, _, regexpData) \ michael@0: macro(_, _, interpreterStack) \ michael@0: macro(_, _, mathCache) \ michael@0: macro(_, _, sourceDataCache) \ michael@0: macro(_, _, scriptData) \ michael@0: michael@0: RuntimeSizes() michael@0: : FOR_EACH_SIZE(ZERO_SIZE) michael@0: scriptSourceInfo(), michael@0: code(), michael@0: gc(), michael@0: notableScriptSources() michael@0: { michael@0: allScriptSources = js_new(); michael@0: if (!allScriptSources || !allScriptSources->init()) michael@0: MOZ_CRASH("oom"); michael@0: } michael@0: michael@0: ~RuntimeSizes() { michael@0: // |allScriptSources| is usually deleted and set to nullptr before this michael@0: // destructor runs. But there are failure cases due to OOMs that may michael@0: // prevent that, so it doesn't hurt to try again here. michael@0: js_delete(allScriptSources); michael@0: } michael@0: michael@0: // The script source measurements in |scriptSourceInfo| are initially for michael@0: // all script sources. At the end, if the measurement granularity is michael@0: // FineGrained, we subtract the measurements of the notable script sources michael@0: // and move them into |notableScriptSources|. michael@0: FOR_EACH_SIZE(DECL_SIZE) michael@0: ScriptSourceInfo scriptSourceInfo; michael@0: CodeSizes code; michael@0: GCSizes gc; michael@0: michael@0: typedef js::HashMap ScriptSourcesHashMap; michael@0: michael@0: // |allScriptSources| is only used transiently. During the reporting phase michael@0: // it is filled with info about every script source in the runtime. It's michael@0: // then used to fill in |notableScriptSources| (which actually gets michael@0: // reported), and immediately discarded afterwards. michael@0: ScriptSourcesHashMap *allScriptSources; michael@0: js::Vector notableScriptSources; michael@0: michael@0: #undef FOR_EACH_SIZE michael@0: }; michael@0: michael@0: struct ZoneStats michael@0: { michael@0: #define FOR_EACH_SIZE(macro) \ michael@0: macro(Other, NotLiveGCThing, gcHeapArenaAdmin) \ michael@0: macro(Other, NotLiveGCThing, unusedGCThings) \ michael@0: macro(Other, IsLiveGCThing, lazyScriptsGCHeap) \ michael@0: macro(Other, NotLiveGCThing, lazyScriptsMallocHeap) \ michael@0: macro(Other, IsLiveGCThing, jitCodesGCHeap) \ michael@0: macro(Other, IsLiveGCThing, typeObjectsGCHeap) \ michael@0: macro(Other, NotLiveGCThing, typeObjectsMallocHeap) \ michael@0: macro(Other, NotLiveGCThing, typePool) \ michael@0: macro(Other, NotLiveGCThing, baselineStubsOptimized) \ michael@0: michael@0: ZoneStats() michael@0: : FOR_EACH_SIZE(ZERO_SIZE) michael@0: stringInfo(), michael@0: extra(), michael@0: allStrings(nullptr), michael@0: notableStrings(), michael@0: isTotals(true) michael@0: {} michael@0: michael@0: ZoneStats(ZoneStats &&other) michael@0: : FOR_EACH_SIZE(COPY_OTHER_SIZE) michael@0: stringInfo(mozilla::Move(other.stringInfo)), michael@0: extra(other.extra), michael@0: allStrings(other.allStrings), michael@0: notableStrings(mozilla::Move(other.notableStrings)), michael@0: isTotals(other.isTotals) michael@0: { michael@0: other.allStrings = nullptr; michael@0: MOZ_ASSERT(!other.isTotals); michael@0: } michael@0: michael@0: ~ZoneStats() { michael@0: // |allStrings| is usually deleted and set to nullptr before this michael@0: // destructor runs. But there are failure cases due to OOMs that may michael@0: // prevent that, so it doesn't hurt to try again here. michael@0: js_delete(allStrings); michael@0: } michael@0: michael@0: bool initStrings(JSRuntime *rt); michael@0: michael@0: void addSizes(const ZoneStats &other) { michael@0: MOZ_ASSERT(isTotals); michael@0: FOR_EACH_SIZE(ADD_OTHER_SIZE) michael@0: stringInfo.add(other.stringInfo); michael@0: } michael@0: michael@0: size_t sizeOfLiveGCThings() const { michael@0: MOZ_ASSERT(isTotals); michael@0: size_t n = 0; michael@0: FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING) michael@0: n += stringInfo.sizeOfLiveGCThings(); michael@0: return n; michael@0: } michael@0: michael@0: void addToTabSizes(JS::TabSizes *sizes) const { michael@0: MOZ_ASSERT(isTotals); michael@0: FOR_EACH_SIZE(ADD_TO_TAB_SIZES) michael@0: stringInfo.addToTabSizes(sizes); michael@0: } michael@0: michael@0: // These string measurements are initially for all strings. At the end, michael@0: // if the measurement granularity is FineGrained, we subtract the michael@0: // measurements of the notable script sources and move them into michael@0: // |notableStrings|. michael@0: FOR_EACH_SIZE(DECL_SIZE) michael@0: StringInfo stringInfo; michael@0: void *extra; // This field can be used by embedders. michael@0: michael@0: typedef js::HashMap StringsHashMap; michael@0: michael@0: // |allStrings| is only used transiently. During the zone traversal it is michael@0: // filled with info about every string in the zone. It's then used to fill michael@0: // in |notableStrings| (which actually gets reported), and immediately michael@0: // discarded afterwards. michael@0: StringsHashMap *allStrings; michael@0: js::Vector notableStrings; michael@0: bool isTotals; michael@0: michael@0: #undef FOR_EACH_SIZE michael@0: }; michael@0: michael@0: struct CompartmentStats michael@0: { michael@0: #define FOR_EACH_SIZE(macro) \ michael@0: macro(Objects, IsLiveGCThing, objectsGCHeapOrdinary) \ michael@0: macro(Objects, IsLiveGCThing, objectsGCHeapFunction) \ michael@0: macro(Objects, IsLiveGCThing, objectsGCHeapDenseArray) \ michael@0: macro(Objects, IsLiveGCThing, objectsGCHeapSlowArray) \ michael@0: macro(Objects, IsLiveGCThing, objectsGCHeapCrossCompartmentWrapper) \ michael@0: macro(Private, NotLiveGCThing, objectsPrivate) \ michael@0: macro(Other, IsLiveGCThing, shapesGCHeapTreeGlobalParented) \ michael@0: macro(Other, IsLiveGCThing, shapesGCHeapTreeNonGlobalParented) \ michael@0: macro(Other, IsLiveGCThing, shapesGCHeapDict) \ michael@0: macro(Other, IsLiveGCThing, shapesGCHeapBase) \ michael@0: macro(Other, NotLiveGCThing, shapesMallocHeapTreeTables) \ michael@0: macro(Other, NotLiveGCThing, shapesMallocHeapDictTables) \ michael@0: macro(Other, NotLiveGCThing, shapesMallocHeapTreeShapeKids) \ michael@0: macro(Other, NotLiveGCThing, shapesMallocHeapCompartmentTables) \ michael@0: macro(Other, IsLiveGCThing, scriptsGCHeap) \ michael@0: macro(Other, NotLiveGCThing, scriptsMallocHeapData) \ michael@0: macro(Other, NotLiveGCThing, baselineData) \ michael@0: macro(Other, NotLiveGCThing, baselineStubsFallback) \ michael@0: macro(Other, NotLiveGCThing, ionData) \ michael@0: macro(Other, NotLiveGCThing, typeInferenceTypeScripts) \ michael@0: macro(Other, NotLiveGCThing, typeInferenceAllocationSiteTables) \ michael@0: macro(Other, NotLiveGCThing, typeInferenceArrayTypeTables) \ michael@0: macro(Other, NotLiveGCThing, typeInferenceObjectTypeTables) \ michael@0: macro(Other, NotLiveGCThing, compartmentObject) \ michael@0: macro(Other, NotLiveGCThing, crossCompartmentWrappersTable) \ michael@0: macro(Other, NotLiveGCThing, regexpCompartment) \ michael@0: macro(Other, NotLiveGCThing, debuggeesSet) \ michael@0: macro(Other, NotLiveGCThing, savedStacksSet) michael@0: michael@0: CompartmentStats() michael@0: : FOR_EACH_SIZE(ZERO_SIZE) michael@0: objectsExtra(), michael@0: extra() michael@0: {} michael@0: michael@0: CompartmentStats(const CompartmentStats &other) michael@0: : FOR_EACH_SIZE(COPY_OTHER_SIZE) michael@0: objectsExtra(other.objectsExtra), michael@0: extra(other.extra) michael@0: {} michael@0: michael@0: void add(const CompartmentStats &other) { michael@0: FOR_EACH_SIZE(ADD_OTHER_SIZE) michael@0: objectsExtra.add(other.objectsExtra); michael@0: // Do nothing with |extra|. michael@0: } michael@0: michael@0: size_t sizeOfLiveGCThings() const { michael@0: size_t n = 0; michael@0: FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING) michael@0: n += objectsExtra.sizeOfLiveGCThings(); michael@0: // Do nothing with |extra|. michael@0: return n; michael@0: } michael@0: michael@0: void addToTabSizes(TabSizes *sizes) const { michael@0: FOR_EACH_SIZE(ADD_TO_TAB_SIZES); michael@0: objectsExtra.addToTabSizes(sizes); michael@0: // Do nothing with |extra|. michael@0: } michael@0: michael@0: FOR_EACH_SIZE(DECL_SIZE) michael@0: ObjectsExtraSizes objectsExtra; michael@0: void *extra; // This field can be used by embedders. michael@0: michael@0: #undef FOR_EACH_SIZE michael@0: }; michael@0: michael@0: typedef js::Vector CompartmentStatsVector; michael@0: typedef js::Vector ZoneStatsVector; michael@0: michael@0: struct RuntimeStats michael@0: { michael@0: #define FOR_EACH_SIZE(macro) \ michael@0: macro(_, _, gcHeapChunkTotal) \ michael@0: macro(_, _, gcHeapDecommittedArenas) \ michael@0: macro(_, _, gcHeapUnusedChunks) \ michael@0: macro(_, _, gcHeapUnusedArenas) \ michael@0: macro(_, _, gcHeapChunkAdmin) \ michael@0: macro(_, _, gcHeapGCThings) \ michael@0: michael@0: RuntimeStats(mozilla::MallocSizeOf mallocSizeOf) michael@0: : FOR_EACH_SIZE(ZERO_SIZE) michael@0: runtime(), michael@0: cTotals(), michael@0: zTotals(), michael@0: compartmentStatsVector(), michael@0: zoneStatsVector(), michael@0: currZoneStats(nullptr), michael@0: mallocSizeOf_(mallocSizeOf) michael@0: {} michael@0: michael@0: // Here's a useful breakdown of the GC heap. michael@0: // michael@0: // - rtStats.gcHeapChunkTotal michael@0: // - decommitted bytes michael@0: // - rtStats.gcHeapDecommittedArenas (decommitted arenas in non-empty chunks) michael@0: // - unused bytes michael@0: // - rtStats.gcHeapUnusedChunks (empty chunks) michael@0: // - rtStats.gcHeapUnusedArenas (empty arenas within non-empty chunks) michael@0: // - rtStats.zTotals.unusedGCThings (empty GC thing slots within non-empty arenas) michael@0: // - used bytes michael@0: // - rtStats.gcHeapChunkAdmin michael@0: // - rtStats.zTotals.gcHeapArenaAdmin michael@0: // - rtStats.gcHeapGCThings (in-use GC things) michael@0: // == rtStats.zTotals.sizeOfLiveGCThings() + rtStats.cTotals.sizeOfLiveGCThings() michael@0: // michael@0: // It's possible that some arenas in empty chunks may be decommitted, but michael@0: // we don't count those under rtStats.gcHeapDecommittedArenas because (a) michael@0: // it's rare, and (b) this means that rtStats.gcHeapUnusedChunks is a michael@0: // multiple of the chunk size, which is good. michael@0: michael@0: FOR_EACH_SIZE(DECL_SIZE) michael@0: michael@0: RuntimeSizes runtime; michael@0: michael@0: CompartmentStats cTotals; // The sum of this runtime's compartments' measurements. michael@0: ZoneStats zTotals; // The sum of this runtime's zones' measurements. michael@0: michael@0: CompartmentStatsVector compartmentStatsVector; michael@0: ZoneStatsVector zoneStatsVector; michael@0: michael@0: ZoneStats *currZoneStats; michael@0: michael@0: mozilla::MallocSizeOf mallocSizeOf_; michael@0: michael@0: virtual void initExtraCompartmentStats(JSCompartment *c, CompartmentStats *cstats) = 0; michael@0: virtual void initExtraZoneStats(JS::Zone *zone, ZoneStats *zstats) = 0; michael@0: michael@0: #undef FOR_EACH_SIZE michael@0: }; michael@0: michael@0: class ObjectPrivateVisitor michael@0: { michael@0: public: michael@0: // Within CollectRuntimeStats, this method is called for each JS object michael@0: // that has an nsISupports pointer. michael@0: virtual size_t sizeOfIncludingThis(nsISupports *aSupports) = 0; michael@0: michael@0: // A callback that gets a JSObject's nsISupports pointer, if it has one. michael@0: // Note: this function does *not* addref |iface|. michael@0: typedef bool(*GetISupportsFun)(JSObject *obj, nsISupports **iface); michael@0: GetISupportsFun getISupports_; michael@0: michael@0: ObjectPrivateVisitor(GetISupportsFun getISupports) michael@0: : getISupports_(getISupports) michael@0: {} michael@0: }; michael@0: michael@0: extern JS_PUBLIC_API(bool) michael@0: CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisitor *opv); michael@0: michael@0: extern JS_PUBLIC_API(size_t) michael@0: SystemCompartmentCount(JSRuntime *rt); michael@0: michael@0: extern JS_PUBLIC_API(size_t) michael@0: UserCompartmentCount(JSRuntime *rt); michael@0: michael@0: extern JS_PUBLIC_API(size_t) michael@0: PeakSizeOfTemporary(const JSRuntime *rt); michael@0: michael@0: extern JS_PUBLIC_API(bool) michael@0: AddSizeOfTab(JSRuntime *rt, JS::HandleObject obj, mozilla::MallocSizeOf mallocSizeOf, michael@0: ObjectPrivateVisitor *opv, TabSizes *sizes); michael@0: michael@0: } // namespace JS michael@0: michael@0: #undef DECL_SIZE michael@0: #undef ZERO_SIZE michael@0: #undef COPY_OTHER_SIZE michael@0: #undef ADD_OTHER_SIZE michael@0: #undef SUB_OTHER_SIZE michael@0: #undef ADD_SIZE_TO_N michael@0: #undef ADD_SIZE_TO_N_IF_LIVE_GC_THING michael@0: #undef ADD_TO_TAB_SIZES michael@0: michael@0: #endif /* js_MemoryMetrics_h */