--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -5,16 +5,17 @@
#include "nsWindowMemoryReporter.h"
#include "nsGlobalWindow.h"
#include "nsIEffectiveTLDService.h"
#include "mozilla/Services.h"
#include "mozilla/Preferences.h"
#include "nsNetCID.h"
#include "nsPrintfCString.h"
+#include "XPCJSMemoryReporter.h"
using namespace mozilla;
nsWindowMemoryReporter::nsWindowMemoryReporter()
: mCheckForGhostWindowsCallbackPending(false)
{
mDetachedWindows.Init();
}
@@ -96,20 +97,24 @@ AppendWindowURI(nsGlobalWindow *aWindow,
// If we're unable to find a URI, we're dealing with a chrome window with
// no document in it (or somesuch), so we call this a "system window".
aStr += NS_LITERAL_CSTRING("[system]");
}
}
NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(DOMStyleMallocSizeOf, "windows")
+// The key is the window ID.
+typedef nsDataHashtable<nsUint64HashKey, nsCString> WindowPaths;
+
static nsresult
CollectWindowReports(nsGlobalWindow *aWindow,
nsWindowSizes *aWindowTotalSizes,
nsTHashtable<nsUint64HashKey> *aGhostWindowIDs,
+ WindowPaths *aWindowPaths,
nsIMemoryMultiReporterCallback *aCb,
nsISupports *aClosure)
{
nsCAutoString windowPath("explicit/window-objects/");
// Avoid calling aWindow->GetTop() if there's no outer window. It will work
// just fine, but will spew a lot of warnings.
nsGlobalWindow *top = NULL;
@@ -135,16 +140,19 @@ CollectWindowReports(nsGlobalWindow *aWi
windowPath += NS_LITERAL_CSTRING("top(none)/detached/");
}
}
windowPath += NS_LITERAL_CSTRING("window(");
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) { \
nsCAutoString path(windowPath); \
path += _pathTail; \
nsresult rv; \
rv = aCb->Callback(EmptyCString(), path, nsIMemoryReporter::KIND_HEAP,\
nsIMemoryReporter::UNITS_BYTES, _amount, \
@@ -289,24 +297,33 @@ nsWindowMemoryReporter::CollectReports(n
nsTHashtable<nsUint64HashKey> ghostWindows;
ghostWindows.Init();
CheckForGhostWindows(&ghostWindows);
nsCOMPtr<nsIEffectiveTLDService> tldService = do_GetService(
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; \
rv = aCb->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
nsIMemoryReporter::KIND_OTHER, \
nsIMemoryReporter::UNITS_BYTES, _amount, \
NS_LITERAL_CSTRING(_desc), aClosure); \
NS_ENSURE_SUCCESS(rv, rv); \
@@ -387,19 +404,19 @@ nsWindowMemoryReporter::CollectReports(n
#undef REPORT
return NS_OK;
}
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
nsWindowMemoryReporter::GetGhostTimeout()
{
return Preferences::GetUint("memory.ghost_window_timeout_seconds", 60);
}
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -128,29 +128,37 @@ NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsW
struct WorkerJSRuntimeStats : public JS::RuntimeStats
{
WorkerJSRuntimeStats()
: JS::RuntimeStats(JsWorkerMallocSizeOf) { }
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<char *>(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");
}
};
class WorkerMemoryReporter MOZ_FINAL : public nsIMemoryMultiReporter
{
WorkerPrivate* mWorkerPrivate;
nsCString mAddressString;
- nsCString mPathPrefix;
+ nsCString mRtPath;
public:
NS_DECL_ISUPPORTS
WorkerMemoryReporter(WorkerPrivate* aWorkerPrivate)
: mWorkerPrivate(aWorkerPrivate)
{
aWorkerPrivate->AssertIsOnWorkerThread();
@@ -170,20 +178,20 @@ public:
mAddressString.Assign(address, addressSize);
}
else {
NS_WARNING("JS_snprintf failed!");
mAddressString.AssignLiteral("<unknown address>");
}
}
- 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
CollectForRuntime(bool aIsQuick, void* aData)
{
AssertIsOnMainThread();
if (mWorkerPrivate) {
@@ -227,17 +235,17 @@ public:
WorkerJSRuntimeStats rtStats;
nsresult rv = CollectForRuntime(/* isQuick = */false, &rtStats);
if (NS_FAILED(rv)) {
return rv;
}
// 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);
}
NS_IMETHOD
GetExplicitNonHeap(PRInt64 *aAmount)
{
AssertIsOnMainThread();
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -80,17 +80,19 @@ struct RuntimeSizes
};
struct CompartmentStats
{
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()!
size_t gcHeapArenaAdmin;
size_t gcHeapUnusedGcThings;
size_t gcHeapObjectsNonFunction;
size_t gcHeapObjectsFunction;
--- a/js/src/MemoryMetrics.cpp
+++ b/js/src/MemoryMetrics.cpp
@@ -191,17 +191,17 @@ CollectRuntimeStats(JSRuntime *rt, Runti
// This just computes rtStats->gcHeapDecommittedArenas.
IterateChunks(rt, rtStats, StatsChunkCallback);
// Take the per-compartment measurements.
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;
for (size_t i = 0; i < rtStats->compartmentStatsVector.length(); i++) {
CompartmentStats &cStats = rtStats->compartmentStatsVector[i];
rtStats->totals.add(cStats);
rtStats->gcHeapGcThings += cStats.gcHeapThingsSize();
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -225,16 +225,28 @@ AssertHeapIsIdle(JSRuntime *rt)
static void
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)
{
/*
* We allow some functions to be called during a GC as long as the argument
* is a flat string, since that will not cause allocation.
*/
JS_ASSERT_IF(cx->runtime->isHeapBusy(), str->isFlat());
}
@@ -2163,16 +2175,24 @@ JS_PUBLIC_API(JSObject *)
JS_GetGlobalForObject(JSContext *cx, JSObject *obj)
{
AssertHeapIsIdle(cx);
assertSameCompartment(cx, 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)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
return GetGlobalForScopeChain(cx);
}
JS_PUBLIC_API(jsval)
@@ -5561,25 +5581,25 @@ JS_IsRunning(JSContext *cx)
while (fp && fp->isDummyFrame())
fp = fp->prev();
return fp != NULL;
}
JS_PUBLIC_API(JSBool)
JS_SaveFrameChain(JSContext *cx)
{
- AssertHeapIsIdle(cx);
+ AssertHeapIsIdleOrIterating(cx);
CHECK_REQUEST(cx);
return cx->stack.saveFrameChain();
}
JS_PUBLIC_API(void)
JS_RestoreFrameChain(JSContext *cx)
{
- AssertHeapIsIdle(cx);
+ AssertHeapIsIdleOrIterating(cx);
CHECK_REQUEST(cx);
cx->stack.restoreFrameChain();
}
#ifdef MOZ_TRACE_JSCALLS
JS_PUBLIC_API(void)
JS_SetFunctionCallback(JSContext *cx, JSFunctionCallback fcb)
{
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3048,16 +3048,23 @@ JS_GetFunctionPrototype(JSContext *cx, J
* which |forObj| was created.
*/
extern JS_PUBLIC_API(JSObject *)
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);
extern JS_PUBLIC_API(JSObject *)
JS_GetScriptedGlobal(JSContext *cx);
/*
* Initialize the 'Reflect' object on a global object.
--- a/js/xpconnect/src/Makefile.in
+++ b/js/xpconnect/src/Makefile.in
@@ -14,16 +14,17 @@ MODULE = xpconnect
LIBRARY_NAME = xpconnect_s
FORCE_STATIC_LIB = 1
LIBXUL_LIBRARY = 1
EXPORTS = \
qsObjectHelper.h \
xpcObjectHelper.h \
xpcpublic.h \
+ XPCJSMemoryReporter.h \
dombindings.h \
dombindings_gen.h
CPPSRCS = \
nsScriptError.cpp \
nsXPConnect.cpp \
XPCCallContext.cpp \
XPCComponents.cpp \
new file mode 100644
--- /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<nsUint64HashKey, nsCString> 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
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -5,20 +5,22 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Per JSRuntime object */
#include "mozilla/Util.h"
#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"
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
#include "nsContentUtils.h"
#include "nsCCUncollectableMarker.h"
@@ -1281,16 +1283,18 @@ static const size_t SUNDRIES_THRESHOLD =
nsresult rv; \
rv = cb->Callback(EmptyCString(), _path, _kind, \
nsIMemoryReporter::UNITS_BYTES, amount, \
NS_LITERAL_CSTRING(_desc), closure); \
NS_ENSURE_SUCCESS(rv, rv); \
rtTotal += amount; \
} while (0)
+NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsMallocSizeOf, "js")
+
namespace xpc {
static nsresult
ReportCompartmentStats(const JS::CompartmentStats &cStats,
const nsACString &pathPrefix,
nsIMemoryMultiReporterCallback *cb,
nsISupports *closure, size_t *gcTotalOut = NULL)
{
@@ -1467,142 +1471,150 @@ ReportCompartmentStats(const JS::Compart
*gcTotalOut += gcTotal;
}
return NS_OK;
}
nsresult
ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats,
- const nsACString &pathPrefix,
+ const nsACString &rtPath,
nsIMemoryMultiReporterCallback *cb,
nsISupports *closure, size_t *rtTotalOut)
{
nsresult rv;
// Report each compartment's numbers.
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<char *>(cStats.extra);
- nsCString pathPrefix2 = pathPrefix + NS_LITERAL_CSTRING("compartment(") +
- nsDependentCString(name) + NS_LITERAL_CSTRING(")/");
+ const char *cPathPrefix = static_cast<char *>(cStats.extra1);
+ const char *cName = static_cast<char *>(cStats.extra2);
- rv = ReportCompartmentStats(cStats, pathPrefix2, cb, closure, &gcTotal);
+ // If cPathPrefix is NULL, cPath is "<rtPath>compartment(<cName>)/"
+ // otherwise, cPath is "<cPathPrefix>compartment(<cName>)/"
+ 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);
}
// Report the rtStats.runtime numbers under "runtime/", and compute their
// total for later.
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 "
"on a per-compartment basis.");
if (rtTotalOut) {
*rtTotalOut = rtTotal;
}
// 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 "
"information.");
// gcTotal is the sum of everything we've reported for the GC heap. It
// should equal rtStats.gcHeapChunkTotal.
JS_ASSERT(gcTotal == rtStats.gcHeapChunkTotal);
return NS_OK;
}
} // namespace xpc
-NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsMallocSizeOf, "js")
-
class JSCompartmentsMultiReporter MOZ_FINAL : public nsIMemoryMultiReporter
{
public:
NS_DECL_ISUPPORTS
NS_IMETHOD GetName(nsACString &name)
{
name.AssignLiteral("compartments");
@@ -1656,127 +1668,158 @@ class JSCompartmentsMultiReporter MOZ_FI
return NS_OK;
}
};
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<nsPIDOMWindow> 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();
// 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.
- XPCJSRuntimeStats rtStats;
+
+ XPCJSRuntimeStats rtStats(windowPaths);
+ if (!rtStats.init(xpcrt))
+ return NS_ERROR_FAILURE;
+
if (!JS::CollectRuntimeStats(xpcrt->GetJSRuntime(), &rtStats))
return NS_ERROR_FAILURE;
size_t xpconnect =
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.
rv = ReportCompartmentStats(rtStats.totals,
NS_LITERAL_CSTRING("js-main-runtime/compartments/"),
cb, closure);
NS_ENSURE_SUCCESS(rv, rv);
// 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,
rtStats.totals.gcHeapUnusedGcThings,
"The same as 'js-main-runtime/compartments/gc-heap/unused-gc-things'.");
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,
rtStats.totals.gcHeapArenaAdmin,
"The same as 'js-main-runtime/compartments/gc-heap/arena-admin'.");
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things"),
nsIMemoryReporter::KIND_OTHER,
@@ -1788,28 +1831,25 @@ public:
REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect"),
nsIMemoryReporter::KIND_HEAP, xpconnect,
"Memory used by XPConnect.");
return NS_OK;
}
- NS_IMETHOD
- GetExplicitNonHeap(PRInt64 *n)
+ nsresult
+ JSMemoryMultiReporter::GetExplicitNonHeap(PRInt64 *n)
{
JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
*reinterpret_cast<int64_t*>(n) = JS::GetExplicitNonHeapForRuntime(rt, JsMallocSizeOf);
return NS_OK;
}
-};
-NS_IMPL_THREADSAFE_ISUPPORTS1(JSMemoryMultiReporter
- , nsIMemoryMultiReporter
- )
+} // namespace xpc
#ifdef MOZ_CRASHREPORTER
static JSBool
DiagnosticMemoryCallback(void *ptr, size_t size)
{
return CrashReporter::RegisterAppMemory(ptr, size) == NS_OK;
}
#endif
@@ -1951,17 +1991,16 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect*
JS_EnumerateDiagnosticMemoryRegions(DiagnosticMemoryCallback);
#endif
JS_SetAccumulateTelemetryCallback(mJSRuntime, AccumulateTelemetryCallback);
js::SetActivityCallback(mJSRuntime, ActivityCallback, this);
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,
sizeof(ObjectHolder), 512))
mJSHolders.ops = nsnull;
mCompartmentSet.init();
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -261,17 +261,17 @@ void SetLocationForGlobal(JSObject *glob
bool
DOM_DefineQuickStubs(JSContext *cx, JSObject *proto, PRUint32 flags,
PRUint32 interfaceCount, const nsIID **interfaceArray);
// This reports all the stats in |rtStats| that belong in the "explicit" tree,
// (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);
/**
* Convert a jsval to PRInt64. Return true on success.
*/
inline bool
ValueToInt64(JSContext *cx, JS::Value v, int64_t *result)
--- a/toolkit/components/aboutmemory/tests/test_memoryReporters.xul
+++ b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul
@@ -38,19 +38,19 @@
let explicitAmounts = [];
let vsizeAmounts = [];
let residentAmounts = [];
let jsGcHeapAmounts = [];
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;
let isAtomTablePresent = false;
let mySandbox = Components.utils.Sandbox(document.nodePrincipal,
{ sandboxName: "this-is-a-sandbox-name" });
@@ -66,20 +66,20 @@
} else if (aPath === "js-main-runtime-gc-heap-committed/used/gc-things") {
jsGcHeapAmounts.push(aAmount);
} else if (aPath === "heap-allocated") {
heapAllocatedAmounts.push(aAmount);
} else if (aPath === "storage-sqlite") {
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) {
isImagesPresent = true;
} else if (aPath.search(/^explicit\/xpti-working-set$/) >= 0) {
isXptiWorkingSetPresent = true;
} else if (aPath.search(/^explicit\/atom-tables$/) >= 0) {
isAtomTablePresent = true;
@@ -139,20 +139,20 @@
checkSpecialReport("explicit", explicitAmounts);
checkSpecialReport("heap-allocated", heapAllocatedAmounts);
}
checkSpecialReport("vsize", vsizeAmounts);
checkSpecialReport("resident", residentAmounts);
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");
]]>
</script>
</window>