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 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Steve Clark <buster@netscape.com>
25 : * HÃ¥kan Waara <hwaara@chello.se>
26 : * Dan Rosen <dr@netscape.com>
27 : * Daniel Glazman <glazman@netscape.com>
28 : * Mats Palmgren <mats.palmgren@bredband.net>
29 : *
30 : * Alternatively, the contents of this file may be used under the terms of
31 : * either of the GNU General Public License Version 2 or later (the "GPL"),
32 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
33 : * in which case the provisions of the GPL or the LGPL are applicable instead
34 : * of those above. If you wish to allow use of your version of this file only
35 : * under the terms of either the GPL or the LGPL, and not to allow others to
36 : * use your version of this file under the terms of the MPL, indicate your
37 : * decision by deleting the provisions above and replace them with the notice
38 : * and other provisions required by the GPL or the LGPL. If you do not delete
39 : * the provisions above, a recipient may use your version of this file under
40 : * the terms of any one of the MPL, the GPL or the LGPL.
41 : *
42 : * ***** END LICENSE BLOCK *****
43 : */
44 :
45 : /* arena allocation for the frame tree and closely-related objects */
46 :
47 : #include "nsPresArena.h"
48 : #include "nsCRT.h"
49 : #include "nsDebug.h"
50 : #include "nsTArray.h"
51 : #include "nsTHashtable.h"
52 : #include "prmem.h"
53 : #include "prinit.h"
54 : #include "prlog.h"
55 :
56 : #ifdef MOZ_CRASHREPORTER
57 : #include "nsICrashReporter.h"
58 : #include "nsCOMPtr.h"
59 : #include "nsServiceManagerUtils.h"
60 : #include "nsPrintfCString.h"
61 : #endif
62 :
63 : // Even on 32-bit systems, we allocate objects from the frame arena
64 : // that require 8-byte alignment. The cast to PRUword is needed
65 : // because plarena isn't as careful about mask construction as it
66 : // ought to be.
67 : #define ALIGN_SHIFT 3
68 : #define PL_ARENA_CONST_ALIGN_MASK ((PRUword(1) << ALIGN_SHIFT) - 1)
69 : #include "plarena.h"
70 :
71 : #ifdef _WIN32
72 : # include <windows.h>
73 : #elif !defined(__OS2__)
74 : # include <unistd.h>
75 : # include <sys/mman.h>
76 : # ifndef MAP_ANON
77 : # ifdef MAP_ANONYMOUS
78 : # define MAP_ANON MAP_ANONYMOUS
79 : # else
80 : # error "Don't know how to get anonymous memory"
81 : # endif
82 : # endif
83 : #endif
84 :
85 : // Size to use for PLArena block allocations.
86 : static const size_t ARENA_PAGE_SIZE = 8192;
87 :
88 : // Freed memory is filled with a poison value, which we arrange to
89 : // form a pointer either to an always-unmapped region of the address
90 : // space, or to a page that has been reserved and rendered
91 : // inaccessible via OS primitives. See tests/TestPoisonArea.cpp for
92 : // extensive discussion of the requirements for this page. The code
93 : // from here to 'class FreeList' needs to be kept in sync with that
94 : // file.
95 :
96 : #ifdef _WIN32
97 : static void *
98 : ReserveRegion(PRUword region, PRUword size)
99 : {
100 : return VirtualAlloc((void *)region, size, MEM_RESERVE, PAGE_NOACCESS);
101 : }
102 :
103 : static void
104 : ReleaseRegion(void *region, PRUword size)
105 : {
106 : VirtualFree(region, size, MEM_RELEASE);
107 : }
108 :
109 : static bool
110 : ProbeRegion(PRUword region, PRUword size)
111 : {
112 : SYSTEM_INFO sinfo;
113 : GetSystemInfo(&sinfo);
114 : if (region >= (PRUword)sinfo.lpMaximumApplicationAddress &&
115 : region + size >= (PRUword)sinfo.lpMaximumApplicationAddress) {
116 : return true;
117 : } else {
118 : return false;
119 : }
120 : }
121 :
122 : static PRUword
123 : GetDesiredRegionSize()
124 : {
125 : SYSTEM_INFO sinfo;
126 : GetSystemInfo(&sinfo);
127 : return sinfo.dwAllocationGranularity;
128 : }
129 :
130 : #define RESERVE_FAILED 0
131 :
132 : #elif defined(__OS2__)
133 : static void *
134 : ReserveRegion(PRUword region, PRUword size)
135 : {
136 : // OS/2 doesn't support allocation at an arbitrary address,
137 : // so return an address that is known to be invalid.
138 : return (void*)0xFFFD0000;
139 : }
140 :
141 : static void
142 : ReleaseRegion(void *region, PRUword size)
143 : {
144 : return;
145 : }
146 :
147 : static bool
148 : ProbeRegion(PRUword region, PRUword size)
149 : {
150 : // There's no reliable way to probe an address in the system
151 : // arena other than by touching it and seeing if a trap occurs.
152 : return false;
153 : }
154 :
155 : static PRUword
156 : GetDesiredRegionSize()
157 : {
158 : // Page size is fixed at 4k.
159 : return 0x1000;
160 : }
161 :
162 : #define RESERVE_FAILED 0
163 :
164 : #else // Unix
165 :
166 : static void *
167 0 : ReserveRegion(PRUword region, PRUword size)
168 : {
169 0 : return mmap((caddr_t)region, size, PROT_NONE, MAP_PRIVATE|MAP_ANON, -1, 0);
170 : }
171 :
172 : static void
173 0 : ReleaseRegion(void *region, PRUword size)
174 : {
175 0 : munmap((caddr_t)region, size);
176 0 : }
177 :
178 : static bool
179 0 : ProbeRegion(PRUword region, PRUword size)
180 : {
181 0 : if (madvise((caddr_t)region, size, MADV_NORMAL)) {
182 0 : return true;
183 : } else {
184 0 : return false;
185 : }
186 : }
187 :
188 : static PRUword
189 0 : GetDesiredRegionSize()
190 : {
191 0 : return sysconf(_SC_PAGESIZE);
192 : }
193 :
194 : #define RESERVE_FAILED MAP_FAILED
195 :
196 : #endif // system dependencies
197 :
198 : PR_STATIC_ASSERT(sizeof(PRUword) == 4 || sizeof(PRUword) == 8);
199 : PR_STATIC_ASSERT(sizeof(PRUword) == sizeof(void *));
200 :
201 : static PRUword
202 0 : ReservePoisonArea(PRUword rgnsize)
203 : {
204 : if (sizeof(PRUword) == 8) {
205 : // Use the hardware-inaccessible region.
206 : // We have to avoid 64-bit constants and shifts by 32 bits, since this
207 : // code is compiled in 32-bit mode, although it is never executed there.
208 : return
209 : (((PRUword(0x7FFFFFFFu) << 31) << 1 | PRUword(0xF0DEAFFFu))
210 : & ~(rgnsize-1));
211 :
212 : } else {
213 : // First see if we can allocate the preferred poison address from the OS.
214 0 : PRUword candidate = (0xF0DEAFFF & ~(rgnsize-1));
215 0 : void *result = ReserveRegion(candidate, rgnsize);
216 0 : if (result == (void *)candidate) {
217 : // success - inaccessible page allocated
218 0 : return candidate;
219 : }
220 :
221 : // That didn't work, so see if the preferred address is within a range
222 : // of permanently inacessible memory.
223 0 : if (ProbeRegion(candidate, rgnsize)) {
224 : // success - selected page cannot be usable memory
225 0 : if (result != RESERVE_FAILED)
226 0 : ReleaseRegion(result, rgnsize);
227 0 : return candidate;
228 : }
229 :
230 : // The preferred address is already in use. Did the OS give us a
231 : // consolation prize?
232 0 : if (result != RESERVE_FAILED) {
233 0 : return PRUword(result);
234 : }
235 :
236 : // It didn't, so try to allocate again, without any constraint on
237 : // the address.
238 0 : result = ReserveRegion(0, rgnsize);
239 0 : if (result != RESERVE_FAILED) {
240 0 : return PRUword(result);
241 : }
242 :
243 0 : NS_RUNTIMEABORT("no usable poison region identified");
244 0 : return 0;
245 : }
246 : }
247 :
248 : static PRUword ARENA_POISON;
249 : static PRCallOnceType ARENA_POISON_guard;
250 :
251 : static PRStatus
252 0 : ARENA_POISON_init()
253 : {
254 0 : PRUword rgnsize = GetDesiredRegionSize();
255 0 : PRUword rgnbase = ReservePoisonArea(rgnsize);
256 :
257 0 : if (rgnsize == 0) // can't happen
258 0 : return PR_FAILURE;
259 :
260 0 : ARENA_POISON = rgnbase + rgnsize/2 - 1;
261 :
262 : #ifdef MOZ_CRASHREPORTER
263 : nsCOMPtr<nsICrashReporter> cr =
264 0 : do_GetService("@mozilla.org/toolkit/crash-reporter;1");
265 : bool enabled;
266 0 : if (cr && NS_SUCCEEDED(cr->GetEnabled(&enabled)) && enabled) {
267 0 : cr->AnnotateCrashReport(NS_LITERAL_CSTRING("FramePoisonBase"),
268 0 : nsPrintfCString(17, "%.16llx", PRUint64(rgnbase)));
269 0 : cr->AnnotateCrashReport(NS_LITERAL_CSTRING("FramePoisonSize"),
270 0 : nsPrintfCString("%lu", PRUint32(rgnsize)));
271 : }
272 : #endif
273 0 : return PR_SUCCESS;
274 : }
275 :
276 : #ifndef DEBUG_TRACEMALLOC_PRESARENA
277 :
278 : // All keys to this hash table fit in 32 bits (see below) so we do not
279 : // bother actually hashing them.
280 :
281 : namespace {
282 :
283 : class FreeList : public PLDHashEntryHdr
284 0 : {
285 : public:
286 : typedef PRUint32 KeyType;
287 : nsTArray<void *> mEntries;
288 : size_t mEntrySize;
289 :
290 : protected:
291 : typedef const void* KeyTypePointer;
292 : KeyTypePointer mKey;
293 :
294 0 : FreeList(KeyTypePointer aKey) : mEntrySize(0), mKey(aKey) {}
295 : // Default copy constructor and destructor are ok.
296 :
297 0 : bool KeyEquals(KeyTypePointer const aKey) const
298 0 : { return mKey == aKey; }
299 :
300 0 : static KeyTypePointer KeyToPointer(KeyType aKey)
301 0 : { return NS_INT32_TO_PTR(aKey); }
302 :
303 0 : static PLDHashNumber HashKey(KeyTypePointer aKey)
304 0 : { return NS_PTR_TO_INT32(aKey); }
305 :
306 : enum { ALLOW_MEMMOVE = false };
307 : friend class nsTHashtable<FreeList>;
308 : };
309 :
310 : }
311 :
312 : struct nsPresArena::State {
313 : nsTHashtable<FreeList> mFreeLists;
314 : PLArenaPool mPool;
315 :
316 0 : State()
317 0 : {
318 0 : mFreeLists.Init();
319 0 : PL_INIT_ARENA_POOL(&mPool, "PresArena", ARENA_PAGE_SIZE);
320 0 : PR_CallOnce(&ARENA_POISON_guard, ARENA_POISON_init);
321 0 : }
322 :
323 0 : ~State()
324 0 : {
325 0 : PL_FinishArenaPool(&mPool);
326 0 : }
327 :
328 0 : void* Allocate(PRUint32 aCode, size_t aSize)
329 : {
330 0 : NS_ABORT_IF_FALSE(aSize > 0, "PresArena cannot allocate zero bytes");
331 :
332 : // We only hand out aligned sizes
333 0 : aSize = PL_ARENA_ALIGN(&mPool, aSize);
334 :
335 : // If there is no free-list entry for this type already, we have
336 : // to create one now, to record its size.
337 0 : FreeList* list = mFreeLists.PutEntry(aCode);
338 0 : if (!list) {
339 0 : return nsnull;
340 : }
341 :
342 0 : nsTArray<void*>::index_type len = list->mEntries.Length();
343 0 : if (list->mEntrySize == 0) {
344 0 : NS_ABORT_IF_FALSE(len == 0, "list with entries but no recorded size");
345 0 : list->mEntrySize = aSize;
346 : } else {
347 0 : NS_ABORT_IF_FALSE(list->mEntrySize == aSize,
348 : "different sizes for same object type code");
349 : }
350 :
351 : void* result;
352 0 : if (len > 0) {
353 : // LIFO behavior for best cache utilization
354 0 : result = list->mEntries.ElementAt(len - 1);
355 0 : list->mEntries.RemoveElementAt(len - 1);
356 : #ifdef DEBUG
357 : {
358 0 : char* p = reinterpret_cast<char*>(result);
359 0 : char* limit = p + list->mEntrySize;
360 0 : for (; p < limit; p += sizeof(PRUword)) {
361 0 : NS_ABORT_IF_FALSE(*reinterpret_cast<PRUword*>(p) == ARENA_POISON,
362 : "PresArena: poison overwritten");
363 : }
364 : }
365 : #endif
366 0 : return result;
367 : }
368 :
369 : // Allocate a new chunk from the arena
370 0 : PL_ARENA_ALLOCATE(result, &mPool, aSize);
371 0 : return result;
372 : }
373 :
374 0 : void Free(PRUint32 aCode, void* aPtr)
375 : {
376 : // Try to recycle this entry.
377 0 : FreeList* list = mFreeLists.GetEntry(aCode);
378 0 : NS_ABORT_IF_FALSE(list, "no free list for pres arena object");
379 0 : NS_ABORT_IF_FALSE(list->mEntrySize > 0, "PresArena cannot free zero bytes");
380 :
381 0 : char* p = reinterpret_cast<char*>(aPtr);
382 0 : char* limit = p + list->mEntrySize;
383 0 : for (; p < limit; p += sizeof(PRUword)) {
384 0 : *reinterpret_cast<PRUword*>(p) = ARENA_POISON;
385 : }
386 :
387 0 : list->mEntries.AppendElement(aPtr);
388 0 : }
389 :
390 0 : size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const
391 : {
392 0 : size_t n = aMallocSizeOf(this);
393 :
394 : // The first PLArena is within the PLArenaPool, i.e. within |this|, so we
395 : // don't measure it. Subsequent PLArenas are by themselves and must be
396 : // measured.
397 0 : const PLArena *arena = mPool.first.next;
398 0 : while (arena) {
399 0 : n += aMallocSizeOf(arena);
400 0 : arena = arena->next;
401 : }
402 0 : return n;
403 : }
404 : };
405 :
406 : size_t
407 0 : nsPresArena::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const
408 : {
409 0 : return mState ? mState->SizeOfIncludingThis(aMallocSizeOf) : 0;
410 : }
411 :
412 : #else
413 : // Stub implementation that forwards everything to malloc and does not
414 : // poison allocations (it still initializes the poison value though,
415 : // for external use through GetPoisonValue()).
416 :
417 : struct nsPresArena::State
418 : {
419 :
420 : State()
421 : {
422 : PR_CallOnce(&ARENA_POISON_guard, ARENA_POISON_init);
423 : }
424 :
425 : void* Allocate(PRUint32 /* unused */, size_t aSize)
426 : {
427 : return PR_Malloc(aSize);
428 : }
429 :
430 : void Free(PRUint32 /* unused */, void* aPtr)
431 : {
432 : PR_Free(aPtr);
433 : }
434 : };
435 :
436 : size_t
437 : nsPresArena::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const
438 : {
439 : return 0;
440 : }
441 :
442 : #endif // DEBUG_TRACEMALLOC_PRESARENA
443 :
444 : // Public interface
445 0 : nsPresArena::nsPresArena()
446 0 : : mState(new nsPresArena::State())
447 0 : {}
448 :
449 0 : nsPresArena::~nsPresArena()
450 : {
451 0 : delete mState;
452 0 : }
453 :
454 : void*
455 0 : nsPresArena::AllocateBySize(size_t aSize)
456 : {
457 : return mState->Allocate(PRUint32(aSize) |
458 : PRUint32(nsQueryFrame::NON_FRAME_MARKER),
459 0 : aSize);
460 : }
461 :
462 : void
463 0 : nsPresArena::FreeBySize(size_t aSize, void* aPtr)
464 : {
465 : mState->Free(PRUint32(aSize) |
466 0 : PRUint32(nsQueryFrame::NON_FRAME_MARKER), aPtr);
467 0 : }
468 :
469 : void*
470 0 : nsPresArena::AllocateByCode(nsQueryFrame::FrameIID aCode, size_t aSize)
471 : {
472 0 : return mState->Allocate(aCode, aSize);
473 : }
474 :
475 : void
476 0 : nsPresArena::FreeByCode(nsQueryFrame::FrameIID aCode, void* aPtr)
477 : {
478 0 : mState->Free(aCode, aPtr);
479 0 : }
480 :
481 : /* static */ PRUword
482 0 : nsPresArena::GetPoisonValue()
483 : {
484 0 : return ARENA_POISON;
485 : }
|