|
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 */ |
|
7 |
|
8 /* arena allocation for the frame tree and closely-related objects */ |
|
9 |
|
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. |
|
19 |
|
20 #include "nsPresArena.h" |
|
21 |
|
22 #include "mozilla/Poison.h" |
|
23 #include "nsDebug.h" |
|
24 #include "nsArenaMemoryStats.h" |
|
25 #include "nsPrintfCString.h" |
|
26 |
|
27 // Size to use for PLArena block allocations. |
|
28 static const size_t ARENA_PAGE_SIZE = 8192; |
|
29 |
|
30 nsPresArena::nsPresArena() |
|
31 { |
|
32 PL_INIT_ARENA_POOL(&mPool, "PresArena", ARENA_PAGE_SIZE); |
|
33 } |
|
34 |
|
35 nsPresArena::~nsPresArena() |
|
36 { |
|
37 #if defined(MOZ_HAVE_MEM_CHECKS) |
|
38 mFreeLists.EnumerateEntries(UnpoisonFreeList, nullptr); |
|
39 #endif |
|
40 PL_FinishArenaPool(&mPool); |
|
41 } |
|
42 |
|
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"); |
|
47 |
|
48 // We only hand out aligned sizes |
|
49 aSize = PL_ARENA_ALIGN(&mPool, aSize); |
|
50 |
|
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); |
|
54 |
|
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 } |
|
63 |
|
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 } |
|
91 |
|
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 } |
|
100 |
|
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"); |
|
108 |
|
109 mozWritePoison(aPtr, list->mEntrySize); |
|
110 |
|
111 MOZ_MAKE_MEM_NOACCESS(aPtr, list->mEntrySize); |
|
112 list->mEntries.AppendElement(aPtr); |
|
113 } |
|
114 |
|
115 /* static */ size_t |
|
116 nsPresArena::SizeOfFreeListEntryExcludingThis( |
|
117 FreeList* aEntry, mozilla::MallocSizeOf aMallocSizeOf, void*) |
|
118 { |
|
119 return aEntry->mEntries.SizeOfExcludingThis(aMallocSizeOf); |
|
120 } |
|
121 |
|
122 struct EnumerateData { |
|
123 nsArenaMemoryStats* stats; |
|
124 size_t total; |
|
125 }; |
|
126 |
|
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 |
|
140 |
|
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; |
|
152 |
|
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 } |
|
172 |
|
173 *p += totalSize; |
|
174 data->total += totalSize; |
|
175 |
|
176 return PL_DHASH_NEXT; |
|
177 } |
|
178 |
|
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. |
|
193 |
|
194 size_t mallocSize = PL_SizeOfArenaPoolExcludingPool(&mPool, aMallocSizeOf); |
|
195 mallocSize += mFreeLists.SizeOfExcludingThis(SizeOfFreeListEntryExcludingThis, |
|
196 aMallocSizeOf); |
|
197 |
|
198 EnumerateData data = { aArenaStats, 0 }; |
|
199 mFreeLists.EnumerateEntries(FreeListEnumerator, &data); |
|
200 aArenaStats->mOther += mallocSize - data.total; |
|
201 } |