mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-02 01:48:05 +00:00
Bug 702300 (part 7) - Add the "compartments" multi-reporter. r=billm.
--HG-- extra : rebase_source : e45e59bd8657197dbd61c257f46ad6396cdda3c3
This commit is contained in:
parent
f9b9961d25
commit
d655a2dc1d
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user