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