Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
michael@0 | 2 | * vim: set ts=2 sw=2 et tw=78: |
michael@0 | 3 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
michael@0 | 6 | */ |
michael@0 | 7 | |
michael@0 | 8 | /* arena allocation for the frame tree and closely-related objects */ |
michael@0 | 9 | |
michael@0 | 10 | // Even on 32-bit systems, we allocate objects from the frame arena |
michael@0 | 11 | // that require 8-byte alignment. The cast to uintptr_t is needed |
michael@0 | 12 | // because plarena isn't as careful about mask construction as it |
michael@0 | 13 | // ought to be. |
michael@0 | 14 | #define ALIGN_SHIFT 3 |
michael@0 | 15 | #define PL_ARENA_CONST_ALIGN_MASK ((uintptr_t(1) << ALIGN_SHIFT) - 1) |
michael@0 | 16 | #include "plarena.h" |
michael@0 | 17 | // plarena.h needs to be included first to make it use the above |
michael@0 | 18 | // PL_ARENA_CONST_ALIGN_MASK in this file. |
michael@0 | 19 | |
michael@0 | 20 | #include "nsPresArena.h" |
michael@0 | 21 | |
michael@0 | 22 | #include "mozilla/Poison.h" |
michael@0 | 23 | #include "nsDebug.h" |
michael@0 | 24 | #include "nsArenaMemoryStats.h" |
michael@0 | 25 | #include "nsPrintfCString.h" |
michael@0 | 26 | |
michael@0 | 27 | // Size to use for PLArena block allocations. |
michael@0 | 28 | static const size_t ARENA_PAGE_SIZE = 8192; |
michael@0 | 29 | |
michael@0 | 30 | nsPresArena::nsPresArena() |
michael@0 | 31 | { |
michael@0 | 32 | PL_INIT_ARENA_POOL(&mPool, "PresArena", ARENA_PAGE_SIZE); |
michael@0 | 33 | } |
michael@0 | 34 | |
michael@0 | 35 | nsPresArena::~nsPresArena() |
michael@0 | 36 | { |
michael@0 | 37 | #if defined(MOZ_HAVE_MEM_CHECKS) |
michael@0 | 38 | mFreeLists.EnumerateEntries(UnpoisonFreeList, nullptr); |
michael@0 | 39 | #endif |
michael@0 | 40 | PL_FinishArenaPool(&mPool); |
michael@0 | 41 | } |
michael@0 | 42 | |
michael@0 | 43 | NS_HIDDEN_(void*) |
michael@0 | 44 | nsPresArena::Allocate(uint32_t aCode, size_t aSize) |
michael@0 | 45 | { |
michael@0 | 46 | NS_ABORT_IF_FALSE(aSize > 0, "PresArena cannot allocate zero bytes"); |
michael@0 | 47 | |
michael@0 | 48 | // We only hand out aligned sizes |
michael@0 | 49 | aSize = PL_ARENA_ALIGN(&mPool, aSize); |
michael@0 | 50 | |
michael@0 | 51 | // If there is no free-list entry for this type already, we have |
michael@0 | 52 | // to create one now, to record its size. |
michael@0 | 53 | FreeList* list = mFreeLists.PutEntry(aCode); |
michael@0 | 54 | |
michael@0 | 55 | nsTArray<void*>::index_type len = list->mEntries.Length(); |
michael@0 | 56 | if (list->mEntrySize == 0) { |
michael@0 | 57 | NS_ABORT_IF_FALSE(len == 0, "list with entries but no recorded size"); |
michael@0 | 58 | list->mEntrySize = aSize; |
michael@0 | 59 | } else { |
michael@0 | 60 | NS_ABORT_IF_FALSE(list->mEntrySize == aSize, |
michael@0 | 61 | "different sizes for same object type code"); |
michael@0 | 62 | } |
michael@0 | 63 | |
michael@0 | 64 | void* result; |
michael@0 | 65 | if (len > 0) { |
michael@0 | 66 | // LIFO behavior for best cache utilization |
michael@0 | 67 | result = list->mEntries.ElementAt(len - 1); |
michael@0 | 68 | list->mEntries.RemoveElementAt(len - 1); |
michael@0 | 69 | #if defined(DEBUG) |
michael@0 | 70 | { |
michael@0 | 71 | MOZ_MAKE_MEM_DEFINED(result, list->mEntrySize); |
michael@0 | 72 | char* p = reinterpret_cast<char*>(result); |
michael@0 | 73 | char* limit = p + list->mEntrySize; |
michael@0 | 74 | for (; p < limit; p += sizeof(uintptr_t)) { |
michael@0 | 75 | uintptr_t val = *reinterpret_cast<uintptr_t*>(p); |
michael@0 | 76 | NS_ABORT_IF_FALSE(val == mozPoisonValue(), |
michael@0 | 77 | nsPrintfCString("PresArena: poison overwritten; " |
michael@0 | 78 | "wanted %.16llx " |
michael@0 | 79 | "found %.16llx " |
michael@0 | 80 | "errors in bits %.16llx", |
michael@0 | 81 | uint64_t(mozPoisonValue()), |
michael@0 | 82 | uint64_t(val), |
michael@0 | 83 | uint64_t(mozPoisonValue() ^ val) |
michael@0 | 84 | ).get()); |
michael@0 | 85 | } |
michael@0 | 86 | } |
michael@0 | 87 | #endif |
michael@0 | 88 | MOZ_MAKE_MEM_UNDEFINED(result, list->mEntrySize); |
michael@0 | 89 | return result; |
michael@0 | 90 | } |
michael@0 | 91 | |
michael@0 | 92 | // Allocate a new chunk from the arena |
michael@0 | 93 | list->mEntriesEverAllocated++; |
michael@0 | 94 | PL_ARENA_ALLOCATE(result, &mPool, aSize); |
michael@0 | 95 | if (!result) { |
michael@0 | 96 | NS_RUNTIMEABORT("out of memory"); |
michael@0 | 97 | } |
michael@0 | 98 | return result; |
michael@0 | 99 | } |
michael@0 | 100 | |
michael@0 | 101 | NS_HIDDEN_(void) |
michael@0 | 102 | nsPresArena::Free(uint32_t aCode, void* aPtr) |
michael@0 | 103 | { |
michael@0 | 104 | // Try to recycle this entry. |
michael@0 | 105 | FreeList* list = mFreeLists.GetEntry(aCode); |
michael@0 | 106 | NS_ABORT_IF_FALSE(list, "no free list for pres arena object"); |
michael@0 | 107 | NS_ABORT_IF_FALSE(list->mEntrySize > 0, "PresArena cannot free zero bytes"); |
michael@0 | 108 | |
michael@0 | 109 | mozWritePoison(aPtr, list->mEntrySize); |
michael@0 | 110 | |
michael@0 | 111 | MOZ_MAKE_MEM_NOACCESS(aPtr, list->mEntrySize); |
michael@0 | 112 | list->mEntries.AppendElement(aPtr); |
michael@0 | 113 | } |
michael@0 | 114 | |
michael@0 | 115 | /* static */ size_t |
michael@0 | 116 | nsPresArena::SizeOfFreeListEntryExcludingThis( |
michael@0 | 117 | FreeList* aEntry, mozilla::MallocSizeOf aMallocSizeOf, void*) |
michael@0 | 118 | { |
michael@0 | 119 | return aEntry->mEntries.SizeOfExcludingThis(aMallocSizeOf); |
michael@0 | 120 | } |
michael@0 | 121 | |
michael@0 | 122 | struct EnumerateData { |
michael@0 | 123 | nsArenaMemoryStats* stats; |
michael@0 | 124 | size_t total; |
michael@0 | 125 | }; |
michael@0 | 126 | |
michael@0 | 127 | #if defined(MOZ_HAVE_MEM_CHECKS) |
michael@0 | 128 | /* static */ PLDHashOperator |
michael@0 | 129 | nsPresArena::UnpoisonFreeList(FreeList* aEntry, void*) |
michael@0 | 130 | { |
michael@0 | 131 | nsTArray<void*>::index_type len; |
michael@0 | 132 | while ((len = aEntry->mEntries.Length())) { |
michael@0 | 133 | void* result = aEntry->mEntries.ElementAt(len - 1); |
michael@0 | 134 | aEntry->mEntries.RemoveElementAt(len - 1); |
michael@0 | 135 | MOZ_MAKE_MEM_UNDEFINED(result, aEntry->mEntrySize); |
michael@0 | 136 | } |
michael@0 | 137 | return PL_DHASH_NEXT; |
michael@0 | 138 | } |
michael@0 | 139 | #endif |
michael@0 | 140 | |
michael@0 | 141 | /* static */ PLDHashOperator |
michael@0 | 142 | nsPresArena::FreeListEnumerator(FreeList* aEntry, void* aData) |
michael@0 | 143 | { |
michael@0 | 144 | EnumerateData* data = static_cast<EnumerateData*>(aData); |
michael@0 | 145 | // Note that we're not measuring the size of the entries on the free |
michael@0 | 146 | // list here. The free list knows how many objects we've allocated |
michael@0 | 147 | // ever (which includes any objects that may be on the FreeList's |
michael@0 | 148 | // |mEntries| at this point) and we're using that to determine the |
michael@0 | 149 | // total size of objects allocated with a given ID. |
michael@0 | 150 | size_t totalSize = aEntry->mEntrySize * aEntry->mEntriesEverAllocated; |
michael@0 | 151 | size_t* p; |
michael@0 | 152 | |
michael@0 | 153 | switch (NS_PTR_TO_INT32(aEntry->mKey)) { |
michael@0 | 154 | #define FRAME_ID(classname) \ |
michael@0 | 155 | case nsQueryFrame::classname##_id: \ |
michael@0 | 156 | p = &data->stats->FRAME_ID_STAT_FIELD(classname); \ |
michael@0 | 157 | break; |
michael@0 | 158 | #include "nsFrameIdList.h" |
michael@0 | 159 | #undef FRAME_ID |
michael@0 | 160 | case nsLineBox_id: |
michael@0 | 161 | p = &data->stats->mLineBoxes; |
michael@0 | 162 | break; |
michael@0 | 163 | case nsRuleNode_id: |
michael@0 | 164 | p = &data->stats->mRuleNodes; |
michael@0 | 165 | break; |
michael@0 | 166 | case nsStyleContext_id: |
michael@0 | 167 | p = &data->stats->mStyleContexts; |
michael@0 | 168 | break; |
michael@0 | 169 | default: |
michael@0 | 170 | return PL_DHASH_NEXT; |
michael@0 | 171 | } |
michael@0 | 172 | |
michael@0 | 173 | *p += totalSize; |
michael@0 | 174 | data->total += totalSize; |
michael@0 | 175 | |
michael@0 | 176 | return PL_DHASH_NEXT; |
michael@0 | 177 | } |
michael@0 | 178 | |
michael@0 | 179 | void |
michael@0 | 180 | nsPresArena::AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf, |
michael@0 | 181 | nsArenaMemoryStats* aArenaStats) |
michael@0 | 182 | { |
michael@0 | 183 | // We do a complicated dance here because we want to measure the |
michael@0 | 184 | // space taken up by the different kinds of objects in the arena, |
michael@0 | 185 | // but we don't have pointers to those objects. And even if we did, |
michael@0 | 186 | // we wouldn't be able to use aMallocSizeOf on them, since they were |
michael@0 | 187 | // allocated out of malloc'd chunks of memory. So we compute the |
michael@0 | 188 | // size of the arena as known by malloc and we add up the sizes of |
michael@0 | 189 | // all the objects that we care about. Subtracting these two |
michael@0 | 190 | // quantities gives us a catch-all "other" number, which includes |
michael@0 | 191 | // slop in the arena itself as well as the size of objects that |
michael@0 | 192 | // we've not measured explicitly. |
michael@0 | 193 | |
michael@0 | 194 | size_t mallocSize = PL_SizeOfArenaPoolExcludingPool(&mPool, aMallocSizeOf); |
michael@0 | 195 | mallocSize += mFreeLists.SizeOfExcludingThis(SizeOfFreeListEntryExcludingThis, |
michael@0 | 196 | aMallocSizeOf); |
michael@0 | 197 | |
michael@0 | 198 | EnumerateData data = { aArenaStats, 0 }; |
michael@0 | 199 | mFreeLists.EnumerateEntries(FreeListEnumerator, &data); |
michael@0 | 200 | aArenaStats->mOther += mallocSize - data.total; |
michael@0 | 201 | } |