nsPresArena.cpp (7261B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 */ 7 8 /* arena allocation for the frame tree and closely-related objects */ 9 10 #include "nsPresArena.h" 11 12 #include <inttypes.h> 13 14 #include "mozilla/ComputedStyle.h" 15 #include "mozilla/ComputedStyleInlines.h" 16 #include "mozilla/Poison.h" 17 #include "nsDebug.h" 18 #include "nsDisplayList.h" 19 #include "nsPrintfCString.h" 20 #include "nsWindowSizes.h" 21 22 using namespace mozilla; 23 24 template <size_t ArenaSize, typename ObjectId, size_t ObjectIdCount> 25 nsPresArena<ArenaSize, ObjectId, ObjectIdCount>::~nsPresArena() { 26 #if defined(MOZ_HAVE_MEM_CHECKS) 27 for (FreeList* entry = mFreeLists; entry != std::end(mFreeLists); ++entry) { 28 for (void* result : entry->mEntries) { 29 MOZ_MAKE_MEM_UNDEFINED(result, entry->mEntrySize); 30 } 31 entry->mEntries.Clear(); 32 } 33 #endif 34 } 35 36 template <size_t ArenaSize, typename ObjectId, size_t ObjectIdCount> 37 void* nsPresArena<ArenaSize, ObjectId, ObjectIdCount>::Allocate(ObjectId aCode, 38 size_t aSize) { 39 MOZ_ASSERT(NS_IsMainThread()); 40 MOZ_ASSERT(aSize > 0, "PresArena cannot allocate zero bytes"); 41 MOZ_ASSERT(size_t(aCode) < std::size(mFreeLists)); 42 43 // We only hand out aligned sizes 44 aSize = mPool.AlignedSize(aSize); 45 46 FreeList* list = &mFreeLists[size_t(aCode)]; 47 48 nsTArray<void*>::index_type len = list->mEntries.Length(); 49 if (list->mEntrySize == 0) { 50 MOZ_ASSERT(len == 0, "list with entries but no recorded size"); 51 list->mEntrySize = aSize; 52 } else { 53 MOZ_ASSERT(list->mEntrySize == aSize, 54 "different sizes for same object type code"); 55 } 56 57 void* result; 58 if (len > 0) { 59 // Remove from the end of the mEntries array to avoid memmoving entries, 60 // and use SetLengthAndRetainStorage to avoid a lot of malloc/free 61 // from ShrinkCapacity on smaller sizes. 500 pointers means the malloc size 62 // for the array is 4096 bytes or more on a 64-bit system. The next smaller 63 // size is 2048 (with jemalloc), which we consider not worth compacting. 64 result = list->mEntries.Elements()[len - 1]; 65 if (list->mEntries.Capacity() > 500) { 66 list->mEntries.RemoveElementAtUnsafe(len - 1); 67 } else { 68 list->mEntries.SetLengthAndRetainStorage(len - 1); 69 } 70 #if defined(DEBUG) 71 { 72 MOZ_MAKE_MEM_DEFINED(result, list->mEntrySize); 73 char* p = reinterpret_cast<char*>(result); 74 char* limit = p + list->mEntrySize; 75 for (; p < limit; p += sizeof(uintptr_t)) { 76 uintptr_t val = *reinterpret_cast<uintptr_t*>(p); 77 if (val != mozPoisonValue()) { 78 MOZ_ReportAssertionFailure( 79 nsPrintfCString("PresArena: poison overwritten; " 80 "wanted %.16" PRIx64 " " 81 "found %.16" PRIx64 " " 82 "errors in bits %.16" PRIx64 " ", 83 uint64_t(mozPoisonValue()), uint64_t(val), 84 uint64_t(mozPoisonValue() ^ val)) 85 .get(), 86 __FILE__, __LINE__); 87 MOZ_CRASH(); 88 } 89 } 90 } 91 #endif 92 MOZ_MAKE_MEM_UNDEFINED(result, list->mEntrySize); 93 return result; 94 } 95 96 // Allocate a new chunk from the arena 97 list->mEntriesEverAllocated++; 98 return mPool.Allocate(aSize); 99 } 100 101 template <size_t ArenaSize, typename ObjectId, size_t ObjectIdCount> 102 void nsPresArena<ArenaSize, ObjectId, ObjectIdCount>::Free(ObjectId aCode, 103 void* aPtr) { 104 MOZ_ASSERT(NS_IsMainThread()); 105 MOZ_ASSERT(size_t(aCode) < std::size(mFreeLists)); 106 107 // Try to recycle this entry. 108 FreeList* list = &mFreeLists[size_t(aCode)]; 109 MOZ_ASSERT(list->mEntrySize > 0, "object of this type was never allocated"); 110 111 mozWritePoison(aPtr, list->mEntrySize); 112 113 MOZ_MAKE_MEM_NOACCESS(aPtr, list->mEntrySize); 114 list->mEntries.AppendElement(aPtr); 115 } 116 117 template <size_t ArenaSize, typename ObjectId, size_t ObjectIdCount> 118 void nsPresArena<ArenaSize, ObjectId, ObjectIdCount>::AddSizeOfExcludingThis( 119 nsWindowSizes& aSizes, ArenaKind aKind) const { 120 // We do a complicated dance here because we want to measure the 121 // space taken up by the different kinds of objects in the arena, 122 // but we don't have pointers to those objects. And even if we did, 123 // we wouldn't be able to use mMallocSizeOf on them, since they were 124 // allocated out of malloc'd chunks of memory. So we compute the 125 // size of the arena as known by malloc and we add up the sizes of 126 // all the objects that we care about. Subtracting these two 127 // quantities gives us a catch-all "other" number, which includes 128 // slop in the arena itself as well as the size of objects that 129 // we've not measured explicitly. 130 131 size_t mallocSize = mPool.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf); 132 133 size_t totalSizeInFreeLists = 0; 134 for (const FreeList* entry = mFreeLists; entry != std::end(mFreeLists); 135 ++entry) { 136 mallocSize += entry->SizeOfExcludingThis(aSizes.mState.mMallocSizeOf); 137 138 // Note that we're not measuring the size of the entries on the free 139 // list here. The free list knows how many objects we've allocated 140 // ever (which includes any objects that may be on the FreeList's 141 // |mEntries| at this point) and we're using that to determine the 142 // total size of objects allocated with a given ID. 143 size_t totalSize = entry->mEntrySize * entry->mEntriesEverAllocated; 144 145 if (aKind == ArenaKind::PresShell) { 146 switch (entry - mFreeLists) { 147 #define PRES_ARENA_OBJECT(name_) \ 148 case eArenaObjectID_##name_: \ 149 aSizes.mArenaSizes.NS_ARENA_SIZES_FIELD(name_) += totalSize; \ 150 break; 151 #include "nsPresArenaObjectList.h" 152 #undef PRES_ARENA_OBJECT 153 default: 154 MOZ_ASSERT_UNREACHABLE("Unknown arena object type"); 155 } 156 } else { 157 MOZ_ASSERT(aKind == ArenaKind::DisplayList); 158 switch (DisplayListArenaObjectId(entry - mFreeLists)) { 159 #define DISPLAY_LIST_ARENA_OBJECT(name_) \ 160 case DisplayListArenaObjectId::name_: \ 161 aSizes.mArenaSizes.NS_ARENA_SIZES_FIELD(name_) += totalSize; \ 162 break; 163 #include "nsDisplayListArenaTypes.h" 164 #undef DISPLAY_LIST_ARENA_OBJECT 165 default: 166 MOZ_ASSERT_UNREACHABLE("Unknown display item arena type"); 167 } 168 } 169 170 totalSizeInFreeLists += totalSize; 171 } 172 173 auto& field = aKind == ArenaKind::PresShell 174 ? aSizes.mLayoutPresShellSize 175 : aSizes.mLayoutRetainedDisplayListSize; 176 177 field += mallocSize - totalSizeInFreeLists; 178 } 179 180 // Explicitly instantiate templates for the used nsPresArena allocator sizes. 181 // This is needed because nsPresArena definition is split across multiple files. 182 template class nsPresArena<8192, ArenaObjectID, eArenaObjectID_COUNT>; 183 template class nsPresArena<32768, DisplayListArenaObjectId, 184 size_t(DisplayListArenaObjectId::COUNT)>;