Bug 698968 - Add mallocSizeOf functions and start using them. r=jlebar,bhackett,jfkthame, sr=bz.

This commit is contained in:
Nicholas Nethercote 2011-11-27 19:03:14 -08:00
parent e05dff6469
commit f102556f32
38 changed files with 385 additions and 249 deletions

View File

@ -95,14 +95,18 @@ NS_IMPL_CYCLE_COLLECTION_3(mozHunspell,
mEncoder, mEncoder,
mDecoder) mDecoder)
// Memory reporting stuff // Memory reporting stuff.
static PRInt64 gHunspellAllocatedSize = 0; static PRInt64 gHunspellAllocatedSize = 0;
void HunspellReportMemoryAllocation(void* ptr) { void HunspellReportMemoryAllocation(void* ptr) {
gHunspellAllocatedSize += moz_malloc_usable_size(ptr); // |computedSize| is zero because we don't know what it is.
gHunspellAllocatedSize +=
mozilla::MemoryReporterMallocSizeOfForCounterInc(ptr, 0);
} }
void HunspellReportMemoryDeallocation(void* ptr) { void HunspellReportMemoryDeallocation(void* ptr) {
gHunspellAllocatedSize -= moz_malloc_usable_size(ptr); // |computedSize| is zero because we don't know what it is.
gHunspellAllocatedSize -=
mozilla::MemoryReporterMallocSizeOfForCounterDec(ptr, 0);
} }
static PRInt64 HunspellGetCurrentAllocatedSize() { static PRInt64 HunspellGetCurrentAllocatedSize() {
return gHunspellAllocatedSize; return gHunspellAllocatedSize;

View File

@ -47,6 +47,7 @@
#include "nsReadableUtils.h" #include "nsReadableUtils.h"
#include "nsExpirationTracker.h" #include "nsExpirationTracker.h"
#include "nsILanguageAtomService.h" #include "nsILanguageAtomService.h"
#include "nsIMemoryReporter.h"
#include "gfxFont.h" #include "gfxFont.h"
#include "gfxPlatform.h" #include "gfxPlatform.h"
@ -1182,8 +1183,8 @@ gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
// synthetic-bold strikes are each offset one device pixel in run direction // synthetic-bold strikes are each offset one device pixel in run direction
// (these values are only needed if IsSyntheticBold() is true) // (these values are only needed if IsSyntheticBold() is true)
double synBoldOnePixelOffset; double synBoldOnePixelOffset = 0;
PRInt32 strikes; PRInt32 strikes = 0;
if (IsSyntheticBold()) { if (IsSyntheticBold()) {
double xscale = CalcXScale(aContext); double xscale = CalcXScale(aContext);
synBoldOnePixelOffset = direction * xscale; synBoldOnePixelOffset = direction * xscale;
@ -4480,21 +4481,15 @@ gfxTextRun::ClusterIterator::ClusterAdvance(PropertyProvider *aProvider) const
return mTextRun->GetAdvanceWidth(mCurrentChar, ClusterLength(), aProvider); return mTextRun->GetAdvanceWidth(mCurrentChar, ClusterLength(), aProvider);
} }
PRUint64 size_t
gfxTextRun::ComputeSize() gfxTextRun::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf)
{ {
PRUint64 total = moz_malloc_usable_size(this); // The second arg is how much gfxTextRun::AllocateStorage would have
if (total == 0) { // allocated.
total = sizeof(gfxTextRun); size_t total =
} aMallocSizeOf(mCharacterGlyphs,
sizeof(CompressedGlyph) *
PRUint64 glyphDataSize = moz_malloc_usable_size(mCharacterGlyphs); GlyphStorageAllocCount(mCharacterCount, mFlags));
if (glyphDataSize == 0) {
// calculate how much gfxTextRun::AllocateStorage would have allocated
glyphDataSize = sizeof(CompressedGlyph) *
GlyphStorageAllocCount(mCharacterCount, mFlags);
}
total += glyphDataSize;
if (mDetailedGlyphs) { if (mDetailedGlyphs) {
total += mDetailedGlyphs->SizeOf(); total += mDetailedGlyphs->SizeOf();
@ -4505,6 +4500,13 @@ gfxTextRun::ComputeSize()
return total; return total;
} }
size_t
gfxTextRun::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf)
{
return aMallocSizeOf(this, sizeof(gfxTextRun)) +
SizeOfExcludingThis(aMallocSizeOf);
}
#ifdef DEBUG #ifdef DEBUG
void void

View File

@ -2048,16 +2048,20 @@ public:
// return storage used by this run, for memory reporter; // return storage used by this run, for memory reporter;
// nsTransformedTextRun needs to override this as it holds additional data // nsTransformedTextRun needs to override this as it holds additional data
virtual PRUint64 ComputeSize(); virtual NS_MUST_OVERRIDE size_t
SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf);
virtual NS_MUST_OVERRIDE size_t
SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf);
void AccountForSize(PRUint64* aTotal) { // Get the size, if it hasn't already been gotten, marking as it goes.
size_t MaybeSizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) {
if (mFlags & gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED) { if (mFlags & gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED) {
return; return 0;
} }
mFlags |= gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED; mFlags |= gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED;
*aTotal += ComputeSize(); return SizeOfIncludingThis(aMallocSizeOf);
} }
void ClearSizeAccounted() { void ResetSizeOfAccountingFlags() {
mFlags &= ~gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED; mFlags &= ~gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED;
} }

View File

@ -135,7 +135,8 @@ public:
#endif #endif
} }
void ComputeStorage(PRUint64 *aTotal); size_t MaybeSizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf);
void ResetSizeOfAccountingFlags();
#ifdef DEBUG #ifdef DEBUG
PRUint32 mGeneration; PRUint32 mGeneration;
@ -219,10 +220,10 @@ protected:
PRUint32 aEnd, PRUint32 aHash); PRUint32 aEnd, PRUint32 aHash);
void Uninit(); void Uninit();
static PLDHashOperator AccountForStorage(CacheHashEntry *aEntry, static PLDHashOperator MaybeSizeOfEntry(CacheHashEntry *aEntry,
void *aUserData); void *aUserData);
static PLDHashOperator ClearSizeAccounted(CacheHashEntry *aEntry, static PLDHashOperator ResetSizeOfEntryAccountingFlags(CacheHashEntry *aEntry,
void *aUserData); void *aUserData);
nsTHashtable<CacheHashEntry> mCache; nsTHashtable<CacheHashEntry> mCache;
@ -914,36 +915,48 @@ TextRunWordCache::RemoveTextRun(gfxTextRun *aTextRun)
#endif #endif
} }
struct SizeOfEntryData {
nsMallocSizeOfFun mMallocSizeOf;
size_t mTotal;
SizeOfEntryData(nsMallocSizeOfFun mallocSizeOf)
: mMallocSizeOf(mallocSizeOf), mTotal(0) { }
};
/*static*/ PLDHashOperator /*static*/ PLDHashOperator
TextRunWordCache::AccountForStorage(CacheHashEntry *aEntry, void *aUserData) TextRunWordCache::MaybeSizeOfEntry(CacheHashEntry *aEntry, void *aUserData)
{ {
gfxTextRun *run = aEntry->mTextRun; gfxTextRun *run = aEntry->mTextRun;
if (run) { if (run) {
PRUint64 *total = static_cast<PRUint64*>(aUserData); SizeOfEntryData *data = static_cast<SizeOfEntryData*>(aUserData);
run->AccountForSize(total); data->mTotal += run->MaybeSizeOfIncludingThis(data->mMallocSizeOf);
} }
return PL_DHASH_NEXT; return PL_DHASH_NEXT;
} }
/*static*/ PLDHashOperator /*static*/ PLDHashOperator
TextRunWordCache::ClearSizeAccounted(CacheHashEntry *aEntry, void *) TextRunWordCache::ResetSizeOfEntryAccountingFlags(CacheHashEntry *aEntry, void *)
{ {
gfxTextRun *run = aEntry->mTextRun; gfxTextRun *run = aEntry->mTextRun;
if (run) { if (run) {
run->ClearSizeAccounted(); run->ResetSizeOfAccountingFlags();
} }
return PL_DHASH_NEXT; return PL_DHASH_NEXT;
} }
size_t
TextRunWordCache::MaybeSizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf)
{
size_t total = mCache.ShallowSizeOfExcludingThis(aMallocSizeOf);
SizeOfEntryData data(aMallocSizeOf);
mCache.EnumerateEntries(MaybeSizeOfEntry, &data);
total += data.mTotal;
return total;
}
void void
TextRunWordCache::ComputeStorage(PRUint64 *aTotal) TextRunWordCache::ResetSizeOfAccountingFlags()
{ {
if (aTotal) { mCache.EnumerateEntries(ResetSizeOfEntryAccountingFlags, nsnull);
*aTotal += mCache.SizeOf();
mCache.EnumerateEntries(AccountForStorage, aTotal);
} else {
mCache.EnumerateEntries(ClearSizeAccounted, nsnull);
}
} }
static bool static bool
@ -1101,12 +1114,20 @@ gfxTextRunWordCache::Flush()
gTextRunWordCache->Flush(); gTextRunWordCache->Flush();
} }
void size_t
gfxTextRunWordCache::ComputeStorage(PRUint64 *aTotal) gfxTextRunWordCache::MaybeSizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf)
{ {
if (!gTextRunWordCache) { if (!gTextRunWordCache) {
return; return 0;
}
return gTextRunWordCache->MaybeSizeOfExcludingThis(aMallocSizeOf);
}
void
gfxTextRunWordCache::ResetSizeOfAccountingFlags()
{
if (gTextRunWordCache) {
gTextRunWordCache->ResetSizeOfAccountingFlags();
} }
gTextRunWordCache->ComputeStorage(aTotal);
} }

View File

@ -106,13 +106,16 @@ public:
static void Flush(); static void Flush();
/** /**
* If aTotal is NULL, just clears the TEXT_RUN_MEMORY_ACCOUNTED flag * This adds the storage used for each textRun to the total, and sets the
* on each textRun found. * TEXT_RUN_MEMORY_ACCOUNTED flag to avoid double- accounting. (Runs with
* If aTotal is non-NULL, adds the storage used for each textRun to the * this flag already set will be skipped.)
* total, and sets the TEXT_RUN_MEMORY_ACCOUNTED flag to avoid double-
* accounting. (Runs with this flag already set will be skipped.)
*/ */
static void ComputeStorage(PRUint64 *aTotal); static size_t MaybeSizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf);
/**
* This clears the TEXT_RUN_MEMORY_ACCOUNTED flag on each textRun found.
*/
static void ResetSizeOfAccountingFlags();
protected: protected:
friend class gfxPlatform; friend class gfxPlatform;

View File

@ -653,14 +653,12 @@ class HashTable : private AllocPolicy
return gen; return gen;
} }
/* size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const {
* This counts the HashTable's |table| array. If |countMe| is true it also return mallocSizeOf(table, capacity() * sizeof(Entry));
* counts the HashTable object itself. }
*/
size_t sizeOf(JSUsableSizeFun usf, bool countMe) const { size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
size_t usable = usf(table) + (countMe ? usf((void*)this) : 0); return mallocSizeOf(this, sizeof(HashTable)) + sizeOfExcludingThis(mallocSizeOf);
return usable ? usable
: (capacity() * sizeof(Entry)) + (countMe ? sizeof(HashTable) : 0);
} }
Ptr lookup(const Lookup &l) const { Ptr lookup(const Lookup &l) const {
@ -1097,7 +1095,16 @@ class HashMap
Range all() const { return impl.all(); } Range all() const { return impl.all(); }
size_t count() const { return impl.count(); } size_t count() const { return impl.count(); }
size_t capacity() const { return impl.capacity(); } size_t capacity() const { return impl.capacity(); }
size_t sizeOf(JSUsableSizeFun usf, bool cm) const { return impl.sizeOf(usf, cm); } size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const {
return impl.sizeOfExcludingThis(mallocSizeOf);
}
size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
/*
* Don't just call |impl.sizeOfExcludingThis()| because there's no
* guarantee that |impl| is the first field in HashMap.
*/
return mallocSizeOf(this, sizeof(*this)) + impl.sizeOfExcludingThis(mallocSizeOf);
}
/* /*
* Typedef for the enumeration class. An Enum may be used to examine and * Typedef for the enumeration class. An Enum may be used to examine and
@ -1298,7 +1305,16 @@ class HashSet
Range all() const { return impl.all(); } Range all() const { return impl.all(); }
size_t count() const { return impl.count(); } size_t count() const { return impl.count(); }
size_t capacity() const { return impl.capacity(); } size_t capacity() const { return impl.capacity(); }
size_t sizeOf(JSUsableSizeFun usf, bool cm) const { return impl.sizeOf(usf, cm); } size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const {
return impl.sizeOfExcludingThis(mallocSizeOf);
}
size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
/*
* Don't just call |impl.sizeOfExcludingThis()| because there's no
* guarantee that |impl| is the first field in HashSet.
*/
return mallocSizeOf(this, sizeof(*this)) + impl.sizeOfExcludingThis(mallocSizeOf);
}
/* /*
* Typedef for the enumeration class. An Enum may be used to examine and * Typedef for the enumeration class. An Enum may be used to examine and

View File

@ -895,11 +895,9 @@ RoundUpPow2(size_t x)
#endif /* defined(__cplusplus) */ #endif /* defined(__cplusplus) */
/* /*
* This signature is for malloc_usable_size-like functions used to measure * This is SpiderMonkey's equivalent to |nsMallocSizeOfFun|.
* memory usage. A return value of zero indicates that the size is unknown,
* and so a fall-back computation should be done for the size.
*/ */
typedef size_t(*JSUsableSizeFun)(void *p); typedef size_t(*JSMallocSizeOfFun)(const void *p, size_t computedSize);
/* sixgill annotation defines */ /* sixgill annotation defines */
#ifndef HAVE_STATIC_ANNOTATIONS #ifndef HAVE_STATIC_ANNOTATIONS

View File

@ -111,9 +111,8 @@ class BumpChunk
void setNext(BumpChunk *succ) { next_ = succ; } void setNext(BumpChunk *succ) { next_ = succ; }
size_t used() const { return bump - bumpBase(); } size_t used() const { return bump - bumpBase(); }
size_t sizeOf(JSUsableSizeFun usf) { size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) {
size_t usable = usf((void*)this); return mallocSizeOf(this, limit - headerBase());
return usable ? usable : limit - headerBase();
} }
void resetBump() { void resetBump() {
@ -294,22 +293,23 @@ class LifoAlloc
return accum; return accum;
} }
/* Get the total size of the arena chunks (including unused space), plus, /* Get the total size of the arena chunks (including unused space). */
* if |countMe| is true, the size of the LifoAlloc itself. */ size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const {
size_t sizeOf(JSUsableSizeFun usf, bool countMe) const {
size_t accum = 0; size_t accum = 0;
if (countMe) {
size_t usable = usf((void*)this);
accum += usable ? usable : sizeof(LifoAlloc);
}
BumpChunk *it = first; BumpChunk *it = first;
while (it) { while (it) {
accum += it->sizeOf(usf); accum += it->sizeOfIncludingThis(mallocSizeOf);
it = it->next(); it = it->next();
} }
return accum; return accum;
} }
/* Like sizeOfExcludingThis(), but includes the size of the LifoAlloc itself. */
size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
return mallocSizeOf(this, sizeof(LifoAlloc)) +
sizeOfExcludingThis(mallocSizeOf);
}
/* Doesn't perform construction; useful for lazily-initialized POD types. */ /* Doesn't perform construction; useful for lazily-initialized POD types. */
template <typename T> template <typename T>
JS_ALWAYS_INLINE JS_ALWAYS_INLINE

View File

@ -116,12 +116,12 @@ typedef struct TypeInferenceMemoryStats
extern JS_FRIEND_API(void) extern JS_FRIEND_API(void)
JS_GetTypeInferenceMemoryStats(JSContext *cx, JSCompartment *compartment, JS_GetTypeInferenceMemoryStats(JSContext *cx, JSCompartment *compartment,
TypeInferenceMemoryStats *stats, TypeInferenceMemoryStats *stats,
JSUsableSizeFun usf); JSMallocSizeOfFun mallocSizeOf);
extern JS_FRIEND_API(void) extern JS_FRIEND_API(void)
JS_GetTypeInferenceObjectStats(/*TypeObject*/ void *object, JS_GetTypeInferenceObjectStats(/*TypeObject*/ void *object,
TypeInferenceMemoryStats *stats, TypeInferenceMemoryStats *stats,
JSUsableSizeFun usf); JSMallocSizeOfFun mallocSizeOf);
extern JS_FRIEND_API(JSPrincipals *) extern JS_FRIEND_API(JSPrincipals *)
JS_GetCompartmentPrincipals(JSCompartment *compartment); JS_GetCompartmentPrincipals(JSCompartment *compartment);

View File

@ -6150,8 +6150,8 @@ TypeSet::dynamicSize()
{ {
/* /*
* This memory is allocated within the temp pool (but accounted for * This memory is allocated within the temp pool (but accounted for
* elsewhere) so we can't use a JSUsableSizeFun to measure it. We must do * elsewhere) so we can't use a JSMallocSizeOfFun to measure it. We must
* it analytically. * do it analytically.
*/ */
uint32 count = baseObjectCount(); uint32 count = baseObjectCount();
if (count >= 2) if (count >= 2)
@ -6164,8 +6164,8 @@ TypeObject::dynamicSize()
{ {
/* /*
* This memory is allocated within the temp pool (but accounted for * This memory is allocated within the temp pool (but accounted for
* elsewhere) so we can't use a JSUsableSizeFun to measure it. We must do * elsewhere) so we can't use a JSMallocSizeOfFun to measure it. We must
* it analytically. * do it analytically.
*/ */
size_t bytes = 0; size_t bytes = 0;
@ -6184,32 +6184,26 @@ TypeObject::dynamicSize()
} }
static void static void
GetScriptMemoryStats(JSScript *script, TypeInferenceMemoryStats *stats, JSUsableSizeFun usf) GetScriptMemoryStats(JSScript *script, TypeInferenceMemoryStats *stats, JSMallocSizeOfFun mallocSizeOf)
{ {
TypeScript *typeScript = script->types; TypeScript *typeScript = script->types;
if (!typeScript) if (!typeScript)
return; return;
size_t usable;
/* If TI is disabled, a single TypeScript is still present. */ /* If TI is disabled, a single TypeScript is still present. */
if (!script->compartment()->types.inferenceEnabled) { if (!script->compartment()->types.inferenceEnabled) {
usable = usf(typeScript); stats->scripts += mallocSizeOf(typeScript, sizeof(TypeScript));
stats->scripts += usable ? usable : sizeof(TypeScript);
return; return;
} }
usable = usf(typeScript->nesting); stats->scripts += mallocSizeOf(typeScript->nesting, sizeof(TypeScriptNesting));
stats->scripts += usable ? usable : sizeof(TypeScriptNesting);
unsigned count = TypeScript::NumTypeSets(script); unsigned count = TypeScript::NumTypeSets(script);
usable = usf(typeScript); stats->scripts += mallocSizeOf(typeScript, sizeof(TypeScript) + count * sizeof(TypeSet));
stats->scripts += usable ? usable : sizeof(TypeScript) + count * sizeof(TypeSet);
TypeResult *result = typeScript->dynamicList; TypeResult *result = typeScript->dynamicList;
while (result) { while (result) {
usable = usf(result); stats->scripts += mallocSizeOf(result, sizeof(TypeResult));
stats->scripts += usable ? usable : sizeof(TypeResult);
result = result->next; result = result->next;
} }
@ -6227,35 +6221,35 @@ GetScriptMemoryStats(JSScript *script, TypeInferenceMemoryStats *stats, JSUsable
JS_FRIEND_API(void) JS_FRIEND_API(void)
JS_GetTypeInferenceMemoryStats(JSContext *cx, JSCompartment *compartment, JS_GetTypeInferenceMemoryStats(JSContext *cx, JSCompartment *compartment,
TypeInferenceMemoryStats *stats, JSUsableSizeFun usf) TypeInferenceMemoryStats *stats,
JSMallocSizeOfFun mallocSizeOf)
{ {
/* /*
* Note: not all data in the pool is temporary, and some will survive GCs * Note: not all data in the pool is temporary, and some will survive GCs
* by being copied to the replacement pool. This memory will be counted * by being copied to the replacement pool. This memory will be counted
* elsewhere and deducted from the amount of temporary data. * elsewhere and deducted from the amount of temporary data.
*/ */
stats->temporary += compartment->typeLifoAlloc.sizeOf(usf, /* countMe = */false); stats->temporary += compartment->typeLifoAlloc.sizeOfExcludingThis(mallocSizeOf);
/* Pending arrays are cleared on GC along with the analysis pool. */ /* Pending arrays are cleared on GC along with the analysis pool. */
size_t usable = usf(compartment->types.pendingArray);
stats->temporary += stats->temporary +=
usable ? usable mallocSizeOf(compartment->types.pendingArray,
: sizeof(TypeCompartment::PendingWork) * compartment->types.pendingCapacity; sizeof(TypeCompartment::PendingWork) * compartment->types.pendingCapacity);
/* TypeCompartment::pendingRecompiles is non-NULL only while inference code is running. */ /* TypeCompartment::pendingRecompiles is non-NULL only while inference code is running. */
JS_ASSERT(!compartment->types.pendingRecompiles); JS_ASSERT(!compartment->types.pendingRecompiles);
for (gc::CellIter i(cx, compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) for (gc::CellIter i(cx, compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next())
GetScriptMemoryStats(i.get<JSScript>(), stats, usf); GetScriptMemoryStats(i.get<JSScript>(), stats, mallocSizeOf);
if (compartment->types.allocationSiteTable) if (compartment->types.allocationSiteTable)
stats->tables += compartment->types.allocationSiteTable->sizeOf(usf, /* countMe = */true); stats->tables += compartment->types.allocationSiteTable->sizeOfIncludingThis(mallocSizeOf);
if (compartment->types.arrayTypeTable) if (compartment->types.arrayTypeTable)
stats->tables += compartment->types.arrayTypeTable->sizeOf(usf, /* countMe = */true); stats->tables += compartment->types.arrayTypeTable->sizeOfIncludingThis(mallocSizeOf);
if (compartment->types.objectTypeTable) { if (compartment->types.objectTypeTable) {
stats->tables += compartment->types.objectTypeTable->sizeOf(usf, /* countMe = */true); stats->tables += compartment->types.objectTypeTable->sizeOfIncludingThis(mallocSizeOf);
for (ObjectTypeTable::Enum e(*compartment->types.objectTypeTable); for (ObjectTypeTable::Enum e(*compartment->types.objectTypeTable);
!e.empty(); !e.empty();
@ -6265,14 +6259,14 @@ JS_GetTypeInferenceMemoryStats(JSContext *cx, JSCompartment *compartment,
const ObjectTableEntry &value = e.front().value; const ObjectTableEntry &value = e.front().value;
/* key.ids and values.types have the same length. */ /* key.ids and values.types have the same length. */
usable = usf(key.ids) + usf(value.types); stats->tables += mallocSizeOf(key.ids, key.nslots * sizeof(jsid)) +
stats->tables += usable ? usable : key.nslots * (sizeof(jsid) + sizeof(Type)); mallocSizeOf(value.types, key.nslots * sizeof(Type));
} }
} }
} }
JS_FRIEND_API(void) JS_FRIEND_API(void)
JS_GetTypeInferenceObjectStats(void *object_, TypeInferenceMemoryStats *stats, JSUsableSizeFun usf) JS_GetTypeInferenceObjectStats(void *object_, TypeInferenceMemoryStats *stats, JSMallocSizeOfFun mallocSizeOf)
{ {
TypeObject *object = (TypeObject *) object_; TypeObject *object = (TypeObject *) object_;
@ -6288,23 +6282,19 @@ JS_GetTypeInferenceObjectStats(void *object_, TypeInferenceMemoryStats *stats, J
if (object->newScript) { if (object->newScript) {
/* The initializerList is tacked onto the end of the TypeNewScript. */ /* The initializerList is tacked onto the end of the TypeNewScript. */
size_t usable = usf(object->newScript); size_t computedSize = sizeof(TypeNewScript);
if (usable) { for (TypeNewScript::Initializer *init = object->newScript->initializerList; ; init++) {
stats->objects += usable; computedSize += sizeof(TypeNewScript::Initializer);
} else { if (init->kind == TypeNewScript::Initializer::DONE)
stats->objects += sizeof(TypeNewScript); break;
for (TypeNewScript::Initializer *init = object->newScript->initializerList; ; init++) {
stats->objects += sizeof(TypeNewScript::Initializer);
if (init->kind == TypeNewScript::Initializer::DONE)
break;
}
} }
stats->objects += mallocSizeOf(object->newScript, computedSize);
} }
if (object->emptyShapes) { if (object->emptyShapes) {
size_t usable = usf(object->emptyShapes);
stats->emptyShapes += stats->emptyShapes +=
usable ? usable : sizeof(EmptyShape*) * gc::FINALIZE_OBJECT_LIMIT; mallocSizeOf(object->emptyShapes,
sizeof(EmptyShape*) * gc::FINALIZE_OBJECT_LIMIT);
} }
/* /*

View File

@ -2708,12 +2708,11 @@ obj_preventExtensions(JSContext *cx, uintN argc, Value *vp)
} }
size_t size_t
JSObject::sizeOfSlotsArray(JSUsableSizeFun usf) JSObject::sizeOfSlotsArray(JSMallocSizeOfFun mallocSizeOf)
{ {
if (!hasSlotsArray()) if (!hasSlotsArray())
return 0; return 0;
size_t usable = usf((void *)slots); return mallocSizeOf(slots, numDynamicSlots(numSlots()) * sizeof(js::Value));
return usable ? usable : numSlots() * sizeof(js::Value);
} }
bool bool
@ -4559,10 +4558,16 @@ JSObject::shrinkSlots(JSContext *cx, size_t newcap)
} }
uint32 fill = newcap; uint32 fill = newcap;
newcap = Max(newcap, size_t(SLOT_CAPACITY_MIN)); if (isDenseArray()) {
newcap = Max(newcap, numFixedSlots()); newcap = Max(newcap, size_t(SLOT_CAPACITY_MIN));
newcap = Max(newcap, numFixedSlots());
} else {
newcap = Max(newcap, numFixedSlots() + SLOT_CAPACITY_MIN);
}
HeapValue *tmpslots = (HeapValue*) cx->realloc_(slots, newcap * sizeof(HeapValue)); uint32 allocCount = numDynamicSlots(newcap);
HeapValue *tmpslots = (HeapValue*) cx->realloc_(slots, allocCount * sizeof(Value));
if (!tmpslots) if (!tmpslots)
return; /* Leave slots at its old size. */ return; /* Leave slots at its old size. */

View File

@ -486,7 +486,7 @@ struct JSObject : js::gc::Cell {
jsuword &initializedLength() { return *newType.unsafeGetUnioned(); } jsuword &initializedLength() { return *newType.unsafeGetUnioned(); }
JS_FRIEND_API(size_t) sizeOfSlotsArray(JSUsableSizeFun usf); JS_FRIEND_API(size_t) sizeOfSlotsArray(JSMallocSizeOfFun mallocSizeOf);
js::HeapPtrObject parent; /* object's parent */ js::HeapPtrObject parent; /* object's parent */
void *privateData; /* private data */ void *privateData; /* private data */

View File

@ -257,9 +257,9 @@ struct PropertyTable {
* This counts the PropertyTable object itself (which must be * This counts the PropertyTable object itself (which must be
* heap-allocated) and its |entries| array. * heap-allocated) and its |entries| array.
*/ */
size_t sizeOf(JSUsableSizeFun usf) const { size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
size_t usable = usf((void*)this) + usf(entries); return mallocSizeOf(this, sizeof(PropertyTable)) +
return usable ? usable : sizeOfEntries(capacity()) + sizeof(PropertyTable); mallocSizeOf(entries, sizeOfEntries(capacity()));
} }
/* Whether we need to grow. We want to do this if the load factor is >= 0.75 */ /* Whether we need to grow. We want to do this if the load factor is >= 0.75 */
@ -449,15 +449,14 @@ struct Shape : public js::gc::Cell
return table; return table;
} }
size_t sizeOfPropertyTable(JSUsableSizeFun usf) const { size_t sizeOfPropertyTableIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
return hasTable() ? getTable()->sizeOf(usf) : 0; return hasTable() ? getTable()->sizeOfIncludingThis(mallocSizeOf) : 0;
} }
size_t sizeOfKids(JSUsableSizeFun usf) const { size_t sizeOfKidsIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
/* Nb: |countMe| is true because the kids HashTable is on the heap. */
JS_ASSERT(!inDictionary()); JS_ASSERT(!inDictionary());
return kids.isHash() return kids.isHash()
? kids.toHash()->sizeOf(usf, /* countMe */true) ? kids.toHash()->sizeOfIncludingThis(mallocSizeOf)
: 0; : 0;
} }

View File

@ -1273,15 +1273,14 @@ JSScript::dataSize()
} }
size_t size_t
JSScript::dataSize(JSUsableSizeFun usf) JSScript::dataSize(JSMallocSizeOfFun mallocSizeOf)
{ {
#if JS_SCRIPT_INLINE_DATA_LIMIT #if JS_SCRIPT_INLINE_DATA_LIMIT
if (data == inlineData) if (data == inlineData)
return 0; return 0;
#endif #endif
size_t usable = usf(data); return mallocSizeOf(data, dataSize());
return usable ? usable : dataSize();
} }
/* /*

View File

@ -660,7 +660,7 @@ struct JSScript : public js::gc::Cell {
} }
/* Size of the JITScript and all sections. (This method is implemented in MethodJIT.h.) */ /* Size of the JITScript and all sections. (This method is implemented in MethodJIT.h.) */
JS_FRIEND_API(size_t) jitDataSize(JSUsableSizeFun usf); JS_FRIEND_API(size_t) jitDataSize(JSMallocSizeOfFun mallocSizeOf);
#endif #endif
@ -682,9 +682,9 @@ struct JSScript : public js::gc::Cell {
* second is the size of the block allocated to hold all the data sections * second is the size of the block allocated to hold all the data sections
* (which can be larger than the in-use size). * (which can be larger than the in-use size).
*/ */
JS_FRIEND_API(size_t) dataSize(); /* Size of all data sections */ JS_FRIEND_API(size_t) dataSize(); /* Size of all data sections */
JS_FRIEND_API(size_t) dataSize(JSUsableSizeFun usf); /* Size of all data sections */ JS_FRIEND_API(size_t) dataSize(JSMallocSizeOfFun mallocSizeOf); /* Size of all data sections */
uint32 numNotes(); /* Number of srcnote slots in the srcnotes section */ uint32 numNotes(); /* Number of srcnote slots in the srcnotes section */
/* Script notes are allocated right after the code. */ /* Script notes are allocated right after the code. */
jssrcnote *notes() { return (jssrcnote *)(code + length); } jssrcnote *notes() { return (jssrcnote *)(code + length); }

View File

@ -1302,22 +1302,21 @@ mjit::JITScript::~JITScript()
} }
size_t size_t
JSScript::jitDataSize(JSUsableSizeFun usf) JSScript::jitDataSize(JSMallocSizeOfFun mallocSizeOf)
{ {
size_t n = 0; size_t n = 0;
if (jitNormal) if (jitNormal)
n += jitNormal->scriptDataSize(usf); n += jitNormal->scriptDataSize(mallocSizeOf);
if (jitCtor) if (jitCtor)
n += jitCtor->scriptDataSize(usf); n += jitCtor->scriptDataSize(mallocSizeOf);
return n; return n;
} }
/* Please keep in sync with Compiler::finishThisUp! */ /* Please keep in sync with Compiler::finishThisUp! */
size_t size_t
mjit::JITScript::scriptDataSize(JSUsableSizeFun usf) mjit::JITScript::scriptDataSize(JSMallocSizeOfFun mallocSizeOf)
{ {
size_t usable = usf ? usf(this) : 0; size_t computedSize =
return usable ? usable :
sizeof(JITScript) + sizeof(JITScript) +
sizeof(NativeMapEntry) * nNmapPairs + sizeof(NativeMapEntry) * nNmapPairs +
sizeof(InlineFrame) * nInlineFrames + sizeof(InlineFrame) * nInlineFrames +
@ -1334,6 +1333,8 @@ mjit::JITScript::scriptDataSize(JSUsableSizeFun usf)
sizeof(ic::SetElementIC) * nSetElems + sizeof(ic::SetElementIC) * nSetElems +
#endif #endif
0; 0;
/* |mallocSizeOf| can be null here. */
return mallocSizeOf ? mallocSizeOf(this, computedSize) : computedSize;
} }
void void

View File

@ -682,8 +682,8 @@ struct JITScript {
void nukeScriptDependentICs(); void nukeScriptDependentICs();
/* |usf| can be NULL here, in which case the fallback size computation will be used. */ /* |mallocSizeOf| can be NULL here, in which case the fallback size computation will be used. */
size_t scriptDataSize(JSUsableSizeFun usf); size_t scriptDataSize(JSMallocSizeOfFun mallocSizeOf);
jsbytecode *nativeToPC(void *returnAddress, CallSite **pinline) const; jsbytecode *nativeToPC(void *returnAddress, CallSite **pinline) const;

View File

@ -3829,9 +3829,9 @@ MJitCodeStats(JSContext *cx, uintN argc, jsval *vp)
#ifdef JS_METHODJIT #ifdef JS_METHODJIT
static size_t static size_t
zero_usable_size(void *p) computedSize(const void *p, size_t size)
{ {
return 0; return size;
} }
static void static void
@ -3842,10 +3842,10 @@ SumJitDataSizeCallback(JSContext *cx, void *data, void *thing,
JS_ASSERT(traceKind == JSTRACE_SCRIPT); JS_ASSERT(traceKind == JSTRACE_SCRIPT);
JSScript *script = static_cast<JSScript *>(thing); JSScript *script = static_cast<JSScript *>(thing);
/* /*
* Passing in zero_usable_size causes jitDataSize to fall back to its * Passing in |computedSize| causes jitDataSize to fall back to its
* secondary size computation. * secondary size computation.
*/ */
*sump += script->jitDataSize(zero_usable_size); *sump += script->jitDataSize(computedSize);
} }
#endif #endif

View File

@ -87,7 +87,7 @@ JSLinearString::mark(JSTracer *)
} }
size_t size_t
JSString::charsHeapSize(JSUsableSizeFun usf) JSString::charsHeapSize(JSMallocSizeOfFun mallocSizeOf)
{ {
/* JSRope: do nothing, we'll count all children chars when we hit the leaf strings. */ /* JSRope: do nothing, we'll count all children chars when we hit the leaf strings. */
if (isRope()) if (isRope())
@ -104,8 +104,7 @@ JSString::charsHeapSize(JSUsableSizeFun usf)
/* JSExtensibleString: count the full capacity, not just the used space. */ /* JSExtensibleString: count the full capacity, not just the used space. */
if (isExtensible()) { if (isExtensible()) {
JSExtensibleString &extensible = asExtensible(); JSExtensibleString &extensible = asExtensible();
size_t usable = usf((void *)extensible.chars()); return mallocSizeOf(extensible.chars(), asExtensible().capacity() * sizeof(jschar));
return usable ? usable : asExtensible().capacity() * sizeof(jschar);
} }
JS_ASSERT(isFixed()); JS_ASSERT(isFixed());
@ -118,10 +117,9 @@ JSString::charsHeapSize(JSUsableSizeFun usf)
if (isInline()) if (isInline())
return 0; return 0;
/* JSAtom, JSFixedString: count the chars. */ /* JSAtom, JSFixedString: count the chars. +1 for the null char. */
JSFixedString &fixed = asFixed(); JSFixedString &fixed = asFixed();
size_t usable = usf((void *)fixed.chars()); return mallocSizeOf(fixed.chars(), (length() + 1) * sizeof(jschar));
return usable ? usable : length() * sizeof(jschar);
} }
static JS_ALWAYS_INLINE bool static JS_ALWAYS_INLINE bool

View File

@ -407,7 +407,7 @@ class JSString : public js::gc::Cell
/* Gets the number of bytes that the chars take on the heap. */ /* Gets the number of bytes that the chars take on the heap. */
JS_FRIEND_API(size_t) charsHeapSize(JSUsableSizeFun usf); JS_FRIEND_API(size_t) charsHeapSize(JSMallocSizeOfFun mallocSizeOf);
/* Offsets for direct field from jit code. */ /* Offsets for direct field from jit code. */

View File

@ -1233,7 +1233,7 @@ CompartmentCallback(JSContext *cx, void *vdata, JSCompartment *compartment)
curr->mjitCodeUnused = unused; curr->mjitCodeUnused = unused;
#endif #endif
JS_GetTypeInferenceMemoryStats(cx, compartment, &curr->typeInferenceMemory, JS_GetTypeInferenceMemoryStats(cx, compartment, &curr->typeInferenceMemory,
moz_malloc_usable_size); MemoryReporterMallocSizeOf);
} }
void void
@ -1278,14 +1278,14 @@ CellCallback(JSContext *cx, void *vdata, void *thing, JSGCTraceKind traceKind,
} else { } else {
curr->gcHeapObjectsNonFunction += thingSize; curr->gcHeapObjectsNonFunction += thingSize;
} }
curr->objectSlots += obj->sizeOfSlotsArray(moz_malloc_usable_size); curr->objectSlots += obj->sizeOfSlotsArray(MemoryReporterMallocSizeOf);
break; break;
} }
case JSTRACE_STRING: case JSTRACE_STRING:
{ {
JSString *str = static_cast<JSString *>(thing); JSString *str = static_cast<JSString *>(thing);
curr->gcHeapStrings += thingSize; curr->gcHeapStrings += thingSize;
curr->stringChars += str->charsHeapSize(moz_malloc_usable_size); curr->stringChars += str->charsHeapSize(MemoryReporterMallocSizeOf);
break; break;
} }
case JSTRACE_SHAPE: case JSTRACE_SHAPE:
@ -1293,11 +1293,14 @@ CellCallback(JSContext *cx, void *vdata, void *thing, JSGCTraceKind traceKind,
js::Shape *shape = static_cast<js::Shape *>(thing); js::Shape *shape = static_cast<js::Shape *>(thing);
if (shape->inDictionary()) { if (shape->inDictionary()) {
curr->gcHeapShapesDict += thingSize; curr->gcHeapShapesDict += thingSize;
curr->shapesExtraDictTables += shape->sizeOfPropertyTable(moz_malloc_usable_size); curr->shapesExtraDictTables +=
shape->sizeOfPropertyTableIncludingThis(MemoryReporterMallocSizeOf);
} else { } else {
curr->gcHeapShapesTree += thingSize; curr->gcHeapShapesTree += thingSize;
curr->shapesExtraTreeTables += shape->sizeOfPropertyTable(moz_malloc_usable_size); curr->shapesExtraTreeTables +=
curr->shapesExtraTreeShapeKids += shape->sizeOfKids(moz_malloc_usable_size); shape->sizeOfPropertyTableIncludingThis(MemoryReporterMallocSizeOf);
curr->shapesExtraTreeShapeKids +=
shape->sizeOfKidsIncludingThis(MemoryReporterMallocSizeOf);
} }
break; break;
} }
@ -1305,9 +1308,9 @@ CellCallback(JSContext *cx, void *vdata, void *thing, JSGCTraceKind traceKind,
{ {
JSScript *script = static_cast<JSScript *>(thing); JSScript *script = static_cast<JSScript *>(thing);
curr->gcHeapScripts += thingSize; curr->gcHeapScripts += thingSize;
curr->scriptData += script->dataSize(moz_malloc_usable_size); curr->scriptData += script->dataSize(MemoryReporterMallocSizeOf);
#ifdef JS_METHODJIT #ifdef JS_METHODJIT
curr->mjitData += script->jitDataSize(moz_malloc_usable_size); curr->mjitData += script->jitDataSize(MemoryReporterMallocSizeOf);
#endif #endif
break; break;
} }
@ -1315,7 +1318,8 @@ CellCallback(JSContext *cx, void *vdata, void *thing, JSGCTraceKind traceKind,
{ {
js::types::TypeObject *obj = static_cast<js::types::TypeObject *>(thing); js::types::TypeObject *obj = static_cast<js::types::TypeObject *>(thing);
curr->gcHeapTypeObjects += thingSize; curr->gcHeapTypeObjects += thingSize;
JS_GetTypeInferenceObjectStats(obj, &curr->typeInferenceMemory, moz_malloc_usable_size); JS_GetTypeInferenceObjectStats(obj, &curr->typeInferenceMemory,
MemoryReporterMallocSizeOf);
break; break;
} }
case JSTRACE_XML: case JSTRACE_XML:
@ -1530,13 +1534,12 @@ CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data)
for (js::ThreadDataIter i(rt); !i.empty(); i.popFront()) for (js::ThreadDataIter i(rt); !i.empty(); i.popFront())
data->stackSize += i.threadData()->stackSpace.committedSize(); data->stackSize += i.threadData()->stackSpace.committedSize();
size_t usable = moz_malloc_usable_size(rt); data->runtimeObjectSize = MemoryReporterMallocSizeOf(rt, sizeof(JSRuntime));
data->runtimeObjectSize = usable ? usable : sizeof(JSRuntime);
// Nb: |countMe| is false because atomState.atoms is within JSRuntime, // Nb: we use sizeOfExcludingThis() because atomState.atoms is within
// and so counted when JSRuntime is counted. // JSRuntime, and so counted when JSRuntime is counted.
data->atomsTableSize = data->atomsTableSize =
rt->atomState.atoms.sizeOf(moz_malloc_usable_size, /* countMe */false); rt->atomState.atoms.sizeOfExcludingThis(MemoryReporterMallocSizeOf);
} }
JS_DestroyContextNoGC(cx); JS_DestroyContextNoGC(cx);

View File

@ -4295,25 +4295,29 @@ nsLayoutUtils::GetFontFacesForText(nsIFrame* aFrame,
} }
/* static */ /* static */
nsresult size_t
nsLayoutUtils::GetTextRunMemoryForFrames(nsIFrame* aFrame, PRUint64* aTotal) nsLayoutUtils::SizeOfTextRunsForFrames(nsIFrame* aFrame,
nsMallocSizeOfFun aMallocSizeOf,
bool clear)
{ {
NS_PRECONDITION(aFrame, "NULL frame pointer"); NS_PRECONDITION(aFrame, "NULL frame pointer");
size_t total = 0;
if (aFrame->GetType() == nsGkAtoms::textFrame) { if (aFrame->GetType() == nsGkAtoms::textFrame) {
nsTextFrame* textFrame = static_cast<nsTextFrame*>(aFrame); nsTextFrame* textFrame = static_cast<nsTextFrame*>(aFrame);
for (PRUint32 i = 0; i < 2; ++i) { for (PRUint32 i = 0; i < 2; ++i) {
gfxTextRun *run = textFrame->GetTextRun( gfxTextRun *run = textFrame->GetTextRun(
(i != 0) ? nsTextFrame::eInflated : nsTextFrame::eNotInflated); (i != 0) ? nsTextFrame::eInflated : nsTextFrame::eNotInflated);
if (run) { if (run) {
if (aTotal) { if (clear) {
run->AccountForSize(aTotal); run->ResetSizeOfAccountingFlags();
} else { } else {
run->ClearSizeAccounted(); total += run->MaybeSizeOfIncludingThis(aMallocSizeOf);
} }
} }
} }
return NS_OK; return total;
} }
nsAutoTArray<nsIFrame::ChildList,4> childListArray; nsAutoTArray<nsIFrame::ChildList,4> childListArray;
@ -4323,11 +4327,10 @@ nsLayoutUtils::GetTextRunMemoryForFrames(nsIFrame* aFrame, PRUint64* aTotal)
!childLists.IsDone(); childLists.Next()) { !childLists.IsDone(); childLists.Next()) {
for (nsFrameList::Enumerator e(childLists.CurrentList()); for (nsFrameList::Enumerator e(childLists.CurrentList());
!e.AtEnd(); e.Next()) { !e.AtEnd(); e.Next()) {
GetTextRunMemoryForFrames(e.get(), aTotal); total += SizeOfTextRunsForFrames(e.get(), aMallocSizeOf, clear);
} }
} }
return total;
return NS_OK;
} }
/* static */ /* static */

View File

@ -1438,17 +1438,18 @@ public:
/** /**
* Walks the frame tree starting at aFrame looking for textRuns. * Walks the frame tree starting at aFrame looking for textRuns.
* If aTotal is NULL, just clears the TEXT_RUN_MEMORY_ACCOUNTED flag * If |clear| is true, just clears the TEXT_RUN_MEMORY_ACCOUNTED flag
* on each textRun found. * on each textRun found (and |aMallocSizeOf| is not used).
* If aTotal is non-NULL, adds the storage used for each textRun to the * If |clear| is false, adds the storage used for each textRun to the
* total, and sets the TEXT_RUN_MEMORY_ACCOUNTED flag to avoid double- * total, and sets the TEXT_RUN_MEMORY_ACCOUNTED flag to avoid double-
* accounting. (Runs with this flag already set will be skipped.) * accounting. (Runs with this flag already set will be skipped.)
* Expected usage pattern is therefore to call twice: * Expected usage pattern is therefore to call twice:
* rv = GetTextRunMemoryForFrames(rootFrame, NULL); * (void)SizeOfTextRunsForFrames(rootFrame, nsnull, true);
* rv = GetTextRunMemoryForFrames(rootFrame, &total); * total = SizeOfTextRunsForFrames(rootFrame, mallocSizeOf, false);
*/ */
static nsresult GetTextRunMemoryForFrames(nsIFrame* aFrame, static size_t SizeOfTextRunsForFrames(nsIFrame* aFrame,
PRUint64* aTotal); nsMallocSizeOfFun aMallocSizeOf,
bool clear);
/** /**
* Checks if CSS 3D transforms are currently enabled. * Checks if CSS 3D transforms are currently enabled.

View File

@ -661,8 +661,7 @@ PresShell::MemoryReporter::SizeEnumerator(PresShellPtrKey *aEntry,
PRUint32 styleSize; PRUint32 styleSize;
styleSize = aShell->StyleSet()->SizeOf(); styleSize = aShell->StyleSet()->SizeOf();
PRUint64 textRunsSize; PRInt64 textRunsSize = aShell->SizeOfTextRuns(MemoryReporterMallocSizeOf);
textRunsSize = aShell->ComputeTextRunMemoryUsed();
data->callback-> data->callback->
Callback(EmptyCString(), arenaPath, nsIMemoryReporter::KIND_HEAP, Callback(EmptyCString(), arenaPath, nsIMemoryReporter::KIND_HEAP,
@ -693,7 +692,7 @@ PresShell::MemoryReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb,
data.closure = aClosure; data.closure = aClosure;
// clear TEXT_RUN_SIZE_ACCOUNTED flag on cached runs // clear TEXT_RUN_SIZE_ACCOUNTED flag on cached runs
gfxTextRunWordCache::ComputeStorage(nsnull); gfxTextRunWordCache::ResetSizeOfAccountingFlags();
sLiveShells->EnumerateEntries(SizeEnumerator, &data); sLiveShells->EnumerateEntries(SizeEnumerator, &data);
@ -704,8 +703,8 @@ PresShell::MemoryReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb,
"not owned by a PresShell's frame tree."); "not owned by a PresShell's frame tree.");
// now total up cached runs that aren't otherwise accounted for // now total up cached runs that aren't otherwise accounted for
PRUint64 textRunWordCacheSize = 0; PRInt64 textRunWordCacheSize =
gfxTextRunWordCache::ComputeStorage(&textRunWordCacheSize); gfxTextRunWordCache::MaybeSizeOfExcludingThis(MemoryReporterMallocSizeOf);
aCb->Callback(EmptyCString(), kTextRunWordCachePath, aCb->Callback(EmptyCString(), kTextRunWordCachePath,
nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES, nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES,
@ -8808,8 +8807,8 @@ PresShell::GetRootPresShell()
return nsnull; return nsnull;
} }
PRUint64 size_t
PresShell::ComputeTextRunMemoryUsed() PresShell::SizeOfTextRuns(nsMallocSizeOfFun aMallocSizeOf)
{ {
nsIFrame* rootFrame = FrameManager()->GetRootFrame(); nsIFrame* rootFrame = FrameManager()->GetRootFrame();
if (!rootFrame) { if (!rootFrame) {
@ -8817,12 +8816,11 @@ PresShell::ComputeTextRunMemoryUsed()
} }
// clear the TEXT_RUN_MEMORY_ACCOUNTED flags // clear the TEXT_RUN_MEMORY_ACCOUNTED flags
nsLayoutUtils::GetTextRunMemoryForFrames(rootFrame, nsnull); nsLayoutUtils::SizeOfTextRunsForFrames(rootFrame, nsnull,
/* clear = */true);
// collect the total memory in use for textruns // collect the total memory in use for textruns
PRUint64 total = 0; return nsLayoutUtils::SizeOfTextRunsForFrames(rootFrame, aMallocSizeOf,
nsLayoutUtils::GetTextRunMemoryForFrames(rootFrame, &total); /* clear = */false);
return total;
} }

View File

@ -890,7 +890,7 @@ public:
return result; return result;
} }
PRUint64 ComputeTextRunMemoryUsed(); size_t SizeOfTextRuns(nsMallocSizeOfFun aMallocSizeOf);
class MemoryReporter : public nsIMemoryMultiReporter class MemoryReporter : public nsIMemoryMultiReporter
{ {

View File

@ -99,28 +99,28 @@ nsTransformedTextRun::SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength,
return changed; return changed;
} }
PRUint64 size_t
nsTransformedTextRun::ComputeSize() nsTransformedTextRun::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf)
{ {
PRUint32 total = gfxTextRun::ComputeSize(); size_t total = gfxTextRun::SizeOfExcludingThis(aMallocSizeOf);
if (moz_malloc_usable_size(this) == 0) {
total += sizeof(nsTransformedTextRun) - sizeof(gfxTextRun);
}
total += mStyles.SizeOf(); total += mStyles.SizeOf();
total += mCapitalize.SizeOf(); total += mCapitalize.SizeOf();
if (mOwnsFactory) { if (mOwnsFactory) {
PRUint32 factorySize = moz_malloc_usable_size(mFactory); // It's not worth the effort to get all the sub-class cases right for a
if (factorySize == 0) { // small size in the fallback case. So we use a |computedSize| of 0, which
// this may not quite account for everything // disables any usable vs. computedSize checking done by aMallocSizeOf.
// (e.g. nsCaseTransformTextRunFactory adds a couple of members) total += aMallocSizeOf(mFactory, 0);
// but I'm not sure it's worth the effort to track more precisely
factorySize = sizeof(nsTransformingTextRunFactory);
}
total += factorySize;
} }
return total; return total;
} }
size_t
nsTransformedTextRun::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf)
{
return aMallocSizeOf(this, sizeof(nsTransformedTextRun)) +
SizeOfExcludingThis(aMallocSizeOf);
}
nsTransformedTextRun* nsTransformedTextRun*
nsTransformingTextRunFactory::MakeTextRun(const PRUnichar* aString, PRUint32 aLength, nsTransformingTextRunFactory::MakeTextRun(const PRUnichar* aString, PRUint32 aLength,
const gfxTextRunFactory::Parameters* aParams, const gfxTextRunFactory::Parameters* aParams,

View File

@ -132,8 +132,9 @@ public:
} }
} }
// override the gfxTextRun impl to account for additional members here // override the gfxTextRun impls to account for additional members here
virtual PRUint64 ComputeSize(); virtual NS_MUST_OVERRIDE size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf);
virtual NS_MUST_OVERRIDE size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf);
nsTransformingTextRunFactory *mFactory; nsTransformingTextRunFactory *mFactory;
nsTArray<nsRefPtr<nsStyleContext> > mStyles; nsTArray<nsRefPtr<nsStyleContext> > mStyles;

View File

@ -268,6 +268,12 @@ moz_malloc_usable_size(void *ptr)
#endif #endif
} }
size_t moz_malloc_size_of(const void *ptr, size_t computedSize)
{
size_t usable = moz_malloc_usable_size((void *)ptr);
return usable ? usable : computedSize;
}
namespace mozilla { namespace mozilla {
const fallible_t fallible = fallible_t(); const fallible_t fallible = fallible_t();

View File

@ -53,7 +53,6 @@
#include "xpcom-config.h" #include "xpcom-config.h"
#define MOZALLOC_HAVE_XMALLOC #define MOZALLOC_HAVE_XMALLOC
#define MOZALLOC_HAVE_MALLOC_USABLE_SIZE
#if defined(MOZALLOC_EXPORT) #if defined(MOZALLOC_EXPORT)
/* do nothing: it's been defined to __declspec(dllexport) by /* do nothing: it's been defined to __declspec(dllexport) by
@ -136,6 +135,8 @@ MOZALLOC_EXPORT char* moz_strdup(const char* str)
MOZALLOC_EXPORT size_t moz_malloc_usable_size(void *ptr); MOZALLOC_EXPORT size_t moz_malloc_usable_size(void *ptr);
MOZALLOC_EXPORT size_t moz_malloc_size_of(const void *ptr, size_t computedSize);
#if defined(HAVE_STRNDUP) #if defined(HAVE_STRNDUP)
MOZALLOC_EXPORT char* moz_xstrndup(const char* str, size_t strsize) MOZALLOC_EXPORT char* moz_xstrndup(const char* str, size_t strsize)
NS_ATTR_MALLOC NS_WARN_UNUSED_RESULT; NS_ATTR_MALLOC NS_WARN_UNUSED_RESULT;

View File

@ -169,8 +169,8 @@ nsDiskCacheMap::Open(nsILocalFile * cacheDirectory)
{ {
// extra scope so the compiler doesn't barf on the above gotos jumping // extra scope so the compiler doesn't barf on the above gotos jumping
// past this declaration down here // past this declaration down here
PRUint32 overhead = moz_malloc_usable_size(mRecordArray); PRUint32 overhead =
overhead = overhead ? overhead : mHeader.mRecordCount * sizeof(nsDiskCacheRecord); moz_malloc_size_of(mRecordArray, mHeader.mRecordCount * sizeof(nsDiskCacheRecord));
mozilla::Telemetry::Accumulate(mozilla::Telemetry::HTTP_DISK_CACHE_OVERHEAD, mozilla::Telemetry::Accumulate(mozilla::Telemetry::HTTP_DISK_CACHE_OVERHEAD,
overhead); overhead);
} }

View File

@ -1295,7 +1295,7 @@ PRInt64 GetHistoryObserversSize()
History* history = History::GetService(); History* history = History::GetService();
if (!history) if (!history)
return 0; return 0;
return sizeof(*history) + history->SizeOf(); return history->SizeOfIncludingThis(MemoryReporterMallocSizeOf);
} }
NS_MEMORY_REPORTER_IMPLEMENT(HistoryService, NS_MEMORY_REPORTER_IMPLEMENT(HistoryService,
@ -1585,9 +1585,10 @@ History::SizeOfEnumerator(KeyClass* aEntry, void* aArg)
} }
PRInt64 PRInt64
History::SizeOf() History::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOfThis)
{ {
PRInt64 size = mObservers.SizeOf(); PRInt64 size = aMallocSizeOfThis(this, sizeof(History)) +
mObservers.ShallowSizeOfExcludingThis(aMallocSizeOfThis);
if (mObservers.IsInitialized()) { if (mObservers.IsInitialized()) {
mObservers.EnumerateEntries(SizeOfEnumerator, &size); mObservers.EnumerateEntries(SizeOfEnumerator, &size);
} }

View File

@ -112,10 +112,10 @@ public:
bool FetchPageInfo(VisitData& _place); bool FetchPageInfo(VisitData& _place);
/** /**
* Get the number of bytes of memory this History object is using (not * Get the number of bytes of memory this History object is using,
* counting sizeof(*this)). * including sizeof(*this))
*/ */
PRInt64 SizeOf(); PRInt64 SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf);
/** /**
* Obtains a pointer to this service. * Obtains a pointer to this service.

View File

@ -21,6 +21,7 @@
* *
* Contributor(s): * Contributor(s):
* Vladimir Vukicevic <vladimir@pobox.com> (original author) * Vladimir Vukicevic <vladimir@pobox.com> (original author)
* Nicholas Nethercote <nnethercote@mozilla.com>
* *
* Alternatively, the contents of this file may be used under the terms of * Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"), * either of the GNU General Public License Version 2 or later (the "GPL"),
@ -40,6 +41,13 @@
interface nsISimpleEnumerator; interface nsISimpleEnumerator;
/*
* Memory reporters measure Firefox's memory usage. They are mainly used to
* generate the about:memory page. You should read
* https://wiki.mozilla.org/Memory_Reporting before writing a memory
* reporter.
*/
/* /*
* An nsIMemoryReporter reports a single memory measurement as an object. * An nsIMemoryReporter reports a single memory measurement as an object.
* Use this when it makes sense to gather this measurement without gathering * Use this when it makes sense to gather this measurement without gathering
@ -306,4 +314,68 @@ nsresult NS_RegisterMemoryMultiReporter (nsIMemoryMultiReporter *reporter);
nsresult NS_UnregisterMemoryReporter (nsIMemoryReporter *reporter); nsresult NS_UnregisterMemoryReporter (nsIMemoryReporter *reporter);
nsresult NS_UnregisterMemoryMultiReporter (nsIMemoryMultiReporter *reporter); nsresult NS_UnregisterMemoryMultiReporter (nsIMemoryMultiReporter *reporter);
namespace mozilla {
/*
* This function should be used by all traversal-based memory reporters.
* - On platforms where moz_malloc_usable_size() returns 0 it just returns
* |computedSize| (this happens on platforms where malloc_usable_size() or
* equivalent isn't available).
* - Otherwise, it |returns moz_malloc_usable_size(p)|, but only after doing
* some sanity checking -- it will assert if the usable size is too
* dissimilar to |computedSize|. (However, this checking is skipped if
* |computedSize| is zero, which is useful if the computation is not worth
* the effort, e.g. because it's tricky and the |computedSize| would be
* small.)
*/
size_t MemoryReporterMallocSizeOf(const void *ptr, size_t computedSize);
/*
* These functions are like MemoryReporterMallocSizeOf(), and should be used by
* all counter-based memory reporters when incrementing/decrementing a counter.
*/
size_t MemoryReporterMallocSizeOfForCounterInc(const void *ptr,
size_t computedSize);
size_t MemoryReporterMallocSizeOfForCounterDec(const void *ptr,
size_t computedSize);
/*
* For the purposes of debugging, temporary profiling, and DMD integration, it
* is sometimes useful to temporarily create multiple variants of
* MemoryReporterMallocSizeOf(), with each one distinguished by a string
* |name|. This macro makes creating such variants easy. |name| isn't used,
* but it will be if extra debugging code is temporarily added.
*/
#define NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(fn, name) \
size_t fn(const void *ptr, size_t computedSize) \
{ \
size_t usable = moz_malloc_usable_size((void*)ptr); \
if (!usable) { \
return computedSize; \
} \
NS_MEMORY_REPORTER_CHECK_SIZES(usable, computedSize); \
return usable; \
}
/*
* This is used by the MemoryReporterMallocSizeOf* functions for checking
* usable against computedSize.
*/
#define NS_MEMORY_REPORTER_CHECK_SIZES(usable, computedSize) \
do { \
/* The factor of two is because no reasonable heap allocator will */ \
/* return a block more than twice the requested size. The one */ \
/* exception is that a request less than N bytes may be rounded up */ \
/* by the allocator to N bytes (we use N = 16 in our checking */ \
/* because that's what the default allocator on Mac uses). Also, if */ \
/* computedSize is 0 we don't check it against usable. */ \
NS_ASSERTION(usable >= computedSize, \
"MemoryReporterMallocSizeOf: computedSize is too big"); \
NS_ASSERTION(usable < computedSize * 2 || usable <= 16 || \
computedSize == 0, \
"MemoryReporterMallocSizeOf: computedSize is too small"); \
} while(0)
}
%} %}

View File

@ -21,6 +21,7 @@
* *
* Contributor(s): * Contributor(s):
* Vladimir Vukicevic <vladimir@pobox.com> (original author) * Vladimir Vukicevic <vladimir@pobox.com> (original author)
* Nicholas Nethercote <nnethercote@mozilla.com>
* *
* Alternatively, the contents of this file may be used under the terms of * Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"), * either of the GNU General Public License Version 2 or later (the "GPL"),
@ -856,3 +857,11 @@ NS_UnregisterMemoryMultiReporter (nsIMemoryMultiReporter *reporter)
return mgr->UnregisterMultiReporter(reporter); return mgr->UnregisterMultiReporter(reporter);
} }
namespace mozilla {
NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(MemoryReporterMallocSizeOf, "default")
NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(MemoryReporterMallocSizeOfForCounterInc, "default")
NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(MemoryReporterMallocSizeOfForCounterDec, "default")
}

View File

@ -57,6 +57,13 @@
*/ */
#include "prtypes.h" #include "prtypes.h"
/*
* This is for functions that are like malloc_usable_size but also take a
* computed size as a fallback. Such functions are used for measuring the size
* of data structures.
*/
typedef size_t(*nsMallocSizeOfFun)(const void *p, size_t computedSize);
/* Core XPCOM declarations. */ /* Core XPCOM declarations. */
/** /**

View File

@ -251,10 +251,14 @@ public:
PL_DHashTableEnumerate(&mTable, PL_DHashStubEnumRemove, nsnull); PL_DHashTableEnumerate(&mTable, PL_DHashStubEnumRemove, nsnull);
} }
PRUint64 SizeOf() /**
* The "Shallow" means that if the entries contain pointers to other objects,
* their size isn't included in the measuring.
*/
size_t ShallowSizeOfExcludingThis(nsMallocSizeOfFun mallocSizeOf)
{ {
if (IsInitialized()) { if (IsInitialized()) {
return PL_DHashTableSizeOf(&mTable); return PL_DHashTableShallowSizeOfExcludingThis(&mTable, mallocSizeOf);
} }
return 0; return 0;
} }

View File

@ -795,22 +795,12 @@ PL_DHashTableEnumerate(PLDHashTable *table, PLDHashEnumerator etor, void *arg)
return i; return i;
} }
PRUint64 size_t
PL_DHashTableSizeOf(PLDHashTable *table) PL_DHashTableShallowSizeOfExcludingThis(PLDHashTable *table,
nsMallocSizeOfFun mallocSizeOf)
{ {
PRUint64 size = 0; return mallocSizeOf(table->entryStore,
PL_DHASH_TABLE_SIZE(table) * table->entrySize);
#ifdef MOZALLOC_HAVE_MALLOC_USABLE_SIZE
// Even when moz_malloc_usable_size is defined, it might always return 0, if
// the allocator in use doesn't support malloc_usable_size.
size = moz_malloc_usable_size(table->entryStore);
#endif
if (size == 0) {
size = PL_DHASH_TABLE_SIZE(table) * table->entrySize;
}
return size;
} }
#ifdef DEBUG #ifdef DEBUG

View File

@ -579,13 +579,13 @@ NS_COM_GLUE PRUint32
PL_DHashTableEnumerate(PLDHashTable *table, PLDHashEnumerator etor, void *arg); PL_DHashTableEnumerate(PLDHashTable *table, PLDHashEnumerator etor, void *arg);
/** /**
* Get the hashtable's "shallow heap size" in bytes. The size returned here * Get the hashtable's entry storage size in bytes, excluding sizeof(*this) and
* includes all the heap memory allocated by the hashtable itself. It does not * any heap memory allocated by the objects in the hash table (hence the
* include sizeof(*this) or heap memory allocated by the objects in the hash * "Shallow").
* table.
*/ */
NS_COM_GLUE PRUint64 NS_COM_GLUE size_t
PL_DHashTableSizeOf(PLDHashTable *table); PL_DHashTableShallowSizeOfExcludingThis(PLDHashTable *table,
nsMallocSizeOfFun mallocSizeOf);
#ifdef DEBUG #ifdef DEBUG
/** /**