Bug 1023719 - Report notable classes in the JS memory reporter. r=till.

--HG--
extra : rebase_source : 5a842c36cf7202f6751290da80cefb005fd8db95
This commit is contained in:
Nicholas Nethercote 2014-04-15 19:43:18 -07:00
Родитель cb1a5a5562
Коммит feeaf7db9a
10 изменённых файлов: 415 добавлений и 224 удалений

Просмотреть файл

@ -115,31 +115,45 @@ enum {
namespace JS {
// Data for tracking memory usage of things hanging off objects.
struct ObjectsExtraSizes
struct ClassInfo
{
#define FOR_EACH_SIZE(macro) \
macro(Objects, NotLiveGCThing, mallocHeapSlots) \
macro(Objects, NotLiveGCThing, mallocHeapElementsNonAsmJS) \
macro(Objects, NotLiveGCThing, mallocHeapElementsAsmJS) \
macro(Objects, NotLiveGCThing, nonHeapElementsAsmJS) \
macro(Objects, NotLiveGCThing, nonHeapElementsMapped) \
macro(Objects, NotLiveGCThing, nonHeapCodeAsmJS) \
macro(Objects, NotLiveGCThing, mallocHeapAsmJSModuleData) \
macro(Objects, NotLiveGCThing, mallocHeapArgumentsData) \
macro(Objects, NotLiveGCThing, mallocHeapRegExpStatics) \
macro(Objects, NotLiveGCThing, mallocHeapPropertyIteratorData) \
macro(Objects, NotLiveGCThing, mallocHeapCtypesData)
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) \
ObjectsExtraSizes()
ClassInfo()
: FOR_EACH_SIZE(ZERO_SIZE)
dummy()
{}
void add(const ObjectsExtraSizes &other) {
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)
@ -156,6 +170,29 @@ struct ObjectsExtraSizes
#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) MOZ_DELETE;
};
// Data for tracking JIT-code memory usage.
struct CodeSizes
{
@ -486,20 +523,7 @@ struct ZoneStats
struct CompartmentStats
{
#define FOR_EACH_SIZE(macro) \
macro(Objects, IsLiveGCThing, objectsGCHeapOrdinary) \
macro(Objects, IsLiveGCThing, objectsGCHeapFunction) \
macro(Objects, IsLiveGCThing, objectsGCHeapDenseArray) \
macro(Objects, IsLiveGCThing, objectsGCHeapSlowArray) \
macro(Objects, IsLiveGCThing, objectsGCHeapCrossCompartmentWrapper) \
macro(Private, NotLiveGCThing, objectsPrivate) \
macro(Other, IsLiveGCThing, shapesGCHeapTreeGlobalParented) \
macro(Other, IsLiveGCThing, shapesGCHeapTreeNonGlobalParented) \
macro(Other, IsLiveGCThing, shapesGCHeapDict) \
macro(Other, IsLiveGCThing, shapesGCHeapBase) \
macro(Other, NotLiveGCThing, shapesMallocHeapTreeTables) \
macro(Other, NotLiveGCThing, shapesMallocHeapDictTables) \
macro(Other, NotLiveGCThing, shapesMallocHeapTreeShapeKids) \
macro(Other, NotLiveGCThing, shapesMallocHeapCompartmentTables) \
macro(Other, IsLiveGCThing, scriptsGCHeap) \
macro(Other, NotLiveGCThing, scriptsMallocHeapData) \
macro(Other, NotLiveGCThing, baselineData) \
@ -510,6 +534,7 @@ struct CompartmentStats
macro(Other, NotLiveGCThing, typeInferenceArrayTypeTables) \
macro(Other, NotLiveGCThing, typeInferenceObjectTypeTables) \
macro(Other, NotLiveGCThing, compartmentObject) \
macro(Other, NotLiveGCThing, compartmentTables) \
macro(Other, NotLiveGCThing, crossCompartmentWrappersTable) \
macro(Other, NotLiveGCThing, regexpCompartment) \
macro(Other, NotLiveGCThing, debuggeesSet) \
@ -517,39 +542,69 @@ struct CompartmentStats
CompartmentStats()
: FOR_EACH_SIZE(ZERO_SIZE)
objectsExtra(),
extra()
classInfo(),
extra(),
allClasses(nullptr),
notableClasses(),
isTotals(true)
{}
CompartmentStats(const CompartmentStats &other)
CompartmentStats(CompartmentStats &&other)
: FOR_EACH_SIZE(COPY_OTHER_SIZE)
objectsExtra(other.objectsExtra),
extra(other.extra)
{}
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);
}
void add(const CompartmentStats &other) {
~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)
objectsExtra.add(other.objectsExtra);
// Do nothing with |extra|.
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 += objectsExtra.sizeOfLiveGCThings();
// Do nothing with |extra|.
n += classInfo.sizeOfLiveGCThings();
return n;
}
void addToTabSizes(TabSizes *sizes) const {
MOZ_ASSERT(isTotals);
FOR_EACH_SIZE(ADD_TO_TAB_SIZES);
objectsExtra.addToTabSizes(sizes);
// Do nothing with |extra|.
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)
ObjectsExtraSizes objectsExtra;
void *extra; // This field can be used by embedders.
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
};

Просмотреть файл

@ -951,7 +951,7 @@ JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
size_t *tiArrayTypeTables,
size_t *tiObjectTypeTables,
size_t *compartmentObject,
size_t *shapesCompartmentTables,
size_t *compartmentTables,
size_t *crossCompartmentWrappersArg,
size_t *regexpCompartment,
size_t *debuggeesSet,
@ -960,10 +960,10 @@ JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
*compartmentObject += mallocSizeOf(this);
types.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables,
tiArrayTypeTables, tiObjectTypeTables);
*shapesCompartmentTables += baseShapes.sizeOfExcludingThis(mallocSizeOf)
+ initialShapes.sizeOfExcludingThis(mallocSizeOf)
+ newTypeObjects.sizeOfExcludingThis(mallocSizeOf)
+ lazyTypeObjects.sizeOfExcludingThis(mallocSizeOf);
*compartmentTables += baseShapes.sizeOfExcludingThis(mallocSizeOf)
+ initialShapes.sizeOfExcludingThis(mallocSizeOf)
+ newTypeObjects.sizeOfExcludingThis(mallocSizeOf)
+ lazyTypeObjects.sizeOfExcludingThis(mallocSizeOf);
*crossCompartmentWrappersArg += crossCompartmentWrappers.sizeOfExcludingThis(mallocSizeOf);
*regexpCompartment += regExps.sizeOfExcludingThis(mallocSizeOf);
*debuggeesSet += debuggees.sizeOfExcludingThis(mallocSizeOf);

Просмотреть файл

@ -225,7 +225,7 @@ struct JSCompartment
size_t *tiArrayTypeTables,
size_t *tiObjectTypeTables,
size_t *compartmentObject,
size_t *shapesCompartmentTables,
size_t *compartmentTables,
size_t *crossCompartmentWrappers,
size_t *regexpCompartment,
size_t *debuggeesSet,

Просмотреть файл

@ -6027,14 +6027,14 @@ js_DumpBacktrace(JSContext *cx)
}
void
JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ObjectsExtraSizes *sizes)
JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo *info)
{
if (hasDynamicSlots())
sizes->mallocHeapSlots += mallocSizeOf(slots);
info->objectsMallocHeapSlots += mallocSizeOf(slots);
if (hasDynamicElements()) {
js::ObjectElements *elements = getElementsHeader();
sizes->mallocHeapElementsNonAsmJS += mallocSizeOf(elements);
info->objectsMallocHeapElementsNonAsmJS += mallocSizeOf(elements);
}
// Other things may be measured in the future if DMD indicates it is worthwhile.
@ -6056,22 +6056,22 @@ JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::Objects
// - ( 1.0%, 96.4%): Proxy
} else if (is<ArgumentsObject>()) {
sizes->mallocHeapArgumentsData += as<ArgumentsObject>().sizeOfMisc(mallocSizeOf);
info->objectsMallocHeapMisc += as<ArgumentsObject>().sizeOfMisc(mallocSizeOf);
} else if (is<RegExpStaticsObject>()) {
sizes->mallocHeapRegExpStatics += as<RegExpStaticsObject>().sizeOfData(mallocSizeOf);
info->objectsMallocHeapMisc += as<RegExpStaticsObject>().sizeOfData(mallocSizeOf);
} else if (is<PropertyIteratorObject>()) {
sizes->mallocHeapPropertyIteratorData += as<PropertyIteratorObject>().sizeOfMisc(mallocSizeOf);
info->objectsMallocHeapMisc += as<PropertyIteratorObject>().sizeOfMisc(mallocSizeOf);
} else if (is<ArrayBufferObject>() || is<SharedArrayBufferObject>()) {
ArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, sizes);
ArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, info);
#ifdef JS_ION
} else if (is<AsmJSModuleObject>()) {
as<AsmJSModuleObject>().addSizeOfMisc(mallocSizeOf, &sizes->nonHeapCodeAsmJS,
&sizes->mallocHeapAsmJSModuleData);
as<AsmJSModuleObject>().addSizeOfMisc(mallocSizeOf, &info->objectsNonHeapCodeAsmJS,
&info->objectsMallocHeapMisc);
#endif
#ifdef JS_HAS_CTYPES
} else {
// This must be the last case.
sizes->mallocHeapCtypesData +=
info->objectsMallocHeapMisc +=
js::SizeOfDataIfCDataObject(mallocSizeOf, const_cast<JSObject *>(this));
#endif
}

Просмотреть файл

@ -27,7 +27,7 @@
#include "vm/Xdr.h"
namespace JS {
struct ObjectsExtraSizes;
struct ClassInfo;
}
namespace js {
@ -334,7 +334,7 @@ class JSObject : public js::ObjectImpl
return lastProperty()->hasTable();
}
void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ObjectsExtraSizes *sizes);
void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo *info);
bool hasIdempotentProtoChain() const;

Просмотреть файл

@ -765,7 +765,7 @@ ArrayBufferObject::stealContents(JSContext *cx, Handle<ArrayBufferObject*> buffe
}
/* static */ void
ArrayBufferObject::addSizeOfExcludingThis(JSObject *obj, mozilla::MallocSizeOf mallocSizeOf, JS::ObjectsExtraSizes *sizes)
ArrayBufferObject::addSizeOfExcludingThis(JSObject *obj, mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo *info)
{
ArrayBufferObject &buffer = AsArrayBuffer(obj);
@ -776,14 +776,14 @@ ArrayBufferObject::addSizeOfExcludingThis(JSObject *obj, mozilla::MallocSizeOf m
#if defined (JS_CPU_X64)
// On x64, ArrayBufferObject::prepareForAsmJS switches the
// ArrayBufferObject to use mmap'd storage.
sizes->nonHeapElementsAsmJS += buffer.byteLength();
info->objectsNonHeapElementsAsmJS += buffer.byteLength();
#else
sizes->mallocHeapElementsAsmJS += mallocSizeOf(buffer.dataPointer());
info->objectsMallocHeapElementsAsmJS += mallocSizeOf(buffer.dataPointer());
#endif
} else if (MOZ_UNLIKELY(buffer.isMappedArrayBuffer())) {
sizes->nonHeapElementsMapped += buffer.byteLength();
info->objectsNonHeapElementsMapped += buffer.byteLength();
} else if (buffer.dataPointer()) {
sizes->mallocHeapElementsNonAsmJS += mallocSizeOf(buffer.dataPointer());
info->objectsMallocHeapElementsNonAsmJS += mallocSizeOf(buffer.dataPointer());
}
}

Просмотреть файл

@ -120,7 +120,7 @@ class ArrayBufferObject : public JSObject
}
static void addSizeOfExcludingThis(JSObject *obj, mozilla::MallocSizeOf mallocSizeOf,
JS::ObjectsExtraSizes *sizes);
JS::ClassInfo *info);
void addView(ArrayBufferViewObject *view);

Просмотреть файл

@ -155,6 +155,37 @@ NotableStringInfo &NotableStringInfo::operator=(NotableStringInfo &&info)
return *this;
}
NotableClassInfo::NotableClassInfo()
: ClassInfo(),
className_(nullptr)
{
}
NotableClassInfo::NotableClassInfo(const char *className, const ClassInfo &info)
: ClassInfo(info)
{
size_t bytes = strlen(className) + 1;
className_ = js_pod_malloc<char>(bytes);
if (!className_)
MOZ_CRASH("oom");
PodCopy(className_, className, bytes);
}
NotableClassInfo::NotableClassInfo(NotableClassInfo &&info)
: ClassInfo(Move(info))
{
className_ = info.className_;
info.className_ = nullptr;
}
NotableClassInfo &NotableClassInfo::operator=(NotableClassInfo &&info)
{
MOZ_ASSERT(this != &info, "self-move assignment is prohibited");
this->~NotableClassInfo();
new (this) NotableClassInfo(Move(info));
return *this;
}
NotableScriptSourceInfo::NotableScriptSourceInfo()
: ScriptSourceInfo(),
filename_(nullptr)
@ -246,6 +277,8 @@ StatsCompartmentCallback(JSRuntime *rt, void *data, JSCompartment *compartment)
// CollectRuntimeStats reserves enough space.
MOZ_ALWAYS_TRUE(rtStats->compartmentStatsVector.growBy(1));
CompartmentStats &cStats = rtStats->compartmentStatsVector.back();
if (!cStats.initClasses(rt))
MOZ_CRASH("oom");
rtStats->initExtraCompartmentStats(compartment, &cStats);
compartment->compartmentStats = &cStats;
@ -256,7 +289,7 @@ StatsCompartmentCallback(JSRuntime *rt, void *data, JSCompartment *compartment)
&cStats.typeInferenceArrayTypeTables,
&cStats.typeInferenceObjectTypeTables,
&cStats.compartmentObject,
&cStats.shapesMallocHeapCompartmentTables,
&cStats.compartmentTables,
&cStats.crossCompartmentWrappersTable,
&cStats.regexpCompartment,
&cStats.debuggeesSet,
@ -292,6 +325,25 @@ enum Granularity {
CoarseGrained // Corresponds to AddSizeOfTab()
};
static void
AddClassInfo(Granularity granularity, CompartmentStats *cStats, const char *className,
JS::ClassInfo &info)
{
if (granularity == FineGrained) {
if (!className)
className = "<no class name>";
CompartmentStats::ClassesHashMap::AddPtr p =
cStats->allClasses->lookupForAdd(className);
if (!p) {
// Ignore failure -- we just won't record the
// object/shape/base-shape as notable.
(void)cStats->allClasses->add(p, className, info);
} else {
p->value().add(info);
}
}
}
// The various kinds of hashing are expensive, and the results are unused when
// doing coarse-grained measurements. Skipping them more than doubles the
// profile speed for complex pages such as gmail.com.
@ -307,16 +359,16 @@ StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKin
case JSTRACE_OBJECT: {
JSObject *obj = static_cast<JSObject *>(thing);
CompartmentStats *cStats = GetCompartmentStats(obj->compartment());
if (obj->is<JSFunction>())
cStats->objectsGCHeapFunction += thingSize;
else if (obj->is<ArrayObject>())
cStats->objectsGCHeapDenseArray += thingSize;
else if (obj->is<CrossCompartmentWrapperObject>())
cStats->objectsGCHeapCrossCompartmentWrapper += thingSize;
else
cStats->objectsGCHeapOrdinary += thingSize;
obj->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &cStats->objectsExtra);
JS::ClassInfo info; // This zeroes all the sizes.
info.objectsGCHeap += thingSize;
obj->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &info);
cStats->classInfo.add(info);
const Class *clasp = obj->getClass();
const char *className = clasp->name;
AddClassInfo(granularity, cStats, className, info);
if (ObjectPrivateVisitor *opv = closure->opv) {
nsISupports *iface;
@ -351,30 +403,36 @@ StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKin
case JSTRACE_SHAPE: {
Shape *shape = static_cast<Shape *>(thing);
CompartmentStats *cStats = GetCompartmentStats(shape->compartment());
if (shape->inDictionary()) {
cStats->shapesGCHeapDict += thingSize;
// nullptr because kidsSize shouldn't be incremented in this case.
shape->addSizeOfExcludingThis(rtStats->mallocSizeOf_,
&cStats->shapesMallocHeapDictTables, nullptr);
} else {
JSObject *parent = shape->base()->getObjectParent();
if (parent && parent->is<GlobalObject>())
cStats->shapesGCHeapTreeGlobalParented += thingSize;
else
cStats->shapesGCHeapTreeNonGlobalParented += thingSize;
JS::ClassInfo info; // This zeroes all the sizes.
if (shape->inDictionary())
info.shapesGCHeapDict += thingSize;
else
info.shapesGCHeapTree += thingSize;
shape->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &info);
shape->addSizeOfExcludingThis(rtStats->mallocSizeOf_,
&cStats->shapesMallocHeapTreeTables,
&cStats->shapesMallocHeapTreeShapeKids);
}
cStats->classInfo.add(info);
const BaseShape *base = shape->base();
const Class *clasp = base->clasp();
const char *className = clasp->name;
AddClassInfo(granularity, cStats, className, info);
break;
}
case JSTRACE_BASE_SHAPE: {
BaseShape *base = static_cast<BaseShape *>(thing);
CompartmentStats *cStats = GetCompartmentStats(base->compartment());
cStats->shapesGCHeapBase += thingSize;
JS::ClassInfo info; // This zeroes all the sizes.
info.shapesGCHeapBase += thingSize;
// No malloc-heap measurements.
cStats->classInfo.add(info);
const Class *clasp = base->clasp();
const char *className = clasp->name;
AddClassInfo(granularity, cStats, className, info);
break;
}
@ -450,6 +508,32 @@ StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKin
zStats->unusedGCThings -= thingSize;
}
bool
ZoneStats::initStrings(JSRuntime *rt)
{
isTotals = false;
allStrings = rt->new_<StringsHashMap>();
if (!allStrings || !allStrings->init()) {
js_delete(allStrings);
allStrings = nullptr;
return false;
}
return true;
}
bool
CompartmentStats::initClasses(JSRuntime *rt)
{
isTotals = false;
allClasses = rt->new_<ClassesHashMap>();
if (!allClasses || !allClasses->init()) {
js_delete(allClasses);
allClasses = nullptr;
return false;
}
return true;
}
static bool
FindNotableStrings(ZoneStats &zStats)
{
@ -482,18 +566,39 @@ FindNotableStrings(ZoneStats &zStats)
return true;
}
bool
ZoneStats::initStrings(JSRuntime *rt)
static bool
FindNotableClasses(CompartmentStats &cStats)
{
isTotals = false;
allStrings = rt->new_<StringsHashMap>();
if (!allStrings)
return false;
if (!allStrings->init()) {
js_delete(allStrings);
allStrings = nullptr;
return false;
using namespace JS;
// We should only run FindNotableClasses once per ZoneStats object.
MOZ_ASSERT(cStats.notableClasses.empty());
for (CompartmentStats::ClassesHashMap::Range r = cStats.allClasses->all();
!r.empty();
r.popFront())
{
const char *className = r.front().key();
ClassInfo &info = r.front().value();
// If this class isn't notable, or if we can't grow the notableStrings
// vector, skip this string.
if (!info.isNotable())
continue;
if (!cStats.notableClasses.growBy(1))
return false;
cStats.notableClasses.back() = NotableClassInfo(className, info);
// We're moving this class from a non-notable to a notable bucket, so
// subtract it out of the non-notable tallies.
cStats.classInfo.subtract(info);
}
// Delete |allClasses| now, rather than waiting for zStats's destruction,
// to reduce peak memory consumption during reporting.
js_delete(cStats.allClasses);
cStats.allClasses = nullptr;
return true;
}
@ -577,11 +682,21 @@ JS::CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisit
MOZ_ASSERT(!zTotals.allStrings);
for (size_t i = 0; i < rtStats->compartmentStatsVector.length(); i++) {
CompartmentStats &cStats = rtStats->compartmentStatsVector[i];
rtStats->cTotals.add(cStats);
CompartmentStatsVector &cs = rtStats->compartmentStatsVector;
CompartmentStats &cTotals = rtStats->cTotals;
// As with the zones, we sum all compartments first, and then get the
// notable classes within each zone.
for (size_t i = 0; i < cs.length(); i++)
cTotals.addSizes(cs[i]);
for (size_t i = 0; i < cs.length(); i++) {
if (!FindNotableClasses(cs[i]))
return false;
}
MOZ_ASSERT(!cTotals.allClasses);
rtStats->gcHeapGCThings = rtStats->zTotals.sizeOfLiveGCThings() +
rtStats->cTotals.sizeOfLiveGCThings();
@ -685,10 +800,8 @@ AddSizeOfTab(JSRuntime *rt, HandleObject obj, MallocSizeOf mallocSizeOf, ObjectP
JS_ASSERT(rtStats.zoneStatsVector.length() == 1);
rtStats.zTotals.addSizes(rtStats.zoneStatsVector[0]);
for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++) {
CompartmentStats &cStats = rtStats.compartmentStatsVector[i];
rtStats.cTotals.add(cStats);
}
for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++)
rtStats.cTotals.addSizes(rtStats.compartmentStatsVector[i]);
for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
comp->compartmentStats = nullptr;

Просмотреть файл

@ -26,6 +26,7 @@
#include "gc/Marking.h"
#include "gc/Rooting.h"
#include "js/HashTable.h"
#include "js/MemoryMetrics.h"
#include "js/RootingAPI.h"
#include "vm/PropDesc.h"
@ -719,12 +720,17 @@ class Shape : public gc::BarrieredCell<Shape>
ShapeTable &table() const { return base()->table(); }
void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
size_t *propTableSize, size_t *kidsSize) const {
if (hasTable())
*propTableSize += table().sizeOfIncludingThis(mallocSizeOf);
JS::ClassInfo *info) const
{
if (hasTable()) {
if (inDictionary())
info->shapesMallocHeapDictTables += table().sizeOfIncludingThis(mallocSizeOf);
else
info->shapesMallocHeapTreeTables += table().sizeOfIncludingThis(mallocSizeOf);
}
if (!inDictionary() && kids.isHash())
*kidsSize += kids.toHash()->sizeOfIncludingThis(mallocSizeOf);
info->shapesMallocHeapTreeKids += kids.toHash()->sizeOfIncludingThis(mallocSizeOf);
}
bool isNative() const {

Просмотреть файл

@ -2012,6 +2012,109 @@ ReportZoneStats(const JS::ZoneStats &zStats,
# undef STRING_LENGTH
}
static nsresult
ReportClassStats(const ClassInfo &classInfo, const nsACString &path,
nsIHandleReportCallback *cb, nsISupports *closure,
size_t &gcTotal)
{
// We deliberately don't use ZCREPORT_BYTES, so that these per-class values
// don't go into sundries.
if (classInfo.objectsGCHeap > 0) {
REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("objects/gc-heap"),
classInfo.objectsGCHeap,
"Objects, including fixed slots.");
}
if (classInfo.objectsMallocHeapSlots > 0) {
REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/malloc-heap/slots"),
KIND_HEAP, classInfo.objectsMallocHeapSlots,
"Non-fixed object slots.");
}
if (classInfo.objectsMallocHeapElementsNonAsmJS > 0) {
REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/malloc-heap/elements/non-asm.js"),
KIND_HEAP, classInfo.objectsMallocHeapElementsNonAsmJS,
"Non-asm.js indexed elements.");
}
// asm.js arrays are heap-allocated on some platforms and
// non-heap-allocated on others. We never put them under sundries,
// because (a) in practice they're almost always larger than the sundries
// threshold, and (b) we'd need a third category of sundries ("non-heap"),
// which would be a pain.
size_t mallocHeapElementsAsmJS = classInfo.objectsMallocHeapElementsAsmJS;
size_t nonHeapElementsAsmJS = classInfo.objectsNonHeapElementsAsmJS;
MOZ_ASSERT(mallocHeapElementsAsmJS == 0 || nonHeapElementsAsmJS == 0);
if (mallocHeapElementsAsmJS > 0) {
REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/malloc-heap/elements/asm.js"),
KIND_HEAP, mallocHeapElementsAsmJS,
"asm.js array buffer elements on the malloc heap.");
}
if (nonHeapElementsAsmJS > 0) {
REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/non-heap/elements/asm.js"),
KIND_NONHEAP, nonHeapElementsAsmJS,
"asm.js array buffer elements outside both the malloc heap and "
"the GC heap.");
}
if (classInfo.objectsNonHeapElementsMapped > 0) {
REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/non-heap/elements/mapped"),
KIND_NONHEAP, classInfo.objectsNonHeapElementsMapped,
"Memory-mapped array buffer elements.");
}
if (classInfo.objectsNonHeapCodeAsmJS > 0) {
REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/non-heap/code/asm.js"),
KIND_NONHEAP, classInfo.objectsNonHeapCodeAsmJS,
"AOT-compiled asm.js code.");
}
if (classInfo.objectsMallocHeapMisc > 0) {
REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/malloc-heap/misc"),
KIND_HEAP, classInfo.objectsMallocHeapMisc,
"Miscellaneous object data.");
}
if (classInfo.shapesGCHeapTree > 0) {
REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("shapes/gc-heap/tree"),
classInfo.shapesGCHeapTree,
"Shapes in a property tree.");
}
if (classInfo.shapesGCHeapDict > 0) {
REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("shapes/gc-heap/dict"),
classInfo.shapesGCHeapDict,
"Shapes in dictionary mode.");
}
if (classInfo.shapesGCHeapBase > 0) {
REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("shapes/gc-heap/base"),
classInfo.shapesGCHeapBase,
"Base shapes, which collate data common to many shapes.");
}
if (classInfo.shapesMallocHeapTreeTables > 0) {
REPORT_BYTES(path + NS_LITERAL_CSTRING("shapes/malloc-heap/tree-tables"),
KIND_HEAP, classInfo.shapesMallocHeapTreeTables,
"Property tables of shapes in a property tree.");
}
if (classInfo.shapesMallocHeapDictTables > 0) {
REPORT_BYTES(path + NS_LITERAL_CSTRING("shapes/malloc-heap/dict-tables"),
KIND_HEAP, classInfo.shapesMallocHeapDictTables,
"Property tables of shapes in dictionary mode.");
}
if (classInfo.shapesMallocHeapTreeKids > 0) {
REPORT_BYTES(path + NS_LITERAL_CSTRING("shapes/malloc-heap/tree-kids"),
KIND_HEAP, classInfo.shapesMallocHeapTreeKids,
"Kid hashes of shapes in a property tree.");
}
return NS_OK;
}
static nsresult
ReportCompartmentStats(const JS::CompartmentStats &cStats,
const xpc::CompartmentStatsExtras &extras,
@ -2024,6 +2127,9 @@ ReportCompartmentStats(const JS::CompartmentStats &cStats,
size_t gcTotal = 0, sundriesGCHeap = 0, sundriesMallocHeap = 0;
nsAutoCString cJSPathPrefix = extras.jsPathPrefix;
nsAutoCString cDOMPathPrefix = extras.domPathPrefix;
nsresult rv;
MOZ_ASSERT(!gcTotalOut == cStats.isTotals);
// Only attempt to prefix if we got a location and the path wasn't already
// prefixed.
@ -2044,26 +2150,25 @@ ReportCompartmentStats(const JS::CompartmentStats &cStats,
}
}
ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/ordinary"),
cStats.objectsGCHeapOrdinary,
"Ordinary objects, i.e. not otherwise distinguished by memory "
"reporters.");
nsCString nonNotablePath = cJSPathPrefix;
nonNotablePath += cStats.isTotals
? NS_LITERAL_CSTRING("classes/")
: NS_LITERAL_CSTRING("classes/class(<non-notable classes>)/");
ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/function"),
cStats.objectsGCHeapFunction,
"Function objects.");
rv = ReportClassStats(cStats.classInfo, nonNotablePath, cb, closure,
gcTotal);
NS_ENSURE_SUCCESS(rv, rv);
ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/dense-array"),
cStats.objectsGCHeapDenseArray,
"Dense array objects.");
for (size_t i = 0; i < cStats.notableClasses.length(); i++) {
MOZ_ASSERT(!cStats.isTotals);
const JS::NotableClassInfo& classInfo = cStats.notableClasses[i];
ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/slow-array"),
cStats.objectsGCHeapSlowArray,
"Slow array objects.");
nsCString classPath = cJSPathPrefix +
nsPrintfCString("classes/class(%s)/", classInfo.className_);
ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/cross-compartment-wrapper"),
cStats.objectsGCHeapCrossCompartmentWrapper,
"Cross-compartment wrapper objects.");
rv = ReportClassStats(classInfo, classPath, cb, closure, gcTotal);
NS_ENSURE_SUCCESS(rv, rv);
}
// Note that we use cDOMPathPrefix here. This is because we measure orphan
// DOM nodes in the JS reporter, but we want to report them in a "dom"
@ -2073,41 +2178,6 @@ ReportCompartmentStats(const JS::CompartmentStats &cStats,
"Orphan DOM nodes, i.e. those that are only reachable from JavaScript "
"objects.");
ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/tree/global-parented"),
cStats.shapesGCHeapTreeGlobalParented,
"Shapes that (a) are in a property tree, and (b) represent an object "
"whose parent is the global object.");
ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/tree/non-global-parented"),
cStats.shapesGCHeapTreeNonGlobalParented,
"Shapes that (a) are in a property tree, and (b) represent an object "
"whose parent is not the global object.");
ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/dict"),
cStats.shapesGCHeapDict,
"Shapes that are in dictionary mode.");
ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/base"),
cStats.shapesGCHeapBase,
"Base shapes, which collate data common to many shapes.");
ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/tree-tables"),
cStats.shapesMallocHeapTreeTables,
"Property tables belonging to shapes that are in a property tree.");
ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/dict-tables"),
cStats.shapesMallocHeapDictTables,
"Property tables that belong to shapes that are in dictionary mode.");
ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/tree-shape-kids"),
cStats.shapesMallocHeapTreeShapeKids,
"Kid hashes that belong to shapes that are in a property tree.");
ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/compartment-tables"),
cStats.shapesMallocHeapCompartmentTables,
"Compartment-wide tables storing shape information used during object "
"construction.");
ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("scripts/gc-heap"),
cStats.scriptsGCHeap,
"JSScript instances. There is one per user-defined function in a "
@ -2149,6 +2219,10 @@ ReportCompartmentStats(const JS::CompartmentStats &cStats,
cStats.compartmentObject,
"The JSCompartment object itself.");
ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("compartment-tables"),
cStats.compartmentTables,
"Compartment-wide tables storing shape and type object information.");
ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("cross-compartment-wrapper-table"),
cStats.crossCompartmentWrappersTable,
"The cross-compartment wrapper table.");
@ -2161,64 +2235,6 @@ ReportCompartmentStats(const JS::CompartmentStats &cStats,
cStats.debuggeesSet,
"The debuggees set.");
ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/slots"),
cStats.objectsExtra.mallocHeapSlots,
"Non-fixed object slot arrays, which represent object properties.");
ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/elements/non-asm.js"),
cStats.objectsExtra.mallocHeapElementsNonAsmJS,
"Non-asm.js indexed elements.");
// asm.js arrays are heap-allocated on some platforms and
// non-heap-allocated on others. We never put them under sundries,
// because (a) in practice they're almost always larger than the sundries
// threshold, and (b) we'd need a third category of sundries ("non-heap"),
// which would be a pain.
size_t mallocHeapElementsAsmJS = cStats.objectsExtra.mallocHeapElementsAsmJS;
size_t nonHeapElementsAsmJS = cStats.objectsExtra.nonHeapElementsAsmJS;
MOZ_ASSERT(mallocHeapElementsAsmJS == 0 || nonHeapElementsAsmJS == 0);
if (mallocHeapElementsAsmJS > 0) {
REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/elements/asm.js"),
KIND_HEAP, mallocHeapElementsAsmJS,
"asm.js array buffer elements on the malloc heap.");
}
if (nonHeapElementsAsmJS > 0) {
REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/non-heap/elements/asm.js"),
KIND_NONHEAP, nonHeapElementsAsmJS,
"asm.js array buffer elements outside both the malloc heap and "
"the GC heap.");
}
if (cStats.objectsExtra.nonHeapElementsMapped > 0) {
REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/non-heap/elements/mapped"),
KIND_NONHEAP, cStats.objectsExtra.nonHeapElementsMapped,
"Memory-mapped array buffer elements.");
}
REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/non-heap/code/asm.js"),
KIND_NONHEAP, cStats.objectsExtra.nonHeapCodeAsmJS,
"AOT-compiled asm.js code.");
ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/asm.js-module-data"),
cStats.objectsExtra.mallocHeapAsmJSModuleData,
"asm.js module data.");
ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/arguments-data"),
cStats.objectsExtra.mallocHeapArgumentsData,
"Data belonging to Arguments objects.");
ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/regexp-statics"),
cStats.objectsExtra.mallocHeapRegExpStatics,
"Data belonging to the RegExpStatics object.");
ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/property-iterator-data"),
cStats.objectsExtra.mallocHeapPropertyIteratorData,
"Data belonging to property iterator objects.");
ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/ctypes-data"),
cStats.objectsExtra.mallocHeapCtypesData,
"Data belonging to ctypes objects.");
if (sundriesGCHeap > 0) {
// We deliberately don't use ZCREPORT_GC_BYTES here.
REPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("sundries/gc-heap"),
@ -2288,9 +2304,10 @@ ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats,
}
for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++) {
JS::CompartmentStats cStats = rtStats.compartmentStatsVector[i];
const JS::CompartmentStats &cStats = rtStats.compartmentStatsVector[i];
const xpc::CompartmentStatsExtras *extras =
static_cast<const xpc::CompartmentStatsExtras*>(cStats.extra);
rv = ReportCompartmentStats(cStats, *extras, addonManager, cb, closure,
&gcTotal);
NS_ENSURE_SUCCESS(rv, rv);