mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 22:25:30 +00:00
Bug 661474 - Add per-compartment memory reporters. r=wmccloskey, dolske.
This commit is contained in:
parent
bb94ffa458
commit
2c2e126833
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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, ''');
|
||||
return aStr.replace(/\&/g, '&').replace(/'/g, ''');
|
||||
}
|
||||
|
||||
// For user-controlled strings.
|
||||
function escapeAll(aStr)
|
||||
{
|
||||
return aStr.replace(/\&/g, '&').replace(/'/g, ''').
|
||||
replace(/\</g, '<').replace(/>/g, '>').
|
||||
replace(/\"/g, '"');
|
||||
}
|
||||
|
||||
// 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 = "";
|
||||
|
@ -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\
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user