Bug 918207 - Support per-tab memory profiling. r=billm,mccr8,till,smaug,nfroyd.

--HG--
extra : rebase_source : 7603eaedbd761c6b231b1b911f719388864755e6
This commit is contained in:
Nicholas Nethercote 2013-10-22 14:53:26 -07:00
parent 2cd62c9469
commit 1bd2044812
14 changed files with 598 additions and 151 deletions

View File

@ -8,6 +8,7 @@
#include "nsWindowMemoryReporter.h"
#include "nsGlobalWindow.h"
#include "nsIDocument.h"
#include "nsIDOMWindowCollection.h"
#include "nsIEffectiveTLDService.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Preferences.h"
@ -31,14 +32,70 @@ nsWindowMemoryReporter::nsWindowMemoryReporter()
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) {

View File

@ -12,10 +12,12 @@
#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
@ -25,25 +27,44 @@
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
};
/**

View File

@ -25,6 +25,37 @@
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
@ -57,11 +88,12 @@ struct InefficientNonFlatteningStringHashPolicy
// 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 {
@ -72,17 +104,17 @@ enum {
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)
@ -101,6 +133,11 @@ struct ZoneStatsPod
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.
@ -115,16 +152,16 @@ namespace JS {
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)
@ -141,6 +178,10 @@ struct ObjectsExtraSizes
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)
@ -151,11 +192,11 @@ struct ObjectsExtraSizes
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)
@ -256,17 +297,17 @@ struct NotableStringInfo : public StringInfo
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)
@ -335,35 +376,35 @@ struct ZoneStats : js::ZoneStatsPod
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)
@ -391,6 +432,12 @@ struct CompartmentStats
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.
@ -401,12 +448,12 @@ struct CompartmentStats
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)
@ -488,6 +535,17 @@ 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 */

View File

@ -26,6 +26,28 @@ js::TraceRuntime(JSTracer *trc)
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,
@ -37,24 +59,25 @@ js::IterateZonesCompartmentsArenasCells(JSRuntime *rt, void *data,
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));
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);
}
}
IterateCompartmentsArenasCells(rt, zone, data,
compartmentCallback, arenaCallback, cellCallback);
}
}
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)
{

View File

@ -1274,9 +1274,9 @@ 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,
@ -1285,6 +1285,17 @@ IterateZonesCompartmentsArenasCells(JSRuntime *rt, void *data,
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.
*/

View File

@ -43,6 +43,7 @@
#include "js/OldDebugAPI.h"
#include "vm/ArgumentsObject.h"
#include "vm/Interpreter.h"
#include "vm/ProxyObject.h"
#include "vm/RegExpStaticsObject.h"
#include "vm/Shape.h"
@ -5680,8 +5681,24 @@ JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::Objects
}
// 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);

View File

@ -378,7 +378,7 @@ class JSObject : public js::ObjectImpl
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,

View File

@ -23,8 +23,9 @@
#include "vm/WrapperObject.h"
using mozilla::DebugOnly;
using mozilla::OldMove;
using mozilla::MallocSizeOf;
using mozilla::MoveRef;
using mozilla::OldMove;
using mozilla::PodEqual;
using namespace js;
@ -268,13 +269,10 @@ StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKin
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;
}
@ -338,7 +336,6 @@ StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKin
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
@ -446,14 +443,12 @@ JS::CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisit
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];
@ -531,3 +526,62 @@ 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

View File

@ -2416,7 +2416,7 @@ class OrphanReporter : public JS::ObjectPrivateVisitor
{
}
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
@ -2674,6 +2674,25 @@ JSReporter::CollectReports(WindowPaths *windowPaths,
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
@ -3040,6 +3059,7 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
RegisterJSMainRuntimeTemporaryPeakDistinguishedAmount(JSMainRuntimeTemporaryPeakDistinguishedAmount);
RegisterJSMainRuntimeCompartmentsSystemDistinguishedAmount(JSMainRuntimeCompartmentsSystemDistinguishedAmount);
RegisterJSMainRuntimeCompartmentsUserDistinguishedAmount(JSMainRuntimeCompartmentsUserDistinguishedAmount);
mozilla::RegisterJSSizeOfTab(JSSizeOfTab);
// Install a JavaScript 'debugger' keyword handler in debug builds only
#ifdef DEBUG

View File

@ -1,3 +1,5 @@
/* -*- 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/. */
@ -5,16 +7,75 @@
#ifndef nsArenaMemoryStats_h
#define nsArenaMemoryStats_h
#define FRAME_ID_STAT_FIELD(classname) mArena##classname
#include "mozilla/Assertions.h"
#include "mozilla/PodOperations.h"
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;
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 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

View File

@ -155,6 +155,21 @@
}
}
// 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);

View File

@ -6,12 +6,13 @@
#include "nsISupports.idl"
interface nsISimpleEnumerator;
interface nsIRunnable;
interface nsICancelableRunnable;
interface nsIDOMWindow;
interface nsIRunnable;
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.
@ -308,12 +309,32 @@ interface nsIMemoryReporterManager : nsISupports
* 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>.
@ -355,6 +376,21 @@ 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)

View File

@ -12,7 +12,10 @@
#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
@ -845,6 +848,7 @@ nsMemoryReporterManager::nsMemoryReporterManager()
mIsRegistrationBlocked(false)
{
PodZero(&mAmountFns);
PodZero(&mSizeOfTabFns);
}
nsMemoryReporterManager::~nsMemoryReporterManager()
@ -1267,6 +1271,56 @@ nsMemoryReporterManager::MinimizeMemoryUsage(nsIRunnable* aCallback,
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.
@ -1294,19 +1348,22 @@ NS_UnregisterMemoryReporter(nsIMemoryReporter* 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; \
}
@ -1315,13 +1372,7 @@ namespace mozilla {
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; \
}
@ -1344,6 +1395,22 @@ 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)

View File

@ -39,6 +39,13 @@ public:
};
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);