mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 918207 - Support per-tab memory profiling. r=billm,mccr8,till,smaug,nfroyd.
--HG-- extra : rebase_source : 7603eaedbd761c6b231b1b911f719388864755e6
This commit is contained in:
parent
2cd62c9469
commit
1bd2044812
@ -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) {
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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 */
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user