mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-29 21:25:35 +00:00
724 lines
23 KiB
C++
724 lines
23 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
* 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/MemoryReporting.h"
|
|
#include "mozilla/PodOperations.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include "jsalloc.h"
|
|
#include "jspubtd.h"
|
|
|
|
#include "js/HashTable.h"
|
|
#include "js/Utility.h"
|
|
#include "js/Vector.h"
|
|
|
|
class nsISupports; // Needed for ObjectPrivateVisitor.
|
|
|
|
namespace JS {
|
|
|
|
struct TabSizes
|
|
{
|
|
enum Kind {
|
|
Objects,
|
|
Strings,
|
|
Private,
|
|
Other
|
|
};
|
|
|
|
TabSizes() { mozilla::PodZero(this); }
|
|
|
|
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;
|
|
size_t strings;
|
|
size_t private_;
|
|
size_t other;
|
|
};
|
|
|
|
} // 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);
|
|
};
|
|
|
|
struct CStringHashPolicy
|
|
{
|
|
typedef const char *Lookup;
|
|
static HashNumber hash(const Lookup &l);
|
|
static bool match(const char *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.
|
|
//
|
|
// In some classes, one or more of the macro arguments aren't used. We use '_'
|
|
// for those.
|
|
//
|
|
#define DECL_SIZE(kind, gc, mSize) size_t mSize;
|
|
#define ZERO_SIZE(kind, gc, mSize) mSize(0),
|
|
#define COPY_OTHER_SIZE(kind, gc, mSize) mSize(other.mSize),
|
|
#define ADD_OTHER_SIZE(kind, gc, mSize) mSize += other.mSize;
|
|
#define SUB_OTHER_SIZE(kind, gc, mSize) MOZ_ASSERT(mSize >= other.mSize); \
|
|
mSize -= other.mSize;
|
|
#define ADD_SIZE_TO_N(kind, gc, mSize) n += mSize;
|
|
#define ADD_SIZE_TO_N_IF_LIVE_GC_THING(kind, gc, mSize) n += (js::gc) ? mSize : 0;
|
|
#define ADD_TO_TAB_SIZES(kind, gc, mSize) sizes->add(JS::TabSizes::kind, 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 {
|
|
|
|
struct ClassInfo
|
|
{
|
|
#define FOR_EACH_SIZE(macro) \
|
|
macro(Objects, IsLiveGCThing, objectsGCHeap) \
|
|
macro(Objects, NotLiveGCThing, objectsMallocHeapSlots) \
|
|
macro(Objects, NotLiveGCThing, objectsMallocHeapElementsNonAsmJS) \
|
|
macro(Objects, NotLiveGCThing, objectsMallocHeapElementsAsmJS) \
|
|
macro(Objects, NotLiveGCThing, objectsNonHeapElementsAsmJS) \
|
|
macro(Objects, NotLiveGCThing, objectsNonHeapElementsMapped) \
|
|
macro(Objects, NotLiveGCThing, objectsNonHeapCodeAsmJS) \
|
|
macro(Objects, NotLiveGCThing, objectsMallocHeapMisc) \
|
|
\
|
|
macro(Other, IsLiveGCThing, shapesGCHeapTree) \
|
|
macro(Other, IsLiveGCThing, shapesGCHeapDict) \
|
|
macro(Other, IsLiveGCThing, shapesGCHeapBase) \
|
|
macro(Other, NotLiveGCThing, shapesMallocHeapTreeTables) \
|
|
macro(Other, NotLiveGCThing, shapesMallocHeapDictTables) \
|
|
macro(Other, NotLiveGCThing, shapesMallocHeapTreeKids) \
|
|
|
|
ClassInfo()
|
|
: FOR_EACH_SIZE(ZERO_SIZE)
|
|
dummy()
|
|
{}
|
|
|
|
void add(const ClassInfo &other) {
|
|
FOR_EACH_SIZE(ADD_OTHER_SIZE)
|
|
}
|
|
|
|
void subtract(const ClassInfo &other) {
|
|
FOR_EACH_SIZE(SUB_OTHER_SIZE)
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
FOR_EACH_SIZE(DECL_SIZE)
|
|
int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
|
|
|
|
#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();
|
|
NotableClassInfo(const char *className, const ClassInfo &info);
|
|
NotableClassInfo(NotableClassInfo &&info);
|
|
NotableClassInfo &operator=(NotableClassInfo &&info);
|
|
|
|
~NotableClassInfo() {
|
|
js_free(className_);
|
|
}
|
|
|
|
char *className_;
|
|
|
|
private:
|
|
NotableClassInfo(const NotableClassInfo& info) = delete;
|
|
};
|
|
|
|
// Data for tracking JIT-code memory usage.
|
|
struct CodeSizes
|
|
{
|
|
#define FOR_EACH_SIZE(macro) \
|
|
macro(_, _, ion) \
|
|
macro(_, _, baseline) \
|
|
macro(_, _, regexp) \
|
|
macro(_, _, other) \
|
|
macro(_, _, unused)
|
|
|
|
CodeSizes()
|
|
: FOR_EACH_SIZE(ZERO_SIZE)
|
|
dummy()
|
|
{}
|
|
|
|
FOR_EACH_SIZE(DECL_SIZE)
|
|
int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
|
|
|
|
#undef FOR_EACH_SIZE
|
|
};
|
|
|
|
// Data for tracking GC memory usage.
|
|
struct GCSizes
|
|
{
|
|
#define FOR_EACH_SIZE(macro) \
|
|
macro(_, _, marker) \
|
|
macro(_, _, nurseryCommitted) \
|
|
macro(_, _, nurseryDecommitted) \
|
|
macro(_, _, nurseryHugeSlots) \
|
|
macro(_, _, storeBufferVals) \
|
|
macro(_, _, storeBufferCells) \
|
|
macro(_, _, storeBufferSlots) \
|
|
macro(_, _, storeBufferWholeCells) \
|
|
macro(_, _, storeBufferRelocVals) \
|
|
macro(_, _, storeBufferRelocCells) \
|
|
macro(_, _, storeBufferGenerics)
|
|
|
|
GCSizes()
|
|
: FOR_EACH_SIZE(ZERO_SIZE)
|
|
dummy()
|
|
{}
|
|
|
|
FOR_EACH_SIZE(DECL_SIZE)
|
|
int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
|
|
|
|
#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, IsLiveGCThing, gcHeapLatin1) \
|
|
macro(Strings, IsLiveGCThing, gcHeapTwoByte) \
|
|
macro(Strings, NotLiveGCThing, mallocHeapLatin1) \
|
|
macro(Strings, NotLiveGCThing, mallocHeapTwoByte)
|
|
|
|
StringInfo()
|
|
: FOR_EACH_SIZE(ZERO_SIZE)
|
|
numCopies(0)
|
|
{}
|
|
|
|
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)
|
|
}
|
|
|
|
FOR_EACH_SIZE(DECL_SIZE)
|
|
uint32_t numCopies; // 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();
|
|
NotableStringInfo(JSString *str, const StringInfo &info);
|
|
NotableStringInfo(NotableStringInfo &&info);
|
|
NotableStringInfo &operator=(NotableStringInfo &&info);
|
|
|
|
~NotableStringInfo() {
|
|
js_free(buffer);
|
|
}
|
|
|
|
char *buffer;
|
|
size_t length;
|
|
|
|
private:
|
|
NotableStringInfo(const NotableStringInfo& info) = delete;
|
|
};
|
|
|
|
// This class holds information about the memory taken up by script sources
|
|
// from a particular file.
|
|
struct ScriptSourceInfo
|
|
{
|
|
#define FOR_EACH_SIZE(macro) \
|
|
macro(_, _, compressed) \
|
|
macro(_, _, uncompressed) \
|
|
macro(_, _, misc)
|
|
|
|
ScriptSourceInfo()
|
|
: FOR_EACH_SIZE(ZERO_SIZE)
|
|
numScripts(0)
|
|
{}
|
|
|
|
void add(const ScriptSourceInfo &other) {
|
|
FOR_EACH_SIZE(ADD_OTHER_SIZE)
|
|
numScripts++;
|
|
}
|
|
|
|
void subtract(const ScriptSourceInfo &other) {
|
|
FOR_EACH_SIZE(SUB_OTHER_SIZE)
|
|
numScripts--;
|
|
}
|
|
|
|
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)
|
|
uint32_t numScripts; // 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();
|
|
NotableScriptSourceInfo(const char *filename, const ScriptSourceInfo &info);
|
|
NotableScriptSourceInfo(NotableScriptSourceInfo &&info);
|
|
NotableScriptSourceInfo &operator=(NotableScriptSourceInfo &&info);
|
|
|
|
~NotableScriptSourceInfo() {
|
|
js_free(filename_);
|
|
}
|
|
|
|
char *filename_;
|
|
|
|
private:
|
|
NotableScriptSourceInfo(const NotableScriptSourceInfo& info) = delete;
|
|
};
|
|
|
|
// These measurements relate directly to the JSRuntime, and not to zones and
|
|
// compartments within it.
|
|
struct RuntimeSizes
|
|
{
|
|
#define FOR_EACH_SIZE(macro) \
|
|
macro(_, _, object) \
|
|
macro(_, _, atomsTable) \
|
|
macro(_, _, contexts) \
|
|
macro(_, _, dtoa) \
|
|
macro(_, _, temporary) \
|
|
macro(_, _, interpreterStack) \
|
|
macro(_, _, mathCache) \
|
|
macro(_, _, uncompressedSourceCache) \
|
|
macro(_, _, compressedSourceSet) \
|
|
macro(_, _, scriptData) \
|
|
|
|
RuntimeSizes()
|
|
: FOR_EACH_SIZE(ZERO_SIZE)
|
|
scriptSourceInfo(),
|
|
code(),
|
|
gc(),
|
|
notableScriptSources()
|
|
{
|
|
allScriptSources = js_new<ScriptSourcesHashMap>();
|
|
if (!allScriptSources || !allScriptSources->init())
|
|
MOZ_CRASH("oom");
|
|
}
|
|
|
|
~RuntimeSizes() {
|
|
// |allScriptSources| is usually deleted and set to nullptr before this
|
|
// destructor runs. But there are failure cases due to OOMs that may
|
|
// prevent that, so it doesn't hurt to try again here.
|
|
js_delete(allScriptSources);
|
|
}
|
|
|
|
// 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|.
|
|
FOR_EACH_SIZE(DECL_SIZE)
|
|
ScriptSourceInfo scriptSourceInfo;
|
|
CodeSizes code;
|
|
GCSizes gc;
|
|
|
|
typedef js::HashMap<const char*, ScriptSourceInfo,
|
|
js::CStringHashPolicy,
|
|
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.
|
|
ScriptSourcesHashMap *allScriptSources;
|
|
js::Vector<NotableScriptSourceInfo, 0, js::SystemAllocPolicy> notableScriptSources;
|
|
|
|
#undef FOR_EACH_SIZE
|
|
};
|
|
|
|
struct ZoneStats
|
|
{
|
|
#define FOR_EACH_SIZE(macro) \
|
|
macro(Other, IsLiveGCThing, symbolsGCHeap) \
|
|
macro(Other, NotLiveGCThing, gcHeapArenaAdmin) \
|
|
macro(Other, NotLiveGCThing, unusedGCThings) \
|
|
macro(Other, IsLiveGCThing, lazyScriptsGCHeap) \
|
|
macro(Other, NotLiveGCThing, lazyScriptsMallocHeap) \
|
|
macro(Other, IsLiveGCThing, jitCodesGCHeap) \
|
|
macro(Other, IsLiveGCThing, typeObjectsGCHeap) \
|
|
macro(Other, NotLiveGCThing, typeObjectsMallocHeap) \
|
|
macro(Other, NotLiveGCThing, typePool) \
|
|
macro(Other, NotLiveGCThing, baselineStubsOptimized) \
|
|
|
|
ZoneStats()
|
|
: FOR_EACH_SIZE(ZERO_SIZE)
|
|
stringInfo(),
|
|
extra(),
|
|
allStrings(nullptr),
|
|
notableStrings(),
|
|
isTotals(true)
|
|
{}
|
|
|
|
ZoneStats(ZoneStats &&other)
|
|
: FOR_EACH_SIZE(COPY_OTHER_SIZE)
|
|
stringInfo(mozilla::Move(other.stringInfo)),
|
|
extra(other.extra),
|
|
allStrings(other.allStrings),
|
|
notableStrings(mozilla::Move(other.notableStrings)),
|
|
isTotals(other.isTotals)
|
|
{
|
|
other.allStrings = nullptr;
|
|
MOZ_ASSERT(!other.isTotals);
|
|
}
|
|
|
|
~ZoneStats() {
|
|
// |allStrings| is usually deleted and set to nullptr before this
|
|
// destructor runs. But there are failure cases due to OOMs that may
|
|
// prevent that, so it doesn't hurt to try again here.
|
|
js_delete(allStrings);
|
|
}
|
|
|
|
bool initStrings(JSRuntime *rt);
|
|
|
|
void addSizes(const ZoneStats &other) {
|
|
MOZ_ASSERT(isTotals);
|
|
FOR_EACH_SIZE(ADD_OTHER_SIZE)
|
|
stringInfo.add(other.stringInfo);
|
|
}
|
|
|
|
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();
|
|
return n;
|
|
}
|
|
|
|
void addToTabSizes(JS::TabSizes *sizes) const {
|
|
MOZ_ASSERT(isTotals);
|
|
FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
|
|
stringInfo.addToTabSizes(sizes);
|
|
}
|
|
|
|
// 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|.
|
|
FOR_EACH_SIZE(DECL_SIZE)
|
|
StringInfo stringInfo;
|
|
void *extra; // 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.
|
|
StringsHashMap *allStrings;
|
|
js::Vector<NotableStringInfo, 0, js::SystemAllocPolicy> notableStrings;
|
|
bool isTotals;
|
|
|
|
#undef FOR_EACH_SIZE
|
|
};
|
|
|
|
struct CompartmentStats
|
|
{
|
|
#define FOR_EACH_SIZE(macro) \
|
|
macro(Private, NotLiveGCThing, objectsPrivate) \
|
|
macro(Other, IsLiveGCThing, scriptsGCHeap) \
|
|
macro(Other, NotLiveGCThing, scriptsMallocHeapData) \
|
|
macro(Other, NotLiveGCThing, baselineData) \
|
|
macro(Other, NotLiveGCThing, baselineStubsFallback) \
|
|
macro(Other, NotLiveGCThing, ionData) \
|
|
macro(Other, NotLiveGCThing, typeInferenceTypeScripts) \
|
|
macro(Other, NotLiveGCThing, typeInferenceAllocationSiteTables) \
|
|
macro(Other, NotLiveGCThing, typeInferenceArrayTypeTables) \
|
|
macro(Other, NotLiveGCThing, typeInferenceObjectTypeTables) \
|
|
macro(Other, NotLiveGCThing, compartmentObject) \
|
|
macro(Other, NotLiveGCThing, compartmentTables) \
|
|
macro(Other, NotLiveGCThing, innerViewsTable) \
|
|
macro(Other, NotLiveGCThing, lazyArrayBuffersTable) \
|
|
macro(Other, NotLiveGCThing, crossCompartmentWrappersTable) \
|
|
macro(Other, NotLiveGCThing, regexpCompartment) \
|
|
macro(Other, NotLiveGCThing, savedStacksSet)
|
|
|
|
CompartmentStats()
|
|
: FOR_EACH_SIZE(ZERO_SIZE)
|
|
classInfo(),
|
|
extra(),
|
|
allClasses(nullptr),
|
|
notableClasses(),
|
|
isTotals(true)
|
|
{}
|
|
|
|
CompartmentStats(CompartmentStats &&other)
|
|
: FOR_EACH_SIZE(COPY_OTHER_SIZE)
|
|
classInfo(mozilla::Move(other.classInfo)),
|
|
extra(other.extra),
|
|
allClasses(other.allClasses),
|
|
notableClasses(mozilla::Move(other.notableClasses)),
|
|
isTotals(other.isTotals)
|
|
{
|
|
other.allClasses = nullptr;
|
|
MOZ_ASSERT(!other.isTotals);
|
|
}
|
|
|
|
~CompartmentStats() {
|
|
// |allClasses| is usually deleted and set to nullptr before this
|
|
// destructor runs. But there are failure cases due to OOMs that may
|
|
// prevent that, so it doesn't hurt to try again here.
|
|
js_delete(allClasses);
|
|
}
|
|
|
|
bool initClasses(JSRuntime *rt);
|
|
|
|
void addSizes(const CompartmentStats &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);
|
|
}
|
|
|
|
// 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|.
|
|
FOR_EACH_SIZE(DECL_SIZE)
|
|
ClassInfo classInfo;
|
|
void *extra; // This field can be used by embedders.
|
|
|
|
typedef js::HashMap<const char*, ClassInfo,
|
|
js::CStringHashPolicy,
|
|
js::SystemAllocPolicy> ClassesHashMap;
|
|
|
|
// These are similar to |allStrings| and |notableStrings| in ZoneStats.
|
|
ClassesHashMap *allClasses;
|
|
js::Vector<NotableClassInfo, 0, js::SystemAllocPolicy> notableClasses;
|
|
bool isTotals;
|
|
|
|
#undef FOR_EACH_SIZE
|
|
};
|
|
|
|
typedef js::Vector<CompartmentStats, 0, js::SystemAllocPolicy> CompartmentStatsVector;
|
|
typedef js::Vector<ZoneStats, 0, js::SystemAllocPolicy> ZoneStatsVector;
|
|
|
|
struct RuntimeStats
|
|
{
|
|
#define FOR_EACH_SIZE(macro) \
|
|
macro(_, _, gcHeapChunkTotal) \
|
|
macro(_, _, gcHeapDecommittedArenas) \
|
|
macro(_, _, gcHeapUnusedChunks) \
|
|
macro(_, _, gcHeapUnusedArenas) \
|
|
macro(_, _, gcHeapChunkAdmin) \
|
|
macro(_, _, gcHeapGCThings) \
|
|
|
|
explicit RuntimeStats(mozilla::MallocSizeOf mallocSizeOf)
|
|
: FOR_EACH_SIZE(ZERO_SIZE)
|
|
runtime(),
|
|
cTotals(),
|
|
zTotals(),
|
|
compartmentStatsVector(),
|
|
zoneStatsVector(),
|
|
currZoneStats(nullptr),
|
|
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 (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.
|
|
|
|
FOR_EACH_SIZE(DECL_SIZE)
|
|
|
|
RuntimeSizes runtime;
|
|
|
|
CompartmentStats cTotals; // The sum of this runtime's compartments' measurements.
|
|
ZoneStats zTotals; // The sum of this runtime's zones' measurements.
|
|
|
|
CompartmentStatsVector compartmentStatsVector;
|
|
ZoneStatsVector zoneStatsVector;
|
|
|
|
ZoneStats *currZoneStats;
|
|
|
|
mozilla::MallocSizeOf mallocSizeOf_;
|
|
|
|
virtual void initExtraCompartmentStats(JSCompartment *c, CompartmentStats *cstats) = 0;
|
|
virtual void initExtraZoneStats(JS::Zone *zone, ZoneStats *zstats) = 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)
|
|
CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisitor *opv, bool anonymize);
|
|
|
|
extern JS_PUBLIC_API(size_t)
|
|
SystemCompartmentCount(JSRuntime *rt);
|
|
|
|
extern JS_PUBLIC_API(size_t)
|
|
UserCompartmentCount(JSRuntime *rt);
|
|
|
|
extern JS_PUBLIC_API(size_t)
|
|
PeakSizeOfTemporary(const JSRuntime *rt);
|
|
|
|
extern JS_PUBLIC_API(bool)
|
|
AddSizeOfTab(JSRuntime *rt, JS::HandleObject obj, mozilla::MallocSizeOf mallocSizeOf,
|
|
ObjectPrivateVisitor *opv, TabSizes *sizes);
|
|
|
|
} // namespace JS
|
|
|
|
#undef DECL_SIZE
|
|
#undef ZERO_SIZE
|
|
#undef COPY_OTHER_SIZE
|
|
#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 */
|