--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -3,16 +3,17 @@
/* 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/. */
#include "amIAddonManager.h"
#include "nsWindowMemoryReporter.h"
#include "nsGlobalWindow.h"
#include "nsIDocument.h"
+#include "nsIDOMWindowCollection.h"
#include "nsIEffectiveTLDService.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "nsNetCID.h"
#include "nsPrintfCString.h"
#include "XPCJSMemoryReporter.h"
@@ -26,24 +27,80 @@ StaticRefPtr<nsWindowMemoryReporter> sWi
nsWindowMemoryReporter::nsWindowMemoryReporter()
: mCheckForGhostWindowsCallbackPending(false)
{
}
NS_IMPL_ISUPPORTS3(nsWindowMemoryReporter, nsIMemoryReporter, nsIObserver,
nsSupportsWeakReference)
-/* static */
-void
+static nsresult
+AddNonJSSizeOfWindowAndItsDescendents(nsGlobalWindow* aWindow,
+ nsTabSizes* aSizes)
+{
+ // Measure the window.
+ nsWindowSizes windowSizes(moz_malloc_size_of);
+ aWindow->AddSizeOfIncludingThis(&windowSizes);
+ windowSizes.addToTabSizes(aSizes);
+
+ // Measure the inner window, if there is one.
+ nsWindowSizes innerWindowSizes(moz_malloc_size_of);
+ nsGlobalWindow* inner = aWindow->GetCurrentInnerWindowInternal();
+ if (inner) {
+ inner->AddSizeOfIncludingThis(&innerWindowSizes);
+ innerWindowSizes.addToTabSizes(aSizes);
+ }
+
+ nsCOMPtr<nsIDOMWindowCollection> frames;
+ nsresult rv = aWindow->GetFrames(getter_AddRefs(frames));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t length;
+ rv = frames->GetLength(&length);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Measure this window's descendents.
+ for (uint32_t i = 0; i < length; i++) {
+ nsCOMPtr<nsIDOMWindow> child;
+ rv = frames->Item(i, getter_AddRefs(child));
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_STATE(child);
+
+ nsGlobalWindow* childWin =
+ static_cast<nsGlobalWindow*>(static_cast<nsIDOMWindow *>(child.get()));
+
+ rv = AddNonJSSizeOfWindowAndItsDescendents(childWin, aSizes);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ return NS_OK;
+}
+
+static nsresult
+NonJSSizeOfTab(nsPIDOMWindow* aWindow, size_t* aDomSize, size_t* aStyleSize, size_t* aOtherSize)
+{
+ nsGlobalWindow* window = static_cast<nsGlobalWindow*>(aWindow);
+
+ nsTabSizes sizes;
+ nsresult rv = AddNonJSSizeOfWindowAndItsDescendents(window, &sizes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aDomSize = sizes.mDom;
+ *aStyleSize = sizes.mStyle;
+ *aOtherSize = sizes.mOther;
+ return NS_OK;
+}
+
+/* static */ void
nsWindowMemoryReporter::Init()
{
MOZ_ASSERT(!sWindowReporter);
sWindowReporter = new nsWindowMemoryReporter();
ClearOnShutdown(&sWindowReporter);
NS_RegisterMemoryReporter(sWindowReporter);
+ RegisterNonJSSizeOfTab(NonJSSizeOfTab);
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) {
// DOM_WINDOW_DESTROYED_TOPIC announces what we call window "detachment",
// when a window's docshell is set to NULL.
os->AddObserver(sWindowReporter, DOM_WINDOW_DESTROYED_TOPIC,
/* weakRef = */ true);
os->AddObserver(sWindowReporter, "after-minimize-memory-usage",
--- a/dom/base/nsWindowMemoryReporter.h
+++ b/dom/base/nsWindowMemoryReporter.h
@@ -7,48 +7,69 @@
#ifndef nsWindowMemoryReporter_h__
#define nsWindowMemoryReporter_h__
#include "nsIMemoryReporter.h"
#include "nsIObserver.h"
#include "nsDataHashtable.h"
#include "nsWeakReference.h"
#include "nsAutoPtr.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Assertions.h"
#include "mozilla/MemoryReporting.h"
+#include "mozilla/PodOperations.h"
#include "mozilla/TimeStamp.h"
#include "nsArenaMemoryStats.h"
-#include "mozilla/Attributes.h"
// This should be used for any nsINode sub-class that has fields of its own
// that it needs to measure; any sub-class that doesn't use it will inherit
// SizeOfExcludingThis from its super-class. SizeOfIncludingThis() need not be
// defined, it is inherited from nsINode.
#define NS_DECL_SIZEOF_EXCLUDING_THIS \
virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
class nsWindowSizes {
+#define FOR_EACH_SIZE(macro) \
+ macro(DOM, mDOMElementNodes) \
+ macro(DOM, mDOMTextNodes) \
+ macro(DOM, mDOMCDATANodes) \
+ macro(DOM, mDOMCommentNodes) \
+ macro(DOM, mDOMEventTargets) \
+ macro(DOM, mDOMOther) \
+ macro(Style, mStyleSheets) \
+ macro(Other, mLayoutPresShell) \
+ macro(Style, mLayoutStyleSets) \
+ macro(Other, mLayoutTextRuns) \
+ macro(Other, mLayoutPresContext) \
+ macro(Other, mPropertyTables) \
+
public:
- nsWindowSizes(mozilla::MallocSizeOf aMallocSizeOf) {
- memset(this, 0, sizeof(nsWindowSizes));
- mMallocSizeOf = aMallocSizeOf;
+ nsWindowSizes(mozilla::MallocSizeOf aMallocSizeOf)
+ :
+ #define ZERO_SIZE(kind, mSize) mSize(0),
+ FOR_EACH_SIZE(ZERO_SIZE)
+ #undef ZERO_SIZE
+ mArenaStats(),
+ mMallocSizeOf(aMallocSizeOf)
+ {}
+
+ void addToTabSizes(nsTabSizes *sizes) const {
+ #define ADD_TO_TAB_SIZES(kind, mSize) sizes->add(nsTabSizes::kind, mSize);
+ FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
+ #undef ADD_TO_TAB_SIZES
+ mArenaStats.addToTabSizes(sizes);
}
- mozilla::MallocSizeOf mMallocSizeOf;
+
+ #define DECL_SIZE(kind, mSize) size_t mSize;
+ FOR_EACH_SIZE(DECL_SIZE);
+ #undef DECL_SIZE
nsArenaMemoryStats mArenaStats;
- size_t mDOMElementNodes;
- size_t mDOMTextNodes;
- size_t mDOMCDATANodes;
- size_t mDOMCommentNodes;
- size_t mDOMEventTargets;
- size_t mDOMOther;
- size_t mStyleSheets;
- size_t mLayoutPresShell;
- size_t mLayoutStyleSets;
- size_t mLayoutTextRuns;
- size_t mLayoutPresContext;
- size_t mPropertyTables;
+ mozilla::MallocSizeOf mMallocSizeOf;
+
+#undef FOR_EACH_SIZE
};
/**
* nsWindowMemoryReporter is responsible for the 'explicit/window-objects'
* memory reporter.
*
* We classify DOM window objects into one of three categories:
*
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -20,16 +20,47 @@
#include "jspubtd.h"
#include "js/HashTable.h"
#include "js/Utility.h"
#include "js/Vector.h"
class nsISupports; // Needed for ObjectPrivateVisitor.
+namespace JS {
+
+struct TabSizes
+{
+ enum Kind {
+ Objects,
+ Strings,
+ Private,
+ Other
+ };
+
+ TabSizes() { mozilla::PodZero(this); }
+
+ void add(Kind kind, size_t n) {
+ switch (kind) {
+ case Objects: objects += n; break;
+ case Strings: strings += n; break;
+ case Private: private_ += n; break;
+ case Other: other += n; break;
+ default: MOZ_CRASH("bad TabSizes kind");
+ }
+ }
+
+ size_t objects;
+ size_t strings;
+ size_t private_;
+ size_t other;
+};
+
+} // namespace JS
+
namespace js {
// In memory reporting, we have concept of "sundries", line items which are too
// small to be worth reporting individually. Under some circumstances, a memory
// reporter gets tossed into the sundries bucket if it's smaller than
// MemoryReportingSundriesThreshold() bytes.
//
// We need to define this value here, rather than in the code which actually
@@ -52,42 +83,43 @@ struct InefficientNonFlatteningStringHas
// without updating all the required methods. So we define a single macro list
// in each class to name the fields (and notable characteristics of them), and
// then use the following macros to transform those lists into the required
// methods.
//
// In some classes, one or more of the macro arguments aren't used. We use '_'
// for those.
//
-#define DECL_SIZE(gc, mSize) size_t mSize;
-#define ZERO_SIZE(gc, mSize) mSize(0),
-#define COPY_OTHER_SIZE(gc, mSize) mSize(other.mSize),
-#define ADD_OTHER_SIZE(gc, mSize) mSize += other.mSize;
-#define ADD_SIZE_TO_N_IF_LIVE_GC_THING(gc, mSize) n += (gc) ? mSize : 0;
+#define DECL_SIZE(kind, gc, mSize) size_t mSize;
+#define ZERO_SIZE(kind, gc, mSize) mSize(0),
+#define COPY_OTHER_SIZE(kind, gc, mSize) mSize(other.mSize),
+#define ADD_OTHER_SIZE(kind, gc, mSize) mSize += other.mSize;
+#define ADD_SIZE_TO_N_IF_LIVE_GC_THING(kind, gc, mSize) n += (js::gc == js::IsLiveGCThing) ? mSize : 0;
+#define ADD_TO_TAB_SIZES(kind, gc, mSize) sizes->add(JS::TabSizes::kind, mSize);
// Used to annotate which size_t fields measure live GC things and which don't.
enum {
NotLiveGCThing = false,
IsLiveGCThing = true
};
struct ZoneStatsPod
{
#define FOR_EACH_SIZE(macro) \
- macro(NotLiveGCThing, gcHeapArenaAdmin) \
- macro(NotLiveGCThing, unusedGCThings) \
- macro(IsLiveGCThing, lazyScriptsGCHeap) \
- macro(NotLiveGCThing, lazyScriptsMallocHeap) \
- macro(IsLiveGCThing, ionCodesGCHeap) \
- macro(IsLiveGCThing, typeObjectsGCHeap) \
- macro(NotLiveGCThing, typeObjectsMallocHeap) \
- macro(NotLiveGCThing, typePool) \
- macro(IsLiveGCThing, stringsShortGCHeap) \
- macro(IsLiveGCThing, stringsNormalGCHeap) \
- macro(NotLiveGCThing, stringsNormalMallocHeap)
+ macro(Other, NotLiveGCThing, gcHeapArenaAdmin) \
+ macro(Other, NotLiveGCThing, unusedGCThings) \
+ macro(Other, IsLiveGCThing, lazyScriptsGCHeap) \
+ macro(Other, NotLiveGCThing, lazyScriptsMallocHeap) \
+ macro(Other, IsLiveGCThing, ionCodesGCHeap) \
+ macro(Other, IsLiveGCThing, typeObjectsGCHeap) \
+ macro(Other, NotLiveGCThing, typeObjectsMallocHeap) \
+ macro(Other, NotLiveGCThing, typePool) \
+ macro(Strings, IsLiveGCThing, stringsShortGCHeap) \
+ macro(Strings, IsLiveGCThing, stringsNormalGCHeap) \
+ macro(Strings, NotLiveGCThing, stringsNormalMallocHeap)
ZoneStatsPod()
: FOR_EACH_SIZE(ZERO_SIZE)
extra()
{}
void add(const ZoneStatsPod &other) {
FOR_EACH_SIZE(ADD_OTHER_SIZE)
@@ -96,71 +128,80 @@ struct ZoneStatsPod
size_t sizeOfLiveGCThings() const {
size_t n = 0;
FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
// Do nothing with |extra|.
return n;
}
+ void addToTabSizes(JS::TabSizes *sizes) const {
+ FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
+ // Do nothing with |extra|.
+ }
+
FOR_EACH_SIZE(DECL_SIZE)
void *extra; // This field can be used by embedders.
#undef FOR_EACH_SIZE
};
} // namespace js
namespace JS {
// Data for tracking memory usage of things hanging off objects.
struct ObjectsExtraSizes
{
#define FOR_EACH_SIZE(macro) \
- macro(js::NotLiveGCThing, mallocHeapSlots) \
- macro(js::NotLiveGCThing, mallocHeapElementsNonAsmJS) \
- macro(js::NotLiveGCThing, mallocHeapElementsAsmJS) \
- macro(js::NotLiveGCThing, nonHeapElementsAsmJS) \
- macro(js::NotLiveGCThing, nonHeapCodeAsmJS) \
- macro(js::NotLiveGCThing, mallocHeapAsmJSModuleData) \
- macro(js::NotLiveGCThing, mallocHeapArgumentsData) \
- macro(js::NotLiveGCThing, mallocHeapRegExpStatics) \
- macro(js::NotLiveGCThing, mallocHeapPropertyIteratorData) \
- macro(js::NotLiveGCThing, mallocHeapCtypesData)
+ macro(Objects, NotLiveGCThing, mallocHeapSlots) \
+ macro(Objects, NotLiveGCThing, mallocHeapElementsNonAsmJS) \
+ macro(Objects, NotLiveGCThing, mallocHeapElementsAsmJS) \
+ macro(Objects, NotLiveGCThing, nonHeapElementsAsmJS) \
+ macro(Objects, NotLiveGCThing, nonHeapCodeAsmJS) \
+ macro(Objects, NotLiveGCThing, mallocHeapAsmJSModuleData) \
+ macro(Objects, NotLiveGCThing, mallocHeapArgumentsData) \
+ macro(Objects, NotLiveGCThing, mallocHeapRegExpStatics) \
+ macro(Objects, NotLiveGCThing, mallocHeapPropertyIteratorData) \
+ macro(Objects, NotLiveGCThing, mallocHeapCtypesData)
ObjectsExtraSizes()
: FOR_EACH_SIZE(ZERO_SIZE)
dummy()
{}
void add(const ObjectsExtraSizes &other) {
FOR_EACH_SIZE(ADD_OTHER_SIZE)
}
size_t sizeOfLiveGCThings() const {
size_t n = 0;
FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
return n;
}
+ void addToTabSizes(TabSizes *sizes) const {
+ FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
+ }
+
FOR_EACH_SIZE(DECL_SIZE)
int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
#undef FOR_EACH_SIZE
};
// Data for tracking JIT-code memory usage.
struct CodeSizes
{
#define FOR_EACH_SIZE(macro) \
- macro(_, ion) \
- macro(_, baseline) \
- macro(_, regexp) \
- macro(_, other) \
- macro(_, unused)
+ macro(_, _, ion) \
+ macro(_, _, baseline) \
+ macro(_, _, regexp) \
+ macro(_, _, other) \
+ macro(_, _, unused)
CodeSizes()
: FOR_EACH_SIZE(ZERO_SIZE)
dummy()
{}
FOR_EACH_SIZE(DECL_SIZE)
int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
@@ -251,27 +292,27 @@ struct NotableStringInfo : public String
char *buffer;
};
// These measurements relate directly to the JSRuntime, and not to zones and
// compartments within it.
struct RuntimeSizes
{
#define FOR_EACH_SIZE(macro) \
- macro(_, object) \
- macro(_, atomsTable) \
- macro(_, contexts) \
- macro(_, dtoa) \
- macro(_, temporary) \
- macro(_, regexpData) \
- macro(_, interpreterStack) \
- macro(_, gcMarker) \
- macro(_, mathCache) \
- macro(_, scriptData) \
- macro(_, scriptSources)
+ macro(_, _, object) \
+ macro(_, _, atomsTable) \
+ macro(_, _, contexts) \
+ macro(_, _, dtoa) \
+ macro(_, _, temporary) \
+ macro(_, _, regexpData) \
+ macro(_, _, interpreterStack) \
+ macro(_, _, gcMarker) \
+ macro(_, _, mathCache) \
+ macro(_, _, scriptData) \
+ macro(_, _, scriptSources)
RuntimeSizes()
: FOR_EACH_SIZE(ZERO_SIZE)
code()
{}
FOR_EACH_SIZE(DECL_SIZE)
CodeSizes code;
@@ -330,45 +371,45 @@ struct ZoneStats : js::ZoneStatsPod
StringsHashMap strings;
js::Vector<NotableStringInfo, 0, js::SystemAllocPolicy> notableStrings;
};
struct CompartmentStats
{
#define FOR_EACH_SIZE(macro) \
- macro(js::IsLiveGCThing, objectsGCHeapOrdinary) \
- macro(js::IsLiveGCThing, objectsGCHeapFunction) \
- macro(js::IsLiveGCThing, objectsGCHeapDenseArray) \
- macro(js::IsLiveGCThing, objectsGCHeapSlowArray) \
- macro(js::IsLiveGCThing, objectsGCHeapCrossCompartmentWrapper) \
- macro(js::NotLiveGCThing, objectsPrivate) \
- macro(js::IsLiveGCThing, shapesGCHeapTreeGlobalParented) \
- macro(js::IsLiveGCThing, shapesGCHeapTreeNonGlobalParented) \
- macro(js::IsLiveGCThing, shapesGCHeapDict) \
- macro(js::IsLiveGCThing, shapesGCHeapBase) \
- macro(js::NotLiveGCThing, shapesMallocHeapTreeTables) \
- macro(js::NotLiveGCThing, shapesMallocHeapDictTables) \
- macro(js::NotLiveGCThing, shapesMallocHeapTreeShapeKids) \
- macro(js::NotLiveGCThing, shapesMallocHeapCompartmentTables) \
- macro(js::IsLiveGCThing, scriptsGCHeap) \
- macro(js::NotLiveGCThing, scriptsMallocHeapData) \
- macro(js::NotLiveGCThing, baselineData) \
- macro(js::NotLiveGCThing, baselineStubsFallback) \
- macro(js::NotLiveGCThing, baselineStubsOptimized) \
- macro(js::NotLiveGCThing, ionData) \
- macro(js::NotLiveGCThing, typeInferenceTypeScripts) \
- macro(js::NotLiveGCThing, typeInferencePendingArrays) \
- macro(js::NotLiveGCThing, typeInferenceAllocationSiteTables) \
- macro(js::NotLiveGCThing, typeInferenceArrayTypeTables) \
- macro(js::NotLiveGCThing, typeInferenceObjectTypeTables) \
- macro(js::NotLiveGCThing, compartmentObject) \
- macro(js::NotLiveGCThing, crossCompartmentWrappersTable) \
- macro(js::NotLiveGCThing, regexpCompartment) \
- macro(js::NotLiveGCThing, debuggeesSet)
+ macro(Objects, IsLiveGCThing, objectsGCHeapOrdinary) \
+ macro(Objects, IsLiveGCThing, objectsGCHeapFunction) \
+ macro(Objects, IsLiveGCThing, objectsGCHeapDenseArray) \
+ macro(Objects, IsLiveGCThing, objectsGCHeapSlowArray) \
+ macro(Objects, IsLiveGCThing, objectsGCHeapCrossCompartmentWrapper) \
+ macro(Private, NotLiveGCThing, objectsPrivate) \
+ macro(Other, IsLiveGCThing, shapesGCHeapTreeGlobalParented) \
+ macro(Other, IsLiveGCThing, shapesGCHeapTreeNonGlobalParented) \
+ macro(Other, IsLiveGCThing, shapesGCHeapDict) \
+ macro(Other, IsLiveGCThing, shapesGCHeapBase) \
+ macro(Other, NotLiveGCThing, shapesMallocHeapTreeTables) \
+ macro(Other, NotLiveGCThing, shapesMallocHeapDictTables) \
+ macro(Other, NotLiveGCThing, shapesMallocHeapTreeShapeKids) \
+ macro(Other, NotLiveGCThing, shapesMallocHeapCompartmentTables) \
+ macro(Other, IsLiveGCThing, scriptsGCHeap) \
+ macro(Other, NotLiveGCThing, scriptsMallocHeapData) \
+ macro(Other, NotLiveGCThing, baselineData) \
+ macro(Other, NotLiveGCThing, baselineStubsFallback) \
+ macro(Other, NotLiveGCThing, baselineStubsOptimized) \
+ macro(Other, NotLiveGCThing, ionData) \
+ macro(Other, NotLiveGCThing, typeInferenceTypeScripts) \
+ macro(Other, NotLiveGCThing, typeInferencePendingArrays) \
+ macro(Other, NotLiveGCThing, typeInferenceAllocationSiteTables) \
+ macro(Other, NotLiveGCThing, typeInferenceArrayTypeTables) \
+ macro(Other, NotLiveGCThing, typeInferenceObjectTypeTables) \
+ macro(Other, NotLiveGCThing, compartmentObject) \
+ macro(Other, NotLiveGCThing, crossCompartmentWrappersTable) \
+ macro(Other, NotLiveGCThing, regexpCompartment) \
+ macro(Other, NotLiveGCThing, debuggeesSet)
CompartmentStats()
: FOR_EACH_SIZE(ZERO_SIZE)
objectsExtra(),
extra()
{}
CompartmentStats(const CompartmentStats &other)
@@ -386,32 +427,38 @@ struct CompartmentStats
size_t sizeOfLiveGCThings() const {
size_t n = 0;
FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
n += objectsExtra.sizeOfLiveGCThings();
// Do nothing with |extra|.
return n;
}
+ void addToTabSizes(TabSizes *sizes) const {
+ FOR_EACH_SIZE(ADD_TO_TAB_SIZES);
+ objectsExtra.addToTabSizes(sizes);
+ // Do nothing with |extra|.
+ }
+
FOR_EACH_SIZE(DECL_SIZE)
ObjectsExtraSizes objectsExtra;
void *extra; // This field can be used by embedders.
#undef FOR_EACH_SIZE
};
struct RuntimeStats
{
#define FOR_EACH_SIZE(macro) \
- macro(_, gcHeapChunkTotal) \
- macro(_, gcHeapDecommittedArenas) \
- macro(_, gcHeapUnusedChunks) \
- macro(_, gcHeapUnusedArenas) \
- macro(_, gcHeapChunkAdmin) \
- macro(_, gcHeapGCThings) \
+ macro(_, _, gcHeapChunkTotal) \
+ macro(_, _, gcHeapDecommittedArenas) \
+ macro(_, _, gcHeapUnusedChunks) \
+ macro(_, _, gcHeapUnusedArenas) \
+ macro(_, _, gcHeapChunkAdmin) \
+ macro(_, _, gcHeapGCThings) \
RuntimeStats(mozilla::MallocSizeOf mallocSizeOf)
: FOR_EACH_SIZE(ZERO_SIZE)
runtime(),
cTotals(),
zTotals(),
compartmentStatsVector(),
zoneStatsVector(),
@@ -483,11 +530,22 @@ extern JS_PUBLIC_API(size_t)
SystemCompartmentCount(JSRuntime *rt);
extern JS_PUBLIC_API(size_t)
UserCompartmentCount(JSRuntime *rt);
extern JS_PUBLIC_API(size_t)
PeakSizeOfTemporary(const JSRuntime *rt);
+extern JS_PUBLIC_API(bool)
+AddSizeOfTab(JSRuntime *rt, JSObject *obj, mozilla::MallocSizeOf mallocSizeOf,
+ ObjectPrivateVisitor *opv, TabSizes *sizes);
+
} // namespace JS
+#undef DECL_SIZE
+#undef ZERO_SIZE
+#undef COPY_OTHER_SIZE
+#undef ADD_OTHER_SIZE
+#undef ADD_SIZE_TO_N_IF_LIVE_GC_THING
+#undef ADD_TO_TAB_SIZES
+
#endif /* js_MemoryMetrics_h */
--- a/js/src/gc/Iteration.cpp
+++ b/js/src/gc/Iteration.cpp
@@ -21,43 +21,66 @@ void
js::TraceRuntime(JSTracer *trc)
{
JS_ASSERT(!IS_GC_MARKING_TRACER(trc));
AutoPrepareForTracing prep(trc->runtime);
MarkRuntime(trc);
}
+static void
+IterateCompartmentsArenasCells(JSRuntime *rt, Zone *zone, void *data,
+ JSIterateCompartmentCallback compartmentCallback,
+ IterateArenaCallback arenaCallback,
+ IterateCellCallback cellCallback)
+{
+ for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
+ (*compartmentCallback)(rt, data, comp);
+
+ for (size_t thingKind = 0; thingKind != FINALIZE_LIMIT; thingKind++) {
+ JSGCTraceKind traceKind = MapAllocToTraceKind(AllocKind(thingKind));
+ size_t thingSize = Arena::thingSize(AllocKind(thingKind));
+
+ for (ArenaIter aiter(zone, AllocKind(thingKind)); !aiter.done(); aiter.next()) {
+ ArenaHeader *aheader = aiter.get();
+ (*arenaCallback)(rt, data, aheader->getArena(), traceKind, thingSize);
+ for (CellIterUnderGC iter(aheader); !iter.done(); iter.next())
+ (*cellCallback)(rt, data, iter.getCell(), traceKind, thingSize);
+ }
+ }
+}
+
void
js::IterateZonesCompartmentsArenasCells(JSRuntime *rt, void *data,
IterateZoneCallback zoneCallback,
JSIterateCompartmentCallback compartmentCallback,
IterateArenaCallback arenaCallback,
IterateCellCallback cellCallback)
{
AutoPrepareForTracing prop(rt);
for (ZonesIter zone(rt); !zone.done(); zone.next()) {
(*zoneCallback)(rt, data, zone);
-
- for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
- (*compartmentCallback)(rt, data, comp);
-
- for (size_t thingKind = 0; thingKind != FINALIZE_LIMIT; thingKind++) {
- JSGCTraceKind traceKind = MapAllocToTraceKind(AllocKind(thingKind));
- size_t thingSize = Arena::thingSize(AllocKind(thingKind));
+ IterateCompartmentsArenasCells(rt, zone, data,
+ compartmentCallback, arenaCallback, cellCallback);
+ }
+}
- for (ArenaIter aiter(zone, AllocKind(thingKind)); !aiter.done(); aiter.next()) {
- ArenaHeader *aheader = aiter.get();
- (*arenaCallback)(rt, data, aheader->getArena(), traceKind, thingSize);
- for (CellIterUnderGC iter(aheader); !iter.done(); iter.next())
- (*cellCallback)(rt, data, iter.getCell(), traceKind, thingSize);
- }
- }
- }
+void
+js::IterateZoneCompartmentsArenasCells(JSRuntime *rt, Zone *zone, void *data,
+ IterateZoneCallback zoneCallback,
+ JSIterateCompartmentCallback compartmentCallback,
+ IterateArenaCallback arenaCallback,
+ IterateCellCallback cellCallback)
+{
+ AutoPrepareForTracing prop(rt);
+
+ (*zoneCallback)(rt, data, zone);
+ IterateCompartmentsArenasCells(rt, zone, data,
+ compartmentCallback, arenaCallback, cellCallback);
}
void
js::IterateChunks(JSRuntime *rt, void *data, IterateChunkCallback chunkCallback)
{
AutoPrepareForTracing prep(rt);
for (js::GCChunkSet::Range r = rt->gcChunkSet.all(); !r.empty(); r.popFront())
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1269,28 +1269,39 @@ MarkStackRangeConservatively(JSTracer *t
typedef void (*IterateChunkCallback)(JSRuntime *rt, void *data, gc::Chunk *chunk);
typedef void (*IterateZoneCallback)(JSRuntime *rt, void *data, JS::Zone *zone);
typedef void (*IterateArenaCallback)(JSRuntime *rt, void *data, gc::Arena *arena,
JSGCTraceKind traceKind, size_t thingSize);
typedef void (*IterateCellCallback)(JSRuntime *rt, void *data, void *thing,
JSGCTraceKind traceKind, size_t thingSize);
/*
- * This function calls |compartmentCallback| on every compartment,
- * |arenaCallback| on every in-use arena, and |cellCallback| on every in-use
- * cell in the GC heap.
+ * This function calls |zoneCallback| on every zone, |compartmentCallback| on
+ * every compartment, |arenaCallback| on every in-use arena, and |cellCallback|
+ * on every in-use cell in the GC heap.
*/
extern void
IterateZonesCompartmentsArenasCells(JSRuntime *rt, void *data,
IterateZoneCallback zoneCallback,
JSIterateCompartmentCallback compartmentCallback,
IterateArenaCallback arenaCallback,
IterateCellCallback cellCallback);
/*
+ * This function is like IterateZonesCompartmentsArenasCells, but does it for a
+ * single zone.
+ */
+extern void
+IterateZoneCompartmentsArenasCells(JSRuntime *rt, Zone *zone, void *data,
+ IterateZoneCallback zoneCallback,
+ JSIterateCompartmentCallback compartmentCallback,
+ IterateArenaCallback arenaCallback,
+ IterateCellCallback cellCallback);
+
+/*
* Invoke chunkCallback on every in-use chunk.
*/
extern void
IterateChunks(JSRuntime *rt, void *data, IterateChunkCallback chunkCallback);
typedef void (*IterateScriptCallback)(JSRuntime *rt, void *data, JSScript *script);
/*
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -38,16 +38,17 @@
#include "frontend/BytecodeCompiler.h"
#include "gc/Marking.h"
#include "jit/AsmJSModule.h"
#include "jit/BaselineJIT.h"
#include "js/MemoryMetrics.h"
#include "js/OldDebugAPI.h"
#include "vm/ArgumentsObject.h"
#include "vm/Interpreter.h"
+#include "vm/ProxyObject.h"
#include "vm/RegExpStaticsObject.h"
#include "vm/Shape.h"
#include "jsatominlines.h"
#include "jsboolinlines.h"
#include "jscntxtinlines.h"
#include "jscompartmentinlines.h"
@@ -5675,18 +5676,34 @@ JSObject::addSizeOfExcludingThis(mozilla
sizes->mallocHeapElementsAsmJS += mallocSizeOf(elements);
#endif
} else {
sizes->mallocHeapElementsNonAsmJS += mallocSizeOf(elements);
}
}
// Other things may be measured in the future if DMD indicates it is worthwhile.
- // Note that sizes->private_ is measured elsewhere.
- if (is<ArgumentsObject>()) {
+ if (is<JSFunction>() ||
+ is<JSObject>() ||
+ is<ArrayObject>() ||
+ is<CallObject>() ||
+ is<RegExpObject>() ||
+ is<ProxyObject>())
+ {
+ // Do nothing. But this function is hot, and we win by getting the
+ // common cases out of the way early. Some stats on the most common
+ // classes, as measured during a vanilla browser session:
+ // - (53.7%, 53.7%): Function
+ // - (18.0%, 71.7%): Object
+ // - (16.9%, 88.6%): Array
+ // - ( 3.9%, 92.5%): Call
+ // - ( 2.8%, 95.3%): RegExp
+ // - ( 1.0%, 96.4%): Proxy
+
+ } else if (is<ArgumentsObject>()) {
sizes->mallocHeapArgumentsData += as<ArgumentsObject>().sizeOfMisc(mallocSizeOf);
} else if (is<RegExpStaticsObject>()) {
sizes->mallocHeapRegExpStatics += as<RegExpStaticsObject>().sizeOfData(mallocSizeOf);
} else if (is<PropertyIteratorObject>()) {
sizes->mallocHeapPropertyIteratorData += as<PropertyIteratorObject>().sizeOfMisc(mallocSizeOf);
#ifdef JS_ION
} else if (is<AsmJSModuleObject>()) {
as<AsmJSModuleObject>().addSizeOfMisc(mallocSizeOf, &sizes->nonHeapCodeAsmJS,
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -373,17 +373,17 @@ class JSObject : public js::ObjectImpl
* The number of allocated slots is not stored explicitly, and changes to
* the slots must track changes in the slot span.
*/
static bool growSlots(js::ThreadSafeContext *cx, js::HandleObject obj, uint32_t oldCount,
uint32_t newCount);
static void shrinkSlots(js::ThreadSafeContext *cx, js::HandleObject obj, uint32_t oldCount,
uint32_t newCount);
- bool hasDynamicSlots() const { return slots != nullptr; }
+ bool hasDynamicSlots() const { return !!slots; }
protected:
static inline bool updateSlotsForSpan(js::ThreadSafeContext *cx,
js::HandleObject obj, size_t oldSpan, size_t newSpan);
public:
/*
* Trigger the write barrier on a range of slots that will no longer be
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -18,18 +18,19 @@
#include "jit/Ion.h"
#include "vm/ArrayObject.h"
#include "vm/Runtime.h"
#include "vm/Shape.h"
#include "vm/String.h"
#include "vm/WrapperObject.h"
using mozilla::DebugOnly;
+using mozilla::MallocSizeOf;
+using mozilla::MoveRef;
using mozilla::OldMove;
-using mozilla::MoveRef;
using mozilla::PodEqual;
using namespace js;
using JS::RuntimeStats;
using JS::ObjectPrivateVisitor;
using JS::ZoneStats;
using JS::CompartmentStats;
@@ -263,23 +264,20 @@ StatsCellCallback(JSRuntime *rt, void *d
cStats->objectsGCHeapDenseArray += thingSize;
else if (obj->is<CrossCompartmentWrapperObject>())
cStats->objectsGCHeapCrossCompartmentWrapper += thingSize;
else
cStats->objectsGCHeapOrdinary += thingSize;
obj->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &cStats->objectsExtra);
- // JSObject::sizeOfExcludingThis() doesn't measure objectsPrivate,
- // so we do it here.
if (ObjectPrivateVisitor *opv = closure->opv) {
nsISupports *iface;
- if (opv->getISupports_(obj, &iface) && iface) {
+ if (opv->getISupports_(obj, &iface) && iface)
cStats->objectsPrivate += opv->sizeOfIncludingThis(iface);
- }
}
break;
}
case JSTRACE_STRING: {
JSString *str = static_cast<JSString *>(thing);
size_t strCharsSize = str->sizeOfExcludingThis(rtStats->mallocSizeOf_);
@@ -333,17 +331,16 @@ StatsCellCallback(JSRuntime *rt, void *d
cStats->shapesGCHeapBase += thingSize;
break;
}
case JSTRACE_SCRIPT: {
JSScript *script = static_cast<JSScript *>(thing);
CompartmentStats *cStats = GetCompartmentStats(script->compartment());
cStats->scriptsGCHeap += thingSize;
-
cStats->scriptsMallocHeapData += script->sizeOfData(rtStats->mallocSizeOf_);
cStats->typeInferenceTypeScripts += script->sizeOfTypeScript(rtStats->mallocSizeOf_);
#ifdef JS_ION
jit::AddSizeOfBaselineData(script, rtStats->mallocSizeOf_, &cStats->baselineData,
&cStats->baselineStubsFallback);
cStats->ionData += jit::SizeOfIonData(script, rtStats->mallocSizeOf_);
#endif
@@ -441,24 +438,22 @@ JS::CollectRuntimeStats(JSRuntime *rt, R
IterateChunks(rt, &rtStats->gcHeapDecommittedArenas,
DecommittedArenasChunkCallback);
// Take the per-compartment measurements.
StatsClosure closure(rtStats, opv);
if (!closure.init())
return false;
- rtStats->runtime.scriptSources = 0;
IterateZonesCompartmentsArenasCells(rt, &closure, StatsZoneCallback, StatsCompartmentCallback,
StatsArenaCallback, StatsCellCallback);
// Take the "explicit/js/runtime/" measurements.
rt->addSizeOfIncludingThis(rtStats->mallocSizeOf_, &rtStats->runtime);
- rtStats->gcHeapGCThings = 0;
for (size_t i = 0; i < rtStats->zoneStatsVector.length(); i++) {
ZoneStats &zStats = rtStats->zoneStatsVector[i];
rtStats->zTotals.add(zStats);
// Move any strings which take up more than the sundries threshold
// (counting all of their copies together) into notableStrings.
FindNotableStrings(zStats);
@@ -526,8 +521,67 @@ JS::UserCompartmentCount(JSRuntime *rt)
}
JS_PUBLIC_API(size_t)
JS::PeakSizeOfTemporary(const JSRuntime *rt)
{
return rt->tempLifoAlloc.peakSizeOfExcludingThis();
}
+namespace JS {
+
+JS_PUBLIC_API(bool)
+AddSizeOfTab(JSRuntime *rt, JSObject *obj, MallocSizeOf mallocSizeOf, ObjectPrivateVisitor *opv,
+ TabSizes *sizes)
+{
+ class SimpleJSRuntimeStats : public JS::RuntimeStats
+ {
+ public:
+ SimpleJSRuntimeStats(MallocSizeOf mallocSizeOf)
+ : JS::RuntimeStats(mallocSizeOf)
+ {}
+
+ virtual void initExtraZoneStats(JS::Zone *zone, JS::ZoneStats *zStats)
+ MOZ_OVERRIDE
+ {}
+
+ virtual void initExtraCompartmentStats(
+ JSCompartment *c, JS::CompartmentStats *cStats) MOZ_OVERRIDE
+ {}
+ };
+
+ SimpleJSRuntimeStats rtStats(mallocSizeOf);
+
+ JS::Zone *zone = GetObjectZone(obj);
+
+ if (!rtStats.compartmentStatsVector.reserve(zone->compartments.length()))
+ return false;
+
+ if (!rtStats.zoneStatsVector.reserve(1))
+ return false;
+
+ // Take the per-compartment measurements.
+ StatsClosure closure(&rtStats, opv);
+ if (!closure.init())
+ return false;
+ IterateZoneCompartmentsArenasCells(rt, zone, &closure, StatsZoneCallback,
+ StatsCompartmentCallback, StatsArenaCallback,
+ StatsCellCallback);
+
+ JS_ASSERT(rtStats.zoneStatsVector.length() == 1);
+ rtStats.zTotals.add(rtStats.zoneStatsVector[0]);
+
+ for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++) {
+ CompartmentStats &cStats = rtStats.compartmentStatsVector[i];
+ rtStats.cTotals.add(cStats);
+ }
+
+ for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
+ comp->compartmentStats = NULL;
+
+ rtStats.zTotals.addToTabSizes(sizes);
+ rtStats.cTotals.addToTabSizes(sizes);
+
+ return true;
+}
+
+} // namespace JS
+
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -2411,17 +2411,17 @@ SizeOfTreeIncludingThis(nsINode *tree)
class OrphanReporter : public JS::ObjectPrivateVisitor
{
public:
OrphanReporter(GetISupportsFun aGetISupports)
: JS::ObjectPrivateVisitor(aGetISupports)
{
}
- virtual size_t sizeOfIncludingThis(nsISupports *aSupports) {
+ virtual size_t sizeOfIncludingThis(nsISupports *aSupports) MOZ_OVERRIDE {
size_t n = 0;
nsCOMPtr<nsINode> node = do_QueryInterface(aSupports);
// https://bugzilla.mozilla.org/show_bug.cgi?id=773533#c11 explains
// that we have to skip XBL elements because they violate certain
// assumptions. Yuk.
if (node && !node->IsInDoc() &&
!(node->IsElement() && node->AsElement()->IsInNamespace(kNameSpaceID_XBL)))
{
@@ -2669,16 +2669,35 @@ JSReporter::CollectReports(WindowPaths *
REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect"),
KIND_HEAP, xpconnect,
"Memory used by XPConnect.");
return NS_OK;
}
+static nsresult
+JSSizeOfTab(JSObject *obj, size_t *jsObjectsSize, size_t *jsStringsSize,
+ size_t *jsPrivateSize, size_t *jsOtherSize)
+{
+ JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
+
+ TabSizes sizes;
+ OrphanReporter orphanReporter(XPCConvert::GetISupportsFromJSObject);
+ NS_ENSURE_TRUE(JS::AddSizeOfTab(rt, obj, moz_malloc_size_of,
+ &orphanReporter, &sizes),
+ NS_ERROR_OUT_OF_MEMORY);
+
+ *jsObjectsSize = sizes.objects;
+ *jsStringsSize = sizes.strings;
+ *jsPrivateSize = sizes.private_;
+ *jsOtherSize = sizes.other;
+ return NS_OK;
+}
+
} // namespace xpc
#ifdef MOZ_CRASHREPORTER
static bool
DiagnosticMemoryCallback(void *ptr, size_t size)
{
return CrashReporter::RegisterAppMemory(ptr, size) == NS_OK;
}
@@ -3035,16 +3054,17 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect*
// Register memory reporters and distinguished amount functions.
NS_RegisterMemoryReporter(new JSMainRuntimeCompartmentsReporter);
NS_RegisterMemoryReporter(new JSMainRuntimeTemporaryPeakReporter());
RegisterJSMainRuntimeGCHeapDistinguishedAmount(JSMainRuntimeGCHeapDistinguishedAmount);
RegisterJSMainRuntimeTemporaryPeakDistinguishedAmount(JSMainRuntimeTemporaryPeakDistinguishedAmount);
RegisterJSMainRuntimeCompartmentsSystemDistinguishedAmount(JSMainRuntimeCompartmentsSystemDistinguishedAmount);
RegisterJSMainRuntimeCompartmentsUserDistinguishedAmount(JSMainRuntimeCompartmentsUserDistinguishedAmount);
+ mozilla::RegisterJSSizeOfTab(JSSizeOfTab);
// Install a JavaScript 'debugger' keyword handler in debug builds only
#ifdef DEBUG
if (!JS_GetGlobalDebugHooks(runtime)->debuggerHandler)
xpc_InstallJSDebuggerKeywordHandler(runtime);
#endif
}
--- a/layout/base/nsArenaMemoryStats.h
+++ b/layout/base/nsArenaMemoryStats.h
@@ -1,20 +1,81 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 nsArenaMemoryStats_h
#define nsArenaMemoryStats_h
+#include "mozilla/Assertions.h"
+#include "mozilla/PodOperations.h"
+
+class nsTabSizes {
+public:
+ enum Kind {
+ DOM, // DOM stuff.
+ Style, // Style stuff.
+ Other // Everything else.
+ };
+
+ nsTabSizes() { mozilla::PodZero(this); }
+
+ void add(Kind kind, size_t n)
+ {
+ switch (kind) {
+ case DOM: mDom += n; break;
+ case Style: mStyle += n; break;
+ case Other: mOther += n; break;
+ default: MOZ_CRASH("bad nsTabSizes kind");
+ }
+ }
+
+ size_t mDom;
+ size_t mStyle;
+ size_t mOther;
+};
+
#define FRAME_ID_STAT_FIELD(classname) mArena##classname
struct nsArenaMemoryStats {
-#define FRAME_ID(classname) size_t FRAME_ID_STAT_FIELD(classname);
-#include "nsFrameIdList.h"
-#undef FRAME_ID
- size_t mLineBoxes;
- size_t mRuleNodes;
- size_t mStyleContexts;
- size_t mOther;
+#define FOR_EACH_SIZE(macro) \
+ macro(Other, mLineBoxes) \
+ macro(Style, mRuleNodes) \
+ macro(Style, mStyleContexts) \
+ macro(Other, mOther)
+
+ nsArenaMemoryStats()
+ :
+ #define ZERO_SIZE(kind, mSize) mSize(0),
+ FOR_EACH_SIZE(ZERO_SIZE)
+ #undef ZERO_SIZE
+ #define FRAME_ID(classname) FRAME_ID_STAT_FIELD(classname)(),
+ #include "nsFrameIdList.h"
+ #undef FRAME_ID
+ dummy()
+ {}
+
+ void addToTabSizes(nsTabSizes *sizes) const
+ {
+ #define ADD_TO_TAB_SIZES(kind, mSize) sizes->add(nsTabSizes::kind, mSize);
+ FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
+ #undef ADD_TO_TAB_SIZES
+ #define FRAME_ID(classname) \
+ sizes->add(nsTabSizes::Other, FRAME_ID_STAT_FIELD(classname));
+ #include "nsFrameIdList.h"
+ #undef FRAME_ID
+ }
+
+ #define DECL_SIZE(kind, mSize) size_t mSize;
+ FOR_EACH_SIZE(DECL_SIZE)
+ #undef DECL_SIZE
+ #define FRAME_ID(classname) size_t FRAME_ID_STAT_FIELD(classname);
+ #include "nsFrameIdList.h"
+ #undef FRAME_ID
+ int dummy; // present just to absorb the trailing comma from FRAME_ID in the
+ // constructor
+
+#undef FOR_EACH_SIZE
};
#endif // nsArenaMemoryStats_h
--- a/toolkit/components/aboutmemory/tests/test_memoryReporters.xul
+++ b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul
@@ -150,16 +150,31 @@
// and this file hasn't been updated appropriately.
dummy = mgr[amounts[i]];
ok(dummy !== undefined,
"accessed an unknown distinguished amount: " + amounts[i]);
} catch (ex) {
}
}
+ // Run sizeOfTab() to make sure it doesn't crash. We can't check the result
+ // values because they're non-deterministic.
+ let jsObjectsSize = {};
+ let jsStringsSize = {};
+ let jsOtherSize = {};
+ let domSize = {};
+ let styleSize = {};
+ let otherSize = {};
+ let totalSize = {};
+ let jsMilliseconds = {};
+ let nonJSMilliseconds = {};
+ mgr.sizeOfTab(window, jsObjectsSize, jsStringsSize, jsOtherSize,
+ domSize, styleSize, otherSize, totalSize,
+ jsMilliseconds, nonJSMilliseconds);
+
let e = mgr.enumerateReporters();
while (e.hasMoreElements()) {
let r = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
r.collectReports(handleReport, null);
// Access |name| to make sure it doesn't crash or assert.
dummy = r.name;
}
--- a/xpcom/base/nsIMemoryReporter.idl
+++ b/xpcom/base/nsIMemoryReporter.idl
@@ -1,22 +1,23 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#include "nsISupports.idl"
-interface nsISimpleEnumerator;
+interface nsICancelableRunnable;
+interface nsIDOMWindow;
interface nsIRunnable;
-interface nsICancelableRunnable;
+interface nsISimpleEnumerator;
/*
- * Memory reporters measure Firefox's memory usage. They are mainly used to
+ * Memory reporters measure Firefox's memory usage. They are primarily used to
* generate the about:memory page. You should read
* https://wiki.mozilla.org/Memory_Reporting before writing a memory
* reporter.
*/
[scriptable, function, uuid(3a61be3b-b93b-461a-a4f8-388214f558b1)]
interface nsIMemoryReporterCallback : nsISupports
{
@@ -303,22 +304,42 @@ interface nsIMemoryReporterManager : nsI
[infallible] readonly attribute boolean hasMozMallocUsableSize;
/*
* Run a series of GC/CC's in an attempt to minimize the application's memory
* usage. When we're finished, we invoke the given runnable if it's not
* null. Returns a reference to the runnable used for carrying out the task.
*/
nsICancelableRunnable minimizeMemoryUsage(in nsIRunnable callback);
+
+ /*
+ * Measure the memory that is known to be owned by this tab, split up into
+ * several broad categories. Note that this will be an underestimate of the
+ * true number, due to imperfect memory reporter coverage (corresponding to
+ * about:memory's "heap-unclassified"), and due to some memory shared between
+ * tabs not being counted.
+ *
+ * The time taken for the measurement (split into JS and non-JS parts) is
+ * also returned.
+ */
+ void sizeOfTab(in nsIDOMWindow window,
+ out int64_t jsObjectsSize, out int64_t jsStringsSize,
+ out int64_t jsOtherSize, out int64_t domSize,
+ out int64_t styleSize, out int64_t otherSize,
+ out int64_t totalSize,
+ out double jsMilliseconds, out double nonJSMilliseconds);
};
%{C++
+#include "js/TypeDecls.h"
#include "nsStringGlue.h"
+class nsPIDOMWindow;
+
// Note that the memory reporters are held in an nsCOMArray, which means
// that individual reporters should be referenced with |nsIMemoryReporter *|
// instead of nsCOMPtr<nsIMemoryReporter>.
XPCOM_API(nsresult) NS_RegisterMemoryReporter(nsIMemoryReporter* aReporter);
XPCOM_API(nsresult) NS_UnregisterMemoryReporter(nsIMemoryReporter* aReporter);
namespace mozilla {
@@ -350,16 +371,31 @@ DECL_UNREGISTER_DISTINGUISHED_AMOUNT(Sto
DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsVirtual)
DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsPhysical)
DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, GhostWindows)
#undef DECL_REGISTER_DISTINGUISHED_AMOUNT
#undef DECL_UNREGISTER_DISTINGUISHED_AMOUNT
+// Likewise for per-tab measurement.
+
+typedef nsresult (*JSSizeOfTabFn)(JSObject* aObj,
+ size_t* aJsObjectsSize,
+ size_t* aJsStringSize,
+ size_t* aJsPrivateSize,
+ size_t* aJsOtherSize);
+typedef nsresult (*NonJSSizeOfTabFn)(nsPIDOMWindow* aWindow,
+ size_t* aDomSize,
+ size_t* aStyleSize,
+ size_t* aOtherSize);
+
+nsresult RegisterJSSizeOfTab(JSSizeOfTabFn aSizeOfTabFn);
+nsresult RegisterNonJSSizeOfTab(NonJSSizeOfTabFn aSizeOfTabFn);
+
}
#if defined(MOZ_DMD)
namespace mozilla {
namespace dmd {
// This runs all the memory reporters but does nothing with the results; i.e.
// it does the minimal amount of work possible for DMD to do its thing.
void RunReporters();
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -7,17 +7,20 @@
#include "nsAtomTable.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsCOMArray.h"
#include "nsServiceManagerUtils.h"
#include "nsMemoryReporterManager.h"
#include "nsISimpleEnumerator.h"
#include "nsThreadUtils.h"
+#include "nsIDOMWindow.h"
+#include "nsPIDOMWindow.h"
#include "nsIObserverService.h"
+#include "nsIGlobalObject.h"
#if defined(XP_LINUX)
#include "nsMemoryInfoDumper.h"
#endif
#include "mozilla/Attributes.h"
#include "mozilla/PodOperations.h"
#include "mozilla/Services.h"
#include "mozilla/Telemetry.h"
@@ -840,16 +843,17 @@ HashtableEnumerator::GetNext(nsISupports
} // anonymous namespace
nsMemoryReporterManager::nsMemoryReporterManager()
: mMutex("nsMemoryReporterManager::mMutex"),
mIsRegistrationBlocked(false)
{
PodZero(&mAmountFns);
+ PodZero(&mSizeOfTabFns);
}
nsMemoryReporterManager::~nsMemoryReporterManager()
{
}
NS_IMETHODIMP
nsMemoryReporterManager::EnumerateReporters(nsISimpleEnumerator** aResult)
@@ -1262,16 +1266,66 @@ nsMemoryReporterManager::MinimizeMemoryU
nsRefPtr<nsICancelableRunnable> runnable =
new MinimizeMemoryUsageRunnable(aCallback);
NS_ADDREF(*aResult = runnable);
return NS_DispatchToMainThread(runnable);
}
+NS_IMETHODIMP
+nsMemoryReporterManager::SizeOfTab(nsIDOMWindow* aTopWindow,
+ int64_t* aJSObjectsSize,
+ int64_t* aJSStringsSize,
+ int64_t* aJSOtherSize,
+ int64_t* aDomSize,
+ int64_t* aStyleSize,
+ int64_t* aOtherSize,
+ int64_t* aTotalSize,
+ double* aJSMilliseconds,
+ double* aNonJSMilliseconds)
+{
+ nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aTopWindow);
+ nsCOMPtr<nsPIDOMWindow> piWindow = do_QueryInterface(aTopWindow);
+ NS_ENSURE_TRUE(!!global && !!piWindow, NS_ERROR_FAILURE);
+
+ TimeStamp t1 = TimeStamp::Now();
+
+ // Measure JS memory consumption (and possibly some non-JS consumption, via
+ // |jsPrivateSize|).
+ size_t jsObjectsSize, jsStringsSize, jsPrivateSize, jsOtherSize;
+ nsresult rv = mSizeOfTabFns.mJS(global->GetGlobalJSObject(),
+ &jsObjectsSize, &jsStringsSize,
+ &jsPrivateSize, &jsOtherSize);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ TimeStamp t2 = TimeStamp::Now();
+
+ // Measure non-JS memory consumption.
+ size_t domSize, styleSize, otherSize;
+ mSizeOfTabFns.mNonJS(piWindow, &domSize, &styleSize, &otherSize);
+
+ TimeStamp t3 = TimeStamp::Now();
+
+ *aTotalSize = 0;
+ #define DO(aN, n) { *aN = (n); *aTotalSize += (n); }
+ DO(aJSObjectsSize, jsObjectsSize);
+ DO(aJSStringsSize, jsStringsSize);
+ DO(aJSOtherSize, jsOtherSize);
+ DO(aDomSize, jsPrivateSize + domSize);
+ DO(aStyleSize, styleSize);
+ DO(aOtherSize, otherSize);
+ #undef DO
+
+ *aJSMilliseconds = (t2 - t1).ToMilliseconds();
+ *aNonJSMilliseconds = (t3 - t2).ToMilliseconds();
+
+ return NS_OK;
+}
+
// Most memory reporters don't need thread safety, but some do. Make them all
// thread-safe just to be safe. Memory reporters are created and destroyed
// infrequently enough that the performance cost should be negligible.
NS_IMPL_ISUPPORTS1(MemoryUniReporter, nsIMemoryReporter)
nsresult
NS_RegisterMemoryReporter(nsIMemoryReporter* aReporter)
{
@@ -1289,44 +1343,41 @@ NS_UnregisterMemoryReporter(nsIMemoryRep
if (!mgr) {
return NS_ERROR_FAILURE;
}
return mgr->UnregisterReporter(aReporter);
}
namespace mozilla {
+#define GET_MEMORY_REPORTER_MANAGER(mgr) \
+ nsCOMPtr<nsIMemoryReporterManager> imgr = \
+ do_GetService("@mozilla.org/memory-reporter-manager;1"); \
+ nsRefPtr<nsMemoryReporterManager> mgr = \
+ static_cast<nsMemoryReporterManager*>(imgr.get()); \
+ if (!mgr) { \
+ return NS_ERROR_FAILURE; \
+ }
+
// Macro for generating functions that register distinguished amount functions
// with the memory reporter manager.
#define DEFINE_REGISTER_DISTINGUISHED_AMOUNT(kind, name) \
nsresult \
Register##name##DistinguishedAmount(kind##AmountFn aAmountFn) \
{ \
- nsCOMPtr<nsIMemoryReporterManager> imgr = \
- do_GetService("@mozilla.org/memory-reporter-manager;1"); \
- nsRefPtr<nsMemoryReporterManager> mgr = \
- static_cast<nsMemoryReporterManager*>(imgr.get()); \
- if (!mgr) { \
- return NS_ERROR_FAILURE; \
- } \
+ GET_MEMORY_REPORTER_MANAGER(mgr) \
mgr->mAmountFns.m##name = aAmountFn; \
return NS_OK; \
}
#define DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT(name) \
nsresult \
Unregister##name##DistinguishedAmount() \
{ \
- nsCOMPtr<nsIMemoryReporterManager> imgr = \
- do_GetService("@mozilla.org/memory-reporter-manager;1"); \
- nsRefPtr<nsMemoryReporterManager> mgr = \
- static_cast<nsMemoryReporterManager*>(imgr.get()); \
- if (!mgr) { \
- return NS_ERROR_FAILURE; \
- } \
+ GET_MEMORY_REPORTER_MANAGER(mgr) \
mgr->mAmountFns.m##name = nullptr; \
return NS_OK; \
}
DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeGCHeap)
DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeTemporaryPeak)
DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsSystem)
DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsUser)
@@ -1339,16 +1390,32 @@ DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT(S
DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsVirtual)
DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsPhysical)
DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, GhostWindows)
#undef DEFINE_REGISTER_DISTINGUISHED_AMOUNT
#undef DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT
+#define DEFINE_REGISTER_SIZE_OF_TAB(name) \
+ nsresult \
+ Register##name##SizeOfTab(name##SizeOfTabFn aSizeOfTabFn) \
+ { \
+ GET_MEMORY_REPORTER_MANAGER(mgr) \
+ mgr->mSizeOfTabFns.m##name = aSizeOfTabFn; \
+ return NS_OK; \
+ }
+
+DEFINE_REGISTER_SIZE_OF_TAB(JS);
+DEFINE_REGISTER_SIZE_OF_TAB(NonJS);
+
+#undef DEFINE_REGISTER_SIZE_OF_TAB
+
+#undef GET_MEMORY_REPORTER_MANAGER
+
}
#if defined(MOZ_DMD)
namespace mozilla {
namespace dmd {
class NullReporterCallback : public nsIMemoryReporterCallback
--- a/xpcom/base/nsMemoryReporterManager.h
+++ b/xpcom/base/nsMemoryReporterManager.h
@@ -34,16 +34,23 @@ public:
mozilla::InfallibleAmountFn mLowMemoryEventsVirtual;
mozilla::InfallibleAmountFn mLowMemoryEventsPhysical;
mozilla::InfallibleAmountFn mGhostWindows;
};
AmountFns mAmountFns;
+ // Functions that measure per-tab memory consumption.
+ struct SizeOfTabFns {
+ mozilla::JSSizeOfTabFn mJS;
+ mozilla::NonJSSizeOfTabFn mNonJS;
+ };
+ SizeOfTabFns mSizeOfTabFns;
+
private:
nsresult RegisterReporterHelper(nsIMemoryReporter *aReporter, bool aForce);
nsTHashtable<nsISupportsHashKey> mReporters;
Mutex mMutex;
bool mIsRegistrationBlocked;
};