Bug 702300 (part 7) - Add the "compartments" multi-reporter. r=billm.

--HG--
extra : rebase_source : e45e59bd8657197dbd61c257f46ad6396cdda3c3
This commit is contained in:
Nicholas Nethercote 2012-02-16 22:10:39 -08:00
parent f9b9961d25
commit d655a2dc1d
6 changed files with 176 additions and 92 deletions

View File

@ -55,10 +55,10 @@ namespace JS {
using namespace js;
static void
CompartmentStatsCallback(JSContext *cx, void *vdata, JSCompartment *compartment)
StatsCompartmentCallback(JSContext *cx, void *data, JSCompartment *compartment)
{
// Append a new CompartmentStats to the vector.
RuntimeStats *rtStats = static_cast<RuntimeStats *>(vdata);
RuntimeStats *rtStats = static_cast<RuntimeStats *>(data);
// CollectRuntimeStats reserves enough space.
MOZ_ALWAYS_TRUE(rtStats->compartmentStatsVector.growBy(1));
@ -75,30 +75,21 @@ CompartmentStatsCallback(JSContext *cx, void *vdata, JSCompartment *compartment)
}
static void
ExplicitNonHeapCompartmentCallback(JSContext *cx, void *data, JSCompartment *compartment)
{
#ifdef JS_METHODJIT
size_t *n = static_cast<size_t *>(data);
*n += compartment->sizeOfMjitCode();
#endif
}
static void
ChunkCallback(JSContext *cx, void *vdata, gc::Chunk *chunk)
StatsChunkCallback(JSContext *cx, void *data, gc::Chunk *chunk)
{
// Nb: This function is only called for dirty chunks, which is why we
// increment gcHeapChunkDirtyDecommitted.
RuntimeStats *rtStats = static_cast<RuntimeStats *>(vdata);
RuntimeStats *rtStats = static_cast<RuntimeStats *>(data);
for (size_t i = 0; i < gc::ArenasPerChunk; i++)
if (chunk->decommittedArenas.get(i))
rtStats->gcHeapChunkDirtyDecommitted += gc::ArenaSize;
}
static void
ArenaCallback(JSContext *cx, void *vdata, gc::Arena *arena,
JSGCTraceKind traceKind, size_t thingSize)
StatsArenaCallback(JSContext *cx, void *data, gc::Arena *arena,
JSGCTraceKind traceKind, size_t thingSize)
{
RuntimeStats *rtStats = static_cast<RuntimeStats *>(vdata);
RuntimeStats *rtStats = static_cast<RuntimeStats *>(data);
rtStats->currCompartmentStats->gcHeapArenaHeaders += sizeof(gc::ArenaHeader);
size_t allocationSpace = arena->thingsSpan(thingSize);
@ -107,15 +98,15 @@ ArenaCallback(JSContext *cx, void *vdata, gc::Arena *arena,
// 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().
// subtracting thingSize for every used cell, in StatsCellCallback().
rtStats->currCompartmentStats->gcHeapArenaUnused += allocationSpace;
}
static void
CellCallback(JSContext *cx, void *vdata, void *thing, JSGCTraceKind traceKind,
size_t thingSize)
StatsCellCallback(JSContext *cx, void *data, void *thing, JSGCTraceKind traceKind,
size_t thingSize)
{
RuntimeStats *rtStats = static_cast<RuntimeStats *>(vdata);
RuntimeStats *rtStats = static_cast<RuntimeStats *>(data);
CompartmentStats *cStats = rtStats->currCompartmentStats;
switch (traceKind) {
case JSTRACE_OBJECT:
@ -185,7 +176,7 @@ CellCallback(JSContext *cx, void *vdata, void *thing, JSGCTraceKind traceKind,
break;
}
}
// Yes, this is a subtraction: see ArenaCallback() for details.
// Yes, this is a subtraction: see StatsArenaCallback() for details.
cStats->gcHeapArenaUnused -= thingSize;
}
@ -210,9 +201,9 @@ CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats)
rtStats->gcHeapChunkTotal =
size_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) * gc::ChunkSize;
IterateCompartmentsArenasCells(cx, rtStats, CompartmentStatsCallback,
ArenaCallback, CellCallback);
IterateChunks(cx, rtStats, ChunkCallback);
IterateCompartmentsArenasCells(cx, rtStats, StatsCompartmentCallback,
StatsArenaCallback, StatsCellCallback);
IterateChunks(cx, rtStats, StatsChunkCallback);
rtStats->runtimeObject = rtStats->mallocSizeOf(rt);
@ -308,6 +299,15 @@ CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats)
return true;
}
static void
ExplicitNonHeapCompartmentCallback(JSContext *cx, void *data, JSCompartment *compartment)
{
#ifdef JS_METHODJIT
size_t *n = static_cast<size_t *>(data);
*n += compartment->sizeOfMjitCode();
#endif
}
JS_PUBLIC_API(bool)
GetExplicitNonHeapForRuntime(JSRuntime *rt, int64_t *amount,
JSMallocSizeOfFun mallocSizeOf)
@ -325,7 +325,7 @@ GetExplicitNonHeapForRuntime(JSRuntime *rt, int64_t *amount,
// explicit/<compartment>/mjit-code
size_t n = 0;
IterateCompartments(cx, &n, ExplicitNonHeapCompartmentCallback);
JS_IterateCompartments(cx, &n, ExplicitNonHeapCompartmentCallback);
*amount += n;
// explicit/runtime/regexp-code
@ -346,6 +346,28 @@ GetExplicitNonHeapForRuntime(JSRuntime *rt, int64_t *amount,
return true;
}
JS_PUBLIC_API(size_t)
SystemCompartmentCount(const JSRuntime *rt)
{
size_t n = 0;
for (size_t i = 0; i < rt->compartments.length(); i++) {
if (rt->compartments[i]->isSystemCompartment)
++n;
}
return n;
}
JS_PUBLIC_API(size_t)
UserCompartmentCount(const JSRuntime *rt)
{
size_t n = 0;
for (size_t i = 0; i < rt->compartments.length(); i++) {
if (!rt->compartments[i]->isSystemCompartment)
++n;
}
return n;
}
} // namespace JS
#endif // JS_THREADSAFE

View File

@ -996,30 +996,6 @@ JS_SetRuntimePrivate(JSRuntime *rt, void *data)
rt->data = data;
}
JS_PUBLIC_API(size_t)
JS::SystemCompartmentCount(const JSRuntime *rt)
{
size_t n = 0;
for (size_t i = 0; i < rt->compartments.length(); i++) {
if (rt->compartments[i]->isSystemCompartment) {
++n;
}
}
return n;
}
JS_PUBLIC_API(size_t)
JS::UserCompartmentCount(const JSRuntime *rt)
{
size_t n = 0;
for (size_t i = 0; i < rt->compartments.length(); i++) {
if (!rt->compartments[i]->isSystemCompartment) {
++n;
}
}
return n;
}
#ifdef JS_THREADSAFE
static void
StartRequest(JSContext *cx)

View File

@ -2690,6 +2690,18 @@ class JS_PUBLIC_API(JSAutoEnterCompartment)
JS_BEGIN_EXTERN_C
#endif
typedef void (*JSIterateCompartmentCallback)(JSContext *cx, void *data,
JSCompartment *compartment);
/*
* This function calls |compartmentCallback| on every compartment. Beware that
* there is no guarantee that the compartment will survive after the callback
* returns.
*/
extern JS_PUBLIC_API(void)
JS_IterateCompartments(JSContext *cx, void *data,
JSIterateCompartmentCallback compartmentCallback);
extern JS_PUBLIC_API(JSObject *)
JS_GetGlobalObject(JSContext *cx);

View File

@ -3789,31 +3789,9 @@ struct IterateCellCallbackOp
void operator()(Cell *cell) { (*callback)(cx, data, cell, traceKind, thingSize); }
};
void
IterateCompartments(JSContext *cx, void *data,
IterateCompartmentCallback compartmentCallback)
{
CHECK_REQUEST(cx);
JSRuntime *rt = cx->runtime;
JS_ASSERT(!rt->gcRunning);
AutoLockGC lock(rt);
AutoHeapSession session(cx);
#ifdef JS_THREADSAFE
rt->gcHelperThread.waitBackgroundSweepEnd();
#endif
AutoUnlockGC unlock(rt);
AutoCopyFreeListToArenas copy(rt);
for (CompartmentsIter c(rt); !c.done(); c.next()) {
(*compartmentCallback)(cx, data, c);
}
}
void
IterateCompartmentsArenasCells(JSContext *cx, void *data,
IterateCompartmentCallback compartmentCallback,
JSIterateCompartmentCallback compartmentCallback,
IterateArenaCallback arenaCallback,
IterateCellCallback cellCallback)
{
@ -4558,6 +4536,27 @@ PurgePCCounts(JSContext *cx)
} /* namespace js */
JS_PUBLIC_API(void)
JS_IterateCompartments(JSContext *cx, void *data,
JSIterateCompartmentCallback compartmentCallback)
{
CHECK_REQUEST(cx);
JSRuntime *rt = cx->runtime;
JS_ASSERT(!rt->gcRunning);
AutoLockGC lock(rt);
AutoHeapSession session(cx);
#ifdef JS_THREADSAFE
rt->gcHelperThread.waitBackgroundSweepEnd();
#endif
AutoUnlockGC unlock(rt);
AutoCopyFreeListToArenas copy(rt);
for (CompartmentsIter c(rt); !c.done(); c.next())
(*compartmentCallback)(cx, data, c);
}
#if JS_HAS_XML_SUPPORT
extern size_t sE4XObjectsCreated;

View File

@ -1896,19 +1896,12 @@ struct FullGCMarker : public GCMarker {
void
MarkStackRangeConservatively(JSTracer *trc, Value *begin, Value *end);
typedef void (*IterateCompartmentCallback)(JSContext *cx, void *data, JSCompartment *compartment);
typedef void (*IterateChunkCallback)(JSContext *cx, void *data, gc::Chunk *chunk);
typedef void (*IterateArenaCallback)(JSContext *cx, void *data, gc::Arena *arena,
JSGCTraceKind traceKind, size_t thingSize);
typedef void (*IterateCellCallback)(JSContext *cx, void *data, void *thing,
JSGCTraceKind traceKind, size_t thingSize);
/*
* This function calls |compartmentCallback| on every compartment.
*/
extern JS_FRIEND_API(void)
IterateCompartments(JSContext *cx, void *data,
IterateCompartmentCallback compartmentCallback);
/*
* This function calls |compartmentCallback| on every compartment,
* |arenaCallback| on every in-use arena, and |cellCallback| on every in-use
@ -1916,7 +1909,7 @@ IterateCompartments(JSContext *cx, void *data,
*/
extern JS_FRIEND_API(void)
IterateCompartmentsArenasCells(JSContext *cx, void *data,
IterateCompartmentCallback compartmentCallback,
JSIterateCompartmentCallback compartmentCallback,
IterateArenaCallback arenaCallback,
IterateCellCallback cellCallback);

View File

@ -1211,7 +1211,7 @@ XPCJSRuntime::~XPCJSRuntime()
namespace xpc {
void*
GetCompartmentName(JSContext *cx, JSCompartment *c)
GetCompartmentNameHelper(JSContext *cx, JSCompartment *c, bool getAddress)
{
nsCString* name = new nsCString();
if (js::IsAtomsCompartmentFor(cx, c)) {
@ -1220,10 +1220,10 @@ GetCompartmentName(JSContext *cx, JSCompartment *c)
if (principals->codebase) {
name->Assign(principals->codebase);
// If it's the system compartment, append the address.
// This means that multiple system compartments (and there
// can be many) can be distinguished.
if (js::IsSystemCompartment(c)) {
// If it's the system compartment and |getAddress| is true, append
// the address. This means that multiple system compartments (and
// there can be many) can be distinguished.
if (getAddress && js::IsSystemCompartment(c)) {
xpc::CompartmentPrivate *compartmentPrivate =
static_cast<xpc::CompartmentPrivate*>(JS_GetCompartmentPrivate(cx, c));
if (compartmentPrivate &&
@ -1251,6 +1251,18 @@ GetCompartmentName(JSContext *cx, JSCompartment *c)
return name;
}
void*
GetCompartmentName(JSContext *cx, JSCompartment *c)
{
return GetCompartmentNameHelper(cx, c, /* get address = */false);
}
void*
GetCompartmentNameAndAddress(JSContext *cx, JSCompartment *c)
{
return GetCompartmentNameHelper(cx, c, /* get address = */true);
}
void
DestroyCompartmentName(void *string)
{
@ -1372,7 +1384,7 @@ GetJSUserCompartmentCount()
// Nb: js-system-compartment-count + js-user-compartment-count could be
// different to the number of compartments reported by
// XPConnectJSCompartmentsMultiReporter if a garbage collection occurred
// JSMemoryMultiReporter if a garbage collection occurred
// between them being consulted. We could move these reporters into
// XPConnectJSCompartmentCount to avoid that problem, but then we couldn't
// easily report them via telemetry, so we live with the small risk of
@ -1705,7 +1717,7 @@ ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats, const nsACStri
// gcTotal is the sum of everything we've reported for the GC heap. It
// should equal rtStats.gcHeapChunkTotal.
JS_ASSERT(gcTotal == rtStats.gcHeapChunkTotal);
JS_ASSERT(size_t(gcTotal) == rtStats.gcHeapChunkTotal);
}
} // namespace memory
@ -1714,7 +1726,77 @@ ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats, const nsACStri
NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsMallocSizeOf, "js")
class XPConnectJSCompartmentsMultiReporter : public nsIMemoryMultiReporter
class JSCompartmentsMultiReporter : public nsIMemoryMultiReporter
{
public:
NS_DECL_ISUPPORTS
NS_IMETHOD GetName(nsACString &name)
{
name.AssignLiteral("compartments");
return NS_OK;
}
typedef js::Vector<nsCString, 0, js::SystemAllocPolicy> Paths;
static void CompartmentCallback(JSContext *cx, void* data, JSCompartment *c)
{
Paths *paths = static_cast<Paths *>(data);
nsCString *name =
static_cast<nsCString *>(xpc::GetCompartmentName(cx, c));
nsCString path;
if (js::IsSystemCompartment(c))
path = NS_LITERAL_CSTRING("compartments/system/") + *name;
else
path = NS_LITERAL_CSTRING("compartments/user/") + *name;
if (!paths->append(path))
return; // silent failure, but it's very unlikely
xpc::DestroyCompartmentName(name);
}
NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback *callback,
nsISupports *closure)
{
// First we collect the compartment paths. Then we report them. Doing
// the two steps interleaved is a bad idea, because calling |callback|
// from within CompartmentCallback() leads to all manner of assertions.
// Collect.
XPCJSRuntime *xpcrt = nsXPConnect::GetRuntimeInstance();
JSContext *cx = JS_NewContext(xpcrt->GetJSRuntime(), 0);
if (!cx)
return NS_ERROR_OUT_OF_MEMORY;
Paths paths;
JS_IterateCompartments(cx, &paths, CompartmentCallback);
JS_DestroyContextNoGC(cx);
// Report.
for (size_t i = 0; i < paths.length(); i++)
// These ones don't need a description, hence the "".
ReportMemory(paths[i],
nsIMemoryReporter::KIND_OTHER,
nsIMemoryReporter::UNITS_COUNT,
1, "", callback, closure);
return NS_OK;
}
NS_IMETHOD
GetExplicitNonHeap(PRInt64 *n)
{
// This reporter does neither "explicit" nor NONHEAP measurements.
*n = 0;
return NS_OK;
}
};
NS_IMPL_THREADSAFE_ISUPPORTS1(JSCompartmentsMultiReporter
, nsIMemoryMultiReporter
)
class JSMemoryMultiReporter : public nsIMemoryMultiReporter
{
public:
NS_DECL_ISUPPORTS
@ -1735,7 +1817,7 @@ public:
// 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.
JS::RuntimeStats rtStats(JsMallocSizeOf, xpc::GetCompartmentName,
JS::RuntimeStats rtStats(JsMallocSizeOf, xpc::GetCompartmentNameAndAddress,
xpc::DestroyCompartmentName);
if (!JS::CollectRuntimeStats(xpcrt->GetJSRuntime(), &rtStats))
return NS_ERROR_FAILURE;
@ -1861,7 +1943,7 @@ public:
}
};
NS_IMPL_THREADSAFE_ISUPPORTS1(XPConnectJSCompartmentsMultiReporter
NS_IMPL_THREADSAFE_ISUPPORTS1(JSMemoryMultiReporter
, nsIMemoryMultiReporter
)
@ -2001,7 +2083,7 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSGCHeap));
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSSystemCompartmentCount));
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSUserCompartmentCount));
NS_RegisterMemoryMultiReporter(new XPConnectJSCompartmentsMultiReporter);
NS_RegisterMemoryMultiReporter(new JSMemoryMultiReporter);
}
if (!JS_DHashTableInit(&mJSHolders, JS_DHashGetStubOps(), nsnull,
@ -2087,7 +2169,7 @@ XPCJSRuntime::OnJSContextNew(JSContext *cx)
ok = mozilla::dom::binding::DefineStaticJSVals(cx);
if (!ok)
return false;
ok = DefineStaticDictionaryJSVals(cx);
}
if (!ok)