Bug 856368 - Merge nsPresArena::mState into nsPresArena itself. r=roc

This commit is contained in:
Mats Palmgren 2013-04-15 22:00:06 +02:00
Родитель 6ce54168af
Коммит 62e2d164d6
3 изменённых файлов: 222 добавлений и 233 удалений

Просмотреть файл

@ -7,13 +7,20 @@
/* arena allocation for the frame tree and closely-related objects */ /* arena allocation for the frame tree and closely-related objects */
// Even on 32-bit systems, we allocate objects from the frame arena
// that require 8-byte alignment. The cast to uintptr_t is needed
// because plarena isn't as careful about mask construction as it
// ought to be.
#define ALIGN_SHIFT 3
#define PL_ARENA_CONST_ALIGN_MASK ((uintptr_t(1) << ALIGN_SHIFT) - 1)
#include "plarena.h"
// plarena.h needs to be included first to make it use the above
// PL_ARENA_CONST_ALIGN_MASK in this file.
#include "nsPresArena.h" #include "nsPresArena.h"
#include "nsCRT.h" #include "nsCRT.h"
#include "nsDebug.h" #include "nsDebug.h"
#include "nsTArray.h"
#include "nsTHashtable.h"
#include "prinit.h" #include "prinit.h"
#include "prlog.h"
#include "nsArenaMemoryStats.h" #include "nsArenaMemoryStats.h"
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "nsServiceManagerUtils.h" #include "nsServiceManagerUtils.h"
@ -23,17 +30,6 @@
#include "nsICrashReporter.h" #include "nsICrashReporter.h"
#endif #endif
#include "mozilla/StandardInteger.h"
#include "mozilla/MemoryChecking.h"
// Even on 32-bit systems, we allocate objects from the frame arena
// that require 8-byte alignment. The cast to uintptr_t is needed
// because plarena isn't as careful about mask construction as it
// ought to be.
#define ALIGN_SHIFT 3
#define PL_ARENA_CONST_ALIGN_MASK ((uintptr_t(1) << ALIGN_SHIFT) - 1)
#include "plarena.h"
#ifdef _WIN32 #ifdef _WIN32
# include <windows.h> # include <windows.h>
#elif !defined(__OS2__) #elif !defined(__OS2__)
@ -239,277 +235,226 @@ ARENA_POISON_init()
return PR_SUCCESS; return PR_SUCCESS;
} }
// All keys to this hash table fit in 32 bits (see below) so we do not nsPresArena::nsPresArena()
// bother actually hashing them.
namespace {
class FreeList : public PLDHashEntryHdr
{ {
public: mFreeLists.Init();
typedef uint32_t KeyType; PL_INIT_ARENA_POOL(&mPool, "PresArena", ARENA_PAGE_SIZE);
nsTArray<void *> mEntries; PR_CallOnce(&ARENA_POISON_guard, ARENA_POISON_init);
size_t mEntrySize;
size_t mEntriesEverAllocated;
typedef const void* KeyTypePointer;
KeyTypePointer mKey;
FreeList(KeyTypePointer aKey)
: mEntrySize(0), mEntriesEverAllocated(0), mKey(aKey) {}
// Default copy constructor and destructor are ok.
bool KeyEquals(KeyTypePointer const aKey) const
{ return mKey == aKey; }
static KeyTypePointer KeyToPointer(KeyType aKey)
{ return NS_INT32_TO_PTR(aKey); }
static PLDHashNumber HashKey(KeyTypePointer aKey)
{ return NS_PTR_TO_INT32(aKey); }
enum { ALLOW_MEMMOVE = false };
};
} }
struct nsPresArena::State { nsPresArena::~nsPresArena()
nsTHashtable<FreeList> mFreeLists; {
PLArenaPool mPool;
State()
{
mFreeLists.Init();
PL_INIT_ARENA_POOL(&mPool, "PresArena", ARENA_PAGE_SIZE);
PR_CallOnce(&ARENA_POISON_guard, ARENA_POISON_init);
}
#if defined(MOZ_HAVE_MEM_CHECKS) #if defined(MOZ_HAVE_MEM_CHECKS)
static PLDHashOperator UnpoisonFreeList(FreeList* aEntry, void*) mFreeLists.EnumerateEntries(UnpoisonFreeList, nullptr);
{
nsTArray<void*>::index_type len;
while ((len = aEntry->mEntries.Length())) {
void* result = aEntry->mEntries.ElementAt(len - 1);
aEntry->mEntries.RemoveElementAt(len - 1);
MOZ_MAKE_MEM_UNDEFINED(result, aEntry->mEntrySize);
}
return PL_DHASH_NEXT;
}
#endif #endif
PL_FinishArenaPool(&mPool);
}
~State() void*
{ nsPresArena::Allocate(uint32_t aCode, size_t aSize)
#if defined(MOZ_HAVE_MEM_CHECKS) {
mFreeLists.EnumerateEntries(UnpoisonFreeList, nullptr); NS_ABORT_IF_FALSE(aSize > 0, "PresArena cannot allocate zero bytes");
#endif
PL_FinishArenaPool(&mPool); // We only hand out aligned sizes
aSize = PL_ARENA_ALIGN(&mPool, aSize);
// If there is no free-list entry for this type already, we have
// to create one now, to record its size.
FreeList* list = mFreeLists.PutEntry(aCode);
nsTArray<void*>::index_type len = list->mEntries.Length();
if (list->mEntrySize == 0) {
NS_ABORT_IF_FALSE(len == 0, "list with entries but no recorded size");
list->mEntrySize = aSize;
} else {
NS_ABORT_IF_FALSE(list->mEntrySize == aSize,
"different sizes for same object type code");
} }
void* Allocate(uint32_t aCode, size_t aSize) void* result;
{ if (len > 0) {
NS_ABORT_IF_FALSE(aSize > 0, "PresArena cannot allocate zero bytes"); // LIFO behavior for best cache utilization
result = list->mEntries.ElementAt(len - 1);
// We only hand out aligned sizes list->mEntries.RemoveElementAt(len - 1);
aSize = PL_ARENA_ALIGN(&mPool, aSize);
// If there is no free-list entry for this type already, we have
// to create one now, to record its size.
FreeList* list = mFreeLists.PutEntry(aCode);
nsTArray<void*>::index_type len = list->mEntries.Length();
if (list->mEntrySize == 0) {
NS_ABORT_IF_FALSE(len == 0, "list with entries but no recorded size");
list->mEntrySize = aSize;
} else {
NS_ABORT_IF_FALSE(list->mEntrySize == aSize,
"different sizes for same object type code");
}
void* result;
if (len > 0) {
// LIFO behavior for best cache utilization
result = list->mEntries.ElementAt(len - 1);
list->mEntries.RemoveElementAt(len - 1);
#if defined(DEBUG) #if defined(DEBUG)
{ {
MOZ_MAKE_MEM_DEFINED(result, list->mEntrySize); MOZ_MAKE_MEM_DEFINED(result, list->mEntrySize);
char* p = reinterpret_cast<char*>(result); char* p = reinterpret_cast<char*>(result);
char* limit = p + list->mEntrySize; char* limit = p + list->mEntrySize;
for (; p < limit; p += sizeof(uintptr_t)) { for (; p < limit; p += sizeof(uintptr_t)) {
uintptr_t val = *reinterpret_cast<uintptr_t*>(p); uintptr_t val = *reinterpret_cast<uintptr_t*>(p);
NS_ABORT_IF_FALSE(val == ARENA_POISON, NS_ABORT_IF_FALSE(val == ARENA_POISON,
nsPrintfCString("PresArena: poison overwritten; " nsPrintfCString("PresArena: poison overwritten; "
"wanted %.16llx " "wanted %.16llx "
"found %.16llx " "found %.16llx "
"errors in bits %.16llx", "errors in bits %.16llx",
uint64_t(ARENA_POISON), uint64_t(ARENA_POISON),
uint64_t(val), uint64_t(val),
uint64_t(ARENA_POISON ^ val) uint64_t(ARENA_POISON ^ val)
).get()); ).get());
}
} }
}
#endif #endif
MOZ_MAKE_MEM_UNDEFINED(result, list->mEntrySize); MOZ_MAKE_MEM_UNDEFINED(result, list->mEntrySize);
return result;
}
// Allocate a new chunk from the arena
list->mEntriesEverAllocated++;
PL_ARENA_ALLOCATE(result, &mPool, aSize);
if (!result) {
NS_RUNTIMEABORT("out of memory");
}
return result; return result;
} }
void Free(uint32_t aCode, void* aPtr) // Allocate a new chunk from the arena
{ list->mEntriesEverAllocated++;
// Try to recycle this entry. PL_ARENA_ALLOCATE(result, &mPool, aSize);
FreeList* list = mFreeLists.GetEntry(aCode); if (!result) {
NS_ABORT_IF_FALSE(list, "no free list for pres arena object"); NS_RUNTIMEABORT("out of memory");
NS_ABORT_IF_FALSE(list->mEntrySize > 0, "PresArena cannot free zero bytes"); }
return result;
}
char* p = reinterpret_cast<char*>(aPtr); void
char* limit = p + list->mEntrySize; nsPresArena::Free(uint32_t aCode, void* aPtr)
for (; p < limit; p += sizeof(uintptr_t)) { {
*reinterpret_cast<uintptr_t*>(p) = ARENA_POISON; // Try to recycle this entry.
} FreeList* list = mFreeLists.GetEntry(aCode);
NS_ABORT_IF_FALSE(list, "no free list for pres arena object");
NS_ABORT_IF_FALSE(list->mEntrySize > 0, "PresArena cannot free zero bytes");
MOZ_MAKE_MEM_NOACCESS(aPtr, list->mEntrySize); char* p = reinterpret_cast<char*>(aPtr);
list->mEntries.AppendElement(aPtr); char* limit = p + list->mEntrySize;
for (; p < limit; p += sizeof(uintptr_t)) {
*reinterpret_cast<uintptr_t*>(p) = ARENA_POISON;
} }
static size_t SizeOfFreeListEntryExcludingThis(FreeList* aEntry, MOZ_MAKE_MEM_NOACCESS(aPtr, list->mEntrySize);
nsMallocSizeOfFun aMallocSizeOf, list->mEntries.AppendElement(aPtr);
void *) }
{
return aEntry->mEntries.SizeOfExcludingThis(aMallocSizeOf); /* static */ size_t
nsPresArena::SizeOfFreeListEntryExcludingThis(
FreeList* aEntry, nsMallocSizeOfFun aMallocSizeOf, void*)
{
return aEntry->mEntries.SizeOfExcludingThis(aMallocSizeOf);
}
size_t
nsPresArena::SizeOfIncludingThisFromMalloc(nsMallocSizeOfFun aMallocSizeOf) const
{
size_t n = aMallocSizeOf(this);
n += PL_SizeOfArenaPoolExcludingPool(&mPool, aMallocSizeOf);
n += mFreeLists.SizeOfExcludingThis(SizeOfFreeListEntryExcludingThis,
aMallocSizeOf);
return n;
}
struct EnumerateData {
nsArenaMemoryStats* stats;
size_t total;
};
#if defined(MOZ_HAVE_MEM_CHECKS)
/* static */ PLDHashOperator
nsPresArena::UnpoisonFreeList(FreeList* aEntry, void*)
{
nsTArray<void*>::index_type len;
while ((len = aEntry->mEntries.Length())) {
void* result = aEntry->mEntries.ElementAt(len - 1);
aEntry->mEntries.RemoveElementAt(len - 1);
MOZ_MAKE_MEM_UNDEFINED(result, aEntry->mEntrySize);
} }
return PL_DHASH_NEXT;
}
#endif
size_t SizeOfIncludingThisFromMalloc(nsMallocSizeOfFun aMallocSizeOf) const /* static */ PLDHashOperator
{ nsPresArena::FreeListEnumerator(FreeList* aEntry, void* aData)
size_t n = aMallocSizeOf(this); {
n += PL_SizeOfArenaPoolExcludingPool(&mPool, aMallocSizeOf); EnumerateData* data = static_cast<EnumerateData*>(aData);
n += mFreeLists.SizeOfExcludingThis(SizeOfFreeListEntryExcludingThis, // Note that we're not measuring the size of the entries on the free
aMallocSizeOf); // list here. The free list knows how many objects we've allocated
return n; // ever (which includes any objects that may be on the FreeList's
} // |mEntries| at this point) and we're using that to determine the
// total size of objects allocated with a given ID.
size_t totalSize = aEntry->mEntrySize * aEntry->mEntriesEverAllocated;
size_t* p;
struct EnumerateData { switch (NS_PTR_TO_INT32(aEntry->mKey)) {
nsArenaMemoryStats* stats; #define FRAME_ID(classname) \
size_t total; case nsQueryFrame::classname##_id: \
}; p = &data->stats->FRAME_ID_STAT_FIELD(classname); \
break;
static PLDHashOperator FreeListEnumerator(FreeList* aEntry, void* aData)
{
EnumerateData* data = static_cast<EnumerateData*>(aData);
// Note that we're not measuring the size of the entries on the free
// list here. The free list knows how many objects we've allocated
// ever (which includes any objects that may be on the FreeList's
// |mEntries| at this point) and we're using that to determine the
// total size of objects allocated with a given ID.
size_t totalSize = aEntry->mEntrySize * aEntry->mEntriesEverAllocated;
size_t* p;
switch (NS_PTR_TO_INT32(aEntry->mKey)) {
#define FRAME_ID(classname) \
case nsQueryFrame::classname##_id: \
p = &data->stats->FRAME_ID_STAT_FIELD(classname); \
break;
#include "nsFrameIdList.h" #include "nsFrameIdList.h"
#undef FRAME_ID #undef FRAME_ID
case nsLineBox_id: case nsLineBox_id:
p = &data->stats->mLineBoxes; p = &data->stats->mLineBoxes;
break; break;
case nsRuleNode_id: case nsRuleNode_id:
p = &data->stats->mRuleNodes; p = &data->stats->mRuleNodes;
break; break;
case nsStyleContext_id: case nsStyleContext_id:
p = &data->stats->mStyleContexts; p = &data->stats->mStyleContexts;
break; break;
default: default:
return PL_DHASH_NEXT;
}
*p += totalSize;
data->total += totalSize;
return PL_DHASH_NEXT; return PL_DHASH_NEXT;
} }
void SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf, *p += totalSize;
nsArenaMemoryStats* aArenaStats) data->total += totalSize;
{
// We do a complicated dance here because we want to measure the
// space taken up by the different kinds of objects in the arena,
// but we don't have pointers to those objects. And even if we did,
// we wouldn't be able to use aMallocSizeOf on them, since they were
// allocated out of malloc'd chunks of memory. So we compute the
// size of the arena as known by malloc and we add up the sizes of
// all the objects that we care about. Subtracting these two
// quantities gives us a catch-all "other" number, which includes
// slop in the arena itself as well as the size of objects that
// we've not measured explicitly.
size_t mallocSize = SizeOfIncludingThisFromMalloc(aMallocSizeOf); return PL_DHASH_NEXT;
EnumerateData data = { aArenaStats, 0 }; }
mFreeLists.EnumerateEntries(FreeListEnumerator, &data);
aArenaStats->mOther = mallocSize - data.total;
}
};
void void
nsPresArena::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf, nsPresArena::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf,
nsArenaMemoryStats* aArenaStats) nsArenaMemoryStats* aArenaStats)
{ {
mState->SizeOfIncludingThis(aMallocSizeOf, aArenaStats); // We do a complicated dance here because we want to measure the
} // space taken up by the different kinds of objects in the arena,
// but we don't have pointers to those objects. And even if we did,
// we wouldn't be able to use aMallocSizeOf on them, since they were
// allocated out of malloc'd chunks of memory. So we compute the
// size of the arena as known by malloc and we add up the sizes of
// all the objects that we care about. Subtracting these two
// quantities gives us a catch-all "other" number, which includes
// slop in the arena itself as well as the size of objects that
// we've not measured explicitly.
// Public interface size_t mallocSize = SizeOfIncludingThisFromMalloc(aMallocSizeOf);
nsPresArena::nsPresArena() EnumerateData data = { aArenaStats, 0 };
: mState(new nsPresArena::State()) mFreeLists.EnumerateEntries(FreeListEnumerator, &data);
{} aArenaStats->mOther = mallocSize - data.total;
nsPresArena::~nsPresArena()
{
delete mState;
} }
void* void*
nsPresArena::AllocateBySize(size_t aSize) nsPresArena::AllocateBySize(size_t aSize)
{ {
return mState->Allocate(uint32_t(aSize) | uint32_t(NON_OBJECT_MARKER), return Allocate(uint32_t(aSize) | uint32_t(NON_OBJECT_MARKER), aSize);
aSize);
} }
void void
nsPresArena::FreeBySize(size_t aSize, void* aPtr) nsPresArena::FreeBySize(size_t aSize, void* aPtr)
{ {
mState->Free(uint32_t(aSize) | uint32_t(NON_OBJECT_MARKER), aPtr); Free(uint32_t(aSize) | uint32_t(NON_OBJECT_MARKER), aPtr);
} }
void* void*
nsPresArena::AllocateByFrameID(nsQueryFrame::FrameIID aID, size_t aSize) nsPresArena::AllocateByFrameID(nsQueryFrame::FrameIID aID, size_t aSize)
{ {
return mState->Allocate(aID, aSize); return Allocate(aID, aSize);
} }
void void
nsPresArena::FreeByFrameID(nsQueryFrame::FrameIID aID, void* aPtr) nsPresArena::FreeByFrameID(nsQueryFrame::FrameIID aID, void* aPtr)
{ {
mState->Free(aID, aPtr); Free(aID, aPtr);
} }
void* void*
nsPresArena::AllocateByObjectID(ObjectID aID, size_t aSize) nsPresArena::AllocateByObjectID(ObjectID aID, size_t aSize)
{ {
return mState->Allocate(aID, aSize); return Allocate(aID, aSize);
} }
void void
nsPresArena::FreeByObjectID(ObjectID aID, void* aPtr) nsPresArena::FreeByObjectID(ObjectID aID, void* aPtr)
{ {
mState->Free(aID, aPtr); Free(aID, aPtr);
} }
/* static */ uintptr_t /* static */ uintptr_t

Просмотреть файл

@ -8,10 +8,13 @@
#ifndef nsPresArena_h___ #ifndef nsPresArena_h___
#define nsPresArena_h___ #define nsPresArena_h___
#include "mozilla/MemoryChecking.h"
#include "mozilla/StandardInteger.h"
#include "nscore.h" #include "nscore.h"
#include "nsQueryFrame.h" #include "nsQueryFrame.h"
#include "nsTArray.h"
#include "mozilla/StandardInteger.h" #include "nsTHashtable.h"
#include "plarena.h"
struct nsArenaMemoryStats; struct nsArenaMemoryStats;
@ -66,8 +69,49 @@ public:
static uintptr_t GetPoisonValue(); static uintptr_t GetPoisonValue();
private: private:
struct State; void* Allocate(uint32_t aCode, size_t aSize);
State* mState; void Free(uint32_t aCode, void* aPtr);
// All keys to this hash table fit in 32 bits (see below) so we do not
// bother actually hashing them.
class FreeList : public PLDHashEntryHdr
{
public:
typedef uint32_t KeyType;
nsTArray<void *> mEntries;
size_t mEntrySize;
size_t mEntriesEverAllocated;
typedef const void* KeyTypePointer;
KeyTypePointer mKey;
FreeList(KeyTypePointer aKey)
: mEntrySize(0), mEntriesEverAllocated(0), mKey(aKey) {}
// Default copy constructor and destructor are ok.
bool KeyEquals(KeyTypePointer const aKey) const
{ return mKey == aKey; }
static KeyTypePointer KeyToPointer(KeyType aKey)
{ return NS_INT32_TO_PTR(aKey); }
static PLDHashNumber HashKey(KeyTypePointer aKey)
{ return NS_PTR_TO_INT32(aKey); }
enum { ALLOW_MEMMOVE = false };
};
#if defined(MOZ_HAVE_MEM_CHECKS)
static PLDHashOperator UnpoisonFreeList(FreeList* aEntry, void*);
#endif
static PLDHashOperator FreeListEnumerator(FreeList* aEntry, void* aData);
static size_t SizeOfFreeListEntryExcludingThis(FreeList* aEntry,
nsMallocSizeOfFun aMallocSizeOf,
void*);
size_t SizeOfIncludingThisFromMalloc(nsMallocSizeOfFun aMallocSizeOf) const;
nsTHashtable<FreeList> mFreeLists;
PLArenaPool mPool;
}; };
#endif #endif

Просмотреть файл

@ -9,17 +9,16 @@
* matching and cascading * matching and cascading
*/ */
#include "mozilla/Util.h" #define PL_ARENA_CONST_ALIGN_MASK 7
// We want page-sized arenas so there's no fragmentation involved.
// Including plarena.h must come first to avoid it being included by some
// header file thereby making PL_ARENA_CONST_ALIGN_MASK ineffective.
#define NS_CASCADEENUMDATA_ARENA_BLOCK_SIZE (4096)
#include "plarena.h"
#include "nsCSSRuleProcessor.h" #include "nsCSSRuleProcessor.h"
#include "nsRuleProcessorData.h" #include "nsRuleProcessorData.h"
#include <algorithm> #include <algorithm>
#define PL_ARENA_CONST_ALIGN_MASK 7
// We want page-sized arenas so there's no fragmentation involved.
#define NS_CASCADEENUMDATA_ARENA_BLOCK_SIZE (4096)
#include "plarena.h"
#include "nsCRT.h" #include "nsCRT.h"
#include "nsIAtom.h" #include "nsIAtom.h"
#include "pldhash.h" #include "pldhash.h"
@ -60,6 +59,7 @@
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
#include "mozilla/LookAndFeel.h" #include "mozilla/LookAndFeel.h"
#include "mozilla/Likely.h" #include "mozilla/Likely.h"
#include "mozilla/Util.h"
using namespace mozilla; using namespace mozilla;
using namespace mozilla::dom; using namespace mozilla::dom;