bug 674480 - add memory reporter for the number of empty GC chunks. r=njn

This commit is contained in:
Igor Bukanov 2011-07-30 00:17:41 +02:00
parent c5379882ab
commit 5778ccd183
5 changed files with 136 additions and 96 deletions

View File

@ -338,14 +338,7 @@ public:
return NS_ERROR_FAILURE;
}
for (CompartmentStats *stats = data.compartmentStatsVector.begin();
stats != data.compartmentStatsVector.end();
++stats)
{
ReportCompartmentStats(*stats, mPathPrefix, aCallback, aClosure);
}
ReportJSStackSizeForRuntime(mRuntime, mPathPrefix, aCallback, aClosure);
ReportJSRuntimeStats(data, mPathPrefix, aCallback, aClosure);
return NS_OK;
}

View File

@ -2688,6 +2688,8 @@ JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key)
return uint32(rt->gcMode);
case JSGC_UNUSED_CHUNKS:
return uint32(rt->gcChunksWaitingToExpire);
case JSGC_TOTAL_CHUNKS:
return uint32(rt->gcUserChunkSet.count() + rt->gcSystemChunkSet.count());
default:
JS_ASSERT(key == JSGC_NUMBER);
return rt->gcNumber;

View File

@ -1831,7 +1831,10 @@ typedef enum JSGCParamKey {
JSGC_MODE = 6,
/* Number of GC chunks waiting to expire. */
JSGC_UNUSED_CHUNKS = 7
JSGC_UNUSED_CHUNKS = 7,
/* Total number of allocated GC chunks. */
JSGC_TOTAL_CHUNKS = 8
} JSGCParamKey;
typedef enum JSGCMode {

View File

@ -1434,9 +1434,6 @@ class XPConnectGCChunkAllocator
public:
XPConnectGCChunkAllocator() {}
PRInt64 GetGCChunkBytesInUse() {
return mNumGCChunksInUse * js::GC_CHUNK_SIZE;
}
private:
virtual void *doAlloc() {
void *chunk;
@ -1447,22 +1444,16 @@ private:
#else
chunk = js::AllocGCChunk();
#endif
if (chunk)
mNumGCChunksInUse++;
return chunk;
}
virtual void doFree(void *chunk) {
mNumGCChunksInUse--;
#ifdef MOZ_MEMORY
free(chunk);
#else
js::FreeGCChunk(chunk);
#endif
}
protected:
PRUint32 mNumGCChunksInUse;
};
static XPConnectGCChunkAllocator gXPCJSChunkAllocator;
@ -1476,11 +1467,19 @@ static XPConnectGCChunkAllocator gXPCJSChunkAllocator;
// 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.
static PRInt64
GetGCChunkTotalBytes()
{
JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
return PRInt64(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) * js::GC_CHUNK_SIZE;
}
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSGCHeap,
"js-gc-heap",
KIND_OTHER,
nsIMemoryReporter::UNITS_BYTES,
gXPCJSChunkAllocator.GetGCChunkBytesInUse,
GetGCChunkTotalBytes,
"Memory used by the garbage-collected JavaScript heap.")
static PRInt64
@ -1591,18 +1590,66 @@ CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data)
}
{
JSAutoRequest ar(cx);
JSAutoRequest ar(cx);
data->compartmentStatsVector.reserve(rt->compartments.length());
js::IterateCompartmentsArenasCells(cx, data, CompartmentCallback,
ArenaCallback, CellCallback);
if (!data->compartmentStatsVector.reserve(rt->compartments.length()))
return false;
data->gcHeapChunkCleanUnused =
PRInt64(JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS)) *
js::GC_CHUNK_SIZE;
data->gcHeapChunkTotal =
PRInt64(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) *
js::GC_CHUNK_SIZE;
js::IterateCompartmentsArenasCells(cx, data, CompartmentCallback,
ArenaCallback, CellCallback);
for(js::ThreadDataIter i(rt); !i.empty(); i.popFront())
data->stackSize += i.threadData()->stackSpace.committedSize();
}
JS_DestroyContextNoGC(cx);
// This is initialized to all bytes stored in used chunks, and then we
// subtract used space from it each time around the loop.
data->gcHeapChunkDirtyUnused = data->gcHeapChunkTotal -
data->gcHeapChunkCleanUnused;
data->gcHeapArenaUnused = 0;
for(CompartmentStats *stats = data->compartmentStatsVector.begin();
stats != data->compartmentStatsVector.end();
++stats)
{
data->gcHeapChunkDirtyUnused -=
stats->gcHeapArenaHeaders + stats->gcHeapArenaPadding +
stats->gcHeapArenaUnused +
stats->gcHeapObjects + stats->gcHeapStrings +
stats->gcHeapShapes + stats->gcHeapXml;
data->gcHeapArenaUnused += stats->gcHeapArenaUnused;
}
size_t numDirtyChunks = (data->gcHeapChunkTotal -
data->gcHeapChunkCleanUnused) /
js::GC_CHUNK_SIZE;
PRInt64 perChunkAdmin =
sizeof(js::gc::Chunk) - (sizeof(js::gc::Arena) * js::gc::ArenasPerChunk);
data->gcHeapChunkAdmin = numDirtyChunks * perChunkAdmin;
data->gcHeapChunkDirtyUnused -= data->gcHeapChunkAdmin;
// Why 10000x? 100x because it's a percentage, and another 100x
// because nsIMemoryReporter requires that for percentage amounts so
// they can be fractional.
data->gcHeapUnusedPercentage = (data->gcHeapChunkCleanUnused +
data->gcHeapChunkDirtyUnused +
data->gcHeapArenaUnused) * 10000 /
data->gcHeapChunkTotal;
return true;
}
void
static void
ReportCompartmentStats(const CompartmentStats &stats,
const nsACString &pathPrefix,
nsIMemoryMultiReporterCallback *callback,
@ -1739,20 +1786,45 @@ ReportCompartmentStats(const CompartmentStats &stats,
}
void
ReportJSStackSizeForRuntime(JSRuntime *rt, const nsACString &pathPrefix,
nsIMemoryMultiReporterCallback *callback,
nsISupports *closure)
ReportJSRuntimeStats(const IterateData &data, const nsACString &pathPrefix,
nsIMemoryMultiReporterCallback *callback,
nsISupports *closure)
{
PRInt64 stackSize = 0;
for(js::ThreadDataIter i(rt); !i.empty(); i.popFront())
stackSize += i.threadData()->stackSpace.committedSize();
for(const CompartmentStats *stats = data.compartmentStatsVector.begin();
stats != data.compartmentStatsVector.end();
++stats)
{
ReportCompartmentStats(*stats, pathPrefix, callback, closure);
}
ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("stack"),
nsIMemoryReporter::KIND_NONHEAP, stackSize,
nsIMemoryReporter::KIND_NONHEAP, data.stackSize,
"Memory used for the JavaScript stack. This is the committed portion "
"of the stack; any uncommitted portion is not measured because it "
"hardly costs anything.",
callback, closure);
ReportMemoryBytes(pathPrefix +
NS_LITERAL_CSTRING("gc-heap-chunk-dirty-unused"),
JS_GC_HEAP_KIND, 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.",
callback, closure);
ReportMemoryBytes(pathPrefix +
NS_LITERAL_CSTRING("gc-heap-chunk-clean-unused"),
JS_GC_HEAP_KIND, data.gcHeapChunkCleanUnused,
"Memory on the garbage-collected JavaScript heap taken by completely empty "
"chunks, that soon will be released unless claimed for new allocations.",
callback, closure);
ReportMemoryBytes(pathPrefix +
NS_LITERAL_CSTRING("gc-heap-chunk-admin"),
JS_GC_HEAP_KIND, data.gcHeapChunkAdmin,
"Memory on the garbage-collected JavaScript heap, within chunks, that is "
"used to hold internal book-keeping information.",
callback, closure);
}
} // namespace memory
@ -1778,66 +1850,27 @@ public:
if(!CollectCompartmentStatsForRuntime(rt, &data))
return NS_ERROR_FAILURE;
PRInt64 gcHeapChunkTotal = gXPCJSChunkAllocator.GetGCChunkBytesInUse();
// This is initialized to gcHeapChunkTotal, and then we subtract used
// space from it each time around the loop.
PRInt64 gcHeapChunkUnused = gcHeapChunkTotal;
PRInt64 gcHeapArenaUnused = 0;
NS_NAMED_LITERAL_CSTRING(pathPrefix, "explicit/js/");
// This is the second step (see above).
for(CompartmentStats *stats = data.compartmentStatsVector.begin();
stats != data.compartmentStatsVector.end();
++stats)
{
gcHeapChunkUnused -=
stats->gcHeapArenaHeaders + stats->gcHeapArenaPadding +
stats->gcHeapArenaUnused +
stats->gcHeapObjects + stats->gcHeapStrings +
stats->gcHeapShapes + stats->gcHeapXml;
ReportJSRuntimeStats(data, pathPrefix, callback, closure);
gcHeapArenaUnused += stats->gcHeapArenaUnused;
ReportCompartmentStats(*stats, pathPrefix, callback, closure);
}
JS_ASSERT(gcHeapChunkTotal % js::GC_CHUNK_SIZE == 0);
size_t numChunks = gcHeapChunkTotal / js::GC_CHUNK_SIZE;
PRInt64 perChunkAdmin =
sizeof(js::gc::Chunk) - (sizeof(js::gc::Arena) * js::gc::ArenasPerChunk);
PRInt64 gcHeapChunkAdmin = numChunks * perChunkAdmin;
gcHeapChunkUnused -= gcHeapChunkAdmin;
// Why 10000x? 100x because it's a percentage, and another 100x
// because nsIMemoryReporter requires that for percentage amounts so
// they can be fractional.
PRInt64 gcHeapUnusedPercentage =
(gcHeapChunkUnused + gcHeapArenaUnused) * 10000 /
gXPCJSChunkAllocator.GetGCChunkBytesInUse();
ReportMemoryBytes(pathPrefix +
NS_LITERAL_CSTRING("gc-heap-chunk-unused"),
JS_GC_HEAP_KIND, gcHeapChunkUnused,
"Memory on the garbage-collected JavaScript heap, within chunks, that "
"could be holding useful data but currently isn't.",
ReportMemoryBytes(NS_LITERAL_CSTRING("js-gc-heap-chunk-dirty-unused"),
nsIMemoryReporter::KIND_OTHER,
data.gcHeapChunkDirtyUnused,
"The same as 'explicit/js/gc-heap-chunk-dirty-unused'. Shown here for "
"easy comparison with other 'js-gc' reporters.",
callback, closure);
ReportMemoryBytes(NS_LITERAL_CSTRING("js-gc-heap-chunk-unused"),
nsIMemoryReporter::KIND_OTHER, gcHeapChunkUnused,
"The same as 'explicit/js/gc-heap-chunk-unused'. Shown here for "
"easy comparison with 'js-gc-heap' and 'js-gc-heap-arena-unused'.",
callback, closure);
ReportMemoryBytes(pathPrefix +
NS_LITERAL_CSTRING("gc-heap-chunk-admin"),
JS_GC_HEAP_KIND, gcHeapChunkAdmin,
"Memory on the garbage-collected JavaScript heap, within chunks, that is "
"used to hold internal book-keeping information.",
ReportMemoryBytes(NS_LITERAL_CSTRING("js-gc-heap-chunk-clean-unused"),
nsIMemoryReporter::KIND_OTHER,
data.gcHeapChunkCleanUnused,
"The same as 'explicit/js/gc-heap-chunk-clean-unused'. Shown here for "
"easy comparison with other 'js-gc' reporters.",
callback, closure);
ReportMemoryBytes(NS_LITERAL_CSTRING("js-gc-heap-arena-unused"),
nsIMemoryReporter::KIND_OTHER, gcHeapArenaUnused,
nsIMemoryReporter::KIND_OTHER, data.gcHeapArenaUnused,
"Memory on the garbage-collected JavaScript heap, within arenas, that "
"could be holding useful data but currently isn't. This is the sum of "
"all compartments' 'gc-heap/arena-unused' numbers.",
@ -1845,14 +1878,13 @@ public:
ReportMemoryPercentage(NS_LITERAL_CSTRING("js-gc-heap-unused-fraction"),
nsIMemoryReporter::KIND_OTHER,
gcHeapUnusedPercentage,
data.gcHeapUnusedPercentage,
"Fraction of the garbage-collected JavaScript heap that is unused. "
"Computed as ('js-gc-heap-chunk-unused' + 'js-gc-heap-arena-unused') / "
"Computed as ('js-gc-heap-chunk-clean-unused' + "
"'js-gc-heap-chunk-dirty-unused' + 'js-gc-heap-arena-unused') / "
"'js-gc-heap'.",
callback, closure);
ReportJSStackSizeForRuntime(rt, pathPrefix, callback, closure);
return NS_OK;
}
};

View File

@ -224,7 +224,23 @@ struct CompartmentStats
struct IterateData
{
IterateData()
: compartmentStatsVector(), currCompartmentStats(NULL) { }
: stackSize(0),
gcHeapChunkTotal(0),
gcHeapChunkCleanUnused(0),
gcHeapChunkDirtyUnused(0),
gcHeapArenaUnused(0),
gcHeapChunkAdmin(0),
gcHeapUnusedPercentage(0),
compartmentStatsVector(),
currCompartmentStats(NULL) { }
PRInt64 stackSize;
PRInt64 gcHeapChunkTotal;
PRInt64 gcHeapChunkCleanUnused;
PRInt64 gcHeapChunkDirtyUnused;
PRInt64 gcHeapArenaUnused;
PRInt64 gcHeapChunkAdmin;
PRInt64 gcHeapUnusedPercentage;
js::Vector<CompartmentStats, 0, js::SystemAllocPolicy> compartmentStatsVector;
CompartmentStats *currCompartmentStats;
@ -234,15 +250,9 @@ JSBool
CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data);
void
ReportCompartmentStats(const CompartmentStats &stats,
const nsACString &pathPrefix,
nsIMemoryMultiReporterCallback *callback,
nsISupports *closure);
void
ReportJSStackSizeForRuntime(JSRuntime *rt, const nsACString &pathPrefix,
nsIMemoryMultiReporterCallback *callback,
nsISupports *closure);
ReportJSRuntimeStats(const IterateData &data, const nsACString &pathPrefix,
nsIMemoryMultiReporterCallback *callback,
nsISupports *closure);
} // namespace memory
} // namespace xpconnect