diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index a3ff7621044..b7acdbf04ef 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -537,7 +537,7 @@ ChunkPool::expire(JSRuntime *rt, bool releaseAll) } JS_FRIEND_API(int64_t) -ChunkPool::countDecommittedArenas(JSRuntime *rt) +ChunkPool::countCleanDecommittedArenas(JSRuntime *rt) { JS_ASSERT(this == &rt->gcChunkPool); diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 6f503b916a3..171dc7a569f 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -786,7 +786,7 @@ class ChunkPool { void expire(JSRuntime *rt, bool releaseAll); /* Must be called either during the GC or with the GC lock taken. */ - JS_FRIEND_API(int64_t) countDecommittedArenas(JSRuntime *rt); + JS_FRIEND_API(int64_t) countCleanDecommittedArenas(JSRuntime *rt); }; inline uintptr_t diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 0ea8e62e892..ce049583566 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -1265,6 +1265,8 @@ CompartmentCallback(JSContext *cx, void *vdata, JSCompartment *compartment) void ChunkCallback(JSContext *cx, void *vdata, js::gc::Chunk *chunk) { + // Nb: This function is only called for dirty chunks, which is why we + // increment gcHeapChunkDirtyDecommitted. IterateData *data = static_cast(vdata); for (uint32 i = 0; i < js::gc::ArenasPerChunk; i++) if (chunk->decommittedArenas.get(i)) @@ -1395,6 +1397,29 @@ ReportMemoryBytes0(const nsCString &path, PRInt32 kind, PRInt64 amount, ReportMemoryBytes(path, kind, amount, desc, callback, closure); } +template +inline void +ReportGCHeapBytes(const nsACString &path, PRInt64 *total, PRInt64 amount, + const char (&desc)[N], + nsIMemoryMultiReporterCallback *callback, + nsISupports *closure) +{ + ReportMemory(path, nsIMemoryReporter::KIND_NONHEAP, nsIMemoryReporter::UNITS_BYTES, amount, + desc, callback, closure); + *total += amount; +} + +template +inline void +ReportGCHeapBytes0(const nsCString &path, PRInt64 *total, PRInt64 amount, + const char (&desc)[N], + nsIMemoryMultiReporterCallback *callback, + nsISupports *closure) +{ + if (amount) + return ReportGCHeapBytes(path, total, amount, desc, callback, closure); +} + template inline void ReportMemoryPercentage(const nsACString &path, PRInt32 kind, PRInt64 amount, @@ -1418,8 +1443,6 @@ MakeMemoryReporterPath(const nsACString &pathPrefix, } // anonymous namespace -#define JS_GC_HEAP_KIND nsIMemoryReporter::KIND_NONHEAP - // We have per-compartment GC heap totals, so we can't put the total GC heap // size in the explicit allocations tree. But it's a useful figure, so put it // in the "others" list. @@ -1548,7 +1571,7 @@ CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data) data->compartmentStatsVector.SetCapacity(rt->compartments.length()); data->gcHeapChunkCleanDecommitted = - rt->gcChunkPool.countDecommittedArenas(rt) * + rt->gcChunkPool.countCleanDecommittedArenas(rt) * js::gc::ArenaSize; data->gcHeapChunkCleanUnused = PRInt64(JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS)) * @@ -1629,6 +1652,7 @@ CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data) stats.gcHeapStrings + stats.gcHeapShapesTree + stats.gcHeapShapesDict + + stats.gcHeapShapesBase + stats.gcHeapScripts + stats.gcHeapTypeObjects + stats.gcHeapXML; @@ -1683,51 +1707,53 @@ CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data) #define SLOP_BYTES_STRING \ " The measurement includes slop bytes caused by the heap allocator rounding up request sizes." -static void +static PRInt64 ReportCompartmentStats(const CompartmentStats &stats, const nsACString &pathPrefix, nsIMemoryMultiReporterCallback *callback, nsISupports *closure) { - ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, + PRInt64 gcTotal = 0; + + ReportGCHeapBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, "gc-heap/arena/headers"), - JS_GC_HEAP_KIND, stats.gcHeapArenaHeaders, + &gcTotal, stats.gcHeapArenaHeaders, "Memory on the compartment's garbage-collected JavaScript heap, within " "arenas, that is used to hold internal book-keeping information.", callback, closure); - ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, + ReportGCHeapBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, "gc-heap/arena/padding"), - JS_GC_HEAP_KIND, stats.gcHeapArenaPadding, + &gcTotal, stats.gcHeapArenaPadding, "Memory on the compartment's garbage-collected JavaScript heap, within " "arenas, that is unused and present only so that other data is aligned. " "This constitutes internal fragmentation.", callback, closure); - ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, + ReportGCHeapBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, "gc-heap/arena/unused"), - JS_GC_HEAP_KIND, stats.gcHeapArenaUnused, + &gcTotal, stats.gcHeapArenaUnused, "Memory on the compartment's garbage-collected JavaScript heap, within " "arenas, that could be holding useful data but currently isn't.", callback, closure); - ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, + ReportGCHeapBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, "gc-heap/objects/non-function"), - JS_GC_HEAP_KIND, stats.gcHeapObjectsNonFunction, + &gcTotal, stats.gcHeapObjectsNonFunction, "Memory on the compartment's garbage-collected JavaScript heap that holds " "non-function objects.", callback, closure); - ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, + ReportGCHeapBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, "gc-heap/objects/function"), - JS_GC_HEAP_KIND, stats.gcHeapObjectsFunction, + &gcTotal, stats.gcHeapObjectsFunction, "Memory on the compartment's garbage-collected JavaScript heap that holds " "function objects.", callback, closure); - ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, + ReportGCHeapBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, "gc-heap/strings"), - JS_GC_HEAP_KIND, stats.gcHeapStrings, + &gcTotal, stats.gcHeapStrings, "Memory on the compartment's garbage-collected JavaScript heap that holds " "string headers. String headers contain various pieces of information " "about a string, but do not contain (except in the case of very short " @@ -1735,45 +1761,45 @@ ReportCompartmentStats(const CompartmentStats &stats, "under 'gc-heap/string-chars' instead.", callback, closure); - ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, + ReportGCHeapBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, "gc-heap/scripts"), - JS_GC_HEAP_KIND, stats.gcHeapScripts, + &gcTotal, stats.gcHeapScripts, "Memory on the compartment's garbage-collected JavaScript heap that holds " "JSScript instances. 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, + ReportGCHeapBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, "gc-heap/shapes/tree"), - JS_GC_HEAP_KIND, stats.gcHeapShapesTree, + &gcTotal, stats.gcHeapShapesTree, "Memory on the compartment's garbage-collected JavaScript heap that holds " "shapes that are in a property tree.", callback, closure); - ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, + ReportGCHeapBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, "gc-heap/shapes/dict"), - JS_GC_HEAP_KIND, stats.gcHeapShapesDict, + &gcTotal, stats.gcHeapShapesDict, "Memory on the compartment's garbage-collected JavaScript heap that holds " "shapes that are in dictionary mode.", callback, closure); - ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, + ReportGCHeapBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, "gc-heap/shapes/base"), - JS_GC_HEAP_KIND, stats.gcHeapShapesBase, + &gcTotal, stats.gcHeapShapesBase, "Memory on the compartment's garbage-collected JavaScript heap that collates " "data common to many shapes.", callback, closure); - ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, + ReportGCHeapBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, "gc-heap/type-objects"), - JS_GC_HEAP_KIND, stats.gcHeapTypeObjects, + &gcTotal, stats.gcHeapTypeObjects, "Memory on the compartment's garbage-collected JavaScript heap that holds " "type inference information.", callback, closure); - ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, + ReportGCHeapBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, "gc-heap/xml"), - JS_GC_HEAP_KIND, stats.gcHeapXML, + &gcTotal, stats.gcHeapXML, "Memory on the compartment's garbage-collected JavaScript heap that holds " "E4X XML objects.", callback, closure); @@ -1858,7 +1884,7 @@ ReportCompartmentStats(const CompartmentStats &stats, ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, "type-inference/object-main"), - JS_GC_HEAP_KIND, + nsIMemoryReporter::KIND_HEAP, stats.typeInferenceMemory.objects, "Memory used during type inference to store types and possible " "property types of JS objects.", @@ -1878,6 +1904,8 @@ ReportCompartmentStats(const CompartmentStats &stats, "Memory used during type inference and compilation to hold transient " "analysis information. Cleared on GC.", callback, closure); + + return gcTotal; } void @@ -1885,11 +1913,12 @@ ReportJSRuntimeStats(const IterateData &data, const nsACString &pathPrefix, nsIMemoryMultiReporterCallback *callback, nsISupports *closure) { + PRInt64 gcTotal = 0; for (PRUint32 index = 0; index < data.compartmentStatsVector.Length(); index++) { - ReportCompartmentStats(data.compartmentStatsVector[index], pathPrefix, - callback, closure); + gcTotal += ReportCompartmentStats(data.compartmentStatsVector[index], pathPrefix, + callback, closure); } ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/runtime-object"), @@ -1939,39 +1968,42 @@ ReportJSRuntimeStats(const IterateData &data, const nsACString &pathPrefix, "Memory used by XPConnect." SLOP_BYTES_STRING, callback, closure); - ReportMemoryBytes(pathPrefix + + ReportGCHeapBytes(pathPrefix + NS_LITERAL_CSTRING("gc-heap-chunk-dirty-unused"), - JS_GC_HEAP_KIND, data.gcHeapChunkDirtyUnused, + &gcTotal, data.gcHeapChunkDirtyUnused, "Memory on the garbage-collected JavaScript heap, within chunks with at " "least one allocated GC thing, that could be holding useful data but " "currently isn't. Memory here is mutually exclusive with memory reported" "under gc-heap-decommitted.", callback, closure); - ReportMemoryBytes(pathPrefix + + ReportGCHeapBytes(pathPrefix + NS_LITERAL_CSTRING("gc-heap-chunk-clean-unused"), - JS_GC_HEAP_KIND, data.gcHeapChunkCleanUnused, + &gcTotal, data.gcHeapChunkCleanUnused, "Memory on the garbage-collected JavaScript heap taken by completely empty " "chunks, that soon will be released unless claimed for new allocations. " "Memory here is mutually exclusive with memory reported under " "gc-heap-decommitted.", callback, closure); - ReportMemoryBytes(pathPrefix + + ReportGCHeapBytes(pathPrefix + NS_LITERAL_CSTRING("gc-heap-decommitted"), - JS_GC_HEAP_KIND, + &gcTotal, data.gcHeapChunkCleanDecommitted + data.gcHeapChunkDirtyDecommitted, "Memory in the address space of the garbage-collected JavaScript heap that " "is currently returned to the OS.", callback, closure); - ReportMemoryBytes(pathPrefix + + ReportGCHeapBytes(pathPrefix + NS_LITERAL_CSTRING("gc-heap-chunk-admin"), - JS_GC_HEAP_KIND, data.gcHeapChunkAdmin, + &gcTotal, data.gcHeapChunkAdmin, "Memory on the garbage-collected JavaScript heap, within chunks, that is " "used to hold internal book-keeping information.", callback, closure); + // gcTotal is the sum of everything we've reported for the GC heap. It + // should equal data.gcHeapChunkTotal. + JS_ASSERT(gcTotal == data.gcHeapChunkTotal); } } // namespace memory