diff options
Diffstat (limited to '')
-rw-r--r-- | layout/base/nsPresArena.cpp | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/layout/base/nsPresArena.cpp b/layout/base/nsPresArena.cpp new file mode 100644 index 0000000000..d164599e3d --- /dev/null +++ b/layout/base/nsPresArena.cpp @@ -0,0 +1,185 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 */ + +#include "nsPresArena.h" + +#include "mozilla/Poison.h" +#include "nsDebug.h" +#include "nsDisplayList.h" +#include "nsPrintfCString.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/ComputedStyle.h" +#include "mozilla/ComputedStyleInlines.h" +#include "nsWindowSizes.h" + +#include <inttypes.h> + +using namespace mozilla; + +template <size_t ArenaSize, typename ObjectId, size_t ObjectIdCount> +nsPresArena<ArenaSize, ObjectId, ObjectIdCount>::~nsPresArena() { +#if defined(MOZ_HAVE_MEM_CHECKS) + for (FreeList* entry = mFreeLists; entry != ArrayEnd(mFreeLists); ++entry) { + for (void* result : entry->mEntries) { + MOZ_MAKE_MEM_UNDEFINED(result, entry->mEntrySize); + } + entry->mEntries.Clear(); + } +#endif +} + +template <size_t ArenaSize, typename ObjectId, size_t ObjectIdCount> +void* nsPresArena<ArenaSize, ObjectId, ObjectIdCount>::Allocate(ObjectId aCode, + size_t aSize) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aSize > 0, "PresArena cannot allocate zero bytes"); + MOZ_ASSERT(size_t(aCode) < ArrayLength(mFreeLists)); + + // We only hand out aligned sizes + aSize = mPool.AlignedSize(aSize); + + FreeList* list = &mFreeLists[size_t(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) { + // Remove from the end of the mEntries array to avoid memmoving entries, + // and use SetLengthAndRetainStorage to avoid a lot of malloc/free + // from ShrinkCapacity on smaller sizes. 500 pointers means the malloc size + // for the array is 4096 bytes or more on a 64-bit system. The next smaller + // size is 2048 (with jemalloc), which we consider not worth compacting. + result = list->mEntries.ElementAt(len - 1); + if (list->mEntries.Capacity() > 500) { + list->mEntries.RemoveElementAt(len - 1); + } else { + list->mEntries.SetLengthAndRetainStorage(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++; + return mPool.Allocate(aSize); +} + +template <size_t ArenaSize, typename ObjectId, size_t ObjectIdCount> +void nsPresArena<ArenaSize, ObjectId, ObjectIdCount>::Free(ObjectId aCode, + void* aPtr) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(size_t(aCode) < ArrayLength(mFreeLists)); + + // Try to recycle this entry. + FreeList* list = &mFreeLists[size_t(aCode)]; + MOZ_ASSERT(list->mEntrySize > 0, "object of this type was never allocated"); + + mozWritePoison(aPtr, list->mEntrySize); + + MOZ_MAKE_MEM_NOACCESS(aPtr, list->mEntrySize); + list->mEntries.AppendElement(aPtr); +} + +template <size_t ArenaSize, typename ObjectId, size_t ObjectIdCount> +void nsPresArena<ArenaSize, ObjectId, ObjectIdCount>::AddSizeOfExcludingThis( + nsWindowSizes& aSizes, ArenaKind aKind) const { + // 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 mMallocSizeOf 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 = mPool.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf); + + size_t totalSizeInFreeLists = 0; + for (const FreeList* entry = mFreeLists; entry != ArrayEnd(mFreeLists); + ++entry) { + mallocSize += entry->SizeOfExcludingThis(aSizes.mState.mMallocSizeOf); + + // 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; + + if (aKind == ArenaKind::PresShell) { + switch (entry - mFreeLists) { +#define PRES_ARENA_OBJECT(name_) \ + case eArenaObjectID_##name_: \ + aSizes.mArenaSizes.NS_ARENA_SIZES_FIELD(name_) += totalSize; \ + break; +#include "nsPresArenaObjectList.h" +#undef PRES_ARENA_OBJECT + default: + MOZ_ASSERT_UNREACHABLE("Unknown arena object type"); + } + } else { + MOZ_ASSERT(aKind == ArenaKind::DisplayList); + switch (DisplayListArenaObjectId(entry - mFreeLists)) { +#define DISPLAY_LIST_ARENA_OBJECT(name_) \ + case DisplayListArenaObjectId::name_: \ + aSizes.mArenaSizes.NS_ARENA_SIZES_FIELD(name_) += totalSize; \ + break; +#include "nsDisplayListArenaTypes.h" +#undef DISPLAY_LIST_ARENA_OBJECT + default: + MOZ_ASSERT_UNREACHABLE("Unknown display item arena type"); + } + } + + totalSizeInFreeLists += totalSize; + } + + auto& field = aKind == ArenaKind::PresShell + ? aSizes.mLayoutPresShellSize + : aSizes.mLayoutRetainedDisplayListSize; + + field += mallocSize - totalSizeInFreeLists; +} + +// Explicitly instantiate templates for the used nsPresArena allocator sizes. +// This is needed because nsPresArena definition is split across multiple files. +template class nsPresArena<8192, ArenaObjectID, eArenaObjectID_COUNT>; +template class nsPresArena<32768, DisplayListArenaObjectId, + size_t(DisplayListArenaObjectId::COUNT)>; |