зеркало из https://github.com/mozilla/gecko-dev.git
248 строки
8.0 KiB
C++
248 строки
8.0 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
* vim: set ts=2 sw=2 et tw=78:
|
|
* 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/.
|
|
*/
|
|
|
|
/* 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 "mozilla/Poison.h"
|
|
#include "nsDebug.h"
|
|
#include "nsArenaMemoryStats.h"
|
|
#include "nsPrintfCString.h"
|
|
#include "nsStyleContext.h"
|
|
|
|
#include <inttypes.h>
|
|
|
|
using namespace mozilla;
|
|
|
|
// Size to use for PLArena block allocations.
|
|
static const size_t ARENA_PAGE_SIZE = 8192;
|
|
|
|
nsPresArena::nsPresArena()
|
|
{
|
|
PL_INIT_ARENA_POOL(&mPool, "PresArena", ARENA_PAGE_SIZE);
|
|
}
|
|
|
|
nsPresArena::~nsPresArena()
|
|
{
|
|
ClearArenaRefPtrs();
|
|
|
|
#if defined(MOZ_HAVE_MEM_CHECKS)
|
|
for (auto iter = mFreeLists.Iter(); !iter.Done(); iter.Next()) {
|
|
FreeList* entry = iter.Get();
|
|
nsTArray<void*>::index_type len;
|
|
while ((len = entry->mEntries.Length())) {
|
|
void* result = entry->mEntries.ElementAt(len - 1);
|
|
entry->mEntries.RemoveElementAt(len - 1);
|
|
MOZ_MAKE_MEM_UNDEFINED(result, entry->mEntrySize);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
PL_FinishArenaPool(&mPool);
|
|
}
|
|
|
|
/* inline */ void
|
|
nsPresArena::ClearArenaRefPtrWithoutDeregistering(void* aPtr,
|
|
ArenaObjectID aObjectID)
|
|
{
|
|
switch (aObjectID) {
|
|
#define PRES_ARENA_OBJECT_WITH_ARENAREFPTR_SUPPORT(name_) \
|
|
case eArenaObjectID_##name_: \
|
|
static_cast<ArenaRefPtr<name_>*>(aPtr)->ClearWithoutDeregistering(); \
|
|
return;
|
|
#include "nsPresArenaObjectList.h"
|
|
#undef PRES_ARENA_OBJECT_WITH_ARENAREFPTR_SUPPORT
|
|
default:
|
|
break;
|
|
}
|
|
switch (aObjectID) {
|
|
#define PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT(name_) \
|
|
case eArenaObjectID_##name_: \
|
|
MOZ_ASSERT(false, #name_ " must be declared in nsPresArenaObjectList.h "\
|
|
"with PRES_ARENA_OBJECT_SUPPORTS_ARENAREFPTR"); \
|
|
break;
|
|
#include "nsPresArenaObjectList.h"
|
|
#undef PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT
|
|
default:
|
|
MOZ_ASSERT(false, "unexpected ArenaObjectID value");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
nsPresArena::ClearArenaRefPtrs()
|
|
{
|
|
for (auto iter = mArenaRefPtrs.Iter(); !iter.Done(); iter.Next()) {
|
|
void* ptr = iter.Key();
|
|
ArenaObjectID id = iter.UserData();
|
|
ClearArenaRefPtrWithoutDeregistering(ptr, id);
|
|
}
|
|
mArenaRefPtrs.Clear();
|
|
}
|
|
|
|
void
|
|
nsPresArena::ClearArenaRefPtrs(ArenaObjectID aObjectID)
|
|
{
|
|
for (auto iter = mArenaRefPtrs.Iter(); !iter.Done(); iter.Next()) {
|
|
void* ptr = iter.Key();
|
|
ArenaObjectID id = iter.UserData();
|
|
if (id == aObjectID) {
|
|
ClearArenaRefPtrWithoutDeregistering(ptr, id);
|
|
iter.Remove();
|
|
}
|
|
}
|
|
}
|
|
|
|
void*
|
|
nsPresArena::Allocate(uint32_t aCode, size_t aSize)
|
|
{
|
|
MOZ_ASSERT(aSize > 0, "PresArena cannot allocate zero bytes");
|
|
|
|
// 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) {
|
|
MOZ_ASSERT(len == 0, "list with entries but no recorded size");
|
|
list->mEntrySize = aSize;
|
|
} else {
|
|
MOZ_ASSERT(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)
|
|
{
|
|
MOZ_MAKE_MEM_DEFINED(result, list->mEntrySize);
|
|
char* p = reinterpret_cast<char*>(result);
|
|
char* limit = p + list->mEntrySize;
|
|
for (; p < limit; p += sizeof(uintptr_t)) {
|
|
uintptr_t val = *reinterpret_cast<uintptr_t*>(p);
|
|
if (val != mozPoisonValue()) {
|
|
MOZ_ReportAssertionFailure(
|
|
nsPrintfCString("PresArena: poison overwritten; "
|
|
"wanted %.16" PRIx64 " "
|
|
"found %.16" PRIx64 " "
|
|
"errors in bits %.16" PRIx64 " ",
|
|
uint64_t(mozPoisonValue()),
|
|
uint64_t(val),
|
|
uint64_t(mozPoisonValue() ^ val)).get(),
|
|
__FILE__, __LINE__);
|
|
MOZ_CRASH();
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
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_ABORT_OOM(aSize);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void
|
|
nsPresArena::Free(uint32_t aCode, void* aPtr)
|
|
{
|
|
// Try to recycle this entry.
|
|
FreeList* list = mFreeLists.GetEntry(aCode);
|
|
MOZ_ASSERT(list, "no free list for pres arena object");
|
|
MOZ_ASSERT(list->mEntrySize > 0, "PresArena cannot free zero bytes");
|
|
|
|
mozWritePoison(aPtr, list->mEntrySize);
|
|
|
|
MOZ_MAKE_MEM_NOACCESS(aPtr, list->mEntrySize);
|
|
list->mEntries.AppendElement(aPtr);
|
|
}
|
|
|
|
void
|
|
nsPresArena::AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
|
|
nsArenaMemoryStats* 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.
|
|
|
|
size_t mallocSize = PL_SizeOfArenaPoolExcludingPool(&mPool, aMallocSizeOf);
|
|
mallocSize += mFreeLists.SizeOfExcludingThis(aMallocSizeOf);
|
|
|
|
size_t totalSizeInFreeLists = 0;
|
|
for (auto iter = mFreeLists.Iter(); !iter.Done(); iter.Next()) {
|
|
FreeList* entry = iter.Get();
|
|
|
|
// 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 = entry->mEntrySize * entry->mEntriesEverAllocated;
|
|
size_t* p;
|
|
|
|
switch (NS_PTR_TO_INT32(entry->mKey)) {
|
|
#define FRAME_ID(classname) \
|
|
case nsQueryFrame::classname##_id: \
|
|
p = &aArenaStats->FRAME_ID_STAT_FIELD(classname); \
|
|
break;
|
|
#include "nsFrameIdList.h"
|
|
#undef FRAME_ID
|
|
case eArenaObjectID_nsLineBox:
|
|
p = &aArenaStats->mLineBoxes;
|
|
break;
|
|
case eArenaObjectID_nsRuleNode:
|
|
p = &aArenaStats->mRuleNodes;
|
|
break;
|
|
case eArenaObjectID_nsStyleContext:
|
|
p = &aArenaStats->mStyleContexts;
|
|
break;
|
|
#define STYLE_STRUCT(name_, checkdata_cb_) \
|
|
case eArenaObjectID_nsStyle##name_:
|
|
#include "nsStyleStructList.h"
|
|
#undef STYLE_STRUCT
|
|
p = &aArenaStats->mStyleStructs;
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
*p += totalSize;
|
|
totalSizeInFreeLists += totalSize;
|
|
}
|
|
|
|
aArenaStats->mOther += mallocSize - totalSizeInFreeLists;
|
|
}
|