summaryrefslogtreecommitdiffstats
path: root/js/public/MemoryMetrics.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/public/MemoryMetrics.h902
1 files changed, 902 insertions, 0 deletions
diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h
new file mode 100644
index 0000000000..adf68f31cd
--- /dev/null
+++ b/js/public/MemoryMetrics.h
@@ -0,0 +1,902 @@
+/* -*- 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 <string.h>
+#include <type_traits>
+
+#include "jspubtd.h"
+
+#include "js/AllocPolicy.h"
+#include "js/GCAPI.h"
+#include "js/HashTable.h"
+#include "js/TracingAPI.h"
+#include "js/Utility.h"
+#include "js/Vector.h"
+
+class nsISupports; // Needed for ObjectPrivateVisitor.
+
+namespace JS {
+
+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_FRIEND_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<int[ServoSizes::servoKind], \
+ int[ServoSizes::GCHeapUsed]>) \
+ ? 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, objectsMallocHeapMisc) \
+ MACRO(Objects, NonHeap, objectsNonHeapElementsNormal) \
+ MACRO(Objects, NonHeap, objectsNonHeapElementsShared) \
+ MACRO(Objects, NonHeap, objectsNonHeapElementsWasm) \
+ 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);
+
+ size_t wasmGuardPages = 0;
+
+#undef FOR_EACH_SIZE
+};
+
+struct ShapeInfo {
+#define FOR_EACH_SIZE(MACRO) \
+ MACRO(Other, GCHeapUsed, shapesGCHeapTree) \
+ MACRO(Other, GCHeapUsed, shapesGCHeapDict) \
+ MACRO(Other, GCHeapUsed, shapesGCHeapBase) \
+ MACRO(Other, MallocHeap, shapesMallocHeapTreeTables) \
+ MACRO(Other, MallocHeap, shapesMallocHeapDictTables) \
+ MACRO(Other, MallocHeap, shapesMallocHeapTreeChildren)
+
+ 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, 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 {
+#define FOR_EACH_SIZE(MACRO) MACRO(_, MallocHeap, tracelogger)
+
+ explicit GlobalStats(mozilla::MallocSizeOf mallocSizeOf)
+ : mallocSizeOf_(mallocSizeOf) {}
+
+ FOR_EACH_SIZE(DECL_SIZE_ZERO);
+
+ HelperThreadStats helperThread;
+
+ mozilla::MallocSizeOf mallocSizeOf_;
+
+#undef FOR_EACH_SIZE
+};
+
+/**
+ * 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, contexts) \
+ MACRO(_, MallocHeap, temporary) \
+ MACRO(_, MallocHeap, interpreterStack) \
+ MACRO(_, MallocHeap, sharedImmutableStringsCache) \
+ MACRO(_, MallocHeap, sharedIntlData) \
+ MACRO(_, MallocHeap, uncompressedSourceCache) \
+ MACRO(_, MallocHeap, scriptData) \
+ MACRO(_, MallocHeap, tracelogger) \
+ MACRO(_, MallocHeap, wasmRuntime) \
+ 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<const char*, ScriptSourceInfo, mozilla::CStringHasher,
+ js::SystemAllocPolicy>
+ 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<ScriptSourcesHashMap> allScriptSources;
+ js::Vector<NotableScriptSourceInfo, 0, js::SystemAllocPolicy>
+ 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, objectGroup) \
+ 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::JitCode:
+ jitcode += n;
+ break;
+ case JS::TraceKind::ObjectGroup:
+ objectGroup += 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, objectGroupsGCHeap) \
+ 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, 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<JSString*, StringInfo,
+ js::InefficientNonFlatteningStringHashPolicy,
+ js::SystemAllocPolicy>
+ 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<StringsHashMap> allStrings;
+ js::Vector<NotableStringInfo, 0, js::SystemAllocPolicy> 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, varNamesSet) \
+ 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<const char*, ClassInfo, mozilla::CStringHasher,
+ js::SystemAllocPolicy>
+ ClassesHashMap;
+
+ // These are similar to |allStrings| and |notableStrings| in ZoneStats.
+ mozilla::Maybe<ClassesHashMap> allClasses;
+ js::Vector<NotableClassInfo, 0, js::SystemAllocPolicy> notableClasses;
+ bool isTotals = true;
+
+#undef FOR_EACH_SIZE
+};
+
+typedef js::Vector<RealmStats, 0, js::SystemAllocPolicy> RealmStatsVector;
+typedef js::Vector<ZoneStats, 0, js::SystemAllocPolicy> 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, gcHeapDecommittedArenas) \
+ 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.gcHeapDecommittedArenas
+ // (decommitted arenas 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 arenas in empty chunks may be decommitted, but
+ // we don't count those under rtStats.gcHeapDecommittedArenas 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 */