michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * vim: set ts=2 sw=2 et tw=78: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: */ michael@0: michael@0: /* arena allocation for the frame tree and closely-related objects */ michael@0: michael@0: // Even on 32-bit systems, we allocate objects from the frame arena michael@0: // that require 8-byte alignment. The cast to uintptr_t is needed michael@0: // because plarena isn't as careful about mask construction as it michael@0: // ought to be. michael@0: #define ALIGN_SHIFT 3 michael@0: #define PL_ARENA_CONST_ALIGN_MASK ((uintptr_t(1) << ALIGN_SHIFT) - 1) michael@0: #include "plarena.h" michael@0: // plarena.h needs to be included first to make it use the above michael@0: // PL_ARENA_CONST_ALIGN_MASK in this file. michael@0: michael@0: #include "nsPresArena.h" michael@0: michael@0: #include "mozilla/Poison.h" michael@0: #include "nsDebug.h" michael@0: #include "nsArenaMemoryStats.h" michael@0: #include "nsPrintfCString.h" michael@0: michael@0: // Size to use for PLArena block allocations. michael@0: static const size_t ARENA_PAGE_SIZE = 8192; michael@0: michael@0: nsPresArena::nsPresArena() michael@0: { michael@0: PL_INIT_ARENA_POOL(&mPool, "PresArena", ARENA_PAGE_SIZE); michael@0: } michael@0: michael@0: nsPresArena::~nsPresArena() michael@0: { michael@0: #if defined(MOZ_HAVE_MEM_CHECKS) michael@0: mFreeLists.EnumerateEntries(UnpoisonFreeList, nullptr); michael@0: #endif michael@0: PL_FinishArenaPool(&mPool); michael@0: } michael@0: michael@0: NS_HIDDEN_(void*) michael@0: nsPresArena::Allocate(uint32_t aCode, size_t aSize) michael@0: { michael@0: NS_ABORT_IF_FALSE(aSize > 0, "PresArena cannot allocate zero bytes"); michael@0: michael@0: // We only hand out aligned sizes michael@0: aSize = PL_ARENA_ALIGN(&mPool, aSize); michael@0: michael@0: // If there is no free-list entry for this type already, we have michael@0: // to create one now, to record its size. michael@0: FreeList* list = mFreeLists.PutEntry(aCode); michael@0: michael@0: nsTArray::index_type len = list->mEntries.Length(); michael@0: if (list->mEntrySize == 0) { michael@0: NS_ABORT_IF_FALSE(len == 0, "list with entries but no recorded size"); michael@0: list->mEntrySize = aSize; michael@0: } else { michael@0: NS_ABORT_IF_FALSE(list->mEntrySize == aSize, michael@0: "different sizes for same object type code"); michael@0: } michael@0: michael@0: void* result; michael@0: if (len > 0) { michael@0: // LIFO behavior for best cache utilization michael@0: result = list->mEntries.ElementAt(len - 1); michael@0: list->mEntries.RemoveElementAt(len - 1); michael@0: #if defined(DEBUG) michael@0: { michael@0: MOZ_MAKE_MEM_DEFINED(result, list->mEntrySize); michael@0: char* p = reinterpret_cast(result); michael@0: char* limit = p + list->mEntrySize; michael@0: for (; p < limit; p += sizeof(uintptr_t)) { michael@0: uintptr_t val = *reinterpret_cast(p); michael@0: NS_ABORT_IF_FALSE(val == mozPoisonValue(), michael@0: nsPrintfCString("PresArena: poison overwritten; " michael@0: "wanted %.16llx " michael@0: "found %.16llx " michael@0: "errors in bits %.16llx", michael@0: uint64_t(mozPoisonValue()), michael@0: uint64_t(val), michael@0: uint64_t(mozPoisonValue() ^ val) michael@0: ).get()); michael@0: } michael@0: } michael@0: #endif michael@0: MOZ_MAKE_MEM_UNDEFINED(result, list->mEntrySize); michael@0: return result; michael@0: } michael@0: michael@0: // Allocate a new chunk from the arena michael@0: list->mEntriesEverAllocated++; michael@0: PL_ARENA_ALLOCATE(result, &mPool, aSize); michael@0: if (!result) { michael@0: NS_RUNTIMEABORT("out of memory"); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: NS_HIDDEN_(void) michael@0: nsPresArena::Free(uint32_t aCode, void* aPtr) michael@0: { michael@0: // Try to recycle this entry. michael@0: FreeList* list = mFreeLists.GetEntry(aCode); michael@0: NS_ABORT_IF_FALSE(list, "no free list for pres arena object"); michael@0: NS_ABORT_IF_FALSE(list->mEntrySize > 0, "PresArena cannot free zero bytes"); michael@0: michael@0: mozWritePoison(aPtr, list->mEntrySize); michael@0: michael@0: MOZ_MAKE_MEM_NOACCESS(aPtr, list->mEntrySize); michael@0: list->mEntries.AppendElement(aPtr); michael@0: } michael@0: michael@0: /* static */ size_t michael@0: nsPresArena::SizeOfFreeListEntryExcludingThis( michael@0: FreeList* aEntry, mozilla::MallocSizeOf aMallocSizeOf, void*) michael@0: { michael@0: return aEntry->mEntries.SizeOfExcludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: struct EnumerateData { michael@0: nsArenaMemoryStats* stats; michael@0: size_t total; michael@0: }; michael@0: michael@0: #if defined(MOZ_HAVE_MEM_CHECKS) michael@0: /* static */ PLDHashOperator michael@0: nsPresArena::UnpoisonFreeList(FreeList* aEntry, void*) michael@0: { michael@0: nsTArray::index_type len; michael@0: while ((len = aEntry->mEntries.Length())) { michael@0: void* result = aEntry->mEntries.ElementAt(len - 1); michael@0: aEntry->mEntries.RemoveElementAt(len - 1); michael@0: MOZ_MAKE_MEM_UNDEFINED(result, aEntry->mEntrySize); michael@0: } michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: #endif michael@0: michael@0: /* static */ PLDHashOperator michael@0: nsPresArena::FreeListEnumerator(FreeList* aEntry, void* aData) michael@0: { michael@0: EnumerateData* data = static_cast(aData); michael@0: // Note that we're not measuring the size of the entries on the free michael@0: // list here. The free list knows how many objects we've allocated michael@0: // ever (which includes any objects that may be on the FreeList's michael@0: // |mEntries| at this point) and we're using that to determine the michael@0: // total size of objects allocated with a given ID. michael@0: size_t totalSize = aEntry->mEntrySize * aEntry->mEntriesEverAllocated; michael@0: size_t* p; michael@0: michael@0: switch (NS_PTR_TO_INT32(aEntry->mKey)) { michael@0: #define FRAME_ID(classname) \ michael@0: case nsQueryFrame::classname##_id: \ michael@0: p = &data->stats->FRAME_ID_STAT_FIELD(classname); \ michael@0: break; michael@0: #include "nsFrameIdList.h" michael@0: #undef FRAME_ID michael@0: case nsLineBox_id: michael@0: p = &data->stats->mLineBoxes; michael@0: break; michael@0: case nsRuleNode_id: michael@0: p = &data->stats->mRuleNodes; michael@0: break; michael@0: case nsStyleContext_id: michael@0: p = &data->stats->mStyleContexts; michael@0: break; michael@0: default: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: *p += totalSize; michael@0: data->total += totalSize; michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: void michael@0: nsPresArena::AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf, michael@0: nsArenaMemoryStats* aArenaStats) michael@0: { michael@0: // We do a complicated dance here because we want to measure the michael@0: // space taken up by the different kinds of objects in the arena, michael@0: // but we don't have pointers to those objects. And even if we did, michael@0: // we wouldn't be able to use aMallocSizeOf on them, since they were michael@0: // allocated out of malloc'd chunks of memory. So we compute the michael@0: // size of the arena as known by malloc and we add up the sizes of michael@0: // all the objects that we care about. Subtracting these two michael@0: // quantities gives us a catch-all "other" number, which includes michael@0: // slop in the arena itself as well as the size of objects that michael@0: // we've not measured explicitly. michael@0: michael@0: size_t mallocSize = PL_SizeOfArenaPoolExcludingPool(&mPool, aMallocSizeOf); michael@0: mallocSize += mFreeLists.SizeOfExcludingThis(SizeOfFreeListEntryExcludingThis, michael@0: aMallocSizeOf); michael@0: michael@0: EnumerateData data = { aArenaStats, 0 }; michael@0: mFreeLists.EnumerateEntries(FreeListEnumerator, &data); michael@0: aArenaStats->mOther += mallocSize - data.total; michael@0: }