Bug 661474 - Add per-compartment memory reporters. r=wmccloskey, dolske.

This commit is contained in:
Nicholas Nethercote 2011-06-30 15:44:17 +10:00
parent bb94ffa458
commit 2c2e126833
11 changed files with 488 additions and 318 deletions

View File

@ -617,11 +617,6 @@ struct JSRuntime {
JSWrapObjectCallback wrapObjectCallback;
JSPreWrapCallback preWrapObjectCallback;
#ifdef JS_METHODJIT
/* This measures the size of JITScripts, native maps and IC structs. */
size_t mjitDataSize;
#endif
/*
* To ensure that cx->malloc does not cause a GC, we set this flag during
* OOM reporting (in js_ReportOutOfMemory). If a GC is requested while

View File

@ -2729,40 +2729,14 @@ TraceRuntime(JSTracer *trc)
MarkRuntime(trc);
}
static void
IterateCompartmentCells(JSContext *cx, JSCompartment *comp, uint64 traceKindMask,
void *data, IterateCallback callback)
{
for (unsigned thingKind = 0; thingKind < FINALIZE_LIMIT; thingKind++) {
size_t traceKind = GetFinalizableTraceKind(thingKind);
if (traceKindMask && !TraceKindInMask(traceKind, traceKindMask))
continue;
size_t thingSize = GCThingSizeMap[thingKind];
ArenaHeader *aheader = comp->arenas[thingKind].getHead();
for (; aheader; aheader = aheader->next) {
Arena *a = aheader->getArena();
FreeSpan firstSpan(aheader->getFirstFreeSpan());
FreeSpan *span = &firstSpan;
for (uintptr_t thing = a->thingsStart(thingSize);; thing += thingSize) {
JS_ASSERT(thing <= a->thingsEnd());
if (thing == span->start) {
if (!span->hasNext())
break;
thing = span->end;
span = span->nextSpan();
} else {
(*callback)(cx, data, traceKind, reinterpret_cast<void *>(thing));
}
}
}
}
}
void
IterateCells(JSContext *cx, JSCompartment *comp, uint64 traceKindMask,
void *data, IterateCallback callback)
IterateCompartmentsArenasCells(JSContext *cx, void *data,
IterateCompartmentCallback compartmentCallback,
IterateArenaCallback arenaCallback,
IterateCellCallback cellCallback)
{
CHECK_REQUEST(cx);
LeaveTrace(cx);
JSRuntime *rt = cx->runtime;
@ -2776,11 +2750,35 @@ IterateCells(JSContext *cx, JSCompartment *comp, uint64 traceKindMask,
AutoUnlockGC unlock(rt);
AutoCopyFreeListToArenas copy(rt);
if (comp) {
IterateCompartmentCells(cx, comp, traceKindMask, data, callback);
} else {
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
IterateCompartmentCells(cx, *c, traceKindMask, data, callback);
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) {
JSCompartment *compartment = *c;
(*compartmentCallback)(cx, data, compartment);
for (unsigned thingKind = 0; thingKind < FINALIZE_LIMIT; thingKind++) {
size_t traceKind = GetFinalizableTraceKind(thingKind);
size_t thingSize = GCThingSizeMap[thingKind];
ArenaHeader *aheader = compartment->arenas[thingKind].getHead();
for (; aheader; aheader = aheader->next) {
Arena *arena = aheader->getArena();
(*arenaCallback)(cx, data, arena, traceKind, thingSize);
FreeSpan firstSpan(aheader->getFirstFreeSpan());
FreeSpan *span = &firstSpan;
for (uintptr_t thing = arena->thingsStart(thingSize); ; thing += thingSize) {
JS_ASSERT(thing <= arena->thingsEnd());
if (thing == span->start) {
if (!span->hasNext())
break;
thing = span->end;
span = span->nextSpan();
} else {
(*cellCallback)(cx, data, reinterpret_cast<void *>(thing), traceKind,
thingSize);
}
}
}
}
}
}

View File

@ -1332,30 +1332,22 @@ struct GCMarker : public JSTracer {
void
MarkStackRangeConservatively(JSTracer *trc, Value *begin, Value *end);
static inline uint64
TraceKindMask(unsigned kind)
{
return uint64(1) << kind;
}
static inline bool
TraceKindInMask(unsigned kind, uint64 mask)
{
return !!(mask & TraceKindMask(kind));
}
typedef void (*IterateCallback)(JSContext *cx, void *data, size_t traceKind, void *obj);
typedef void (*IterateCompartmentCallback)(JSContext *cx, void *data, JSCompartment *compartment);
typedef void (*IterateArenaCallback)(JSContext *cx, void *data, gc::Arena *arena, size_t traceKind,
size_t thingSize);
typedef void (*IterateCellCallback)(JSContext *cx, void *data, void *thing, size_t traceKind,
size_t thingSize);
/*
* This function calls |callback| on every cell in the GC heap. If |comp| is
* non-null, then it only selects cells in that compartment. If |traceKindMask|
* is non-zero, then only cells whose traceKind belongs to the mask will be
* selected. The mask should be constructed by ORing |TraceKindMask(...)|
* results.
* This function calls |compartmentCallback| on every compartment,
* |arenaCallback| on every in-use arena, and |cellCallback| on every in-use
* cell in the GC heap.
*/
extern JS_FRIEND_API(void)
IterateCells(JSContext *cx, JSCompartment *comp, uint64 traceKindMask,
void *data, IterateCallback callback);
IterateCompartmentsArenasCells(JSContext *cx, void *data,
IterateCompartmentCallback compartmentCallback,
IterateArenaCallback arenaCallback,
IterateCellCallback cellCallback);
} /* namespace js */

View File

@ -575,6 +575,9 @@ struct JSScript {
return JITScript_Invalid;
return JITScript_Valid;
}
// This method is implemented in MethodJIT.h.
JS_FRIEND_API(size_t) jitDataSize();/* Size of the JITScript and all sections */
#endif
JS_FRIEND_API(size_t) totalSize(); /* Size of the JSScript and all sections */

View File

@ -816,9 +816,6 @@ mjit::Compiler::finishThisUp(JITScript **jitp)
*jitp = jit;
/* We tolerate a race in the stats. */
cx->runtime->mjitDataSize += dataSize;
return Compile_Okay;
}

View File

@ -875,6 +875,17 @@ mjit::JITScript::~JITScript()
#endif
}
size_t
JSScript::jitDataSize()
{
size_t n = 0;
if (jitNormal)
n += jitNormal->scriptDataSize();
if (jitCtor)
n += jitCtor->scriptDataSize();
return n;
}
/* Please keep in sync with Compiler::finishThisUp! */
size_t
mjit::JITScript::scriptDataSize()
@ -905,8 +916,6 @@ mjit::ReleaseScriptCode(JSContext *cx, JSScript *script)
JITScript *jscr;
if ((jscr = script->jitNormal)) {
cx->runtime->mjitDataSize -= jscr->scriptDataSize();
jscr->~JITScript();
cx->free_(jscr);
script->jitNormal = NULL;
@ -914,8 +923,6 @@ mjit::ReleaseScriptCode(JSContext *cx, JSScript *script)
}
if ((jscr = script->jitCtor)) {
cx->runtime->mjitDataSize -= jscr->scriptDataSize();
jscr->~JITScript();
cx->free_(jscr);
script->jitCtor = NULL;

View File

@ -4715,7 +4715,18 @@ JSBool
MJitDataStats(JSContext *cx, uintN argc, jsval *vp)
{
#ifdef JS_METHODJIT
JS_SET_RVAL(cx, vp, INT_TO_JSVAL(cx->runtime->mjitDataSize));
JSRuntime *rt = cx->runtime;
AutoLockGC lock(rt);
size_t n = 0;
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) {
for (JSScript *script = (JSScript *)(*c)->scripts.next;
&script->links != &(*c)->scripts;
script = (JSScript *)script->links.next)
{
n += script->jitDataSize();
}
}
JS_SET_RVAL(cx, vp, INT_TO_JSVAL(n));
#else
JS_SET_RVAL(cx, vp, JSVAL_VOID);
#endif

View File

@ -1259,29 +1259,21 @@ protected:
static XPConnectGCChunkAllocator gXPCJSChunkAllocator;
#ifdef MOZ_MEMORY
#define JS_GC_HEAP_KIND KIND_HEAP
#define JS_GC_HEAP_KIND nsIMemoryReporter::KIND_HEAP
#else
#define JS_GC_HEAP_KIND KIND_MAPPED
#define JS_GC_HEAP_KIND nsIMemoryReporter::KIND_MAPPED
#endif
// 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.
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSGCHeap,
"explicit/js/gc-heap",
JS_GC_HEAP_KIND,
UNITS_BYTES,
"js-gc-heap",
KIND_OTHER,
nsIMemoryReporter::UNITS_BYTES,
gXPCJSChunkAllocator.GetGCChunkBytesInUse,
"Memory used by the garbage-collected JavaScript heap.")
static PRInt64
GetPerCompartmentSize(PRInt64 (*f)(JSCompartment *c))
{
JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
js::AutoLockGC lock(rt);
PRInt64 n = 0;
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
n += f(*c);
return n;
}
static PRInt64
GetJSStack()
{
@ -1295,246 +1287,384 @@ GetJSStack()
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSStack,
"explicit/js/stack",
KIND_MAPPED,
UNITS_BYTES,
nsIMemoryReporter::UNITS_BYTES,
GetJSStack,
"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.")
static PRInt64
GetCompartmentScriptsSize(JSCompartment *c)
class XPConnectJSCompartmentsMultiReporter : public nsIMemoryMultiReporter
{
PRInt64 n = 0;
for (JSScript *script = (JSScript *)c->scripts.next;
&script->links != &c->scripts;
script = (JSScript *)script->links.next)
private:
struct CompartmentStats
{
n += script->totalSize();
}
return n;
}
CompartmentStats(JSContext *cx, JSCompartment *c) {
memset(this, 0, sizeof(*this));
static PRInt64
GetJSScripts()
{
return GetPerCompartmentSize(GetCompartmentScriptsSize);
}
if (c == cx->runtime->atomsCompartment) {
name = NS_LITERAL_CSTRING("atoms");
} else if (c->principals) {
if (c->principals->codebase) {
// A hack: replace forward slashes with '\\' so they aren't
// treated as path separators. Users of the reporters
// (such as about:memory) have to undo this change.
name.Assign(c->principals->codebase);
char* cur = name.BeginWriting();
char* end = name.EndWriting();
for (; cur < end; ++cur) {
if ('/' == *cur) {
*cur = '\\';
}
}
} else {
name = NS_LITERAL_CSTRING("null-codebase");
}
} else {
name = NS_LITERAL_CSTRING("null-principal");
}
}
struct PRInt64Data {
PRInt64Data() : n(0) { }
PRInt64 n;
};
nsCString name;
PRInt64 gcHeapArenaHeaders;
PRInt64 gcHeapArenaPadding;
PRInt64 gcHeapArenaUnused;
// This function is miscompiled with MSVC 2005 when PGO is on. See bug 664647.
#ifdef _MSC_VER
#pragma optimize("", off)
PRInt64 gcHeapObjects;
PRInt64 gcHeapStrings;
PRInt64 gcHeapShapes;
PRInt64 gcHeapXml;
PRInt64 objectSlots;
PRInt64 stringChars;
PRInt64 scripts;
#ifdef JS_METHODJIT
PRInt64 mjitCode;
PRInt64 mjitData;
#endif
void
GetJSObjectSlotsCallback(JSContext *cx, void *v, size_t traceKind, void *thing)
{
JS_ASSERT(traceKind == JSTRACE_OBJECT);
JSObject *obj = (JSObject *)thing;
if (obj->hasSlotsArray()) {
PRInt64Data *data = (PRInt64Data *) v;
data->n += obj->numSlots() * sizeof(js::Value);
}
}
#ifdef _MSC_VER
#pragma optimize("", on)
#ifdef JS_TRACER
PRInt64 tjitCode;
PRInt64 tjitDataAllocatorsMain;
PRInt64 tjitDataAllocatorsReserve;
#endif
};
static PRInt64
GetJSObjectSlots()
{
JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
JSContext *cx = JS_NewContext(rt, 0);
if (!cx) {
NS_ERROR("couldn't create context for memory tracing");
return (PRInt64) -1;
struct IterateData
{
IterateData(JSRuntime *rt)
: compartmentStatsVector()
, currCompartmentStats(NULL)
{
compartmentStatsVector.reserve(rt->compartments.length());
}
js::Vector<CompartmentStats, 0, js::SystemAllocPolicy> compartmentStatsVector;
CompartmentStats *currCompartmentStats;
};
static PRInt64
GetCompartmentScriptsSize(JSCompartment *c)
{
PRInt64 n = 0;
for (JSScript *script = (JSScript *)c->scripts.next;
&script->links != &c->scripts;
script = (JSScript *)script->links.next)
{
n += script->totalSize();
}
return n;
}
PRInt64Data data;
js::IterateCells(cx, NULL, js::TraceKindMask(JSTRACE_OBJECT), &data,
*GetJSObjectSlotsCallback);
#ifdef JS_METHODJIT
JS_DestroyContextNoGC(cx);
return data.n;
}
// This function is miscompiled with MSVC 2005 when PGO is on. See bug 664647.
#ifdef _MSC_VER
#pragma optimize("", off)
#endif
void
GetJSStringCharsCallback(JSContext *cx, void *v, size_t traceKind, void *thing)
{
JS_ASSERT(traceKind == JSTRACE_STRING);
JSString *str = (JSString *)thing;
PRInt64Data *data = (PRInt64Data *) v;
data->n += str->charsHeapSize();
}
#ifdef _MSC_VER
#pragma optimize("", on)
#endif
static PRInt64
GetJSStringChars()
{
JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
JSContext *cx = JS_NewContext(rt, 0);
if (!cx) {
NS_ERROR("couldn't create context for memory tracing");
return (PRInt64) -1;
static PRInt64
GetCompartmentMjitCodeSize(JSCompartment *c)
{
return c->getMjitCodeSize();
}
PRInt64Data data;
js::IterateCells(cx, NULL, js::TraceKindMask(JSTRACE_STRING), &data,
*GetJSStringCharsCallback);
static PRInt64
GetCompartmentMjitDataSize(JSCompartment *c)
{
PRInt64 n = 0;
for (JSScript *script = (JSScript *)c->scripts.next;
&script->links != &c->scripts;
script = (JSScript *)script->links.next)
{
n += script->jitDataSize();
}
return n;
}
JS_DestroyContextNoGC(cx);
#endif // JS_METHODJIT
return data.n;
}
#ifdef JS_TRACER
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSScripts,
"explicit/js/scripts",
KIND_HEAP,
UNITS_BYTES,
GetJSScripts,
"Memory allocated for 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. Each JSScript includes byte-code and "
"various other things.")
static PRInt64
GetCompartmentTjitCodeSize(JSCompartment *c)
{
if (c->hasTraceMonitor()) {
size_t total, frag_size, free_size;
c->traceMonitor()->getCodeAllocStats(total, frag_size, free_size);
return total;
}
return 0;
}
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSObjectSlots,
"explicit/js/object-slots",
KIND_HEAP,
UNITS_BYTES,
GetJSObjectSlots,
static PRInt64
GetCompartmentTjitDataAllocatorsMainSize(JSCompartment *c)
{
return c->hasTraceMonitor()
? c->traceMonitor()->getVMAllocatorsMainSize()
: 0;
}
static PRInt64
GetCompartmentTjitDataAllocatorsReserveSize(JSCompartment *c)
{
return c->hasTraceMonitor()
? c->traceMonitor()->getVMAllocatorsReserveSize()
: 0;
}
#endif // JS_TRACER
static void
CompartmentCallback(JSContext *cx, void *vdata, JSCompartment *compartment)
{
// Append a new CompartmentStats to the vector.
IterateData *data = static_cast<IterateData *>(vdata);
CompartmentStats compartmentStats(cx, compartment);
data->compartmentStatsVector.infallibleAppend(compartmentStats);
CompartmentStats *curr = data->compartmentStatsVector.end() - 1;
data->currCompartmentStats = curr;
// Get the compartment-level numbers.
curr->scripts = GetCompartmentScriptsSize(compartment);
#ifdef JS_METHODJIT
curr->mjitCode = GetCompartmentMjitCodeSize(compartment);
curr->mjitData = GetCompartmentMjitDataSize(compartment);
#endif
#ifdef JS_TRACER
curr->tjitCode = GetCompartmentTjitCodeSize(compartment);
curr->tjitDataAllocatorsMain = GetCompartmentTjitDataAllocatorsMainSize(compartment);
curr->tjitDataAllocatorsReserve = GetCompartmentTjitDataAllocatorsReserveSize(compartment);
#endif
}
static void
ArenaCallback(JSContext *cx, void *vdata, js::gc::Arena *arena,
size_t traceKind, size_t thingSize)
{
IterateData *data = static_cast<IterateData *>(vdata);
data->currCompartmentStats->gcHeapArenaHeaders +=
sizeof(js::gc::ArenaHeader);
data->currCompartmentStats->gcHeapArenaPadding +=
arena->thingsStartOffset(thingSize) - sizeof(js::gc::ArenaHeader);
// We don't call the callback on unused things. So we compute the
// unused space like this: arenaUnused = maxArenaUnused - arenaUsed.
// We do this by setting arenaUnused to maxArenaUnused here, and then
// subtracting thingSize for every used cell, in CellCallback().
data->currCompartmentStats->gcHeapArenaUnused += arena->thingsSpan(thingSize);
}
static void
CellCallback(JSContext *cx, void *vdata, void *thing, size_t traceKind,
size_t thingSize)
{
IterateData *data = static_cast<IterateData *>(vdata);
CompartmentStats *curr = data->currCompartmentStats;
if (traceKind == JSTRACE_OBJECT) {
JSObject *obj = static_cast<JSObject *>(thing);
curr->gcHeapObjects += thingSize;
if (obj->hasSlotsArray()) {
curr->objectSlots += obj->numSlots() * sizeof(js::Value);
}
} else if (traceKind == JSTRACE_STRING) {
JSString *str = static_cast<JSString *>(thing);
curr->gcHeapStrings += thingSize;
curr->stringChars += str->charsHeapSize();
} else if (traceKind == JSTRACE_SHAPE) {
curr->gcHeapShapes += thingSize;
} else {
JS_ASSERT(traceKind == JSTRACE_XML);
curr->gcHeapXml += thingSize;
}
// Yes, this is a subtraction: see ArenaCallback() for details.
curr->gcHeapArenaUnused -= thingSize;
}
public:
NS_DECL_ISUPPORTS
XPConnectJSCompartmentsMultiReporter()
{
}
nsCString mkPath(const nsACString &compartmentName,
const char* reporterName)
{
nsCString path(NS_LITERAL_CSTRING("explicit/js/compartment("));
path += compartmentName;
path += NS_LITERAL_CSTRING(")/");
path += nsDependentCString(reporterName);
return path;
}
NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback *callback,
nsISupports *closure)
{
JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
IterateData data(rt);
// In the first step we get all the stats and stash them in a local
// data structure. In the second step we pass all the stashed stats to
// the callback. Separating these steps is important because the
// callback may be a JS function, and executing JS while getting these
// stats seems like a bad idea.
{
JSContext *cx = JS_NewContext(rt, 0);
if (!cx) {
NS_ERROR("couldn't create context for memory tracing");
return NS_ERROR_FAILURE;
}
JS_BeginRequest(cx);
js::IterateCompartmentsArenasCells(cx, &data, CompartmentCallback, ArenaCallback,
CellCallback);
JS_EndRequest(cx);
JS_DestroyContextNoGC(cx);
}
NS_NAMED_LITERAL_CSTRING(p, "");
PRInt64 gcHeapChunkTotal = gXPCJSChunkAllocator.GetGCChunkBytesInUse();
// This is initialized to gcHeapChunkUnused, and then we subtract used
// space from it each time around the loop.
PRInt64 gcHeapChunkUnused = gcHeapChunkTotal;
#define DO(path, kind, amount, desc) \
callback->Callback(p, path, kind, nsIMemoryReporter::UNITS_BYTES, \
amount, NS_LITERAL_CSTRING(desc), closure);
// This is the second step (see above).
for (CompartmentStats *stats = data.compartmentStatsVector.begin();
stats != data.compartmentStatsVector.end();
++stats)
{
nsCString &name = stats->name;
gcHeapChunkUnused -=
stats->gcHeapArenaHeaders + stats->gcHeapArenaPadding +
stats->gcHeapArenaUnused +
stats->gcHeapObjects + stats->gcHeapStrings +
stats->gcHeapShapes + stats->gcHeapXml;
DO(mkPath(name, "gc-heap/arena-headers"),
JS_GC_HEAP_KIND, stats->gcHeapArenaHeaders,
"Memory on the garbage-collected JavaScript heap, within arenas, that is "
"used to hold internal book-keeping information.");
DO(mkPath(name, "gc-heap/arena-padding"),
JS_GC_HEAP_KIND, stats->gcHeapArenaPadding,
"Memory on the garbage-collected JavaScript heap, within arenas, that is "
"unused and present only so that other data is aligned.");
DO(mkPath(name, "gc-heap/arena-unused"),
JS_GC_HEAP_KIND, stats->gcHeapArenaUnused,
"Memory on the garbage-collected JavaScript heap, within arenas, that "
"could be holding useful data but currently isn't.");
DO(mkPath(name, "gc-heap/objects"),
JS_GC_HEAP_KIND, stats->gcHeapObjects,
"Memory on the garbage-collected JavaScript heap that holds objects.");
DO(mkPath(name, "gc-heap/strings"),
JS_GC_HEAP_KIND, stats->gcHeapStrings,
"Memory on the garbage-collected JavaScript heap that holds string "
"headers.");
DO(mkPath(name, "gc-heap/shapes"),
JS_GC_HEAP_KIND, stats->gcHeapShapes,
"Memory on the garbage-collected JavaScript heap that holds shapes. "
"A shape is an internal data structure that makes property accesses "
"fast.");
DO(mkPath(name, "gc-heap/xml"),
JS_GC_HEAP_KIND, stats->gcHeapXml,
"Memory on the garbage-collected JavaScript heap that holds E4X XML "
"objects.");
DO(mkPath(name, "object-slots"),
nsIMemoryReporter::KIND_HEAP, stats->objectSlots,
"Memory allocated for 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 JavaScript heap; those slots "
"are not counted here.")
"are not counted here, but in 'gc-heap/objects'.");
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSStringChars,
"explicit/js/string-chars",
KIND_HEAP,
UNITS_BYTES,
GetJSStringChars,
DO(mkPath(name, "string-chars"),
nsIMemoryReporter::KIND_HEAP, stats->stringChars,
"Memory allocated to hold string characters. Not all of this allocated "
"memory is necessarily used to hold characters. Each string also "
"includes a header which is stored on the JavaScript heap; that header "
"is not counted here.")
"is not counted here, but in 'gc-heap/strings'.");
DO(mkPath(name, "scripts"),
nsIMemoryReporter::KIND_HEAP, stats->scripts,
"Memory allocated for 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. Each JSScript includes byte-code and "
"various other things.");
#ifdef JS_METHODJIT
DO(mkPath(name, "mjit-code"),
nsIMemoryReporter::KIND_MAPPED, stats->mjitCode,
"Memory used by the method JIT to hold generated code.");
static PRInt64
GetCompartmentMjitCodeSize(JSCompartment *c)
{
return c->getMjitCodeSize();
}
static PRInt64
GetJSMjitCode()
{
return GetPerCompartmentSize(GetCompartmentMjitCodeSize);
}
static PRInt64
GetJSMJitData()
{
JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
return rt->mjitDataSize;
}
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSMjitCode,
"explicit/js/mjit-code",
KIND_MAPPED,
UNITS_BYTES,
GetJSMjitCode,
"Memory used by the method JIT to hold generated code.")
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSMjitData,
"explicit/js/mjit-data",
KIND_HEAP,
UNITS_BYTES,
GetJSMJitData,
DO(mkPath(name, "mjit-data"),
nsIMemoryReporter::KIND_HEAP, stats->mjitData,
"Memory used by the method JIT for the following data: "
"JITScripts, native maps, and inline cache structs.")
#endif // JS_METHODJIT
"JITScripts, native maps, and inline cache structs.");
#endif
#ifdef JS_TRACER
DO(mkPath(name, "tjit-code"),
nsIMemoryReporter::KIND_MAPPED, stats->tjitCode,
"Memory used by the trace JIT to hold generated code.");
static PRInt64
GetCompartmentTjitCode(JSCompartment *c)
{
if (c->hasTraceMonitor()) {
size_t total, frag_size, free_size;
c->traceMonitor()->getCodeAllocStats(total, frag_size, free_size);
return total;
}
return 0;
}
DO(mkPath(name, "tjit-data/allocators-main"),
nsIMemoryReporter::KIND_HEAP, stats->tjitDataAllocatorsMain,
"Memory used by the trace JIT's VMAllocators.");
static PRInt64
GetCompartmentTjitDataAllocatorsMain(JSCompartment *c)
{
return c->hasTraceMonitor()
? c->traceMonitor()->getVMAllocatorsMainSize()
: 0;
}
static PRInt64
GetCompartmentTjitDataAllocatorsReserve(JSCompartment *c)
{
return c->hasTraceMonitor()
? c->traceMonitor()->getVMAllocatorsReserveSize()
: 0;
}
static PRInt64
GetJSTjitCode()
{
return GetPerCompartmentSize(GetCompartmentTjitCode);
}
static PRInt64
GetJSTjitDataAllocatorsMain()
{
return GetPerCompartmentSize(GetCompartmentTjitDataAllocatorsMain);
}
static PRInt64
GetJSTjitDataAllocatorsReserve()
{
return GetPerCompartmentSize(GetCompartmentTjitDataAllocatorsReserve);
}
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSTjitCode,
"explicit/js/tjit-code",
KIND_MAPPED,
UNITS_BYTES,
GetJSTjitCode,
"Memory used by the trace JIT to hold generated code.")
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSTjitDataAllocatorsMain,
"explicit/js/tjit-data/allocators-main",
KIND_HEAP,
UNITS_BYTES,
GetJSTjitDataAllocatorsMain,
"Memory used by the trace JIT's VMAllocators.")
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSTjitDataAllocatorsReserve,
"explicit/js/tjit-data/allocators-reserve",
KIND_HEAP,
UNITS_BYTES,
GetJSTjitDataAllocatorsReserve,
DO(mkPath(name, "tjit-data/allocators-reserve"),
nsIMemoryReporter::KIND_HEAP, stats->tjitDataAllocatorsReserve,
"Memory used by the trace JIT and held in reserve for VMAllocators "
"in case of OOM.")
"in case of OOM.");
#endif
}
#endif // JS_TRACER
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;
DO(NS_LITERAL_CSTRING("explicit/js/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.");
DO(NS_LITERAL_CSTRING("explicit/js/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.");
return NS_OK;
}
};
NS_IMPL_THREADSAFE_ISUPPORTS1(
XPConnectJSCompartmentsMultiReporter
, nsIMemoryMultiReporter
)
XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
: mXPConnect(aXPConnect),
@ -1603,18 +1733,7 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSGCHeap));
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSStack));
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSScripts));
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSObjectSlots));
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSStringChars));
#ifdef JS_METHODJIT
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSMjitCode));
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSMjitData));
#endif
#ifdef JS_TRACER
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSTjitCode));
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSTjitDataAllocatorsMain));
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSTjitDataAllocatorsReserve));
#endif
NS_RegisterMemoryMultiReporter(new XPConnectJSCompartmentsMultiReporter);
}
if(!JS_DHashTableInit(&mJSHolders, JS_DHashGetStubOps(), nsnull,

View File

@ -243,7 +243,6 @@ function update()
"reporter to see a detailed description of what it measures.</span>"
"</div>";
var div = document.createElement("div");
div.innerHTML = text;
content.appendChild(div);
@ -252,7 +251,7 @@ function update()
// Compare two memory reporter nodes. We want to group together measurements
// with the same units, so sort first by the nodes' _units field, then sort by
// the amount if the units are equal.
function cmp_amount(a, b)
function cmpAmount(a, b)
{
if (a._units != b._units)
return a._units - b._units; // use the enum order from nsIMemoryReporter
@ -451,7 +450,7 @@ function genProcessText(aProcess, aReporters)
*/
function filterTree(aT)
{
aT._kids.sort(cmp_amount);
aT._kids.sort(cmpAmount);
for (var i = 0; i < aT._kids.length; i++) {
if (shouldOmit(aT._kids[i]._amount)) {
@ -460,21 +459,26 @@ function genProcessText(aProcess, aReporters)
// replace them with a single aggregate node.
var i0 = i;
var aggBytes = 0;
var aggNames = [];
for ( ; i < aT._kids.length; i++) {
aggBytes += aT._kids[i]._amount;
aggNames.push(aT._kids[i]._name);
}
aT._kids.splice(i0);
var n = i - i0;
var rSub = {
_name: "(" + n + " omitted)",
_kind: KIND_OTHER,
_description: "Omitted sub-trees: " + aggNames.join(", ") + ".",
_amount: aggBytes,
_description: n + " sub-trees that were below the " +
omitThresholdPerc + "% significance threshold. " +
"Click 'More verbose' at the bottom of this page " +
"to see them.",
_kids: []
};
// Add the "omitted" sub-tree at the end and then resort, because the
// sum of the omitted sub-trees may be larger than some of the
// shown sub-trees.
aT._kids[i0] = rSub;
aT._kids.sort(cmpAmount);
break;
}
filterTree(aT._kids[i]);
@ -648,7 +652,41 @@ function kindToString(aKind)
function escapeQuotes(aStr)
{
return aStr.replace(/'/g, '&#39;');
return aStr.replace(/\&/g, '&amp;').replace(/'/g, '&#39;');
}
// For user-controlled strings.
function escapeAll(aStr)
{
return aStr.replace(/\&/g, '&amp;').replace(/'/g, '&#39;').
replace(/\</g, '&lt;').replace(/>/g, '&gt;').
replace(/\"/g, '&quot;');
}
// Compartment reporter names are URLs and so can include forward slashes. But
// forward slash is the memory reporter path separator. So the memory
// reporters change them to backslashes. Undo that here.
function flipBackslashes(aStr)
{
return aStr.replace(/\\/g, '/');
}
// Truncate the URL in a compartment name if not in verbose mode.
function truncateCompartmentName(aStr)
{
return (gVerbose)
? aStr
: aStr.replace(/compartment\((.{40}).*\)/, 'compartment($1...)');
}
function prepName(aStr)
{
return escapeAll(flipBackslashes(truncateCompartmentName(aStr)));
}
function prepDesc(aStr)
{
return escapeQuotes(flipBackslashes(aStr));
}
function genMrNameText(aKind, aDesc, aName, aHasProblem)
@ -658,8 +696,8 @@ function genMrNameText(aKind, aDesc, aName, aHasProblem)
"The reported value is the sum of all entries below '" + aName + "', " +
"which is probably less than the true value.";
var text = "-- <span class='mrName hasDesc' title='" +
kindToString(aKind) + escapeQuotes(aDesc) +
"'>" + aName + "</span>";
kindToString(aKind) + prepDesc(aDesc) +
"'>" + prepName(aName) + "</span>";
text += aHasProblem
? " <span class='mrStar' title=\"" + problemDesc + "\">[*]</span>\n"
: "\n";
@ -802,7 +840,7 @@ function genOtherText(aReporters)
_path: r._path,
_kind: r._kind,
_units: r._units,
_amount: hasProblem ? 0 : r._amount,
_amount: hasProblem ? 0 : r._amount,
_description: r._description,
_hasProblem: hasProblem
};
@ -813,7 +851,7 @@ function genOtherText(aReporters)
}
}
}
rArray.sort(cmp_amount);
rArray.sort(cmpAmount);
// Generate text for the not-yet-printed values.
var text = "";

View File

@ -84,8 +84,14 @@
f("2nd", "heap-unused", OTHER, 100 * MB),
f("2nd", "explicit/a/b/c", HEAP, 498 * MB),
f("2nd", "explicit/a/b/c", HEAP, 1 * MB), // dup: merge
f("2nd", "explicit/b", HEAP, 400 * MB),
f("2nd", "other1", OTHER, 777 * MB),
f("2nd", "explicit/flip\\the\\backslashes",
HEAP, 200 * MB),
f("2nd", "explicit/compartment(this-will-be-truncated-in-non-verbose-mode-abcdefghijklmnopqrstuvwxyz)",
HEAP, 200 * MB),
// The escaping of compartment names must prevent this script from running.
f("2nd", "danger<script>window.alert(1)</script>",
OTHER, 666 * MB),
f("2nd", "other1", OTHER, 111 * MB),
// kUnknown should be handled gracefully for "heap-used", non-leaf
// reporters, leaf-reporters, and "other" reporters.
@ -146,12 +152,14 @@ Explicit Allocations\n\
├────499.00 MB (49.90%) -- a\n\
│ └──499.00 MB (49.90%) -- b\n\
│ └──499.00 MB (49.90%) -- c\n\
├────400.00 MB (40.00%) -- b\n\
├────200.00 MB (20.00%) -- flip/the/backslashes\n\
├────200.00 MB (20.00%) -- compartment(this-will-be-truncated-in-non-verbose-mo...)\n\
└────101.00 MB (10.10%) -- heap-unclassified\n\
\n\
Other Measurements\n\
1,000.00 MB -- heap-used\n\
777.00 MB -- other1\n\
666.00 MB -- danger<script>window.alert(1)</script>\n\
111.00 MB -- other1\n\
100.00 MB -- heap-unused\n\
\n\
3rd Process\n\
@ -209,12 +217,14 @@ Explicit Allocations\n\
├────523,239,424 B (49.90%) -- a\n\
│ └──523,239,424 B (49.90%) -- b\n\
│ └──523,239,424 B (49.90%) -- c\n\
├────419,430,400 B (40.00%) -- b\n\
├────209,715,200 B (20.00%) -- flip/the/backslashes\n\
├────209,715,200 B (20.00%) -- compartment(this-will-be-truncated-in-non-verbose-mode-abcdefghijklmnopqrstuvwxyz)\n\
└────105,906,176 B (10.10%) -- heap-unclassified\n\
\n\
Other Measurements\n\
1,048,576,000 B -- heap-used\n\
814,743,552 B -- other1\n\
698,351,616 B -- danger<script>window.alert(1)</script>\n\
116,391,936 B -- other1\n\
104,857,600 B -- heap-unused\n\
\n\
3rd Process\n\

View File

@ -54,7 +54,7 @@ const TELEMETRY_INTERVAL = 60;
const TELEMETRY_DELAY = 60000;
// about:memory values to turn into histograms
const MEM_HISTOGRAMS = {
"explicit/js/gc-heap": "MEMORY_JS_GC_HEAP",
"js-gc-heap": "MEMORY_JS_GC_HEAP",
"resident": "MEMORY_RESIDENT",
"explicit/layout/all": "MEMORY_LAYOUT_ALL",
"hard-page-faults": "HARD_PAGE_FAULTS"