/src/mozilla-central/layout/base/nsPresArena.cpp
Line | Count | Source (jump to first uncovered line) |
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 "mozilla/Poison.h" |
13 | | #include "nsDebug.h" |
14 | | #include "nsPrintfCString.h" |
15 | | #include "FrameLayerBuilder.h" |
16 | | #include "mozilla/ArrayUtils.h" |
17 | | #include "mozilla/ComputedStyle.h" |
18 | | #include "mozilla/ComputedStyleInlines.h" |
19 | | #include "nsWindowSizes.h" |
20 | | |
21 | | #include <inttypes.h> |
22 | | |
23 | | using namespace mozilla; |
24 | | |
25 | | nsPresArena::nsPresArena() |
26 | 0 | { |
27 | 0 | } |
28 | | |
29 | | nsPresArena::~nsPresArena() |
30 | 0 | { |
31 | 0 | ClearArenaRefPtrs(); |
32 | 0 |
|
33 | | #if defined(MOZ_HAVE_MEM_CHECKS) |
34 | | for (FreeList* entry = mFreeLists; entry != ArrayEnd(mFreeLists); ++entry) { |
35 | | nsTArray<void*>::index_type len; |
36 | | while ((len = entry->mEntries.Length())) { |
37 | | void* result = entry->mEntries.ElementAt(len - 1); |
38 | | entry->mEntries.RemoveElementAt(len - 1); |
39 | | MOZ_MAKE_MEM_UNDEFINED(result, entry->mEntrySize); |
40 | | } |
41 | | } |
42 | | #endif |
43 | | } |
44 | | |
45 | | /* inline */ void |
46 | | nsPresArena::ClearArenaRefPtrWithoutDeregistering(void* aPtr, |
47 | | ArenaObjectID aObjectID) |
48 | 0 | { |
49 | 0 | switch (aObjectID) { |
50 | 0 | // We use ArenaRefPtr<ComputedStyle>, which can be ComputedStyle |
51 | 0 | // or GeckoComputedStyle. GeckoComputedStyle is actually arena managed, |
52 | 0 | // but ComputedStyle isn't. |
53 | 0 | case eArenaObjectID_GeckoComputedStyle: |
54 | 0 | static_cast<ArenaRefPtr<ComputedStyle>*>(aPtr)->ClearWithoutDeregistering(); |
55 | 0 | return; |
56 | 0 | default: |
57 | 0 | MOZ_ASSERT(false, "unexpected ArenaObjectID value"); |
58 | 0 | break; |
59 | 0 | } |
60 | 0 | } |
61 | | |
62 | | void |
63 | | nsPresArena::ClearArenaRefPtrs() |
64 | 0 | { |
65 | 0 | for (auto iter = mArenaRefPtrs.Iter(); !iter.Done(); iter.Next()) { |
66 | 0 | void* ptr = iter.Key(); |
67 | 0 | ArenaObjectID id = iter.UserData(); |
68 | 0 | ClearArenaRefPtrWithoutDeregistering(ptr, id); |
69 | 0 | } |
70 | 0 | mArenaRefPtrs.Clear(); |
71 | 0 | } |
72 | | |
73 | | void |
74 | | nsPresArena::ClearArenaRefPtrs(ArenaObjectID aObjectID) |
75 | 0 | { |
76 | 0 | for (auto iter = mArenaRefPtrs.Iter(); !iter.Done(); iter.Next()) { |
77 | 0 | void* ptr = iter.Key(); |
78 | 0 | ArenaObjectID id = iter.UserData(); |
79 | 0 | if (id == aObjectID) { |
80 | 0 | ClearArenaRefPtrWithoutDeregistering(ptr, id); |
81 | 0 | iter.Remove(); |
82 | 0 | } |
83 | 0 | } |
84 | 0 | } |
85 | | |
86 | | void* |
87 | | nsPresArena::Allocate(uint32_t aCode, size_t aSize) |
88 | 0 | { |
89 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
90 | 0 | MOZ_ASSERT(aSize > 0, "PresArena cannot allocate zero bytes"); |
91 | 0 | MOZ_ASSERT(aCode < ArrayLength(mFreeLists)); |
92 | 0 |
|
93 | 0 | // We only hand out aligned sizes |
94 | 0 | aSize = mPool.AlignedSize(aSize); |
95 | 0 |
|
96 | 0 | FreeList* list = &mFreeLists[aCode]; |
97 | 0 |
|
98 | 0 | nsTArray<void*>::index_type len = list->mEntries.Length(); |
99 | 0 | if (list->mEntrySize == 0) { |
100 | 0 | MOZ_ASSERT(len == 0, "list with entries but no recorded size"); |
101 | 0 | list->mEntrySize = aSize; |
102 | 0 | } else { |
103 | 0 | MOZ_ASSERT(list->mEntrySize == aSize, |
104 | 0 | "different sizes for same object type code"); |
105 | 0 | } |
106 | 0 |
|
107 | 0 | void* result; |
108 | 0 | if (len > 0) { |
109 | 0 | // Remove from the end of the mEntries array to avoid memmoving entries, |
110 | 0 | // and use SetLengthAndRetainStorage to avoid a lot of malloc/free |
111 | 0 | // from ShrinkCapacity on smaller sizes. 500 pointers means the malloc size |
112 | 0 | // for the array is 4096 bytes or more on a 64-bit system. The next smaller |
113 | 0 | // size is 2048 (with jemalloc), which we consider not worth compacting. |
114 | 0 | result = list->mEntries.ElementAt(len - 1); |
115 | 0 | if (list->mEntries.Capacity() > 500) { |
116 | 0 | list->mEntries.RemoveElementAt(len - 1); |
117 | 0 | } else { |
118 | 0 | list->mEntries.SetLengthAndRetainStorage(len - 1); |
119 | 0 | } |
120 | | #if defined(DEBUG) |
121 | | { |
122 | | MOZ_MAKE_MEM_DEFINED(result, list->mEntrySize); |
123 | | char* p = reinterpret_cast<char*>(result); |
124 | | char* limit = p + list->mEntrySize; |
125 | | for (; p < limit; p += sizeof(uintptr_t)) { |
126 | | uintptr_t val = *reinterpret_cast<uintptr_t*>(p); |
127 | | if (val != mozPoisonValue()) { |
128 | | MOZ_ReportAssertionFailure( |
129 | | nsPrintfCString("PresArena: poison overwritten; " |
130 | | "wanted %.16" PRIx64 " " |
131 | | "found %.16" PRIx64 " " |
132 | | "errors in bits %.16" PRIx64 " ", |
133 | | uint64_t(mozPoisonValue()), |
134 | | uint64_t(val), |
135 | | uint64_t(mozPoisonValue() ^ val)).get(), |
136 | | __FILE__, __LINE__); |
137 | | MOZ_CRASH(); |
138 | | } |
139 | | } |
140 | | } |
141 | | #endif |
142 | 0 | MOZ_MAKE_MEM_UNDEFINED(result, list->mEntrySize); |
143 | 0 | return result; |
144 | 0 | } |
145 | 0 |
|
146 | 0 | // Allocate a new chunk from the arena |
147 | 0 | list->mEntriesEverAllocated++; |
148 | 0 | return mPool.Allocate(aSize); |
149 | 0 | } |
150 | | |
151 | | void |
152 | | nsPresArena::Free(uint32_t aCode, void* aPtr) |
153 | 0 | { |
154 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
155 | 0 | MOZ_ASSERT(aCode < ArrayLength(mFreeLists)); |
156 | 0 |
|
157 | 0 | // Try to recycle this entry. |
158 | 0 | FreeList* list = &mFreeLists[aCode]; |
159 | 0 | MOZ_ASSERT(list->mEntrySize > 0, "object of this type was never allocated"); |
160 | 0 |
|
161 | 0 | mozWritePoison(aPtr, list->mEntrySize); |
162 | 0 |
|
163 | 0 | MOZ_MAKE_MEM_NOACCESS(aPtr, list->mEntrySize); |
164 | 0 | list->mEntries.AppendElement(aPtr); |
165 | 0 | } |
166 | | |
167 | | void |
168 | | nsPresArena::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const |
169 | 0 | { |
170 | 0 | // We do a complicated dance here because we want to measure the |
171 | 0 | // space taken up by the different kinds of objects in the arena, |
172 | 0 | // but we don't have pointers to those objects. And even if we did, |
173 | 0 | // we wouldn't be able to use mMallocSizeOf on them, since they were |
174 | 0 | // allocated out of malloc'd chunks of memory. So we compute the |
175 | 0 | // size of the arena as known by malloc and we add up the sizes of |
176 | 0 | // all the objects that we care about. Subtracting these two |
177 | 0 | // quantities gives us a catch-all "other" number, which includes |
178 | 0 | // slop in the arena itself as well as the size of objects that |
179 | 0 | // we've not measured explicitly. |
180 | 0 |
|
181 | 0 | size_t mallocSize = mPool.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf); |
182 | 0 |
|
183 | 0 | size_t totalSizeInFreeLists = 0; |
184 | 0 | for (const FreeList* entry = mFreeLists; |
185 | 0 | entry != ArrayEnd(mFreeLists); |
186 | 0 | ++entry) { |
187 | 0 | mallocSize += entry->SizeOfExcludingThis(aSizes.mState.mMallocSizeOf); |
188 | 0 |
|
189 | 0 | // Note that we're not measuring the size of the entries on the free |
190 | 0 | // list here. The free list knows how many objects we've allocated |
191 | 0 | // ever (which includes any objects that may be on the FreeList's |
192 | 0 | // |mEntries| at this point) and we're using that to determine the |
193 | 0 | // total size of objects allocated with a given ID. |
194 | 0 | size_t totalSize = entry->mEntrySize * entry->mEntriesEverAllocated; |
195 | 0 |
|
196 | 0 | switch (entry - mFreeLists) { |
197 | 0 | #define FRAME_ID(classname, ...) \ |
198 | 0 | case nsQueryFrame::classname##_id: \ |
199 | 0 | aSizes.mArenaSizes.NS_ARENA_SIZES_FIELD(classname) += totalSize; \ |
200 | 0 | break; |
201 | 0 | #define ABSTRACT_FRAME_ID(...) |
202 | 0 | #include "nsFrameIdList.h" |
203 | 0 | #undef FRAME_ID |
204 | 0 | #undef ABSTRACT_FRAME_ID |
205 | 0 | case eArenaObjectID_nsLineBox: |
206 | 0 | aSizes.mArenaSizes.mLineBoxes += totalSize; |
207 | 0 | break; |
208 | 0 | default: |
209 | 0 | continue; |
210 | 0 | } |
211 | 0 | |
212 | 0 | totalSizeInFreeLists += totalSize; |
213 | 0 | } |
214 | 0 |
|
215 | 0 | aSizes.mLayoutPresShellSize += mallocSize - totalSizeInFreeLists; |
216 | 0 | } |