diff -r 000000000000 -r 6474c204b198 layout/base/nsPresArena.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layout/base/nsPresArena.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,201 @@ +/* -*- 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" + +// 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() +{ +#if defined(MOZ_HAVE_MEM_CHECKS) + mFreeLists.EnumerateEntries(UnpoisonFreeList, nullptr); +#endif + PL_FinishArenaPool(&mPool); +} + +NS_HIDDEN_(void*) +nsPresArena::Allocate(uint32_t aCode, size_t aSize) +{ + NS_ABORT_IF_FALSE(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::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) + { + MOZ_MAKE_MEM_DEFINED(result, list->mEntrySize); + char* p = reinterpret_cast(result); + char* limit = p + list->mEntrySize; + for (; p < limit; p += sizeof(uintptr_t)) { + uintptr_t val = *reinterpret_cast(p); + NS_ABORT_IF_FALSE(val == mozPoisonValue(), + nsPrintfCString("PresArena: poison overwritten; " + "wanted %.16llx " + "found %.16llx " + "errors in bits %.16llx", + uint64_t(mozPoisonValue()), + uint64_t(val), + uint64_t(mozPoisonValue() ^ val) + ).get()); + } + } +#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_RUNTIMEABORT("out of memory"); + } + return result; +} + +NS_HIDDEN_(void) +nsPresArena::Free(uint32_t aCode, void* aPtr) +{ + // 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"); + + mozWritePoison(aPtr, list->mEntrySize); + + MOZ_MAKE_MEM_NOACCESS(aPtr, list->mEntrySize); + list->mEntries.AppendElement(aPtr); +} + +/* static */ size_t +nsPresArena::SizeOfFreeListEntryExcludingThis( + FreeList* aEntry, mozilla::MallocSizeOf aMallocSizeOf, void*) +{ + return aEntry->mEntries.SizeOfExcludingThis(aMallocSizeOf); +} + +struct EnumerateData { + nsArenaMemoryStats* stats; + size_t total; +}; + +#if defined(MOZ_HAVE_MEM_CHECKS) +/* static */ PLDHashOperator +nsPresArena::UnpoisonFreeList(FreeList* aEntry, void*) +{ + nsTArray::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 + +/* static */ PLDHashOperator +nsPresArena::FreeListEnumerator(FreeList* aEntry, void* aData) +{ + EnumerateData* data = static_cast(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" +#undef FRAME_ID + case nsLineBox_id: + p = &data->stats->mLineBoxes; + break; + case nsRuleNode_id: + p = &data->stats->mRuleNodes; + break; + case nsStyleContext_id: + p = &data->stats->mStyleContexts; + break; + default: + return PL_DHASH_NEXT; + } + + *p += totalSize; + data->total += totalSize; + + return PL_DHASH_NEXT; +} + +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(SizeOfFreeListEntryExcludingThis, + aMallocSizeOf); + + EnumerateData data = { aArenaStats, 0 }; + mFreeLists.EnumerateEntries(FreeListEnumerator, &data); + aArenaStats->mOther += mallocSize - data.total; +}