diff options
Diffstat (limited to 'layout/base/nsPresArena.cpp')
-rw-r--r-- | layout/base/nsPresArena.cpp | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/layout/base/nsPresArena.cpp b/layout/base/nsPresArena.cpp new file mode 100644 index 000000000..2b1f072df --- /dev/null +++ b/layout/base/nsPresArena.cpp @@ -0,0 +1,247 @@ +/* -*- 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; +} |