Line data Source code
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 0 : nsPresArena::nsPresArena()
26 : {
27 0 : }
28 :
29 0 : nsPresArena::~nsPresArena()
30 : {
31 0 : ClearArenaRefPtrs();
32 :
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 0 : }
44 :
45 : /* inline */ void
46 0 : nsPresArena::ClearArenaRefPtrWithoutDeregistering(void* aPtr,
47 : ArenaObjectID aObjectID)
48 : {
49 0 : switch (aObjectID) {
50 : // We use ArenaRefPtr<ComputedStyle>, which can be ComputedStyle
51 : // or GeckoComputedStyle. GeckoComputedStyle is actually arena managed,
52 : // but ComputedStyle isn't.
53 : case eArenaObjectID_GeckoComputedStyle:
54 0 : static_cast<ArenaRefPtr<ComputedStyle>*>(aPtr)->ClearWithoutDeregistering();
55 : return;
56 : default:
57 0 : MOZ_ASSERT(false, "unexpected ArenaObjectID value");
58 : break;
59 : }
60 : }
61 :
62 : void
63 0 : nsPresArena::ClearArenaRefPtrs()
64 : {
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 : }
70 0 : mArenaRefPtrs.Clear();
71 0 : }
72 :
73 : void
74 0 : nsPresArena::ClearArenaRefPtrs(ArenaObjectID aObjectID)
75 : {
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 : }
83 : }
84 0 : }
85 :
86 : void*
87 0 : nsPresArena::Allocate(uint32_t aCode, size_t aSize)
88 : {
89 0 : MOZ_ASSERT(NS_IsMainThread());
90 0 : MOZ_ASSERT(aSize > 0, "PresArena cannot allocate zero bytes");
91 1332 : MOZ_ASSERT(aCode < ArrayLength(mFreeLists));
92 :
93 : // We only hand out aligned sizes
94 1332 : aSize = mPool.AlignedSize(aSize);
95 :
96 1332 : FreeList* list = &mFreeLists[aCode];
97 :
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 247 : list->mEntrySize = aSize;
102 : } else {
103 1085 : MOZ_ASSERT(list->mEntrySize == aSize,
104 : "different sizes for same object type code");
105 : }
106 :
107 : void* result;
108 1332 : if (len > 0) {
109 : // Remove from the end of the mEntries array to avoid memmoving entries,
110 : // and use SetLengthAndRetainStorage to avoid a lot of malloc/free
111 : // from ShrinkCapacity on smaller sizes. 500 pointers means the malloc size
112 : // for the array is 4096 bytes or more on a 64-bit system. The next smaller
113 : // 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 : } else {
118 212 : list->mEntries.SetLengthAndRetainStorage(len - 1);
119 : }
120 : #if defined(DEBUG)
121 : {
122 : MOZ_MAKE_MEM_DEFINED(result, list->mEntrySize);
123 0 : char* p = reinterpret_cast<char*>(result);
124 0 : char* limit = p + list->mEntrySize;
125 0 : for (; p < limit; p += sizeof(uintptr_t)) {
126 0 : uintptr_t val = *reinterpret_cast<uintptr_t*>(p);
127 1 : if (val != mozPoisonValue()) {
128 0 : MOZ_ReportAssertionFailure(
129 0 : 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 0 : uint64_t(mozPoisonValue() ^ val)).get(),
136 0 : __FILE__, __LINE__);
137 0 : MOZ_CRASH();
138 : }
139 : }
140 : }
141 : #endif
142 : MOZ_MAKE_MEM_UNDEFINED(result, list->mEntrySize);
143 : return result;
144 : }
145 :
146 : // Allocate a new chunk from the arena
147 0 : list->mEntriesEverAllocated++;
148 1120 : return mPool.Allocate(aSize);
149 : }
150 :
151 : void
152 918 : nsPresArena::Free(uint32_t aCode, void* aPtr)
153 : {
154 918 : MOZ_ASSERT(NS_IsMainThread());
155 918 : MOZ_ASSERT(aCode < ArrayLength(mFreeLists));
156 :
157 : // Try to recycle this entry.
158 918 : FreeList* list = &mFreeLists[aCode];
159 0 : MOZ_ASSERT(list->mEntrySize > 0, "object of this type was never allocated");
160 :
161 918 : mozWritePoison(aPtr, list->mEntrySize);
162 :
163 : MOZ_MAKE_MEM_NOACCESS(aPtr, list->mEntrySize);
164 918 : list->mEntries.AppendElement(aPtr);
165 918 : }
166 :
167 : void
168 13 : nsPresArena::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const
169 : {
170 : // We do a complicated dance here because we want to measure the
171 : // space taken up by the different kinds of objects in the arena,
172 : // but we don't have pointers to those objects. And even if we did,
173 : // we wouldn't be able to use mMallocSizeOf on them, since they were
174 : // allocated out of malloc'd chunks of memory. So we compute the
175 : // size of the arena as known by malloc and we add up the sizes of
176 : // all the objects that we care about. Subtracting these two
177 : // quantities gives us a catch-all "other" number, which includes
178 : // slop in the arena itself as well as the size of objects that
179 : // we've not measured explicitly.
180 :
181 0 : size_t mallocSize = mPool.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
182 :
183 0 : size_t totalSizeInFreeLists = 0;
184 4862 : for (const FreeList* entry = mFreeLists;
185 0 : entry != ArrayEnd(mFreeLists);
186 : ++entry) {
187 4836 : mallocSize += entry->SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
188 :
189 : // Note that we're not measuring the size of the entries on the free
190 : // list here. The free list knows how many objects we've allocated
191 : // ever (which includes any objects that may be on the FreeList's
192 : // |mEntries| at this point) and we're using that to determine the
193 : // total size of objects allocated with a given ID.
194 0 : size_t totalSize = entry->mEntrySize * entry->mEntriesEverAllocated;
195 :
196 2418 : switch (entry - mFreeLists) {
197 : #define FRAME_ID(classname, ...) \
198 : case nsQueryFrame::classname##_id: \
199 : aSizes.mArenaSizes.NS_ARENA_SIZES_FIELD(classname) += totalSize; \
200 : break;
201 : #define ABSTRACT_FRAME_ID(...)
202 : #include "nsFrameIdList.h"
203 : #undef FRAME_ID
204 : #undef ABSTRACT_FRAME_ID
205 : case eArenaObjectID_nsLineBox:
206 13 : aSizes.mArenaSizes.mLineBoxes += totalSize;
207 13 : break;
208 : default:
209 : continue;
210 : }
211 :
212 1846 : totalSizeInFreeLists += totalSize;
213 : }
214 :
215 : aSizes.mLayoutPresShellSize += mallocSize - totalSizeInFreeLists;
216 : }
|