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: #include "js/MemoryMetrics.h" michael@0: michael@0: #include "mozilla/DebugOnly.h" michael@0: michael@0: #include "jsapi.h" michael@0: #include "jscompartment.h" michael@0: #include "jsgc.h" michael@0: #include "jsobj.h" michael@0: #include "jsscript.h" michael@0: michael@0: #include "jit/BaselineJIT.h" michael@0: #include "jit/Ion.h" michael@0: #include "vm/ArrayObject.h" michael@0: #include "vm/Runtime.h" michael@0: #include "vm/Shape.h" michael@0: #include "vm/String.h" michael@0: #include "vm/WrapperObject.h" michael@0: michael@0: using mozilla::DebugOnly; michael@0: using mozilla::MallocSizeOf; michael@0: using mozilla::Move; michael@0: using mozilla::PodCopy; michael@0: using mozilla::PodEqual; michael@0: michael@0: using namespace js; michael@0: michael@0: using JS::RuntimeStats; michael@0: using JS::ObjectPrivateVisitor; michael@0: using JS::ZoneStats; michael@0: using JS::CompartmentStats; michael@0: michael@0: namespace js { michael@0: michael@0: JS_FRIEND_API(size_t) michael@0: MemoryReportingSundriesThreshold() michael@0: { michael@0: return 8 * 1024; michael@0: } michael@0: michael@0: /* static */ HashNumber michael@0: InefficientNonFlatteningStringHashPolicy::hash(const Lookup &l) michael@0: { michael@0: ScopedJSFreePtr ownedChars; michael@0: const jschar *chars; michael@0: if (l->hasPureChars()) { michael@0: chars = l->pureChars(); michael@0: } else { michael@0: // Slowest hash function evar! michael@0: if (!l->copyNonPureChars(/* tcx */ nullptr, ownedChars)) michael@0: MOZ_CRASH("oom"); michael@0: chars = ownedChars; michael@0: } michael@0: michael@0: return mozilla::HashString(chars, l->length()); michael@0: } michael@0: michael@0: /* static */ bool michael@0: InefficientNonFlatteningStringHashPolicy::match(const JSString *const &k, const Lookup &l) michael@0: { michael@0: // We can't use js::EqualStrings, because that flattens our strings. michael@0: if (k->length() != l->length()) michael@0: return false; michael@0: michael@0: const jschar *c1; michael@0: ScopedJSFreePtr ownedChars1; michael@0: if (k->hasPureChars()) { michael@0: c1 = k->pureChars(); michael@0: } else { michael@0: if (!k->copyNonPureChars(/* tcx */ nullptr, ownedChars1)) michael@0: MOZ_CRASH("oom"); michael@0: c1 = ownedChars1; michael@0: } michael@0: michael@0: const jschar *c2; michael@0: ScopedJSFreePtr ownedChars2; michael@0: if (l->hasPureChars()) { michael@0: c2 = l->pureChars(); michael@0: } else { michael@0: if (!l->copyNonPureChars(/* tcx */ nullptr, ownedChars2)) michael@0: MOZ_CRASH("oom"); michael@0: c2 = ownedChars2; michael@0: } michael@0: michael@0: return PodEqual(c1, c2, k->length()); michael@0: } michael@0: michael@0: /* static */ HashNumber michael@0: CStringHashPolicy::hash(const Lookup &l) michael@0: { michael@0: return mozilla::HashString(l); michael@0: } michael@0: michael@0: /* static */ bool michael@0: CStringHashPolicy::match(const char *const &k, const Lookup &l) michael@0: { michael@0: return strcmp(k, l) == 0; michael@0: } michael@0: michael@0: } // namespace js michael@0: michael@0: namespace JS { michael@0: michael@0: NotableStringInfo::NotableStringInfo() michael@0: : StringInfo(), michael@0: buffer(0), michael@0: length(0) michael@0: { michael@0: } michael@0: michael@0: NotableStringInfo::NotableStringInfo(JSString *str, const StringInfo &info) michael@0: : StringInfo(info), michael@0: length(str->length()) michael@0: { michael@0: size_t bufferSize = Min(str->length() + 1, size_t(MAX_SAVED_CHARS)); michael@0: buffer = js_pod_malloc(bufferSize); michael@0: if (!buffer) { michael@0: MOZ_CRASH("oom"); michael@0: } michael@0: michael@0: const jschar* chars; michael@0: ScopedJSFreePtr ownedChars; michael@0: if (str->hasPureChars()) { michael@0: chars = str->pureChars(); michael@0: } else { michael@0: if (!str->copyNonPureChars(/* tcx */ nullptr, ownedChars)) michael@0: MOZ_CRASH("oom"); michael@0: chars = ownedChars; michael@0: } michael@0: michael@0: // We might truncate |str| even if it's much shorter than 1024 chars, if michael@0: // |str| contains unicode chars. Since this is just for a memory reporter, michael@0: // we don't care. michael@0: PutEscapedString(buffer, bufferSize, chars, str->length(), /* quote */ 0); michael@0: } michael@0: michael@0: NotableStringInfo::NotableStringInfo(NotableStringInfo &&info) michael@0: : StringInfo(Move(info)), michael@0: length(info.length) michael@0: { michael@0: buffer = info.buffer; michael@0: info.buffer = nullptr; michael@0: } michael@0: michael@0: NotableStringInfo &NotableStringInfo::operator=(NotableStringInfo &&info) michael@0: { michael@0: MOZ_ASSERT(this != &info, "self-move assignment is prohibited"); michael@0: this->~NotableStringInfo(); michael@0: new (this) NotableStringInfo(Move(info)); michael@0: return *this; michael@0: } michael@0: michael@0: NotableScriptSourceInfo::NotableScriptSourceInfo() michael@0: : ScriptSourceInfo(), michael@0: filename_(nullptr) michael@0: { michael@0: } michael@0: michael@0: NotableScriptSourceInfo::NotableScriptSourceInfo(const char *filename, const ScriptSourceInfo &info) michael@0: : ScriptSourceInfo(info) michael@0: { michael@0: size_t bytes = strlen(filename) + 1; michael@0: filename_ = js_pod_malloc(bytes); michael@0: if (!filename_) michael@0: MOZ_CRASH("oom"); michael@0: PodCopy(filename_, filename, bytes); michael@0: } michael@0: michael@0: NotableScriptSourceInfo::NotableScriptSourceInfo(NotableScriptSourceInfo &&info) michael@0: : ScriptSourceInfo(Move(info)) michael@0: { michael@0: filename_ = info.filename_; michael@0: info.filename_ = nullptr; michael@0: } michael@0: michael@0: NotableScriptSourceInfo &NotableScriptSourceInfo::operator=(NotableScriptSourceInfo &&info) michael@0: { michael@0: MOZ_ASSERT(this != &info, "self-move assignment is prohibited"); michael@0: this->~NotableScriptSourceInfo(); michael@0: new (this) NotableScriptSourceInfo(Move(info)); michael@0: return *this; michael@0: } michael@0: michael@0: michael@0: } // namespace JS michael@0: michael@0: typedef HashSet, SystemAllocPolicy> SourceSet; michael@0: michael@0: struct StatsClosure michael@0: { michael@0: RuntimeStats *rtStats; michael@0: ObjectPrivateVisitor *opv; michael@0: SourceSet seenSources; michael@0: StatsClosure(RuntimeStats *rt, ObjectPrivateVisitor *v) : rtStats(rt), opv(v) {} michael@0: bool init() { michael@0: return seenSources.init(); michael@0: } michael@0: }; michael@0: michael@0: static void michael@0: DecommittedArenasChunkCallback(JSRuntime *rt, void *data, gc::Chunk *chunk) michael@0: { michael@0: // This case is common and fast to check. Do it first. michael@0: if (chunk->decommittedArenas.isAllClear()) michael@0: return; michael@0: michael@0: size_t n = 0; michael@0: for (size_t i = 0; i < gc::ArenasPerChunk; i++) { michael@0: if (chunk->decommittedArenas.get(i)) michael@0: n += gc::ArenaSize; michael@0: } michael@0: JS_ASSERT(n > 0); michael@0: *static_cast(data) += n; michael@0: } michael@0: michael@0: static void michael@0: StatsZoneCallback(JSRuntime *rt, void *data, Zone *zone) michael@0: { michael@0: // Append a new CompartmentStats to the vector. michael@0: RuntimeStats *rtStats = static_cast(data)->rtStats; michael@0: michael@0: // CollectRuntimeStats reserves enough space. michael@0: MOZ_ALWAYS_TRUE(rtStats->zoneStatsVector.growBy(1)); michael@0: ZoneStats &zStats = rtStats->zoneStatsVector.back(); michael@0: if (!zStats.initStrings(rt)) michael@0: MOZ_CRASH("oom"); michael@0: rtStats->initExtraZoneStats(zone, &zStats); michael@0: rtStats->currZoneStats = &zStats; michael@0: michael@0: zone->addSizeOfIncludingThis(rtStats->mallocSizeOf_, michael@0: &zStats.typePool, michael@0: &zStats.baselineStubsOptimized); michael@0: } michael@0: michael@0: static void michael@0: StatsCompartmentCallback(JSRuntime *rt, void *data, JSCompartment *compartment) michael@0: { michael@0: // Append a new CompartmentStats to the vector. michael@0: RuntimeStats *rtStats = static_cast(data)->rtStats; michael@0: michael@0: // CollectRuntimeStats reserves enough space. michael@0: MOZ_ALWAYS_TRUE(rtStats->compartmentStatsVector.growBy(1)); michael@0: CompartmentStats &cStats = rtStats->compartmentStatsVector.back(); michael@0: rtStats->initExtraCompartmentStats(compartment, &cStats); michael@0: michael@0: compartment->compartmentStats = &cStats; michael@0: michael@0: // Measure the compartment object itself, and things hanging off it. michael@0: compartment->addSizeOfIncludingThis(rtStats->mallocSizeOf_, michael@0: &cStats.typeInferenceAllocationSiteTables, michael@0: &cStats.typeInferenceArrayTypeTables, michael@0: &cStats.typeInferenceObjectTypeTables, michael@0: &cStats.compartmentObject, michael@0: &cStats.shapesMallocHeapCompartmentTables, michael@0: &cStats.crossCompartmentWrappersTable, michael@0: &cStats.regexpCompartment, michael@0: &cStats.debuggeesSet, michael@0: &cStats.savedStacksSet); michael@0: } michael@0: michael@0: static void michael@0: StatsArenaCallback(JSRuntime *rt, void *data, gc::Arena *arena, michael@0: JSGCTraceKind traceKind, size_t thingSize) michael@0: { michael@0: RuntimeStats *rtStats = static_cast(data)->rtStats; michael@0: michael@0: // The admin space includes (a) the header and (b) the padding between the michael@0: // end of the header and the start of the first GC thing. michael@0: size_t allocationSpace = arena->thingsSpan(thingSize); michael@0: rtStats->currZoneStats->gcHeapArenaAdmin += gc::ArenaSize - allocationSpace; michael@0: michael@0: // We don't call the callback on unused things. So we compute the michael@0: // unused space like this: arenaUnused = maxArenaUnused - arenaUsed. michael@0: // We do this by setting arenaUnused to maxArenaUnused here, and then michael@0: // subtracting thingSize for every used cell, in StatsCellCallback(). michael@0: rtStats->currZoneStats->unusedGCThings += allocationSpace; michael@0: } michael@0: michael@0: static CompartmentStats * michael@0: GetCompartmentStats(JSCompartment *comp) michael@0: { michael@0: return static_cast(comp->compartmentStats); michael@0: } michael@0: michael@0: enum Granularity { michael@0: FineGrained, // Corresponds to CollectRuntimeStats() michael@0: CoarseGrained // Corresponds to AddSizeOfTab() michael@0: }; michael@0: michael@0: // The various kinds of hashing are expensive, and the results are unused when michael@0: // doing coarse-grained measurements. Skipping them more than doubles the michael@0: // profile speed for complex pages such as gmail.com. michael@0: template michael@0: static void michael@0: StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKind, michael@0: size_t thingSize) michael@0: { michael@0: StatsClosure *closure = static_cast(data); michael@0: RuntimeStats *rtStats = closure->rtStats; michael@0: ZoneStats *zStats = rtStats->currZoneStats; michael@0: switch (traceKind) { michael@0: case JSTRACE_OBJECT: { michael@0: JSObject *obj = static_cast(thing); michael@0: CompartmentStats *cStats = GetCompartmentStats(obj->compartment()); michael@0: if (obj->is()) michael@0: cStats->objectsGCHeapFunction += thingSize; michael@0: else if (obj->is()) michael@0: cStats->objectsGCHeapDenseArray += thingSize; michael@0: else if (obj->is()) michael@0: cStats->objectsGCHeapCrossCompartmentWrapper += thingSize; michael@0: else michael@0: cStats->objectsGCHeapOrdinary += thingSize; michael@0: michael@0: obj->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &cStats->objectsExtra); michael@0: michael@0: if (ObjectPrivateVisitor *opv = closure->opv) { michael@0: nsISupports *iface; michael@0: if (opv->getISupports_(obj, &iface) && iface) michael@0: cStats->objectsPrivate += opv->sizeOfIncludingThis(iface); michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case JSTRACE_STRING: { michael@0: JSString *str = static_cast(thing); michael@0: michael@0: JS::StringInfo info; michael@0: info.gcHeap = thingSize; michael@0: info.mallocHeap = str->sizeOfExcludingThis(rtStats->mallocSizeOf_); michael@0: info.numCopies = 1; michael@0: michael@0: zStats->stringInfo.add(info); michael@0: michael@0: if (granularity == FineGrained) { michael@0: ZoneStats::StringsHashMap::AddPtr p = zStats->allStrings->lookupForAdd(str); michael@0: if (!p) { michael@0: // Ignore failure -- we just won't record the string as notable. michael@0: (void)zStats->allStrings->add(p, str, info); michael@0: } else { michael@0: p->value().add(info); michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case JSTRACE_SHAPE: { michael@0: Shape *shape = static_cast(thing); michael@0: CompartmentStats *cStats = GetCompartmentStats(shape->compartment()); michael@0: if (shape->inDictionary()) { michael@0: cStats->shapesGCHeapDict += thingSize; michael@0: michael@0: // nullptr because kidsSize shouldn't be incremented in this case. michael@0: shape->addSizeOfExcludingThis(rtStats->mallocSizeOf_, michael@0: &cStats->shapesMallocHeapDictTables, nullptr); michael@0: } else { michael@0: JSObject *parent = shape->base()->getObjectParent(); michael@0: if (parent && parent->is()) michael@0: cStats->shapesGCHeapTreeGlobalParented += thingSize; michael@0: else michael@0: cStats->shapesGCHeapTreeNonGlobalParented += thingSize; michael@0: michael@0: shape->addSizeOfExcludingThis(rtStats->mallocSizeOf_, michael@0: &cStats->shapesMallocHeapTreeTables, michael@0: &cStats->shapesMallocHeapTreeShapeKids); michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case JSTRACE_BASE_SHAPE: { michael@0: BaseShape *base = static_cast(thing); michael@0: CompartmentStats *cStats = GetCompartmentStats(base->compartment()); michael@0: cStats->shapesGCHeapBase += thingSize; michael@0: break; michael@0: } michael@0: michael@0: case JSTRACE_SCRIPT: { michael@0: JSScript *script = static_cast(thing); michael@0: CompartmentStats *cStats = GetCompartmentStats(script->compartment()); michael@0: cStats->scriptsGCHeap += thingSize; michael@0: cStats->scriptsMallocHeapData += script->sizeOfData(rtStats->mallocSizeOf_); michael@0: cStats->typeInferenceTypeScripts += script->sizeOfTypeScript(rtStats->mallocSizeOf_); michael@0: #ifdef JS_ION michael@0: jit::AddSizeOfBaselineData(script, rtStats->mallocSizeOf_, &cStats->baselineData, michael@0: &cStats->baselineStubsFallback); michael@0: cStats->ionData += jit::SizeOfIonData(script, rtStats->mallocSizeOf_); michael@0: #endif michael@0: michael@0: ScriptSource *ss = script->scriptSource(); michael@0: SourceSet::AddPtr entry = closure->seenSources.lookupForAdd(ss); michael@0: if (!entry) { michael@0: (void)closure->seenSources.add(entry, ss); // Not much to be done on failure. michael@0: michael@0: JS::ScriptSourceInfo info; // This zeroes all the sizes. michael@0: ss->addSizeOfIncludingThis(rtStats->mallocSizeOf_, &info); michael@0: MOZ_ASSERT(info.compressed == 0 || info.uncompressed == 0); michael@0: michael@0: rtStats->runtime.scriptSourceInfo.add(info); michael@0: michael@0: if (granularity == FineGrained) { michael@0: const char* filename = ss->filename(); michael@0: if (!filename) michael@0: filename = ""; michael@0: michael@0: JS::RuntimeSizes::ScriptSourcesHashMap::AddPtr p = michael@0: rtStats->runtime.allScriptSources->lookupForAdd(filename); michael@0: if (!p) { michael@0: // Ignore failure -- we just won't record the script source as notable. michael@0: (void)rtStats->runtime.allScriptSources->add(p, filename, info); michael@0: } else { michael@0: p->value().add(info); michael@0: } michael@0: } michael@0: } michael@0: michael@0: break; michael@0: } michael@0: michael@0: case JSTRACE_LAZY_SCRIPT: { michael@0: LazyScript *lazy = static_cast(thing); michael@0: zStats->lazyScriptsGCHeap += thingSize; michael@0: zStats->lazyScriptsMallocHeap += lazy->sizeOfExcludingThis(rtStats->mallocSizeOf_); michael@0: break; michael@0: } michael@0: michael@0: case JSTRACE_JITCODE: { michael@0: #ifdef JS_ION michael@0: zStats->jitCodesGCHeap += thingSize; michael@0: // The code for a script is counted in ExecutableAllocator::sizeOfCode(). michael@0: #endif michael@0: break; michael@0: } michael@0: michael@0: case JSTRACE_TYPE_OBJECT: { michael@0: types::TypeObject *obj = static_cast(thing); michael@0: zStats->typeObjectsGCHeap += thingSize; michael@0: zStats->typeObjectsMallocHeap += obj->sizeOfExcludingThis(rtStats->mallocSizeOf_); michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("invalid traceKind"); michael@0: } michael@0: michael@0: // Yes, this is a subtraction: see StatsArenaCallback() for details. michael@0: zStats->unusedGCThings -= thingSize; michael@0: } michael@0: michael@0: static bool michael@0: FindNotableStrings(ZoneStats &zStats) michael@0: { michael@0: using namespace JS; michael@0: michael@0: // We should only run FindNotableStrings once per ZoneStats object. michael@0: MOZ_ASSERT(zStats.notableStrings.empty()); michael@0: michael@0: for (ZoneStats::StringsHashMap::Range r = zStats.allStrings->all(); !r.empty(); r.popFront()) { michael@0: michael@0: JSString *str = r.front().key(); michael@0: StringInfo &info = r.front().value(); michael@0: michael@0: if (!info.isNotable()) michael@0: continue; michael@0: michael@0: if (!zStats.notableStrings.growBy(1)) michael@0: return false; michael@0: michael@0: zStats.notableStrings.back() = NotableStringInfo(str, info); michael@0: michael@0: // We're moving this string from a non-notable to a notable bucket, so michael@0: // subtract it out of the non-notable tallies. michael@0: zStats.stringInfo.subtract(info); michael@0: } michael@0: // Delete |allStrings| now, rather than waiting for zStats's destruction, michael@0: // to reduce peak memory consumption during reporting. michael@0: js_delete(zStats.allStrings); michael@0: zStats.allStrings = nullptr; michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ZoneStats::initStrings(JSRuntime *rt) michael@0: { michael@0: isTotals = false; michael@0: allStrings = rt->new_(); michael@0: if (!allStrings) michael@0: return false; michael@0: if (!allStrings->init()) { michael@0: js_delete(allStrings); michael@0: allStrings = nullptr; michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: FindNotableScriptSources(JS::RuntimeSizes &runtime) michael@0: { michael@0: using namespace JS; michael@0: michael@0: // We should only run FindNotableScriptSources once per RuntimeSizes. michael@0: MOZ_ASSERT(runtime.notableScriptSources.empty()); michael@0: michael@0: for (RuntimeSizes::ScriptSourcesHashMap::Range r = runtime.allScriptSources->all(); michael@0: !r.empty(); michael@0: r.popFront()) michael@0: { michael@0: const char *filename = r.front().key(); michael@0: ScriptSourceInfo &info = r.front().value(); michael@0: michael@0: if (!info.isNotable()) michael@0: continue; michael@0: michael@0: if (!runtime.notableScriptSources.growBy(1)) michael@0: return false; michael@0: michael@0: runtime.notableScriptSources.back() = NotableScriptSourceInfo(filename, info); michael@0: michael@0: // We're moving this script source from a non-notable to a notable michael@0: // bucket, so subtract its sizes from the non-notable tallies. michael@0: runtime.scriptSourceInfo.subtract(info); michael@0: } michael@0: // Delete |allScriptSources| now, rather than waiting for zStats's michael@0: // destruction, to reduce peak memory consumption during reporting. michael@0: js_delete(runtime.allScriptSources); michael@0: runtime.allScriptSources = nullptr; michael@0: return true; michael@0: } michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS::CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisitor *opv) michael@0: { michael@0: if (!rtStats->compartmentStatsVector.reserve(rt->numCompartments)) michael@0: return false; michael@0: michael@0: if (!rtStats->zoneStatsVector.reserve(rt->zones.length())) michael@0: return false; michael@0: michael@0: rtStats->gcHeapChunkTotal = michael@0: size_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) * gc::ChunkSize; michael@0: michael@0: rtStats->gcHeapUnusedChunks = michael@0: size_t(JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS)) * gc::ChunkSize; michael@0: michael@0: IterateChunks(rt, &rtStats->gcHeapDecommittedArenas, michael@0: DecommittedArenasChunkCallback); michael@0: michael@0: // Take the per-compartment measurements. michael@0: StatsClosure closure(rtStats, opv); michael@0: if (!closure.init()) michael@0: return false; michael@0: IterateZonesCompartmentsArenasCells(rt, &closure, StatsZoneCallback, StatsCompartmentCallback, michael@0: StatsArenaCallback, StatsCellCallback); michael@0: michael@0: // Take the "explicit/js/runtime/" measurements. michael@0: rt->addSizeOfIncludingThis(rtStats->mallocSizeOf_, &rtStats->runtime); michael@0: michael@0: if (!FindNotableScriptSources(rtStats->runtime)) michael@0: return false; michael@0: michael@0: ZoneStatsVector &zs = rtStats->zoneStatsVector; michael@0: ZoneStats &zTotals = rtStats->zTotals; michael@0: michael@0: // We don't look for notable strings for zTotals. So we first sum all the michael@0: // zones' measurements to get the totals. Then we find the notable strings michael@0: // within each zone. michael@0: for (size_t i = 0; i < zs.length(); i++) michael@0: zTotals.addSizes(zs[i]); michael@0: michael@0: for (size_t i = 0; i < zs.length(); i++) michael@0: if (!FindNotableStrings(zs[i])) michael@0: return false; michael@0: michael@0: MOZ_ASSERT(!zTotals.allStrings); michael@0: michael@0: for (size_t i = 0; i < rtStats->compartmentStatsVector.length(); i++) { michael@0: CompartmentStats &cStats = rtStats->compartmentStatsVector[i]; michael@0: rtStats->cTotals.add(cStats); michael@0: } michael@0: michael@0: rtStats->gcHeapGCThings = rtStats->zTotals.sizeOfLiveGCThings() + michael@0: rtStats->cTotals.sizeOfLiveGCThings(); michael@0: michael@0: #ifdef DEBUG michael@0: // Check that the in-arena measurements look ok. michael@0: size_t totalArenaSize = rtStats->zTotals.gcHeapArenaAdmin + michael@0: rtStats->zTotals.unusedGCThings + michael@0: rtStats->gcHeapGCThings; michael@0: JS_ASSERT(totalArenaSize % gc::ArenaSize == 0); michael@0: #endif michael@0: michael@0: for (CompartmentsIter comp(rt, WithAtoms); !comp.done(); comp.next()) michael@0: comp->compartmentStats = nullptr; michael@0: michael@0: size_t numDirtyChunks = michael@0: (rtStats->gcHeapChunkTotal - rtStats->gcHeapUnusedChunks) / gc::ChunkSize; michael@0: size_t perChunkAdmin = michael@0: sizeof(gc::Chunk) - (sizeof(gc::Arena) * gc::ArenasPerChunk); michael@0: rtStats->gcHeapChunkAdmin = numDirtyChunks * perChunkAdmin; michael@0: michael@0: // |gcHeapUnusedArenas| is the only thing left. Compute it in terms of michael@0: // all the others. See the comment in RuntimeStats for explanation. michael@0: rtStats->gcHeapUnusedArenas = rtStats->gcHeapChunkTotal - michael@0: rtStats->gcHeapDecommittedArenas - michael@0: rtStats->gcHeapUnusedChunks - michael@0: rtStats->zTotals.unusedGCThings - michael@0: rtStats->gcHeapChunkAdmin - michael@0: rtStats->zTotals.gcHeapArenaAdmin - michael@0: rtStats->gcHeapGCThings; michael@0: return true; michael@0: } michael@0: michael@0: JS_PUBLIC_API(size_t) michael@0: JS::SystemCompartmentCount(JSRuntime *rt) michael@0: { michael@0: size_t n = 0; michael@0: for (CompartmentsIter comp(rt, WithAtoms); !comp.done(); comp.next()) { michael@0: if (comp->isSystem) michael@0: ++n; michael@0: } michael@0: return n; michael@0: } michael@0: michael@0: JS_PUBLIC_API(size_t) michael@0: JS::UserCompartmentCount(JSRuntime *rt) michael@0: { michael@0: size_t n = 0; michael@0: for (CompartmentsIter comp(rt, WithAtoms); !comp.done(); comp.next()) { michael@0: if (!comp->isSystem) michael@0: ++n; michael@0: } michael@0: return n; michael@0: } michael@0: michael@0: JS_PUBLIC_API(size_t) michael@0: JS::PeakSizeOfTemporary(const JSRuntime *rt) michael@0: { michael@0: return rt->tempLifoAlloc.peakSizeOfExcludingThis(); michael@0: } michael@0: michael@0: namespace JS { michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: AddSizeOfTab(JSRuntime *rt, HandleObject obj, MallocSizeOf mallocSizeOf, ObjectPrivateVisitor *opv, michael@0: TabSizes *sizes) michael@0: { michael@0: class SimpleJSRuntimeStats : public JS::RuntimeStats michael@0: { michael@0: public: michael@0: SimpleJSRuntimeStats(MallocSizeOf mallocSizeOf) michael@0: : JS::RuntimeStats(mallocSizeOf) michael@0: {} michael@0: michael@0: virtual void initExtraZoneStats(JS::Zone *zone, JS::ZoneStats *zStats) michael@0: MOZ_OVERRIDE michael@0: {} michael@0: michael@0: virtual void initExtraCompartmentStats( michael@0: JSCompartment *c, JS::CompartmentStats *cStats) MOZ_OVERRIDE michael@0: {} michael@0: }; michael@0: michael@0: SimpleJSRuntimeStats rtStats(mallocSizeOf); michael@0: michael@0: JS::Zone *zone = GetObjectZone(obj); michael@0: michael@0: if (!rtStats.compartmentStatsVector.reserve(zone->compartments.length())) michael@0: return false; michael@0: michael@0: if (!rtStats.zoneStatsVector.reserve(1)) michael@0: return false; michael@0: michael@0: // Take the per-compartment measurements. michael@0: StatsClosure closure(&rtStats, opv); michael@0: if (!closure.init()) michael@0: return false; michael@0: IterateZoneCompartmentsArenasCells(rt, zone, &closure, StatsZoneCallback, michael@0: StatsCompartmentCallback, StatsArenaCallback, michael@0: StatsCellCallback); michael@0: michael@0: JS_ASSERT(rtStats.zoneStatsVector.length() == 1); michael@0: rtStats.zTotals.addSizes(rtStats.zoneStatsVector[0]); michael@0: michael@0: for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++) { michael@0: CompartmentStats &cStats = rtStats.compartmentStatsVector[i]; michael@0: rtStats.cTotals.add(cStats); michael@0: } michael@0: michael@0: for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) michael@0: comp->compartmentStats = nullptr; michael@0: michael@0: rtStats.zTotals.addToTabSizes(sizes); michael@0: rtStats.cTotals.addToTabSizes(sizes); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: } // namespace JS michael@0: