зеркало из https://github.com/mozilla/gecko-dev.git
Bug 674721 - 'Add memory reporting for web worker data'. r=jst+njn.
--HG-- extra : transplant_source : 9%D0%8D%BF%CE%B1Z%CC%85%EF%F5e%28%0B%A9%D1%18%99%21%11
This commit is contained in:
Родитель
b760b58bf8
Коммит
893fdf3927
|
@ -44,11 +44,13 @@
|
|||
#include "nsIObserverService.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsIJSContextStack.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsISupportsPriority.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
#include "jsprf.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDOMJSUtils.h"
|
||||
|
@ -58,6 +60,7 @@
|
|||
#include "nsThreadUtils.h"
|
||||
#include "nsXPCOM.h"
|
||||
#include "nsXPCOMPrivate.h"
|
||||
#include "xpcpublic.h"
|
||||
|
||||
#include "Events.h"
|
||||
#include "EventTarget.h"
|
||||
|
@ -69,6 +72,7 @@ USING_WORKERS_NAMESPACE
|
|||
using mozilla::MutexAutoLock;
|
||||
using mozilla::MutexAutoUnlock;
|
||||
using mozilla::Preferences;
|
||||
using namespace mozilla::xpconnect::memory;
|
||||
|
||||
// The size of the worker runtime heaps in bytes.
|
||||
#define WORKER_RUNTIME_HEAPSIZE 32 * 1024 * 1024
|
||||
|
@ -285,6 +289,70 @@ CreateJSContextForWorker(WorkerPrivate* aWorkerPrivate)
|
|||
return workerCx;
|
||||
}
|
||||
|
||||
class WorkerMemoryReporter : public nsIMemoryMultiReporter
|
||||
{
|
||||
JSRuntime* mRuntime;
|
||||
nsCString mPathPrefix;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
WorkerMemoryReporter(WorkerPrivate* aWorkerPrivate, JSRuntime* aRuntime)
|
||||
: mRuntime(aRuntime)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
nsCString escapedDomain(aWorkerPrivate->Domain());
|
||||
escapedDomain.ReplaceChar('/', '\\');
|
||||
|
||||
NS_ConvertUTF16toUTF8 escapedURL(aWorkerPrivate->ScriptURL());
|
||||
escapedURL.ReplaceChar('/', '\\');
|
||||
|
||||
// 64bit address plus '0x' plus null terminator.
|
||||
char address[21];
|
||||
JSUint32 addressSize =
|
||||
JS_snprintf(address, sizeof(address), "0x%llx", aWorkerPrivate);
|
||||
if (addressSize == JSUint32(-1)) {
|
||||
NS_WARNING("JS_snprintf failed!");
|
||||
address[0] = '\0';
|
||||
addressSize = 0;
|
||||
}
|
||||
|
||||
mPathPrefix = NS_LITERAL_CSTRING("explicit/dom/workers(") +
|
||||
escapedDomain + NS_LITERAL_CSTRING(")/worker(") +
|
||||
escapedURL + NS_LITERAL_CSTRING(", ") +
|
||||
nsDependentCString(address, addressSize) +
|
||||
NS_LITERAL_CSTRING(")/");
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
CollectReports(nsIMemoryMultiReporterCallback* aCallback,
|
||||
nsISupports* aClosure)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
JS_TriggerAllOperationCallbacks(mRuntime);
|
||||
|
||||
IterateData data;
|
||||
if (!CollectCompartmentStatsForRuntime(mRuntime, &data)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
for (CompartmentStats *stats = data.compartmentStatsVector.begin();
|
||||
stats != data.compartmentStatsVector.end();
|
||||
++stats)
|
||||
{
|
||||
ReportCompartmentStats(*stats, mPathPrefix, aCallback, aClosure);
|
||||
}
|
||||
|
||||
ReportJSStackSizeForRuntime(mRuntime, mPathPrefix, aCallback, aClosure);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(WorkerMemoryReporter, nsIMemoryMultiReporter)
|
||||
|
||||
class WorkerThreadRunnable : public nsRunnable
|
||||
{
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
|
@ -311,12 +379,23 @@ public:
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
{
|
||||
JSAutoRequest ar(cx);
|
||||
workerPrivate->DoRunLoop(cx);
|
||||
JSRuntime* rt = JS_GetRuntime(cx);
|
||||
|
||||
nsRefPtr<WorkerMemoryReporter> reporter =
|
||||
new WorkerMemoryReporter(workerPrivate, rt);
|
||||
if (NS_FAILED(NS_RegisterMemoryMultiReporter(reporter))) {
|
||||
NS_WARNING("Failed to register memory reporter!");
|
||||
reporter = nsnull;
|
||||
}
|
||||
|
||||
JSRuntime* rt = JS_GetRuntime(cx);
|
||||
workerPrivate->DoRunLoop(cx);
|
||||
|
||||
if (reporter) {
|
||||
if (NS_FAILED(NS_UnregisterMemoryMultiReporter(reporter))) {
|
||||
NS_WARNING("Failed to unregister memory reporter!");
|
||||
}
|
||||
reporter = nsnull;
|
||||
}
|
||||
|
||||
// XXX Bug 666963 - CTypes can create another JSContext for use with
|
||||
// closures, and then it holds that context in a reserved slot on the CType
|
||||
|
|
|
@ -2042,6 +2042,8 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
|
|||
{
|
||||
MutexAutoUnlock unlock(mMutex);
|
||||
|
||||
JSAutoRequest ar(aCx);
|
||||
|
||||
#ifdef EXTRA_GC
|
||||
// Find GC bugs...
|
||||
JS_GC(aCx);
|
||||
|
@ -2054,6 +2056,8 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
|
|||
currentStatus = mStatus;
|
||||
}
|
||||
|
||||
JSAutoRequest ar(aCx);
|
||||
|
||||
#ifdef EXTRA_GC
|
||||
// Find GC bugs...
|
||||
JS_GC(aCx);
|
||||
|
@ -2093,6 +2097,8 @@ WorkerPrivate::OperationCallback(JSContext* aCx)
|
|||
{
|
||||
AssertIsOnWorkerThread();
|
||||
|
||||
JS_YieldRequest(aCx);
|
||||
|
||||
bool mayContinue = true;
|
||||
|
||||
for (;;) {
|
||||
|
@ -2126,6 +2132,8 @@ WorkerPrivate::OperationCallback(JSContext* aCx)
|
|||
if (!mControlQueue.IsEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
JSAutoSuspendRequest asr(aCx);
|
||||
mCondVar.Wait(PR_MillisecondsToInterval(RemainingRunTimeMS()));
|
||||
}
|
||||
}
|
||||
|
@ -2480,6 +2488,7 @@ WorkerPrivate::RunSyncLoop(JSContext* aCx, PRUint32 aSyncLoopKey)
|
|||
MutexAutoLock lock(mMutex);
|
||||
|
||||
while (!mControlQueue.Pop(event) && !syncQueue->mQueue.Pop(event)) {
|
||||
JSAutoSuspendRequest asr(aCx);
|
||||
mCondVar.Wait();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
/* Per JSRuntime object */
|
||||
|
||||
#include "xpcprivate.h"
|
||||
#include "xpcpublic.h"
|
||||
#include "WrapperFactory.h"
|
||||
#include "dom_quickstubs.h"
|
||||
|
||||
|
@ -57,6 +58,7 @@
|
|||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::xpconnect::memory;
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
|
@ -1227,6 +1229,205 @@ XPCJSRuntime::~XPCJSRuntime()
|
|||
XPCPerThreadData::ShutDown();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
|
||||
PRInt64
|
||||
GetCompartmentMjitCodeSize(JSCompartment *c)
|
||||
{
|
||||
return c->getMjitCodeSize();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endif // JS_METHODJIT
|
||||
|
||||
#ifdef JS_TRACER
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
PRInt64
|
||||
GetCompartmentTjitDataAllocatorsMainSize(JSCompartment *c)
|
||||
{
|
||||
return c->hasTraceMonitor()
|
||||
? c->traceMonitor()->getVMAllocatorsMainSize()
|
||||
: 0;
|
||||
}
|
||||
|
||||
PRInt64
|
||||
GetCompartmentTjitDataAllocatorsReserveSize(JSCompartment *c)
|
||||
{
|
||||
return c->hasTraceMonitor()
|
||||
? c->traceMonitor()->getVMAllocatorsReserveSize()
|
||||
: 0;
|
||||
}
|
||||
|
||||
#endif // JS_TRACER
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
curr->gcHeapObjects += thingSize;
|
||||
JSObject *obj = static_cast<JSObject *>(thing);
|
||||
if(obj->hasSlotsArray())
|
||||
curr->objectSlots += obj->numSlots() * sizeof(js::Value);
|
||||
}
|
||||
else if(traceKind == JSTRACE_STRING)
|
||||
{
|
||||
curr->gcHeapStrings += thingSize;
|
||||
JSString *str = static_cast<JSString *>(thing);
|
||||
curr->stringChars += str->charsHeapSize();
|
||||
}
|
||||
else if(traceKind == JSTRACE_SHAPE)
|
||||
{
|
||||
curr->gcHeapShapes += thingSize;
|
||||
js::Shape *shape = static_cast<js::Shape *>(thing);
|
||||
if(shape->hasTable())
|
||||
curr->propertyTables += shape->getTable()->sizeOf();
|
||||
}
|
||||
else
|
||||
{
|
||||
JS_ASSERT(traceKind == JSTRACE_XML);
|
||||
curr->gcHeapXml += thingSize;
|
||||
}
|
||||
// Yes, this is a subtraction: see ArenaCallback() for details.
|
||||
curr->gcHeapArenaUnused -= thingSize;
|
||||
}
|
||||
|
||||
template <int N>
|
||||
inline void
|
||||
ReportMemory(const nsACString &path, PRInt32 kind, PRInt32 units,
|
||||
PRInt64 amount, const char (&desc)[N],
|
||||
nsIMemoryMultiReporterCallback *callback, nsISupports *closure)
|
||||
{
|
||||
callback->Callback(NS_LITERAL_CSTRING(""), path, kind, units, amount,
|
||||
NS_LITERAL_CSTRING(desc), closure);
|
||||
}
|
||||
|
||||
template <int N>
|
||||
inline void
|
||||
ReportMemoryBytes(const nsACString &path, PRInt32 kind, PRInt64 amount,
|
||||
const char (&desc)[N],
|
||||
nsIMemoryMultiReporterCallback *callback,
|
||||
nsISupports *closure)
|
||||
{
|
||||
ReportMemory(path, kind, nsIMemoryReporter::UNITS_BYTES, amount, desc,
|
||||
callback, closure);
|
||||
}
|
||||
|
||||
template <int N>
|
||||
inline void
|
||||
ReportMemoryBytes0(const nsCString &path, PRInt32 kind, PRInt64 amount,
|
||||
const char (&desc)[N],
|
||||
nsIMemoryMultiReporterCallback *callback,
|
||||
nsISupports *closure)
|
||||
{
|
||||
if(amount)
|
||||
ReportMemoryBytes(path, kind, amount, desc, callback, closure);
|
||||
}
|
||||
|
||||
template <int N>
|
||||
inline void
|
||||
ReportMemoryPercentage(const nsACString &path, PRInt32 kind, PRInt64 amount,
|
||||
const char (&desc)[N],
|
||||
nsIMemoryMultiReporterCallback *callback,
|
||||
nsISupports *closure)
|
||||
{
|
||||
ReportMemory(path, kind, nsIMemoryReporter::UNITS_PERCENTAGE, amount, desc,
|
||||
callback, closure);
|
||||
}
|
||||
|
||||
template <int N>
|
||||
inline const nsCString
|
||||
MakeMemoryReporterPath(const nsACString &pathPrefix,
|
||||
const nsACString &compartmentName,
|
||||
const char (&reporterName)[N])
|
||||
{
|
||||
return pathPrefix + NS_LITERAL_CSTRING("compartment(") + compartmentName +
|
||||
NS_LITERAL_CSTRING(")/") + nsDependentCString(reporterName);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
class XPConnectGCChunkAllocator
|
||||
: public js::GCChunkAllocator
|
||||
{
|
||||
|
@ -1282,25 +1483,6 @@ NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSGCHeap,
|
|||
gXPCJSChunkAllocator.GetGCChunkBytesInUse,
|
||||
"Memory used by the garbage-collected JavaScript heap.")
|
||||
|
||||
static PRInt64
|
||||
GetJSStack()
|
||||
{
|
||||
JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
|
||||
PRInt64 n = 0;
|
||||
for (js::ThreadDataIter i(rt); !i.empty(); i.popFront())
|
||||
n += i.threadData()->stackSpace.committedSize();
|
||||
return n;
|
||||
}
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSStack,
|
||||
"explicit/js/stack",
|
||||
KIND_NONHEAP,
|
||||
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
|
||||
GetJSSystemCompartmentCount()
|
||||
{
|
||||
|
@ -1354,262 +1536,247 @@ NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSUserCompartmentCount,
|
|||
"compartments listed under 'js' if a garbage collection occurs at an "
|
||||
"inopportune time, but such cases should be rare.")
|
||||
|
||||
namespace mozilla {
|
||||
namespace xpconnect {
|
||||
namespace memory {
|
||||
|
||||
CompartmentStats::CompartmentStats(JSContext *cx, JSCompartment *c)
|
||||
{
|
||||
memset(this, 0, sizeof(*this));
|
||||
|
||||
if(c == cx->runtime->atomsCompartment)
|
||||
{
|
||||
name.AssignLiteral("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);
|
||||
name.ReplaceChar('/', '\\');
|
||||
|
||||
// If it's the system compartment, append the address.
|
||||
// This means that multiple system compartments (and there
|
||||
// can be many) can be distinguished.
|
||||
if(c->isSystemCompartment)
|
||||
{
|
||||
// ample; 64-bit address max is 18 chars
|
||||
static const int maxLength = 31;
|
||||
nsPrintfCString address(maxLength, ", 0x%llx", PRUint64(c));
|
||||
name.Append(address);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
name.AssignLiteral("null-codebase");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
name.AssignLiteral("null-principal");
|
||||
}
|
||||
}
|
||||
|
||||
JSBool
|
||||
CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data)
|
||||
{
|
||||
JSContext *cx = JS_NewContext(rt, 0);
|
||||
if(!cx)
|
||||
{
|
||||
NS_ERROR("couldn't create context for memory tracing");
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
data->compartmentStatsVector.reserve(rt->compartments.length());
|
||||
js::IterateCompartmentsArenasCells(cx, data, CompartmentCallback,
|
||||
ArenaCallback, CellCallback);
|
||||
}
|
||||
|
||||
JS_DestroyContextNoGC(cx);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ReportCompartmentStats(const CompartmentStats &stats,
|
||||
const nsACString &pathPrefix,
|
||||
nsIMemoryMultiReporterCallback *callback,
|
||||
nsISupports *closure)
|
||||
{
|
||||
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
|
||||
"gc-heap/arena-headers"),
|
||||
JS_GC_HEAP_KIND, stats.gcHeapArenaHeaders,
|
||||
"Memory on the compartment's garbage-collected JavaScript heap, within "
|
||||
"arenas, that is used to hold internal book-keeping information.",
|
||||
callback, closure);
|
||||
|
||||
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
|
||||
"gc-heap/arena-padding"),
|
||||
JS_GC_HEAP_KIND, stats.gcHeapArenaPadding,
|
||||
"Memory on the compartment's garbage-collected JavaScript heap, within "
|
||||
"arenas, that is unused and present only so that other data is aligned. "
|
||||
"This constitutes internal fragmentation.",
|
||||
callback, closure);
|
||||
|
||||
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
|
||||
"gc-heap/arena-unused"),
|
||||
JS_GC_HEAP_KIND, stats.gcHeapArenaUnused,
|
||||
"Memory on the compartment's garbage-collected JavaScript heap, within "
|
||||
"arenas, that could be holding useful data but currently isn't.",
|
||||
callback, closure);
|
||||
|
||||
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
|
||||
"gc-heap/objects"),
|
||||
JS_GC_HEAP_KIND, stats.gcHeapObjects,
|
||||
"Memory on the compartment's garbage-collected JavaScript heap that holds "
|
||||
"objects.",
|
||||
callback, closure);
|
||||
|
||||
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
|
||||
"gc-heap/strings"),
|
||||
JS_GC_HEAP_KIND, stats.gcHeapStrings,
|
||||
"Memory on the compartment's garbage-collected JavaScript heap that holds "
|
||||
"string headers. String headers contain various pieces of information "
|
||||
"about a string, but do not contain (except in the case of very short "
|
||||
"strings) the string characters; characters in longer strings are counted "
|
||||
"under 'gc-heap/string-chars' instead.",
|
||||
callback, closure);
|
||||
|
||||
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
|
||||
"gc-heap/shapes"),
|
||||
JS_GC_HEAP_KIND, stats.gcHeapShapes,
|
||||
"Memory on the compartment's garbage-collected JavaScript heap that holds "
|
||||
"shapes. A shape is an internal data structure that makes JavaScript "
|
||||
"property accesses fast.",
|
||||
callback, closure);
|
||||
|
||||
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
|
||||
"gc-heap/xml"),
|
||||
JS_GC_HEAP_KIND, stats.gcHeapXml,
|
||||
"Memory on the compartment's garbage-collected JavaScript heap that holds "
|
||||
"E4X XML objects.",
|
||||
callback, closure);
|
||||
|
||||
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
|
||||
"object-slots"),
|
||||
nsIMemoryReporter::KIND_HEAP, stats.objectSlots,
|
||||
"Memory allocated for the compartment's non-fixed object slot arrays, "
|
||||
"which are used to represent object properties. Some objects also "
|
||||
"contain a fixed number of slots which are stored on the compartment's "
|
||||
"JavaScript heap; those slots are not counted here, but in "
|
||||
"'gc-heap/objects' instead.",
|
||||
callback, closure);
|
||||
|
||||
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
|
||||
"string-chars"),
|
||||
nsIMemoryReporter::KIND_HEAP, stats.stringChars,
|
||||
"Memory allocated to hold the compartment's string characters. Sometimes "
|
||||
"more memory is allocated than necessary, to simplify string "
|
||||
"concatenation. Each string also includes a header which is stored on the "
|
||||
"compartment's JavaScript heap; that header is not counted here, but in "
|
||||
"'gc-heap/strings' instead.",
|
||||
callback, closure);
|
||||
|
||||
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
|
||||
"property-tables"),
|
||||
nsIMemoryReporter::KIND_HEAP, stats.propertyTables,
|
||||
"Memory allocated for the compartment's property tables. A property "
|
||||
"table is an internal data structure that makes JavaScript property "
|
||||
"accesses fast.",
|
||||
callback, closure);
|
||||
|
||||
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
|
||||
"scripts"),
|
||||
nsIMemoryReporter::KIND_HEAP, stats.scripts,
|
||||
"Memory allocated for the compartment's 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.",
|
||||
callback, closure);
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
|
||||
"mjit-code"),
|
||||
nsIMemoryReporter::KIND_NONHEAP, stats.mjitCode,
|
||||
"Memory used by the method JIT to hold the compartment's generated code.",
|
||||
callback, closure);
|
||||
|
||||
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
|
||||
"mjit-data"),
|
||||
nsIMemoryReporter::KIND_HEAP, stats.mjitData,
|
||||
"Memory used by the method JIT for the compartment's compilation data: "
|
||||
"JITScripts, native maps, and inline cache structs.",
|
||||
callback, closure);
|
||||
#endif
|
||||
#ifdef JS_TRACER
|
||||
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
|
||||
"tjit-code"),
|
||||
nsIMemoryReporter::KIND_NONHEAP, stats.tjitCode,
|
||||
"Memory used by the trace JIT to hold the compartment's generated code.",
|
||||
callback, closure);
|
||||
|
||||
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
|
||||
"tjit-data/allocators-main"),
|
||||
nsIMemoryReporter::KIND_HEAP,
|
||||
stats.tjitDataAllocatorsMain,
|
||||
"Memory used by the trace JIT to store the compartment's trace-related "
|
||||
"data. This data is allocated via the compartment's VMAllocators.",
|
||||
callback, closure);
|
||||
|
||||
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
|
||||
"tjit-data/allocators-reserve"),
|
||||
nsIMemoryReporter::KIND_HEAP,
|
||||
stats.tjitDataAllocatorsReserve,
|
||||
"Memory used by the trace JIT and held in reserve for the compartment's "
|
||||
"VMAllocators in case of OOM.",
|
||||
callback, closure);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
ReportJSStackSizeForRuntime(JSRuntime *rt, const nsACString &pathPrefix,
|
||||
nsIMemoryMultiReporterCallback *callback,
|
||||
nsISupports *closure)
|
||||
{
|
||||
PRInt64 stackSize = 0;
|
||||
for(js::ThreadDataIter i(rt); !i.empty(); i.popFront())
|
||||
stackSize += i.threadData()->stackSpace.committedSize();
|
||||
|
||||
ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("stack"),
|
||||
nsIMemoryReporter::KIND_NONHEAP, stackSize,
|
||||
"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.",
|
||||
callback, closure);
|
||||
}
|
||||
|
||||
} // namespace memory
|
||||
} // namespace xpconnect
|
||||
} // namespace mozilla
|
||||
|
||||
class XPConnectJSCompartmentsMultiReporter : public nsIMemoryMultiReporter
|
||||
{
|
||||
private:
|
||||
struct CompartmentStats
|
||||
{
|
||||
CompartmentStats(JSContext *cx, JSCompartment *c) {
|
||||
memset(this, 0, sizeof(*this));
|
||||
|
||||
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 = '\\';
|
||||
}
|
||||
}
|
||||
// If it's the system compartment, append the address.
|
||||
// This means that multiple system compartments (and there
|
||||
// can be many) can be distinguished.
|
||||
if (c->isSystemCompartment) {
|
||||
static const int maxLength = 31; // ample; 64-bit address max is 18 chars
|
||||
nsPrintfCString address(maxLength, ", 0x%llx", PRUint64(c));
|
||||
name.Append(address);
|
||||
}
|
||||
} else {
|
||||
name = NS_LITERAL_CSTRING("null-codebase");
|
||||
}
|
||||
} else {
|
||||
name = NS_LITERAL_CSTRING("null-principal");
|
||||
}
|
||||
}
|
||||
|
||||
nsCString name;
|
||||
PRInt64 gcHeapArenaHeaders;
|
||||
PRInt64 gcHeapArenaPadding;
|
||||
PRInt64 gcHeapArenaUnused;
|
||||
|
||||
PRInt64 gcHeapObjects;
|
||||
PRInt64 gcHeapStrings;
|
||||
PRInt64 gcHeapShapes;
|
||||
PRInt64 gcHeapXml;
|
||||
|
||||
PRInt64 objectSlots;
|
||||
PRInt64 stringChars;
|
||||
PRInt64 propertyTables;
|
||||
|
||||
PRInt64 scripts;
|
||||
#ifdef JS_METHODJIT
|
||||
PRInt64 mjitCode;
|
||||
PRInt64 mjitData;
|
||||
#endif
|
||||
#ifdef JS_TRACER
|
||||
PRInt64 tjitCode;
|
||||
PRInt64 tjitDataAllocatorsMain;
|
||||
PRInt64 tjitDataAllocatorsReserve;
|
||||
#endif
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
|
||||
static PRInt64
|
||||
GetCompartmentMjitCodeSize(JSCompartment *c)
|
||||
{
|
||||
return c->getMjitCodeSize();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endif // JS_METHODJIT
|
||||
|
||||
#ifdef JS_TRACER
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
curr->gcHeapObjects += thingSize;
|
||||
JSObject *obj = static_cast<JSObject *>(thing);
|
||||
if (obj->hasSlotsArray()) {
|
||||
curr->objectSlots += obj->numSlots() * sizeof(js::Value);
|
||||
}
|
||||
} else if (traceKind == JSTRACE_STRING) {
|
||||
curr->gcHeapStrings += thingSize;
|
||||
JSString *str = static_cast<JSString *>(thing);
|
||||
curr->stringChars += str->charsHeapSize();
|
||||
} else if (traceKind == JSTRACE_SHAPE) {
|
||||
curr->gcHeapShapes += thingSize;
|
||||
js::Shape *shape = static_cast<js::Shape *>(thing);
|
||||
if (shape->hasTable()) {
|
||||
curr->propertyTables += shape->getTable()->sizeOf();
|
||||
}
|
||||
} 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, "");
|
||||
IterateData data;
|
||||
if(!CollectCompartmentStatsForRuntime(rt, &data))
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
PRInt64 gcHeapChunkTotal = gXPCJSChunkAllocator.GetGCChunkBytesInUse();
|
||||
// This is initialized to gcHeapChunkTotal, and then we subtract used
|
||||
|
@ -1617,27 +1784,13 @@ public:
|
|||
PRInt64 gcHeapChunkUnused = gcHeapChunkTotal;
|
||||
PRInt64 gcHeapArenaUnused = 0;
|
||||
|
||||
#define BYTES(path, kind, amount, desc) \
|
||||
callback->Callback(p, path, kind, nsIMemoryReporter::UNITS_BYTES, \
|
||||
amount, NS_LITERAL_CSTRING(desc), closure)
|
||||
|
||||
#define BYTES0(path, kind, amount, desc) \
|
||||
do { \
|
||||
if (amount != 0) \
|
||||
BYTES(path, kind, amount, desc); \
|
||||
} while (0)
|
||||
|
||||
#define PERCENTAGE(path, kind, amount, desc) \
|
||||
callback->Callback(p, path, kind, nsIMemoryReporter::UNITS_PERCENTAGE, \
|
||||
amount, NS_LITERAL_CSTRING(desc), closure);
|
||||
NS_NAMED_LITERAL_CSTRING(pathPrefix, "explicit/js/");
|
||||
|
||||
// This is the second step (see above).
|
||||
for (CompartmentStats *stats = data.compartmentStatsVector.begin();
|
||||
stats != data.compartmentStatsVector.end();
|
||||
++stats)
|
||||
for(CompartmentStats *stats = data.compartmentStatsVector.begin();
|
||||
stats != data.compartmentStatsVector.end();
|
||||
++stats)
|
||||
{
|
||||
nsCString &name = stats->name;
|
||||
|
||||
gcHeapChunkUnused -=
|
||||
stats->gcHeapArenaHeaders + stats->gcHeapArenaPadding +
|
||||
stats->gcHeapArenaUnused +
|
||||
|
@ -1646,100 +1799,7 @@ public:
|
|||
|
||||
gcHeapArenaUnused += stats->gcHeapArenaUnused;
|
||||
|
||||
BYTES0(mkPath(name, "gc-heap/arena-headers"),
|
||||
JS_GC_HEAP_KIND, stats->gcHeapArenaHeaders,
|
||||
"Memory on the compartment's garbage-collected JavaScript heap, within "
|
||||
"arenas, that is used to hold internal book-keeping information.");
|
||||
|
||||
BYTES0(mkPath(name, "gc-heap/arena-padding"),
|
||||
JS_GC_HEAP_KIND, stats->gcHeapArenaPadding,
|
||||
"Memory on the compartment's garbage-collected JavaScript heap, within "
|
||||
"arenas, that is unused and present only so that other data is aligned. "
|
||||
"This constitutes internal fragmentation.");
|
||||
|
||||
BYTES0(mkPath(name, "gc-heap/arena-unused"),
|
||||
JS_GC_HEAP_KIND, stats->gcHeapArenaUnused,
|
||||
"Memory on the compartment's garbage-collected JavaScript heap, within "
|
||||
"arenas, that could be holding useful data but currently isn't.");
|
||||
|
||||
BYTES0(mkPath(name, "gc-heap/objects"),
|
||||
JS_GC_HEAP_KIND, stats->gcHeapObjects,
|
||||
"Memory on the compartment's garbage-collected JavaScript heap that holds "
|
||||
"objects.");
|
||||
|
||||
BYTES0(mkPath(name, "gc-heap/strings"),
|
||||
JS_GC_HEAP_KIND, stats->gcHeapStrings,
|
||||
"Memory on the compartment's garbage-collected JavaScript heap that holds "
|
||||
"string headers. String headers contain various pieces of information "
|
||||
"about a string, but do not contain (except in the case of very short "
|
||||
"strings) the string characters; characters in longer strings are counted "
|
||||
"under 'gc-heap/string-chars' instead.");
|
||||
|
||||
BYTES0(mkPath(name, "gc-heap/shapes"),
|
||||
JS_GC_HEAP_KIND, stats->gcHeapShapes,
|
||||
"Memory on the compartment's garbage-collected JavaScript heap that holds "
|
||||
"shapes. A shape is an internal data structure that makes JavaScript "
|
||||
"property accesses fast.");
|
||||
|
||||
BYTES0(mkPath(name, "gc-heap/xml"),
|
||||
JS_GC_HEAP_KIND, stats->gcHeapXml,
|
||||
"Memory on the compartment's garbage-collected JavaScript heap that holds "
|
||||
"E4X XML objects.");
|
||||
|
||||
BYTES0(mkPath(name, "object-slots"),
|
||||
nsIMemoryReporter::KIND_HEAP, stats->objectSlots,
|
||||
"Memory allocated for the compartment's non-fixed object slot arrays, "
|
||||
"which are used to represent object properties. Some objects also "
|
||||
"contain a fixed number of slots which are stored on the compartment's "
|
||||
"JavaScript heap; those slots are not counted here, but in "
|
||||
"'gc-heap/objects' instead.");
|
||||
|
||||
BYTES0(mkPath(name, "string-chars"),
|
||||
nsIMemoryReporter::KIND_HEAP, stats->stringChars,
|
||||
"Memory allocated to hold the compartment's string characters. Sometimes "
|
||||
"more memory is allocated than necessary, to simplify string "
|
||||
"concatenation. Each string also includes a header which is stored on the "
|
||||
"compartment's JavaScript heap; that header is not counted here, but in "
|
||||
"'gc-heap/strings' instead.");
|
||||
|
||||
BYTES0(mkPath(name, "property-tables"),
|
||||
nsIMemoryReporter::KIND_HEAP, stats->propertyTables,
|
||||
"Memory allocated for the compartment's property tables. A property "
|
||||
"table is an internal data structure that makes JavaScript property "
|
||||
"accesses fast.");
|
||||
|
||||
BYTES0(mkPath(name, "scripts"),
|
||||
nsIMemoryReporter::KIND_HEAP, stats->scripts,
|
||||
"Memory allocated for the compartment's 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
|
||||
BYTES0(mkPath(name, "mjit-code"),
|
||||
nsIMemoryReporter::KIND_NONHEAP, stats->mjitCode,
|
||||
"Memory used by the method JIT to hold the compartment's generated code.");
|
||||
|
||||
BYTES0(mkPath(name, "mjit-data"),
|
||||
nsIMemoryReporter::KIND_HEAP, stats->mjitData,
|
||||
"Memory used by the method JIT for the compartment's compilation data: "
|
||||
"JITScripts, native maps, and inline cache structs.");
|
||||
#endif
|
||||
#ifdef JS_TRACER
|
||||
BYTES0(mkPath(name, "tjit-code"),
|
||||
nsIMemoryReporter::KIND_NONHEAP, stats->tjitCode,
|
||||
"Memory used by the trace JIT to hold the compartment's generated code.");
|
||||
|
||||
BYTES0(mkPath(name, "tjit-data/allocators-main"),
|
||||
nsIMemoryReporter::KIND_HEAP, stats->tjitDataAllocatorsMain,
|
||||
"Memory used by the trace JIT to store the compartment's trace-related "
|
||||
"data. This data is allocated via the compartment's VMAllocators.");
|
||||
|
||||
BYTES0(mkPath(name, "tjit-data/allocators-reserve"),
|
||||
nsIMemoryReporter::KIND_HEAP, stats->tjitDataAllocatorsReserve,
|
||||
"Memory used by the trace JIT and held in reserve for the compartment's "
|
||||
"VMAllocators in case of OOM.");
|
||||
#endif
|
||||
ReportCompartmentStats(*stats, pathPrefix, callback, closure);
|
||||
}
|
||||
|
||||
JS_ASSERT(gcHeapChunkTotal % js::GC_CHUNK_SIZE == 0);
|
||||
|
@ -1756,36 +1816,47 @@ public:
|
|||
(gcHeapChunkUnused + gcHeapArenaUnused) * 10000 /
|
||||
gXPCJSChunkAllocator.GetGCChunkBytesInUse();
|
||||
|
||||
BYTES(NS_LITERAL_CSTRING("explicit/js/gc-heap-chunk-unused"),
|
||||
JS_GC_HEAP_KIND, gcHeapChunkUnused,
|
||||
ReportMemoryBytes(pathPrefix +
|
||||
NS_LITERAL_CSTRING("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.");
|
||||
"could be holding useful data but currently isn't.",
|
||||
callback, closure);
|
||||
|
||||
BYTES(NS_LITERAL_CSTRING("js-gc-heap-chunk-unused"),
|
||||
nsIMemoryReporter::KIND_OTHER, gcHeapChunkUnused,
|
||||
ReportMemoryBytes(NS_LITERAL_CSTRING("js-gc-heap-chunk-unused"),
|
||||
nsIMemoryReporter::KIND_OTHER, gcHeapChunkUnused,
|
||||
"The same as 'explicit/js/gc-heap-chunk-unused'. Shown here for "
|
||||
"easy comparison with 'js-gc-heap' and 'js-gc-heap-arena-unused'.");
|
||||
"easy comparison with 'js-gc-heap' and 'js-gc-heap-arena-unused'.",
|
||||
callback, closure);
|
||||
|
||||
BYTES(NS_LITERAL_CSTRING("explicit/js/gc-heap-chunk-admin"),
|
||||
JS_GC_HEAP_KIND, gcHeapChunkAdmin,
|
||||
ReportMemoryBytes(pathPrefix +
|
||||
NS_LITERAL_CSTRING("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.");
|
||||
"used to hold internal book-keeping information.",
|
||||
callback, closure);
|
||||
|
||||
BYTES(NS_LITERAL_CSTRING("js-gc-heap-arena-unused"),
|
||||
nsIMemoryReporter::KIND_OTHER, gcHeapArenaUnused,
|
||||
ReportMemoryBytes(NS_LITERAL_CSTRING("js-gc-heap-arena-unused"),
|
||||
nsIMemoryReporter::KIND_OTHER, gcHeapArenaUnused,
|
||||
"Memory on the garbage-collected JavaScript heap, within arenas, that "
|
||||
"could be holding useful data but currently isn't. This is the sum of "
|
||||
"all compartments' 'gc-heap/arena-unused' numbers.");
|
||||
"all compartments' 'gc-heap/arena-unused' numbers.",
|
||||
callback, closure);
|
||||
|
||||
PERCENTAGE(NS_LITERAL_CSTRING("js-gc-heap-unused-fraction"),
|
||||
nsIMemoryReporter::KIND_OTHER, gcHeapUnusedPercentage,
|
||||
ReportMemoryPercentage(NS_LITERAL_CSTRING("js-gc-heap-unused-fraction"),
|
||||
nsIMemoryReporter::KIND_OTHER,
|
||||
gcHeapUnusedPercentage,
|
||||
"Fraction of the garbage-collected JavaScript heap that is unused. "
|
||||
"Computed as ('js-gc-heap-chunk-unused' + 'js-gc-heap-arena-unused') / "
|
||||
"'js-gc-heap'.");
|
||||
"'js-gc-heap'.",
|
||||
callback, closure);
|
||||
|
||||
ReportJSStackSizeForRuntime(rt, pathPrefix, callback, closure);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(
|
||||
XPConnectJSCompartmentsMultiReporter
|
||||
, nsIMemoryMultiReporter
|
||||
|
@ -1868,7 +1939,6 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
|
|||
mJSRuntime->setCustomGCChunkAllocator(&gXPCJSChunkAllocator);
|
||||
|
||||
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSGCHeap));
|
||||
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSStack));
|
||||
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSSystemCompartmentCount));
|
||||
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSUserCompartmentCount));
|
||||
NS_RegisterMemoryMultiReporter(new XPConnectJSCompartmentsMultiReporter);
|
||||
|
|
|
@ -43,10 +43,12 @@
|
|||
#include "jsapi.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsgc.h"
|
||||
#include "jspubtd.h"
|
||||
|
||||
#include "nsISupports.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsStringGlue.h"
|
||||
|
||||
class nsIPrincipal;
|
||||
|
||||
|
@ -183,4 +185,67 @@ nsWrapperCache::GetWrapper() const
|
|||
return obj;
|
||||
}
|
||||
|
||||
class nsIMemoryMultiReporterCallback;
|
||||
|
||||
namespace mozilla {
|
||||
namespace xpconnect {
|
||||
namespace memory {
|
||||
|
||||
struct CompartmentStats
|
||||
{
|
||||
CompartmentStats(JSContext *cx, JSCompartment *c);
|
||||
|
||||
nsCString name;
|
||||
PRInt64 gcHeapArenaHeaders;
|
||||
PRInt64 gcHeapArenaPadding;
|
||||
PRInt64 gcHeapArenaUnused;
|
||||
|
||||
PRInt64 gcHeapObjects;
|
||||
PRInt64 gcHeapStrings;
|
||||
PRInt64 gcHeapShapes;
|
||||
PRInt64 gcHeapXml;
|
||||
|
||||
PRInt64 objectSlots;
|
||||
PRInt64 stringChars;
|
||||
PRInt64 propertyTables;
|
||||
|
||||
PRInt64 scripts;
|
||||
#ifdef JS_METHODJIT
|
||||
PRInt64 mjitCode;
|
||||
PRInt64 mjitData;
|
||||
#endif
|
||||
#ifdef JS_TRACER
|
||||
PRInt64 tjitCode;
|
||||
PRInt64 tjitDataAllocatorsMain;
|
||||
PRInt64 tjitDataAllocatorsReserve;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct IterateData
|
||||
{
|
||||
IterateData()
|
||||
: compartmentStatsVector(), currCompartmentStats(NULL) { }
|
||||
|
||||
js::Vector<CompartmentStats, 0, js::SystemAllocPolicy> compartmentStatsVector;
|
||||
CompartmentStats *currCompartmentStats;
|
||||
};
|
||||
|
||||
JSBool
|
||||
CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data);
|
||||
|
||||
void
|
||||
ReportCompartmentStats(const CompartmentStats &stats,
|
||||
const nsACString &pathPrefix,
|
||||
nsIMemoryMultiReporterCallback *callback,
|
||||
nsISupports *closure);
|
||||
|
||||
void
|
||||
ReportJSStackSizeForRuntime(JSRuntime *rt, const nsACString &pathPrefix,
|
||||
nsIMemoryMultiReporterCallback *callback,
|
||||
nsISupports *closure);
|
||||
|
||||
} // namespace memory
|
||||
} // namespace xpconnect
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче