From 12f9b52d1ee3643731be209cb71f672bf750dbf0 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 5 Jul 2012 21:12:37 -0700 Subject: [PATCH] Bug 687724 (part 3) - Report JS memory consumption for compartments that are associated with |window| objects under "window-objects". r=bholley,jlebar,luke. --HG-- extra : rebase_source : 20329a698154ef61cea74247d3dccc9f01899829 --- dom/base/nsWindowMemoryReporter.cpp | 25 ++- dom/workers/WorkerPrivate.cpp | 28 ++- js/public/MemoryMetrics.h | 4 +- js/src/MemoryMetrics.cpp | 2 +- js/src/jsapi.cpp | 24 ++- js/src/jsapi.h | 7 + js/xpconnect/src/Makefile.in | 1 + js/xpconnect/src/XPCJSMemoryReporter.h | 33 ++++ js/xpconnect/src/XPCJSRuntime.cpp | 171 +++++++++++------- js/xpconnect/src/xpcpublic.h | 2 +- .../tests/test_memoryReporters.xul | 26 +-- 11 files changed, 225 insertions(+), 98 deletions(-) create mode 100644 js/xpconnect/src/XPCJSMemoryReporter.h diff --git a/dom/base/nsWindowMemoryReporter.cpp b/dom/base/nsWindowMemoryReporter.cpp index 02e225434cca..2adecece81d0 100644 --- a/dom/base/nsWindowMemoryReporter.cpp +++ b/dom/base/nsWindowMemoryReporter.cpp @@ -10,6 +10,7 @@ #include "mozilla/Preferences.h" #include "nsNetCID.h" #include "nsPrintfCString.h" +#include "XPCJSMemoryReporter.h" using namespace mozilla; @@ -101,10 +102,14 @@ AppendWindowURI(nsGlobalWindow *aWindow, nsACString& aStr) NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(DOMStyleMallocSizeOf, "windows") +// The key is the window ID. +typedef nsDataHashtable WindowPaths; + static nsresult CollectWindowReports(nsGlobalWindow *aWindow, nsWindowSizes *aWindowTotalSizes, nsTHashtable *aGhostWindowIDs, + WindowPaths *aWindowPaths, nsIMemoryMultiReporterCallback *aCb, nsISupports *aClosure) { @@ -140,6 +145,9 @@ CollectWindowReports(nsGlobalWindow *aWindow, AppendWindowURI(aWindow, windowPath); windowPath += NS_LITERAL_CSTRING(")"); + // Remember the path for later. + aWindowPaths->Put(aWindow->WindowID(), windowPath); + #define REPORT(_pathTail, _amount, _desc) \ do { \ if (_amount > 0) { \ @@ -294,14 +302,23 @@ nsWindowMemoryReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb, NS_EFFECTIVETLDSERVICE_CONTRACTID); NS_ENSURE_STATE(tldService); + WindowPaths windowPaths; + windowPaths.Init(); + // Collect window memory usage. nsWindowSizes windowTotalSizes(NULL); for (PRUint32 i = 0; i < windows.Length(); i++) { nsresult rv = CollectWindowReports(windows[i], &windowTotalSizes, - &ghostWindows, aCb, aClosure); + &ghostWindows, &windowPaths, + aCb, aClosure); NS_ENSURE_SUCCESS(rv, rv); } + // Report JS memory usage. We do this from here because the JS memory + // multi-reporter needs to be passed |windowPaths|. + nsresult rv = xpc::JSMemoryMultiReporter::CollectReports(&windowPaths, aCb, aClosure); + NS_ENSURE_SUCCESS(rv, rv); + #define REPORT(_path, _amount, _desc) \ do { \ nsresult rv; \ @@ -392,9 +409,9 @@ nsWindowMemoryReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb, NS_IMETHODIMP nsWindowMemoryReporter::GetExplicitNonHeap(PRInt64* aAmount) { - // This reporter only measures heap memory. - *aAmount = 0; - return NS_OK; + // This reporter only measures heap memory, so we don't need to report any + // bytes for it. However, the JS multi-reporter needs to be invoked. + return xpc::JSMemoryMultiReporter::GetExplicitNonHeap(aAmount); } PRUint32 diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 41585321c4b3..ecfcfd5906fd 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -133,11 +133,19 @@ struct WorkerJSRuntimeStats : public JS::RuntimeStats virtual void initExtraCompartmentStats(JSCompartment *c, JS::CompartmentStats *cstats) MOZ_OVERRIDE { - MOZ_ASSERT(!cstats->extra); + MOZ_ASSERT(!cstats->extra1); + MOZ_ASSERT(!cstats->extra2); - // ReportJSRuntimeExplicitTreeStats expects that cstats->extra is a char pointer - const char *name = js::IsAtomsCompartment(c) ? "Web Worker Atoms" : "Web Worker"; - cstats->extra = const_cast(name); + // ReportJSRuntimeExplicitTreeStats expects that cstats->{extra1,extra2} + // are char pointers. + + // This is the |cPathPrefix|. Using NULL here means that we'll end up + // using WorkerMemoryReporter::mRtPath as the path prefix for each + // compartment. See xpc::ReportJSRuntimeExplicitTreeStats(). + cstats->extra1 = NULL; + + // This is the |cName|. + cstats->extra2 = (void *)(js::IsAtomsCompartment(c) ? "Web Worker Atoms" : "Web Worker"); } }; @@ -145,7 +153,7 @@ class WorkerMemoryReporter MOZ_FINAL : public nsIMemoryMultiReporter { WorkerPrivate* mWorkerPrivate; nsCString mAddressString; - nsCString mPathPrefix; + nsCString mRtPath; public: NS_DECL_ISUPPORTS @@ -175,10 +183,10 @@ public: } } - mPathPrefix = NS_LITERAL_CSTRING("explicit/dom/workers(") + - escapedDomain + NS_LITERAL_CSTRING(")/worker(") + - escapedURL + NS_LITERAL_CSTRING(", ") + mAddressString + - NS_LITERAL_CSTRING(")/"); + mRtPath = NS_LITERAL_CSTRING("explicit/workers/workers(") + + escapedDomain + NS_LITERAL_CSTRING(")/worker(") + + escapedURL + NS_LITERAL_CSTRING(", ") + mAddressString + + NS_LITERAL_CSTRING(")/"); } nsresult @@ -232,7 +240,7 @@ public: // Always report, even if we're disabled, so that we at least get an entry // in about::memory. - return xpc::ReportJSRuntimeExplicitTreeStats(rtStats, mPathPrefix, + return xpc::ReportJSRuntimeExplicitTreeStats(rtStats, mRtPath, aCallback, aClosure); } diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h index b8c3e5d678f5..1b4b284913b0 100644 --- a/js/public/MemoryMetrics.h +++ b/js/public/MemoryMetrics.h @@ -85,7 +85,9 @@ struct CompartmentStats memset(this, 0, sizeof(*this)); } - void *extra; + // These fields can be used by embedders. + void *extra1; + void *extra2; // If you add a new number, remember to update add() and maybe // gcHeapThingsSize()! diff --git a/js/src/MemoryMetrics.cpp b/js/src/MemoryMetrics.cpp index 986c2a98e8eb..ee757fb98b16 100644 --- a/js/src/MemoryMetrics.cpp +++ b/js/src/MemoryMetrics.cpp @@ -196,7 +196,7 @@ CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats) IterateCompartmentsArenasCells(rt, rtStats, StatsCompartmentCallback, StatsArenaCallback, StatsCellCallback); - // Take the "explcit/js/runtime/" measurements. + // Take the "explicit/js/runtime/" measurements. rt->sizeOfIncludingThis(rtStats->mallocSizeOf, &rtStats->runtime); rtStats->gcHeapGcThings = 0; diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index c086e99491c2..dd5fd40f0bc2 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -229,6 +229,18 @@ AssertHeapIsIdle(JSContext *cx) AssertHeapIsIdle(cx->runtime); } +static void +AssertHeapIsIdleOrIterating(JSRuntime *rt) +{ + JS_ASSERT(rt->heapState != JSRuntime::Collecting); +} + +static void +AssertHeapIsIdleOrIterating(JSContext *cx) +{ + AssertHeapIsIdleOrIterating(cx->runtime); +} + static void AssertHeapIsIdleOrStringIsFlat(JSContext *cx, JSString *str) { @@ -2167,6 +2179,14 @@ JS_GetGlobalForObject(JSContext *cx, JSObject *obj) return &obj->global(); } +JS_PUBLIC_API(JSObject *) +JS_GetGlobalForCompartmentOrNull(JSContext *cx, JSCompartment *c) +{ + AssertHeapIsIdleOrIterating(cx); + assertSameCompartment(cx, c); + return c->maybeGlobal(); +} + JS_PUBLIC_API(JSObject *) JS_GetGlobalForScopeChain(JSContext *cx) { @@ -5566,7 +5586,7 @@ JS_IsRunning(JSContext *cx) JS_PUBLIC_API(JSBool) JS_SaveFrameChain(JSContext *cx) { - AssertHeapIsIdle(cx); + AssertHeapIsIdleOrIterating(cx); CHECK_REQUEST(cx); return cx->stack.saveFrameChain(); } @@ -5574,7 +5594,7 @@ JS_SaveFrameChain(JSContext *cx) JS_PUBLIC_API(void) JS_RestoreFrameChain(JSContext *cx) { - AssertHeapIsIdle(cx); + AssertHeapIsIdleOrIterating(cx); CHECK_REQUEST(cx); cx->stack.restoreFrameChain(); } diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 6f811677bfdb..7ee50f096f79 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -3053,6 +3053,13 @@ JS_GetObjectPrototype(JSContext *cx, JSObject *forObj); extern JS_PUBLIC_API(JSObject *) JS_GetGlobalForObject(JSContext *cx, JSObject *obj); +/* + * May return NULL, if |c| never had a global (e.g. the atoms compartment), or + * if |c|'s global has been collected. + */ +extern JS_PUBLIC_API(JSObject *) +JS_GetGlobalForCompartmentOrNull(JSContext *cx, JSCompartment *c); + extern JS_PUBLIC_API(JSObject *) JS_GetGlobalForScopeChain(JSContext *cx); diff --git a/js/xpconnect/src/Makefile.in b/js/xpconnect/src/Makefile.in index f985a1de3290..3c9383835328 100644 --- a/js/xpconnect/src/Makefile.in +++ b/js/xpconnect/src/Makefile.in @@ -19,6 +19,7 @@ EXPORTS = \ qsObjectHelper.h \ xpcObjectHelper.h \ xpcpublic.h \ + XPCJSMemoryReporter.h \ dombindings.h \ dombindings_gen.h diff --git a/js/xpconnect/src/XPCJSMemoryReporter.h b/js/xpconnect/src/XPCJSMemoryReporter.h new file mode 100644 index 000000000000..e84d78063815 --- /dev/null +++ b/js/xpconnect/src/XPCJSMemoryReporter.h @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef XPCJSMemoryReporter_h +#define XPCJSMemoryReporter_h + +class nsISupports; +class nsIMemoryMultiReporterCallback; + +namespace xpc { + +// The key is the window ID. +typedef nsDataHashtable WindowPaths; + +// This is very nearly an instance of nsIMemoryMultiReporter, but it's not, +// because it's invoked by nsWindowMemoryReporter in order to get |windowPaths| +// in CollectReports. +class JSMemoryMultiReporter +{ +public: + static nsresult CollectReports(WindowPaths *windowPaths, nsIMemoryMultiReporterCallback *cb, + nsISupports *closure); + + static nsresult GetExplicitNonHeap(PRInt64 *n); +}; + +} + +#endif diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 8a84878cc7ae..4b9c6bfa2531 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -10,10 +10,12 @@ #include "xpcprivate.h" #include "xpcpublic.h" +#include "XPCJSMemoryReporter.h" #include "WrapperFactory.h" #include "dom_quickstubs.h" #include "nsIMemoryReporter.h" +#include "nsPIDOMWindow.h" #include "nsPrintfCString.h" #include "mozilla/FunctionTimer.h" #include "prsystem.h" @@ -1286,6 +1288,8 @@ static const size_t SUNDRIES_THRESHOLD = 8192; rtTotal += amount; \ } while (0) +NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsMallocSizeOf, "js") + namespace xpc { static nsresult @@ -1472,7 +1476,7 @@ ReportCompartmentStats(const JS::CompartmentStats &cStats, nsresult ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats, - const nsACString &pathPrefix, + const nsACString &rtPath, nsIMemoryMultiReporterCallback *cb, nsISupports *closure, size_t *rtTotalOut) { @@ -1483,11 +1487,21 @@ ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats, size_t gcTotal = 0; for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++) { JS::CompartmentStats cStats = rtStats.compartmentStatsVector[i]; - const char *name = static_cast(cStats.extra); - nsCString pathPrefix2 = pathPrefix + NS_LITERAL_CSTRING("compartment(") + - nsDependentCString(name) + NS_LITERAL_CSTRING(")/"); + const char *cPathPrefix = static_cast(cStats.extra1); + const char *cName = static_cast(cStats.extra2); - rv = ReportCompartmentStats(cStats, pathPrefix2, cb, closure, &gcTotal); + // If cPathPrefix is NULL, cPath is "compartment()/" + // otherwise, cPath is "compartment()/" + nsCString cPath; + if (cPathPrefix) { + cPath.Assign(nsDependentCString(cPathPrefix)); + } else { + cPath.Assign(rtPath); + } + cPath += NS_LITERAL_CSTRING("compartment(") + + nsDependentCString(cName) + NS_LITERAL_CSTRING(")/"); + + rv = ReportCompartmentStats(cStats, cPath, cb, closure, &gcTotal); NS_ENSURE_SUCCESS(rv, rv); } @@ -1496,62 +1510,62 @@ ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats, size_t rtTotal = 0; - RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/runtime-object"), + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/runtime-object"), nsIMemoryReporter::KIND_HEAP, rtStats.runtime.object, "Memory used by the JSRuntime object."); - RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/atoms-table"), + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/atoms-table"), nsIMemoryReporter::KIND_HEAP, rtStats.runtime.atomsTable, "Memory used by the atoms table."); - RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/contexts"), + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/contexts"), nsIMemoryReporter::KIND_HEAP, rtStats.runtime.contexts, "Memory used by JSContext objects and certain structures " "hanging off them."); - RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/dtoa"), + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/dtoa"), nsIMemoryReporter::KIND_HEAP, rtStats.runtime.dtoa, "Memory used by DtoaState, which is used for converting " "strings to numbers and vice versa."); - RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/temporary"), + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/temporary"), nsIMemoryReporter::KIND_HEAP, rtStats.runtime.temporary, "Memory held transiently in JSRuntime and used during " "compilation. It mostly holds parse nodes."); - RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/mjit-code"), + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/mjit-code"), nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.mjitCode, "Memory used by the method JIT to hold the runtime's " "generated code."); - RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/regexp-code"), + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/regexp-code"), nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.regexpCode, "Memory used by the regexp JIT to hold generated code."); - RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/unused-code-memory"), + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/unused-code-memory"), nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.unusedCodeMemory, "Memory allocated by the method and/or regexp JIT to hold the " "runtime's code, but which is currently unused."); - RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/stack-committed"), + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/stack-committed"), nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.stackCommitted, "Memory used for the JS call stack. This is the committed " "portion of the stack; the uncommitted portion is not " "measured because it hardly costs anything."); - RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/gc-marker"), + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc-marker"), nsIMemoryReporter::KIND_HEAP, rtStats.runtime.gcMarker, "Memory used for the GC mark stack and gray roots."); - RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/math-cache"), + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/math-cache"), nsIMemoryReporter::KIND_HEAP, rtStats.runtime.mathCache, "Memory used for the math cache."); - RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/script-filenames"), + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/script-filenames"), nsIMemoryReporter::KIND_HEAP, rtStats.runtime.scriptFilenames, "Memory used for the table holding script filenames."); - RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/compartment-objects"), + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/compartment-objects"), nsIMemoryReporter::KIND_HEAP, rtStats.runtime.compartmentObjects, "Memory used for JSCompartment objects. These are fairly " "small and all the same size, so they're not worth reporting " @@ -1563,25 +1577,25 @@ ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats, // Report GC numbers that don't belong to a compartment. - REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/unused-arenas"), + REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/unused-arenas"), rtStats.gcHeapUnusedArenas, "Memory on the garbage-collected JavaScript heap taken by " "empty arenas within non-empty chunks."); - REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/unused-chunks"), + REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/unused-chunks"), rtStats.gcHeapUnusedChunks, "Memory on the garbage-collected JavaScript heap taken by " "empty chunks, which will soon be released unless claimed " "for new allocations."); - REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/decommitted-arenas"), + REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/decommitted-arenas"), rtStats.gcHeapDecommittedArenas, "Memory on the garbage-collected JavaScript heap, " "in arenas in non-empty chunks, that is returned to the OS. " "This means it takes up address space but no physical " "memory or swap space."); - REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/chunk-admin"), + REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/chunk-admin"), rtStats.gcHeapChunkAdmin, "Memory on the garbage-collected JavaScript heap, within " "chunks, that is used to hold internal bookkeeping " @@ -1596,8 +1610,6 @@ ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats, } // namespace xpc -NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsMallocSizeOf, "js") - class JSCompartmentsMultiReporter MOZ_FINAL : public nsIMemoryMultiReporter { public: @@ -1661,36 +1673,64 @@ NS_IMPL_THREADSAFE_ISUPPORTS1(JSCompartmentsMultiReporter , nsIMemoryMultiReporter ) -struct XPCJSRuntimeStats : public JS::RuntimeStats { - XPCJSRuntimeStats() - : JS::RuntimeStats(JsMallocSizeOf) { } +namespace xpc { + +class XPCJSRuntimeStats : public JS::RuntimeStats +{ + JSContext *mCx; + WindowPaths *mWindowPaths; + + public: + XPCJSRuntimeStats(WindowPaths *windowPaths) + : JS::RuntimeStats(JsMallocSizeOf), mCx(NULL), mWindowPaths(windowPaths) + { } + + bool init(XPCJSRuntime *xpcrt) { + mCx = JS_NewContext(xpcrt->GetJSRuntime(), 0); + return !!mCx; + } ~XPCJSRuntimeStats() { - for (size_t i = 0; i != compartmentStatsVector.length(); ++i) - free(compartmentStatsVector[i].extra); + JS_DestroyContextNoGC(mCx); + + for (size_t i = 0; i != compartmentStatsVector.length(); ++i) { + free(compartmentStatsVector[i].extra1); + free(compartmentStatsVector[i].extra2); + } } virtual void initExtraCompartmentStats(JSCompartment *c, JS::CompartmentStats *cstats) MOZ_OVERRIDE { - nsCAutoString name; - GetCompartmentName(c, name); - cstats->extra = strdup(name.get()); + nsCAutoString cName, cPathPrefix; + GetCompartmentName(c, cName); + + // Get the compartment's global. + if (JSObject *global = JS_GetGlobalForCompartmentOrNull(mCx, c)) { + nsISupports *native = nsXPConnect::GetXPConnect()->GetNativeOfWrapper(mCx, global); + if (nsCOMPtr piwindow = do_QueryInterface(native)) { + // The global is a |window| object. Use the path prefix that + // we should have already created for it. + if (mWindowPaths->Get(piwindow->WindowID(), &cPathPrefix)) { + cPathPrefix.AppendLiteral("/js/"); + } else { + cPathPrefix.AssignLiteral("explicit/js-non-window/compartments/unknown-window-global/"); + } + } else { + cPathPrefix.AssignLiteral("explicit/js-non-window/compartments/non-window-global/"); + } + } else { + cPathPrefix.AssignLiteral("explicit/js-non-window/compartments/no-global/"); + } + + cstats->extra1 = strdup(cPathPrefix.get()); + cstats->extra2 = strdup(cName.get()); } }; -class JSMemoryMultiReporter MOZ_FINAL : public nsIMemoryMultiReporter -{ -public: - NS_DECL_ISUPPORTS - - NS_IMETHOD GetName(nsACString &name) - { - name.AssignLiteral("js"); - return NS_OK; - } - - NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback *cb, - nsISupports *closure) + nsresult + JSMemoryMultiReporter::CollectReports(WindowPaths *windowPaths, + nsIMemoryMultiReporterCallback *cb, + nsISupports *closure) { XPCJSRuntime *xpcrt = nsXPConnect::GetRuntimeInstance(); @@ -1699,7 +1739,11 @@ 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. - XPCJSRuntimeStats rtStats; + + XPCJSRuntimeStats rtStats(windowPaths); + if (!rtStats.init(xpcrt)) + return NS_ERROR_FAILURE; + if (!JS::CollectRuntimeStats(xpcrt->GetJSRuntime(), &rtStats)) return NS_ERROR_FAILURE; @@ -1707,15 +1751,14 @@ public: xpcrt->SizeOfIncludingThis(JsMallocSizeOf) + XPCWrappedNativeScope::SizeOfAllScopesIncludingThis(JsMallocSizeOf); - NS_NAMED_LITERAL_CSTRING(explicitJs, "explicit/js/"); - // This is the second step (see above). First we report stuff in the // "explicit" tree, then we report other stuff. + nsresult rv; size_t rtTotal = 0; - nsresult rv = - xpc::ReportJSRuntimeExplicitTreeStats(rtStats, explicitJs, cb, - closure, &rtTotal); + rv = xpc::ReportJSRuntimeExplicitTreeStats(rtStats, + NS_LITERAL_CSTRING("explicit/js-non-window/"), + cb, closure, &rtTotal); NS_ENSURE_SUCCESS(rv, rv); // Report the sums of the compartment numbers. @@ -1727,41 +1770,41 @@ public: // Report the sum of the runtime/ numbers. REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/runtime"), nsIMemoryReporter::KIND_OTHER, rtTotal, - "The sum of all measurements under 'explicit/js/runtime/'."); + "The sum of all measurements under 'explicit/js-non-window/runtime/'."); // Report the numbers for memory outside of compartments. REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/decommitted-arenas"), nsIMemoryReporter::KIND_OTHER, rtStats.gcHeapDecommittedArenas, - "The same as 'explicit/js/gc-heap/decommitted-arenas'."); + "The same as 'explicit/js-non-window/gc-heap/decommitted-arenas'."); REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/unused-chunks"), nsIMemoryReporter::KIND_OTHER, rtStats.gcHeapUnusedChunks, - "The same as 'explicit/js/gc-heap/unused-chunks'."); + "The same as 'explicit/js-non-window/gc-heap/unused-chunks'."); REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/unused-arenas"), nsIMemoryReporter::KIND_OTHER, rtStats.gcHeapUnusedArenas, - "The same as 'explicit/js/gc-heap/unused-arenas'."); + "The same as 'explicit/js-non-window/gc-heap/unused-arenas'."); REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/chunk-admin"), nsIMemoryReporter::KIND_OTHER, rtStats.gcHeapChunkAdmin, - "The same as 'explicit/js/gc-heap/chunk-admin'."); + "The same as 'explicit/js-non-window/gc-heap/chunk-admin'."); // Report a breakdown of the committed GC space. REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/chunks"), nsIMemoryReporter::KIND_OTHER, rtStats.gcHeapUnusedChunks, - "The same as 'explicit/js/gc-heap/unused-chunks'."); + "The same as 'explicit/js-non-window/gc-heap/unused-chunks'."); REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/arenas"), nsIMemoryReporter::KIND_OTHER, rtStats.gcHeapUnusedArenas, - "The same as 'explicit/js/gc-heap/unused-arenas'."); + "The same as 'explicit/js-non-window/gc-heap/unused-arenas'."); REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things"), nsIMemoryReporter::KIND_OTHER, @@ -1771,7 +1814,7 @@ public: REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/chunk-admin"), nsIMemoryReporter::KIND_OTHER, rtStats.gcHeapChunkAdmin, - "The same as 'explicit/js/gc-heap/chunk-admin'."); + "The same as 'explicit/js-non-window/gc-heap/chunk-admin'."); REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/arena-admin"), nsIMemoryReporter::KIND_OTHER, @@ -1793,18 +1836,15 @@ public: return NS_OK; } - NS_IMETHOD - GetExplicitNonHeap(PRInt64 *n) + nsresult + JSMemoryMultiReporter::GetExplicitNonHeap(PRInt64 *n) { JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime(); *reinterpret_cast(n) = JS::GetExplicitNonHeapForRuntime(rt, JsMallocSizeOf); return NS_OK; } -}; -NS_IMPL_THREADSAFE_ISUPPORTS1(JSMemoryMultiReporter - , nsIMemoryMultiReporter - ) +} // namespace xpc #ifdef MOZ_CRASHREPORTER static JSBool @@ -1956,7 +1996,6 @@ 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 JSMemoryMultiReporter); NS_RegisterMemoryMultiReporter(new JSCompartmentsMultiReporter); if (!JS_DHashTableInit(&mJSHolders, JS_DHashGetStubOps(), nsnull, diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h index 44ec80c7230d..d86b209df2f2 100644 --- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -266,7 +266,7 @@ DOM_DefineQuickStubs(JSContext *cx, JSObject *proto, PRUint32 flags, // (which isn't all of them). nsresult ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats, - const nsACString &pathPrefix, + const nsACString &rtPath, nsIMemoryMultiReporterCallback *cb, nsISupports *closure, size_t *rtTotal = NULL); diff --git a/toolkit/components/aboutmemory/tests/test_memoryReporters.xul b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul index d7c9a253d3cd..a50be2178866 100644 --- a/toolkit/components/aboutmemory/tests/test_memoryReporters.xul +++ b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul @@ -43,9 +43,9 @@ let heapAllocatedAmounts = []; let storageSqliteAmounts = []; - let areJsCompartmentsPresent = false; + let areJsNonWindowCompartmentsPresent = false; + let areWindowObjectsJsCompartmentsPresent = false; let isSandboxLocationShown = false; - let areWindowObjectsPresent = false; let isPlacesPresent = false; let isImagesPresent = false; let isXptiWorkingSetPresent = false; @@ -71,10 +71,10 @@ storageSqliteAmounts.push(aAmount); // Check the presence of some other notable reporters. - } else if (aPath.search(/^explicit\/js\/compartment\(/) >= 0) { - areJsCompartmentsPresent = true; - } else if (aPath.search(/^explicit\/window-objects\/top\(/) >= 0) { - areWindowObjectsPresent = true; + } else if (aPath.search(/^explicit\/js-non-window\/.*compartment\(/) >= 0) { + areJsNonWindowCompartmentsPresent = true; + } else if (aPath.search(/^explicit\/window-objects\/top\(.*\/js\/compartment\(/) >= 0) { + areWindowObjectsJsCompartmentsPresent = true; } else if (aPath.search(/^explicit\/storage\/sqlite\/places.sqlite/) >= 0) { isPlacesPresent = true; } else if (aPath.search(/^explicit\/images/) >= 0) { @@ -144,13 +144,13 @@ checkSpecialReport("js-main-runtime-gc-heap-committed/used/gc-things", jsGcHeapAmounts); checkSpecialReport("storage-sqlite", storageSqliteAmounts); - ok(areJsCompartmentsPresent, "js compartments are present"); - ok(isSandboxLocationShown, "sandbox locations are present"); - ok(areWindowObjectsPresent, "window objects are present"); - ok(isPlacesPresent, "places is present"); - ok(isImagesPresent, "images is present"); - ok(isXptiWorkingSetPresent, "xpti-working-set is present"); - ok(isAtomTablePresent, "atom-table is present"); + ok(areJsNonWindowCompartmentsPresent, "js-non-window compartments are present"); + ok(areWindowObjectsJsCompartmentsPresent, "window-objects/.../js compartments are present"); + ok(isSandboxLocationShown, "sandbox locations are present"); + ok(isPlacesPresent, "places is present"); + ok(isImagesPresent, "images is present"); + ok(isXptiWorkingSetPresent, "xpti-working-set is present"); + ok(isAtomTablePresent, "atom-table is present"); ]]>