From 5164fb3ecf2f5768a7e88ec569a56c9a45d823c5 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 4 Sep 2011 18:32:50 -0700 Subject: [PATCH] Bug 676732 - Measure and/or avoid slop in important JS memory reporters. r=dmandelin. --- js/src/jsobj.cpp | 9 ++++++ js/src/jsobj.h | 2 ++ js/src/jsscope.h | 7 +++- js/src/jsscript.h | 5 +-- js/src/jstracer.cpp | 6 ++-- js/src/methodjit/Compiler.cpp | 3 +- js/src/methodjit/MethodJIT.cpp | 12 ++++--- js/src/methodjit/MethodJIT.h | 2 +- js/src/shell/js.cpp | 6 ++-- js/src/xpconnect/src/xpcjsruntime.cpp | 46 +++++++++++++-------------- js/src/xpconnect/src/xpcpublic.h | 4 ++- 11 files changed, 62 insertions(+), 40 deletions(-) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index d19eacb976c..ae09e2dc86a 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2717,6 +2717,15 @@ obj_preventExtensions(JSContext *cx, uintN argc, Value *vp) return obj->preventExtensions(cx, &props); } +size_t +JSObject::sizeOfSlotsArray(size_t(*mus)(void *)) +{ + if (!hasSlotsArray()) + return 0; + size_t usable = mus((void *)slots); + return usable ? usable : numSlots() * sizeof(js::Value); +} + bool JSObject::sealOrFreeze(JSContext *cx, ImmutabilityType it) { diff --git a/js/src/jsobj.h b/js/src/jsobj.h index a58fa4ad05f..729c3c1d387 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -478,6 +478,8 @@ struct JSObject : js::gc::Cell { jsuword initializedLength; }; + size_t sizeOfSlotsArray(size_t(*mus)(void *)); + JSObject *parent; /* object's parent */ void *privateData; /* private data */ jsuword capacity; /* total number of available slots */ diff --git a/js/src/jsscope.h b/js/src/jsscope.h index e4ed68c0d13..354d2a5a60b 100644 --- a/js/src/jsscope.h +++ b/js/src/jsscope.h @@ -251,7 +251,12 @@ struct PropertyTable { /* Computes the size of the entries array for a given capacity. */ static size_t sizeOfEntries(size_t cap) { return cap * sizeof(Shape *); } - size_t sizeOf() const { + size_t sizeOf(size_t(*mus)(void *)) const { + if (mus) { + size_t usable = mus((void*)this) + mus(entries); + if (usable) + return usable; + } return sizeOfEntries(capacity()) + sizeof(PropertyTable); } diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 47e45d8fb4c..e719efea596 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -674,8 +674,9 @@ struct JSScript : public js::gc::Cell { return JITScript_Valid; } - // This method is implemented in MethodJIT.h. - JS_FRIEND_API(size_t) jitDataSize();/* Size of the JITScript and all sections */ + /* Size of the JITScript and all sections. (This method is implemented in MethodJIT.h.) */ + JS_FRIEND_API(size_t) jitDataSize(size_t(*mus)(void *)); + #endif jsbytecode *main() { diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index a90b1d0ff74..34fcef2469c 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -183,9 +183,9 @@ using namespace js::tjit; * * FIXME: Bug 624590 is open to get rid of all this. */ -static const size_t DataReserveSize = 12500 * sizeof(uintptr_t); -static const size_t TraceReserveSize = 5000 * sizeof(uintptr_t); -static const size_t TempReserveSize = 1000 * sizeof(uintptr_t); +static const size_t DataReserveSize = 8192 * sizeof(uintptr_t); +static const size_t TraceReserveSize = 512 * sizeof(uintptr_t); +static const size_t TempReserveSize = 4096 * sizeof(uintptr_t); void* nanojit::Allocator::allocChunk(size_t nbytes, bool fallible) diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 4b6438e1543..180e794722d 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -1328,7 +1328,8 @@ mjit::Compiler::finishThisUp(JITScript **jitp) #endif JS_ASSERT(size_t(cursor - (uint8*)jit) == dataSize); - JS_ASSERT(jit->scriptDataSize() == dataSize); + /* Pass in NULL here -- we don't want slop bytes to be counted. */ + JS_ASSERT(jit->scriptDataSize(NULL) == dataSize); /* Link fast and slow paths together. */ stubcc.fixCrossJumps(result, masm.size(), masm.size() + stubcc.size()); diff --git a/js/src/methodjit/MethodJIT.cpp b/js/src/methodjit/MethodJIT.cpp index f796039c1f9..194293f31ba 100644 --- a/js/src/methodjit/MethodJIT.cpp +++ b/js/src/methodjit/MethodJIT.cpp @@ -1137,21 +1137,23 @@ mjit::JITScript::~JITScript() } size_t -JSScript::jitDataSize() +JSScript::jitDataSize(size_t(*mus)(void *)) { size_t n = 0; if (jitNormal) - n += jitNormal->scriptDataSize(); + n += jitNormal->scriptDataSize(mus); if (jitCtor) - n += jitCtor->scriptDataSize(); + n += jitCtor->scriptDataSize(mus); return n; } /* Please keep in sync with Compiler::finishThisUp! */ size_t -mjit::JITScript::scriptDataSize() +mjit::JITScript::scriptDataSize(size_t(*mus)(void *)) { - return sizeof(JITScript) + + size_t usable = mus ? mus(this) : 0; + return usable ? usable : + sizeof(JITScript) + sizeof(NativeMapEntry) * nNmapPairs + sizeof(InlineFrame) * nInlineFrames + sizeof(CallSite) * nCallSites + diff --git a/js/src/methodjit/MethodJIT.h b/js/src/methodjit/MethodJIT.h index 6e250ebf3d0..e346ca6a95e 100644 --- a/js/src/methodjit/MethodJIT.h +++ b/js/src/methodjit/MethodJIT.h @@ -640,7 +640,7 @@ struct JITScript { void trace(JSTracer *trc); - size_t scriptDataSize(); + size_t scriptDataSize(size_t(*mus)(void *)); jsbytecode *nativeToPC(void *returnAddress, CallSite **pinline) const; diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index e2b9537d518..146fab30c1f 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -4070,13 +4070,13 @@ MJitCodeStats(JSContext *cx, uintN argc, jsval *vp) #ifdef JS_METHODJIT static void -SumJitDataSizeCallabck(JSContext *cx, void *data, void *thing, +SumJitDataSizeCallback(JSContext *cx, void *data, void *thing, JSGCTraceKind traceKind, size_t thingSize) { size_t *sump = static_cast(data); JS_ASSERT(traceKind == JSTRACE_SCRIPT); JSScript *script = static_cast(thing); - *sump += script->jitDataSize(); + *sump += script->jitDataSize(NULL); } #endif @@ -4086,7 +4086,7 @@ MJitDataStats(JSContext *cx, uintN argc, jsval *vp) { #ifdef JS_METHODJIT size_t n = 0; - IterateCells(cx, NULL, gc::FINALIZE_TYPE_OBJECT, &n, SumJitDataSizeCallabck); + IterateCells(cx, NULL, gc::FINALIZE_TYPE_OBJECT, &n, SumJitDataSizeCallback); JS_SET_RVAL(cx, vp, INT_TO_JSVAL(n)); #else JS_SET_RVAL(cx, vp, JSVAL_VOID); diff --git a/js/src/xpconnect/src/xpcjsruntime.cpp b/js/src/xpconnect/src/xpcjsruntime.cpp index 02cfac7dd8f..3860dce81dd 100644 --- a/js/src/xpconnect/src/xpcjsruntime.cpp +++ b/js/src/xpconnect/src/xpcjsruntime.cpp @@ -1346,7 +1346,7 @@ CellCallback(JSContext *cx, void *vdata, void *thing, JSGCTraceKind traceKind, case JSTRACE_OBJECT: { JSObject *obj = static_cast(thing); - curr->objectSlots += JS_ObjectCountDynamicSlots(obj) * sizeof(js::Value); + curr->objectSlots += obj->sizeOfSlotsArray(moz_malloc_usable_size); break; } case JSTRACE_STRING: @@ -1359,15 +1359,19 @@ CellCallback(JSContext *cx, void *vdata, void *thing, JSGCTraceKind traceKind, { js::Shape *shape = static_cast(thing); if(shape->hasTable()) - curr->propertyTables += shape->getTable()->sizeOf(); + curr->propertyTables += + shape->getTable()->sizeOf(moz_malloc_usable_size); break; } case JSTRACE_SCRIPT: { JSScript *script = static_cast(thing); - curr->scriptData += script->dataSize(); + if (script->data != script->inlineData) { + size_t usable = moz_malloc_usable_size(script->data); + curr->scriptData += usable ? usable : script->dataSize(); + } #ifdef JS_METHODJIT - curr->mjitData += script->jitDataSize(); + curr->mjitData += script->jitDataSize(moz_malloc_usable_size); #endif break; } @@ -1628,6 +1632,8 @@ CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data) for(js::ThreadDataIter i(rt); !i.empty(); i.popFront()) data->stackSize += i.threadData()->stackSpace.committedSize(); + size_t usable = moz_malloc_usable_size(rt); + data->runtimeObjectSize += usable ? usable : sizeof(JSRuntime); data->atomsTableSize += rt->atomState.atoms.tableSize(); } @@ -1674,6 +1680,9 @@ CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data) return true; } +#define SLOP_BYTES_STRING \ + " The measurement includes slop bytes caused by the heap allocator rounding up request sizes." + static void ReportCompartmentStats(const CompartmentStats &stats, const nsACString &pathPrefix, @@ -1736,7 +1745,7 @@ ReportCompartmentStats(const CompartmentStats &stats, callback, closure); ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, - "gc-heap/shapes"), + "gc-heap/type-objects"), JS_GC_HEAP_KIND, stats.gcHeapKinds[JSTRACE_TYPE_OBJECT], "Memory on the compartment's garbage-collected JavaScript heap that holds " "type inference information.", @@ -1756,7 +1765,7 @@ ReportCompartmentStats(const CompartmentStats &stats, "which are used to represent object properties. Some objects also " "contain a fixed number of slots which are stored on the compartment's " "JavaScript heap; those slots are not counted here, but in " - "'gc-heap/objects' instead.", + "'gc-heap/objects' instead." SLOP_BYTES_STRING, callback, closure); ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, @@ -1774,7 +1783,7 @@ ReportCompartmentStats(const CompartmentStats &stats, nsIMemoryReporter::KIND_HEAP, stats.propertyTables, "Memory allocated for the compartment's property tables. A property " "table is an internal data structure that makes JavaScript property " - "accesses fast.", + "accesses fast." SLOP_BYTES_STRING, callback, closure); ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, @@ -1784,20 +1793,11 @@ ReportCompartmentStats(const CompartmentStats &stats, "Arrays attached to prototype JS objects managing shape information.", callback, closure); - ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, - "scripts"), - nsIMemoryReporter::KIND_HEAP, - stats.gcHeapKinds[JSTRACE_SCRIPT], - "Memory allocated for the compartment's JSScripts. A JSScript is created " - "for each user-defined function in a script. One is also created for " - "the top-level code in a script.", - callback, closure); - ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, "script-data"), nsIMemoryReporter::KIND_HEAP, stats.scriptData, "Memory allocated for JSScript bytecode and various variable-length " - "tables.", + "tables." SLOP_BYTES_STRING, callback, closure); #ifdef JS_METHODJIT @@ -1824,7 +1824,7 @@ ReportCompartmentStats(const CompartmentStats &stats, "mjit-data"), nsIMemoryReporter::KIND_HEAP, stats.mjitData, "Memory used by the method JIT for the compartment's compilation data: " - "JITScripts, native maps, and inline cache structs.", + "JITScripts, native maps, and inline cache structs." SLOP_BYTES_STRING, callback, closure); #endif #ifdef JS_TRACER @@ -1884,11 +1884,11 @@ ReportCompartmentStats(const CompartmentStats &stats, callback, closure); ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, - "type-inference-temporary"), + "analysis-temporary"), nsIMemoryReporter::KIND_HEAP, stats.typeInferenceMemory.temporary, - "Memory used during type inference to hold transient analysis " - "information. Cleared on GC.", + "Memory used during type inference and compilation to hold transient " + "analysis information. Cleared on GC.", callback, closure); } @@ -1906,8 +1906,8 @@ ReportJSRuntimeStats(const IterateData &data, const nsACString &pathPrefix, } ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/runtime-object"), - nsIMemoryReporter::KIND_NONHEAP, sizeof(JSRuntime), - "Memory used by the JSRuntime object.", + nsIMemoryReporter::KIND_NONHEAP, data.runtimeObjectSize, + "Memory used by the JSRuntime object." SLOP_BYTES_STRING, callback, closure); ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/atoms-table"), diff --git a/js/src/xpconnect/src/xpcpublic.h b/js/src/xpconnect/src/xpcpublic.h index b1f037efc7b..520f2f7a2ab 100644 --- a/js/src/xpconnect/src/xpcpublic.h +++ b/js/src/xpconnect/src/xpcpublic.h @@ -227,7 +227,8 @@ struct CompartmentStats struct IterateData { IterateData() - : atomsTableSize(0), + : runtimeObjectSize(0), + atomsTableSize(0), stackSize(0), gcHeapChunkTotal(0), gcHeapChunkCleanUnused(0), @@ -238,6 +239,7 @@ struct IterateData compartmentStatsVector(), currCompartmentStats(NULL) { } + PRInt64 runtimeObjectSize; PRInt64 atomsTableSize; PRInt64 stackSize; PRInt64 gcHeapChunkTotal;