Bug 711130 (part 5) - Overhaul the "other measurements" measurements for JS memory consumption. r=terrence.

--HG--
extra : rebase_source : 492af75618c40eb2a92be2756f67db93834729cf
This commit is contained in:
Nicholas Nethercote 2012-06-25 17:08:59 -07:00
parent 647a888256
commit 26f000b52d
7 changed files with 368 additions and 350 deletions

View File

@ -30,6 +30,13 @@ struct TypeInferenceSizes
size_t objects;
size_t tables;
size_t temporary;
void add(TypeInferenceSizes &sizes) {
this->scripts += sizes.scripts;
this->objects += sizes.objects;
this->tables += sizes.tables;
this->temporary += sizes.temporary;
}
};
// These measurements relate directly to the JSRuntime, and not to
@ -79,6 +86,9 @@ struct CompartmentStats
}
void *extra;
// If you add a new number, remember to update add() and maybe
// gcHeapThingsSize()!
size_t gcHeapArenaAdmin;
size_t gcHeapUnusedGcThings;
@ -107,6 +117,45 @@ struct CompartmentStats
size_t crossCompartmentWrappers;
TypeInferenceSizes typeInferenceSizes;
// Add cStats's numbers to this object's numbers.
void add(CompartmentStats &cStats) {
#define ADD(x) this->x += cStats.x
ADD(gcHeapArenaAdmin);
ADD(gcHeapUnusedGcThings);
ADD(gcHeapObjectsNonFunction);
ADD(gcHeapObjectsFunction);
ADD(gcHeapStrings);
ADD(gcHeapShapesTree);
ADD(gcHeapShapesDict);
ADD(gcHeapShapesBase);
ADD(gcHeapScripts);
ADD(gcHeapTypeObjects);
#if JS_HAS_XML_SUPPORT
ADD(gcHeapXML);
#endif
ADD(objectSlots);
ADD(objectElements);
ADD(objectMisc);
ADD(stringChars);
ADD(shapesExtraTreeTables);
ADD(shapesExtraDictTables);
ADD(shapesExtraTreeShapeKids);
ADD(shapesCompartmentTables);
ADD(scriptData);
ADD(mjitData);
ADD(crossCompartmentWrappers);
#undef ADD
typeInferenceSizes.add(cStats.typeInferenceSizes);
}
// The size of all the live things in the GC heap.
size_t gcHeapThingsSize();
};
struct RuntimeStats
@ -114,21 +163,14 @@ struct RuntimeStats
RuntimeStats(JSMallocSizeOfFun mallocSizeOf)
: runtime()
, gcHeapChunkTotal(0)
, gcHeapCommitted(0)
, gcHeapUnused(0)
, gcHeapUnusedChunks(0)
, gcHeapUnusedArenas(0)
, gcHeapChunkCleanDecommitted(0)
, gcHeapChunkDirtyDecommitted(0)
, gcHeapUnusedChunks(0)
, gcHeapUnusedArenas(0)
, gcHeapUnusedGcThings(0)
, gcHeapChunkAdmin(0)
, totalObjects(0)
, totalShapes(0)
, totalScripts(0)
, totalStrings(0)
, totalMjit(0)
, totalTypeInference(0)
, totalAnalysisTemp(0)
, gcHeapGcThings(0)
, totals()
, compartmentStatsVector()
, currCompartmentStats(NULL)
, mallocSizeOf(mallocSizeOf)
@ -136,23 +178,35 @@ struct RuntimeStats
RuntimeSizes runtime;
// If you add a new number, remember to update the constructor!
// Here's a useful breakdown of the GC heap.
//
// - rtStats.gcHeapChunkTotal
// - decommitted bytes
// - rtStats.gcHeapChunkCleanDecommitted (decommitted arenas in empty chunks)
// - rtStats.gcHeapChunkDirtyDecommitted (decommitted arenas in non-empty chunks)
// - unused bytes
// - rtStats.gcHeapUnusedChunks (empty chunks)
// - rtStats.gcHeapUnusedArenas (empty arenas within non-empty chunks)
// - rtStats.total.gcHeapUnusedGcThings (empty GC thing slots within non-empty arenas)
// - used bytes
// - rtStats.gcHeapChunkAdmin
// - rtStats.total.gcHeapArenaAdmin
// - rtStats.gcHeapGcThings (in-use GC things)
size_t gcHeapChunkTotal;
size_t gcHeapCommitted;
size_t gcHeapUnused;
size_t gcHeapUnusedChunks;
size_t gcHeapUnusedArenas;
size_t gcHeapChunkCleanDecommitted;
size_t gcHeapChunkDirtyDecommitted;
size_t gcHeapUnusedChunks;
size_t gcHeapUnusedArenas;
size_t gcHeapUnusedGcThings;
size_t gcHeapChunkAdmin;
size_t totalObjects;
size_t totalShapes;
size_t totalScripts;
size_t totalStrings;
size_t totalMjit;
size_t totalTypeInference;
size_t totalAnalysisTemp;
size_t gcHeapGcThings;
// The sum of all compartment's measurements.
CompartmentStats totals;
js::Vector<CompartmentStats, 0, js::SystemAllocPolicy> compartmentStatsVector;
CompartmentStats *currCompartmentStats;

View File

@ -22,6 +22,34 @@ namespace JS {
using namespace js;
size_t
CompartmentStats::gcHeapThingsSize()
{
// These are just the GC-thing measurements.
size_t n = 0;
n += gcHeapObjectsNonFunction;
n += gcHeapObjectsFunction;
n += gcHeapStrings;
n += gcHeapShapesTree;
n += gcHeapShapesDict;
n += gcHeapShapesBase;
n += gcHeapScripts;
n += gcHeapTypeObjects;
#if JS_HAS_XML_SUPPORT
n += gcHeapXML;
#endif
#ifdef DEBUG
size_t n2 = n;
n2 += gcHeapArenaAdmin;
n2 += gcHeapUnusedGcThings;
// These numbers should sum to a multiple of the arena size.
JS_ASSERT(n2 % gc::ArenaSize == 0);
#endif
return n;
}
static void
StatsCompartmentCallback(JSRuntime *rt, void *data, JSCompartment *compartment)
{
@ -157,71 +185,32 @@ CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats)
if (!rtStats->compartmentStatsVector.reserve(rt->compartments.length()))
return false;
rtStats->gcHeapChunkCleanDecommitted =
rt->gcChunkPool.countCleanDecommittedArenas(rt) * gc::ArenaSize;
rtStats->gcHeapUnusedChunks =
size_t(JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS)) * gc::ChunkSize -
rtStats->gcHeapChunkCleanDecommitted;
rtStats->gcHeapChunkTotal =
size_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) * gc::ChunkSize;
IterateCompartmentsArenasCells(rt, rtStats, StatsCompartmentCallback,
StatsArenaCallback, StatsCellCallback);
rtStats->gcHeapChunkCleanDecommitted =
rt->gcChunkPool.countCleanDecommittedArenas(rt) * gc::ArenaSize;
rtStats->gcHeapUnusedChunks =
size_t(JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS)) * gc::ChunkSize -
rtStats->gcHeapChunkCleanDecommitted;
// This just computes rtStats->gcHeapChunkDirtyDecommitted.
IterateChunks(rt, rtStats, StatsChunkCallback);
// Take the per-compartment measurements.
IterateCompartmentsArenasCells(rt, rtStats, StatsCompartmentCallback,
StatsArenaCallback, StatsCellCallback);
// Take the "explcit/js/runtime/" measurements.
rt->sizeOfIncludingThis(rtStats->mallocSizeOf, &rtStats->runtime);
// This is initialized to all bytes stored in used chunks, and then we
// subtract used space from it each time around the loop.
rtStats->gcHeapUnusedArenas = rtStats->gcHeapChunkTotal -
rtStats->gcHeapUnusedChunks -
rtStats->gcHeapChunkCleanDecommitted -
rtStats->gcHeapChunkDirtyDecommitted;
rtStats->gcHeapGcThings = 0;
for (size_t i = 0; i < rtStats->compartmentStatsVector.length(); i++) {
CompartmentStats &cStats = rtStats->compartmentStatsVector[i];
rtStats->totalMjit = rtStats->runtime.mjitCode;
for (size_t index = 0;
index < rtStats->compartmentStatsVector.length();
index++) {
CompartmentStats &cStats = rtStats->compartmentStatsVector[index];
size_t used = cStats.gcHeapArenaAdmin +
cStats.gcHeapUnusedGcThings +
cStats.gcHeapObjectsNonFunction +
cStats.gcHeapObjectsFunction +
cStats.gcHeapStrings +
cStats.gcHeapShapesTree +
cStats.gcHeapShapesDict +
cStats.gcHeapShapesBase +
cStats.gcHeapScripts +
#if JS_HAS_XML_SUPPORT
cStats.gcHeapXML +
#endif
cStats.gcHeapTypeObjects;
rtStats->gcHeapUnusedArenas -= used;
rtStats->gcHeapUnusedGcThings += cStats.gcHeapUnusedGcThings;
rtStats->totalObjects += cStats.gcHeapObjectsNonFunction +
cStats.gcHeapObjectsFunction +
cStats.objectSlots +
cStats.objectElements +
cStats.objectMisc;
rtStats->totalShapes += cStats.gcHeapShapesTree +
cStats.gcHeapShapesDict +
cStats.gcHeapShapesBase +
cStats.shapesExtraTreeTables +
cStats.shapesExtraDictTables +
cStats.shapesCompartmentTables;
rtStats->totalScripts += cStats.gcHeapScripts +
cStats.scriptData;
rtStats->totalStrings += cStats.gcHeapStrings +
cStats.stringChars;
rtStats->totalMjit += cStats.mjitData;
rtStats->totalTypeInference += cStats.gcHeapTypeObjects +
cStats.typeInferenceSizes.objects +
cStats.typeInferenceSizes.scripts +
cStats.typeInferenceSizes.tables;
rtStats->totalAnalysisTemp += cStats.typeInferenceSizes.temporary;
rtStats->totals.add(cStats);
rtStats->gcHeapGcThings += cStats.gcHeapThingsSize();
}
size_t numDirtyChunks =
@ -231,14 +220,16 @@ CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats)
rtStats->gcHeapChunkAdmin = numDirtyChunks * perChunkAdmin;
rtStats->gcHeapUnusedArenas -= rtStats->gcHeapChunkAdmin;
rtStats->gcHeapUnused = rtStats->gcHeapUnusedArenas +
rtStats->gcHeapUnusedChunks +
rtStats->gcHeapUnusedGcThings;
rtStats->gcHeapCommitted = rtStats->gcHeapChunkTotal -
rtStats->gcHeapChunkCleanDecommitted -
rtStats->gcHeapChunkDirtyDecommitted;
// |gcHeapUnusedArenas| is the only thing left. Compute it in terms of
// all the others. See the comment in RuntimeStats for explanation.
rtStats->gcHeapUnusedArenas = rtStats->gcHeapChunkTotal -
rtStats->gcHeapChunkCleanDecommitted -
rtStats->gcHeapChunkDirtyDecommitted -
rtStats->gcHeapUnusedChunks -
rtStats->totals.gcHeapUnusedGcThings -
rtStats->gcHeapChunkAdmin -
rtStats->totals.gcHeapArenaAdmin -
rtStats->gcHeapGcThings;
return true;
}

View File

@ -1227,25 +1227,9 @@ static const size_t SUNDRIES_THRESHOLD = 8192;
NS_ENSURE_SUCCESS(rv, rv); \
} while (0)
#define CREPORT(_path, _kind, _units, _amount, _desc) \
do { \
size_t amount = _amount; /* evaluate _amount only once */ \
if (amount >= SUNDRIES_THRESHOLD) { \
nsresult rv; \
rv = cb->Callback(EmptyCString(), _path, _kind, _units, amount, \
NS_LITERAL_CSTRING(_desc), closure); \
NS_ENSURE_SUCCESS(rv, rv); \
} else { \
otherSundries += amount; \
} \
} while (0)
#define REPORT_BYTES(_path, _kind, _amount, _desc) \
REPORT(_path, _kind, nsIMemoryReporter::UNITS_BYTES, _amount, _desc);
#define CREPORT_BYTES(_path, _kind, _amount, _desc) \
CREPORT(_path, _kind, nsIMemoryReporter::UNITS_BYTES, _amount, _desc);
#define REPORT_GC_BYTES(_path, _amount, _desc) \
do { \
size_t amount = _amount; /* evaluate _amount only once */ \
@ -1258,6 +1242,23 @@ static const size_t SUNDRIES_THRESHOLD = 8192;
gcTotal += amount; \
} while (0)
// Nb: all non-GC compartment reports are currently KIND_HEAP, and this macro
// relies on that.
#define CREPORT_BYTES(_path, _amount, _desc) \
do { \
size_t amount = _amount; /* evaluate _amount only once */ \
if (amount >= SUNDRIES_THRESHOLD) { \
nsresult rv; \
rv = cb->Callback(EmptyCString(), _path, \
nsIMemoryReporter::KIND_HEAP, \
nsIMemoryReporter::UNITS_BYTES, amount, \
NS_LITERAL_CSTRING(_desc), closure); \
NS_ENSURE_SUCCESS(rv, rv); \
} else { \
otherSundries += amount; \
} \
} while (0)
#define CREPORT_GC_BYTES(_path, _amount, _desc) \
do { \
size_t amount = _amount; /* evaluate _amount only once */ \
@ -1274,18 +1275,16 @@ static const size_t SUNDRIES_THRESHOLD = 8192;
} \
} while (0)
template <int N>
inline const nsCString
MakePath(const nsACString &pathPrefix, const JS::CompartmentStats &cStats,
const char (&reporterName)[N])
{
const char *name = static_cast<char *>(cStats.extra);
if (!name)
name = "error while initializing compartment name";
return pathPrefix + NS_LITERAL_CSTRING("compartment(") +
nsDependentCString(name) + NS_LITERAL_CSTRING(")/") +
nsDependentCString(reporterName);
}
#define RREPORT_BYTES(_path, _kind, _amount, _desc) \
do { \
size_t amount = _amount; /* evaluate _amount only once */ \
nsresult rv; \
rv = cb->Callback(EmptyCString(), _path, _kind, \
nsIMemoryReporter::UNITS_BYTES, amount, \
NS_LITERAL_CSTRING(_desc), closure); \
NS_ENSURE_SUCCESS(rv, rv); \
rtTotal += amount; \
} while (0)
namespace xpc {
@ -1293,182 +1292,180 @@ static nsresult
ReportCompartmentStats(const JS::CompartmentStats &cStats,
const nsACString &pathPrefix,
nsIMemoryMultiReporterCallback *cb,
nsISupports *closure, size_t *gcTotalOut)
nsISupports *closure, size_t *gcTotalOut = NULL)
{
size_t gcTotal = 0, gcHeapSundries = 0, otherSundries = 0;
CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/arena-admin"),
CREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/arena-admin"),
cStats.gcHeapArenaAdmin,
"Memory on the compartment's garbage-collected JavaScript "
"Memory on the garbage-collected JavaScript "
"heap, within arenas, that is used (a) to hold internal "
"bookkeeping information, and (b) to provide padding to "
"align GC things.");
CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/unused-gc-things"),
CREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/unused-gc-things"),
cStats.gcHeapUnusedGcThings,
"Memory on the compartment's garbage-collected JavaScript "
"Memory on the garbage-collected JavaScript "
"heap taken by empty GC thing slots within non-empty "
"arenas.");
CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/objects/non-function"),
CREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/objects/non-function"),
cStats.gcHeapObjectsNonFunction,
"Memory on the compartment's garbage-collected JavaScript "
"Memory on the garbage-collected JavaScript "
"heap that holds non-function objects.");
CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/objects/function"),
CREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/objects/function"),
cStats.gcHeapObjectsFunction,
"Memory on the compartment's garbage-collected JavaScript "
"Memory on the garbage-collected JavaScript "
"heap that holds function objects.");
CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/strings"),
CREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/strings"),
cStats.gcHeapStrings,
"Memory on the compartment's garbage-collected JavaScript "
"Memory on the 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 strings) the "
"string characters; characters in longer strings are "
"counted " "under 'gc-heap/string-chars' instead.");
"counted under 'gc-heap/string-chars' instead.");
CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/scripts"),
CREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/scripts"),
cStats.gcHeapScripts,
"Memory on the compartment's garbage-collected JavaScript "
"Memory on the 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.");
CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/shapes/tree"),
CREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/shapes/tree"),
cStats.gcHeapShapesTree,
"Memory on the compartment's garbage-collected JavaScript "
"Memory on the garbage-collected JavaScript "
"heap that holds shapes that are in a property tree.");
CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/shapes/dict"),
CREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/shapes/dict"),
cStats.gcHeapShapesDict,
"Memory on the compartment's garbage-collected JavaScript "
"Memory on the garbage-collected JavaScript "
"heap that holds shapes that are in dictionary mode.");
CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/shapes/base"),
CREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/shapes/base"),
cStats.gcHeapShapesBase,
"Memory on the compartment's garbage-collected JavaScript "
"Memory on the garbage-collected JavaScript "
"heap that collates data common to many shapes.");
CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/type-objects"),
CREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/type-objects"),
cStats.gcHeapTypeObjects,
"Memory on the compartment's garbage-collected JavaScript "
"Memory on the garbage-collected JavaScript "
"heap that holds type inference information.");
#if JS_HAS_XML_SUPPORT
CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/xml"),
CREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/xml"),
cStats.gcHeapXML,
"Memory on the compartment's garbage-collected JavaScript "
"Memory on the garbage-collected JavaScript "
"heap that holds E4X XML objects.");
#endif
CREPORT_BYTES(MakePath(pathPrefix, cStats, "objects/slots"),
nsIMemoryReporter::KIND_HEAP, cStats.objectSlots,
"Memory allocated for the compartment's non-fixed object "
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("objects/slots"),
cStats.objectSlots,
"Memory allocated for the non-fixed object "
"slot arrays, 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 "
"stored on the JavaScript heap; those slots "
"are not counted here, but in 'gc-heap/objects' instead.");
CREPORT_BYTES(MakePath(pathPrefix, cStats, "objects/elements"),
nsIMemoryReporter::KIND_HEAP, cStats.objectElements,
"Memory allocated for the compartment's object element "
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("objects/elements"),
cStats.objectElements,
"Memory allocated for object element "
"arrays, which are used to represent indexed object "
"properties.");
CREPORT_BYTES(MakePath(pathPrefix, cStats, "objects/misc"),
nsIMemoryReporter::KIND_HEAP, cStats.objectMisc,
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("objects/misc"),
cStats.objectMisc,
"Memory allocated for various small, miscellaneous "
"structures that hang off certain kinds of objects.");
CREPORT_BYTES(MakePath(pathPrefix, cStats, "string-chars"),
nsIMemoryReporter::KIND_HEAP, cStats.stringChars,
"Memory allocated to hold the compartment's string "
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("string-chars"),
cStats.stringChars,
"Memory allocated to hold string "
"characters. Sometimes more memory is allocated than "
"necessary, to simplify string concatenation. Each string "
"also includes a header which is stored on the "
"compartment's JavaScript heap; that header is not counted "
"here, but in 'gc-heap/strings' instead.");
CREPORT_BYTES(MakePath(pathPrefix, cStats, "shapes-extra/tree-tables"),
nsIMemoryReporter::KIND_HEAP, cStats.shapesExtraTreeTables,
"Memory allocated for the compartment's property tables "
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes-extra/tree-tables"),
cStats.shapesExtraTreeTables,
"Memory allocated for the property tables "
"that belong to shapes that are in a property tree.");
CREPORT_BYTES(MakePath(pathPrefix, cStats, "shapes-extra/dict-tables"),
nsIMemoryReporter::KIND_HEAP, cStats.shapesExtraDictTables,
"Memory allocated for the compartment's property tables "
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes-extra/dict-tables"),
cStats.shapesExtraDictTables,
"Memory allocated for the property tables "
"that belong to shapes that are in dictionary mode.");
CREPORT_BYTES(MakePath(pathPrefix, cStats, "shapes-extra/tree-shape-kids"),
nsIMemoryReporter::KIND_HEAP, cStats.shapesExtraTreeShapeKids,
"Memory allocated for the compartment's kid hashes that "
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes-extra/tree-shape-kids"),
cStats.shapesExtraTreeShapeKids,
"Memory allocated for the kid hashes that "
"belong to shapes that are in a property tree.");
CREPORT_BYTES(MakePath(pathPrefix, cStats, "shapes-extra/compartment-tables"),
nsIMemoryReporter::KIND_HEAP, cStats.shapesCompartmentTables,
"Memory used by compartment wide tables storing shape "
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes-extra/compartment-tables"),
cStats.shapesCompartmentTables,
"Memory used by compartment-wide tables storing shape "
"information for use during object construction.");
CREPORT_BYTES(MakePath(pathPrefix, cStats, "script-data"),
nsIMemoryReporter::KIND_HEAP, cStats.scriptData,
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("script-data"),
cStats.scriptData,
"Memory allocated for JSScript bytecode and various "
"variable-length tables.");
CREPORT_BYTES(MakePath(pathPrefix, cStats, "mjit-data"),
nsIMemoryReporter::KIND_HEAP, cStats.mjitData,
"Memory used by the method JIT for the compartment's "
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("mjit-data"),
cStats.mjitData,
"Memory used by the method JIT for "
"compilation data: JITScripts, native maps, and inline "
"cache structs.");
CREPORT_BYTES(MakePath(pathPrefix, cStats, "cross-compartment-wrappers"),
nsIMemoryReporter::KIND_HEAP, cStats.crossCompartmentWrappers,
"Memory used by the compartment's cross-compartment "
"wrappers.");
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("cross-compartment-wrappers"),
cStats.crossCompartmentWrappers,
"Memory used by cross-compartment wrappers.");
CREPORT_BYTES(MakePath(pathPrefix, cStats, "type-inference/script-main"),
nsIMemoryReporter::KIND_HEAP,
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-inference/script-main"),
cStats.typeInferenceSizes.scripts,
"Memory used during type inference to store type sets of "
"variables and dynamically observed types.");
CREPORT_BYTES(MakePath(pathPrefix, cStats, "type-inference/object-main"),
nsIMemoryReporter::KIND_HEAP,
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-inference/object-main"),
cStats.typeInferenceSizes.objects,
"Memory used during type inference to store types and "
"possible property types of JS objects.");
CREPORT_BYTES(MakePath(pathPrefix, cStats, "type-inference/tables"),
nsIMemoryReporter::KIND_HEAP,
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-inference/tables"),
cStats.typeInferenceSizes.tables,
"Memory used during type inference for compartment-wide "
"tables.");
CREPORT_BYTES(MakePath(pathPrefix, cStats, "analysis-temporary"),
nsIMemoryReporter::KIND_HEAP,
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("analysis-temporary"),
cStats.typeInferenceSizes.temporary,
"Memory used during type inference and compilation to hold "
"transient analysis information. Cleared on GC.");
if (gcHeapSundries > 0) {
REPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/sundries"),
// We deliberately don't use CREPORT_GC_BYTES here.
REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/sundries"),
gcHeapSundries,
"The sum of all this compartment's gc-heap "
"The sum of all the gc-heap "
"measurements that are too small to be worth showing "
"individually.");
}
if (otherSundries > 0) {
REPORT_BYTES(MakePath(pathPrefix, cStats, "other-sundries"),
nsIMemoryReporter::KIND_HEAP,
otherSundries,
"The sum of all this compartment's non-gc-heap "
// We deliberately don't use CREPORT_BYTES here.
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("other-sundries"),
nsIMemoryReporter::KIND_HEAP, otherSundries,
"The sum of all the non-gc-heap "
"measurements that are too small to be worth showing "
"individually.");
}
*gcTotalOut += gcTotal;
if (gcTotalOut) {
*gcTotalOut += gcTotal;
}
return NS_OK;
}
@ -1477,78 +1474,94 @@ nsresult
ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats,
const nsACString &pathPrefix,
nsIMemoryMultiReporterCallback *cb,
nsISupports *closure)
nsISupports *closure, size_t *rtTotalOut)
{
nsresult rv;
// Report each compartment's numbers.
size_t gcTotal = 0;
for (size_t index = 0;
index < rtStats.compartmentStatsVector.length();
index++) {
rv = ReportCompartmentStats(rtStats.compartmentStatsVector[index],
pathPrefix, cb, closure, &gcTotal);
for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++) {
JS::CompartmentStats cStats = rtStats.compartmentStatsVector[i];
const char *name = static_cast<char *>(cStats.extra);
nsCString pathPrefix2 = pathPrefix + NS_LITERAL_CSTRING("compartment(") +
nsDependentCString(name) + NS_LITERAL_CSTRING(")/");
rv = ReportCompartmentStats(cStats, pathPrefix2, cb, closure, &gcTotal);
NS_ENSURE_SUCCESS(rv, rv);
}
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/runtime-object"),
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.object,
"Memory used by the JSRuntime object.");
// Report the rtStats.runtime numbers under "runtime/", and compute their
// total for later.
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/atoms-table"),
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.atomsTable,
"Memory used by the atoms table.");
size_t rtTotal = 0;
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/contexts"),
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.contexts,
"Memory used by JSContext objects and certain structures "
"hanging off them.");
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/runtime-object"),
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.object,
"Memory used by the JSRuntime object.");
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/dtoa"),
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.dtoa,
"Memory used by DtoaState, which is used for converting "
"strings to numbers and vice versa.");
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/atoms-table"),
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.atomsTable,
"Memory used by the atoms table.");
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/temporary"),
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.temporary,
"Memory held transiently in JSRuntime and used during "
"compilation. It mostly holds parse nodes.");
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/contexts"),
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.contexts,
"Memory used by JSContext objects and certain structures "
"hanging off them.");
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/mjit-code"),
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.mjitCode,
"Memory used by the method JIT to hold the runtime's "
"generated code.");
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/dtoa"),
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.dtoa,
"Memory used by DtoaState, which is used for converting "
"strings to numbers and vice versa.");
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/regexp-code"),
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.regexpCode,
"Memory used by the regexp JIT to hold generated code.");
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/temporary"),
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.temporary,
"Memory held transiently in JSRuntime and used during "
"compilation. It mostly holds parse nodes.");
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/unused-code-memory"),
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.unusedCodeMemory,
"Memory allocated by the method and/or regexp JIT to hold the "
"runtime's code, but which is currently unused.");
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/mjit-code"),
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.mjitCode,
"Memory used by the method JIT to hold the runtime's "
"generated code.");
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/stack-committed"),
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.stackCommitted,
"Memory used for the JS call stack. This is the committed "
"portion of the stack; the uncommitted portion is not "
"measured because it hardly costs anything.");
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/regexp-code"),
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.regexpCode,
"Memory used by the regexp JIT to hold generated code.");
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/gc-marker"),
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.gcMarker,
"Memory used for the GC mark stack and gray roots.");
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/unused-code-memory"),
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.unusedCodeMemory,
"Memory allocated by the method and/or regexp JIT to hold the "
"runtime's code, but which is currently unused.");
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/math-cache"),
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.mathCache,
"Memory used for the math cache.");
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/stack-committed"),
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.stackCommitted,
"Memory used for the JS call stack. This is the committed "
"portion of the stack; the uncommitted portion is not "
"measured because it hardly costs anything.");
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/script-filenames"),
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.scriptFilenames,
"Memory used for the table holding script filenames.");
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/gc-marker"),
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.gcMarker,
"Memory used for the GC mark stack and gray roots.");
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/compartment-objects"),
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.compartmentObjects,
"Memory used for JSCompartment objects. These are fairly "
"small and all the same size, so they're not worth reporting "
"on a per-compartment basis.");
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/math-cache"),
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.mathCache,
"Memory used for the math cache.");
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/script-filenames"),
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.scriptFilenames,
"Memory used for the table holding script filenames.");
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/compartment-objects"),
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.compartmentObjects,
"Memory used for JSCompartment objects. These are fairly "
"small and all the same size, so they're not worth reporting "
"on a per-compartment basis.");
if (rtTotalOut) {
*rtTotalOut = rtTotal;
}
// Report GC numbers that don't belong to a compartment.
REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/unused-arenas"),
rtStats.gcHeapUnusedArenas,
@ -1697,125 +1710,83 @@ public:
// This is the second step (see above). First we report stuff in the
// "explicit" tree, then we report other stuff.
size_t rtTotal = 0;
nsresult rv =
xpc::ReportJSRuntimeExplicitTreeStats(rtStats, explicitJs, cb,
closure);
closure, &rtTotal);
NS_ENSURE_SUCCESS(rv, rv);
REPORT_BYTES(explicitJs + NS_LITERAL_CSTRING("xpconnect"),
nsIMemoryReporter::KIND_HEAP, xpconnect,
"Memory used by XPConnect.");
// Report the sums of the compartment numbers.
rv = ReportCompartmentStats(rtStats.totals,
NS_LITERAL_CSTRING("js-main-runtime/compartments/"),
cb, closure);
NS_ENSURE_SUCCESS(rv, rv);
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-unused-arenas"),
nsIMemoryReporter::KIND_OTHER,
rtStats.gcHeapUnusedArenas,
"The same as 'explicit/js/gc-heap/unused-arenas'. "
"Shown here for easy comparison with other 'js-gc' "
"reporters.");
// Report the sum of the runtime/ numbers.
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/runtime"),
nsIMemoryReporter::KIND_OTHER, rtTotal,
"The sum of all measurements under 'explicit/js/runtime/'.");
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-unused-chunks"),
nsIMemoryReporter::KIND_OTHER,
rtStats.gcHeapUnusedChunks,
"The same as 'explicit/js/gc-heap/unused-chunks'. "
"Shown here for easy comparison with other 'js-gc' "
"reporters.");
// Report the numbers for memory outside of compartments.
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-unused-gc-things"),
nsIMemoryReporter::KIND_OTHER,
rtStats.gcHeapUnusedGcThings,
"Memory on the main JSRuntime's garbage-collected "
"JavaScript heap taken by empty GC thing slots within "
"non-empty arenas. This is the sum of all compartments' "
"'gc-heap/unused-gc-things' numbers.");
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed-unused"),
nsIMemoryReporter::KIND_OTHER,
rtStats.gcHeapUnused,
"Amount of the GC heap that's committed, but that is "
"neither part of an active allocation nor being used for "
"bookkeeping. Equal to 'gc-heap-unused-chunks' + "
"'gc-heap-unused-arenas' + 'gc-heap-unused-gc-things'.");
// Why 10000x? 100x because it's a percentage, and another 100x
// because nsIMemoryReporter requires that for UNITS_PERCENTAGE
// reporters so we can get two decimal places out of the integer value.
REPORT(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed-unused-ratio"),
nsIMemoryReporter::KIND_OTHER,
nsIMemoryReporter::UNITS_PERCENTAGE,
(PRInt64) 10000 * rtStats.gcHeapUnused /
((double) rtStats.gcHeapCommitted - rtStats.gcHeapUnused),
"Ratio of committed, unused bytes to allocated bytes; i.e. "
"'gc-heap-committed-unused' / 'gc-heap-allocated'. This "
"measures the overhead of the GC heap allocator relative to the "
"amount of memory allocated.");
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed"),
nsIMemoryReporter::KIND_OTHER,
rtStats.gcHeapCommitted,
"Committed memory (i.e., in physical memory or swap) "
"used by the garbage-collected JavaScript heap.");
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-allocated"),
nsIMemoryReporter::KIND_OTHER,
(rtStats.gcHeapCommitted - rtStats.gcHeapUnused),
"Amount of the GC heap used for active allocations and "
"bookkeeping. This is calculated as 'gc-heap-committed' "
"- 'gc-heap-unused'.");
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-decommitted"),
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/decommitted"),
nsIMemoryReporter::KIND_OTHER,
rtStats.gcHeapChunkCleanDecommitted + rtStats.gcHeapChunkDirtyDecommitted,
"The same as 'explicit/js/gc-heap/decommitted'. Shown "
"here for easy comparison with other 'js-gc' reporters.");
"The same as 'explicit/js/gc-heap/decommitted'.");
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-objects"),
nsIMemoryReporter::KIND_OTHER, rtStats.totalObjects,
"Memory used for all object-related data in the main "
"JSRuntime. This is the sum of all compartments' "
"'gc-heap/objects-non-function', "
"'gc-heap/objects-function' and 'object-slots' numbers.");
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/unused-chunks"),
nsIMemoryReporter::KIND_OTHER,
rtStats.gcHeapUnusedChunks,
"The same as 'explicit/js/gc-heap/unused-chunks'.");
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-shapes"),
nsIMemoryReporter::KIND_OTHER, rtStats.totalShapes,
"Memory used for all shape-related data in the main "
"JSRuntime. This is the sum of all compartments' "
"'gc-heap/shapes/tree', 'gc-heap/shapes/dict', "
"'gc-heap/shapes/base', 'shapes-extra/tree-tables', "
"'shapes-extra/dict-tables', "
"'shapes-extra/tree-shape-kids' and "
"'shapes-extra/empty-shape-arrays'.");
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/unused-arenas"),
nsIMemoryReporter::KIND_OTHER,
rtStats.gcHeapUnusedArenas,
"The same as 'explicit/js/gc-heap/unused-arenas'.");
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-scripts"),
nsIMemoryReporter::KIND_OTHER, rtStats.totalScripts,
"Memory used for all script-related data in the main "
"JSRuntime. This is the sum of all compartments' "
"'gc-heap/scripts' and 'script-data' numbers.");
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/chunk-admin"),
nsIMemoryReporter::KIND_OTHER,
rtStats.gcHeapChunkAdmin,
"The same as 'explicit/js/gc-heap/chunk-admin'.");
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-strings"),
nsIMemoryReporter::KIND_OTHER, rtStats.totalStrings,
"Memory used for all string-related data in the main "
"JSRuntime. This is the sum of all compartments' "
"'gc-heap/strings' and 'string-chars' numbers.");
// Report a breakdown of the committed GC space.
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-mjit"),
nsIMemoryReporter::KIND_OTHER, rtStats.totalMjit,
"Memory used by the method JIT in the main JSRuntime. "
"This is the sum of all compartments' 'mjit/code', and "
"'mjit/data' numbers.");
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/chunks"),
nsIMemoryReporter::KIND_OTHER,
rtStats.gcHeapUnusedChunks,
"The same as 'explicit/js/gc-heap/unused-chunks'.");
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-type-inference"),
nsIMemoryReporter::KIND_OTHER, rtStats.totalTypeInference,
"Non-transient memory used by type inference in the main "
"JSRuntime. This is the sum of all compartments' "
"'gc-heap/type-objects', 'type-inference/script-main', "
"'type-inference/object-main' and "
"'type-inference/tables' numbers.");
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/arenas"),
nsIMemoryReporter::KIND_OTHER,
rtStats.gcHeapUnusedArenas,
"The same as 'explicit/js/gc-heap/unused-arenas'.");
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-analysis-temporary"),
nsIMemoryReporter::KIND_OTHER, rtStats.totalAnalysisTemp,
"Memory used transiently during type inference and "
"compilation in the main JSRuntime. This is the sum of "
"all compartments' 'analysis-temporary' numbers.");
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things"),
nsIMemoryReporter::KIND_OTHER,
rtStats.totals.gcHeapUnusedGcThings,
"The same as 'js-main-runtime/compartments/gc-heap/unused-gc-things'.");
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/chunk-admin"),
nsIMemoryReporter::KIND_OTHER,
rtStats.gcHeapChunkAdmin,
"The same as 'explicit/js/gc-heap/chunk-admin'.");
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/arena-admin"),
nsIMemoryReporter::KIND_OTHER,
rtStats.totals.gcHeapArenaAdmin,
"The same as 'js-main-runtime/compartments/gc-heap/arena-admin'.");
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things"),
nsIMemoryReporter::KIND_OTHER,
rtStats.gcHeapGcThings,
"Memory on the garbage-collected JavaScript heap that holds GC things such "
"as objects, strings, scripts, etc.")
// Report xpconnect.
REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect"),
nsIMemoryReporter::KIND_HEAP, xpconnect,
"Memory used by XPConnect.");
return NS_OK;
}

View File

@ -268,7 +268,7 @@ nsresult
ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats,
const nsACString &pathPrefix,
nsIMemoryMultiReporterCallback *cb,
nsISupports *closure);
nsISupports *closure, size_t *rtTotal = NULL);
/**
* Convert a jsval to PRInt64. Return true on success.

View File

@ -511,7 +511,6 @@ function getTreesByProcess(aMgr, aTreesByProcess, aDegeneratesByProcess,
assert(aDescription !== "", "empty smaps description");
} else {
assert(aKind === KIND_OTHER, "bad other kind");
assert(gSentenceRegExp.test(aDescription),
"non-sentence other description");
}

View File

@ -63,7 +63,7 @@
vsizeAmounts.push(aAmount);
} else if (aPath === "resident") {
residentAmounts.push(aAmount);
} else if (aPath === "js-main-runtime-gc-heap-committed") {
} else if (aPath === "js-main-runtime-gc-heap-committed/used/gc-things") {
jsGcHeapAmounts.push(aAmount);
} else if (aPath === "heap-allocated") {
heapAllocatedAmounts.push(aAmount);
@ -141,7 +141,7 @@
}
checkSpecialReport("vsize", vsizeAmounts);
checkSpecialReport("resident", residentAmounts);
checkSpecialReport("js-main-runtime-gc-heap-committed", jsGcHeapAmounts);
checkSpecialReport("js-main-runtime-gc-heap-committed/used/gc-things", jsGcHeapAmounts);
checkSpecialReport("storage-sqlite", storageSqliteAmounts);
ok(areJsCompartmentsPresent, "js compartments are present");

View File

@ -45,8 +45,8 @@ interface nsISimpleEnumerator;
* OTHER, units COUNT, an amount of 1, and a description that's an empty
* string.
*
* - All other reports must have kind OTHER, and a description that is a
* sentence.
* - All other reports are unconstrained except that they must have a
* description that is a sentence.
*/
[scriptable, uuid(b2c39f65-1799-4b92-a806-ab3cf6af3cfa)]
interface nsIMemoryReporter : nsISupports
@ -114,6 +114,9 @@ interface nsIMemoryReporter : nsISupports
*
* - OTHER: reporters which don't fit into either of these categories.
* They can have any units.
*
* The kind only matters for reporters in the "explicit" tree;
* aboutMemory.js uses it to calculate "heap-unclassified".
*/
const PRInt32 KIND_NONHEAP = 0;
const PRInt32 KIND_HEAP = 1;