|
1 /* -*- Mode: C; tab-width: 8; c-basic-offset: 8; indent-tabs-mode: t -*- */ |
|
2 /* vim:set softtabstop=8 shiftwidth=8 noet: */ |
|
3 /*- |
|
4 * Copyright (C) 2006-2008 Jason Evans <jasone@FreeBSD.org>. |
|
5 * All rights reserved. |
|
6 * |
|
7 * Redistribution and use in source and binary forms, with or without |
|
8 * modification, are permitted provided that the following conditions |
|
9 * are met: |
|
10 * 1. Redistributions of source code must retain the above copyright |
|
11 * notice(s), this list of conditions and the following disclaimer as |
|
12 * the first lines of this file unmodified other than the possible |
|
13 * addition of one or more copyright notices. |
|
14 * 2. Redistributions in binary form must reproduce the above copyright |
|
15 * notice(s), this list of conditions and the following disclaimer in |
|
16 * the documentation and/or other materials provided with the |
|
17 * distribution. |
|
18 * |
|
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY |
|
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE |
|
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
|
26 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
|
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
|
28 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
|
29 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
30 * |
|
31 ******************************************************************************* |
|
32 * |
|
33 * This allocator implementation is designed to provide scalable performance |
|
34 * for multi-threaded programs on multi-processor systems. The following |
|
35 * features are included for this purpose: |
|
36 * |
|
37 * + Multiple arenas are used if there are multiple CPUs, which reduces lock |
|
38 * contention and cache sloshing. |
|
39 * |
|
40 * + Cache line sharing between arenas is avoided for internal data |
|
41 * structures. |
|
42 * |
|
43 * + Memory is managed in chunks and runs (chunks can be split into runs), |
|
44 * rather than as individual pages. This provides a constant-time |
|
45 * mechanism for associating allocations with particular arenas. |
|
46 * |
|
47 * Allocation requests are rounded up to the nearest size class, and no record |
|
48 * of the original request size is maintained. Allocations are broken into |
|
49 * categories according to size class. Assuming runtime defaults, 4 kB pages |
|
50 * and a 16 byte quantum on a 32-bit system, the size classes in each category |
|
51 * are as follows: |
|
52 * |
|
53 * |=====================================| |
|
54 * | Category | Subcategory | Size | |
|
55 * |=====================================| |
|
56 * | Small | Tiny | 2 | |
|
57 * | | | 4 | |
|
58 * | | | 8 | |
|
59 * | |----------------+---------| |
|
60 * | | Quantum-spaced | 16 | |
|
61 * | | | 32 | |
|
62 * | | | 48 | |
|
63 * | | | ... | |
|
64 * | | | 480 | |
|
65 * | | | 496 | |
|
66 * | | | 512 | |
|
67 * | |----------------+---------| |
|
68 * | | Sub-page | 1 kB | |
|
69 * | | | 2 kB | |
|
70 * |=====================================| |
|
71 * | Large | 4 kB | |
|
72 * | | 8 kB | |
|
73 * | | 12 kB | |
|
74 * | | ... | |
|
75 * | | 1012 kB | |
|
76 * | | 1016 kB | |
|
77 * | | 1020 kB | |
|
78 * |=====================================| |
|
79 * | Huge | 1 MB | |
|
80 * | | 2 MB | |
|
81 * | | 3 MB | |
|
82 * | | ... | |
|
83 * |=====================================| |
|
84 * |
|
85 * NOTE: Due to Mozilla bug 691003, we cannot reserve less than one word for an |
|
86 * allocation on Linux or Mac. So on 32-bit *nix, the smallest bucket size is |
|
87 * 4 bytes, and on 64-bit, the smallest bucket size is 8 bytes. |
|
88 * |
|
89 * A different mechanism is used for each category: |
|
90 * |
|
91 * Small : Each size class is segregated into its own set of runs. Each run |
|
92 * maintains a bitmap of which regions are free/allocated. |
|
93 * |
|
94 * Large : Each allocation is backed by a dedicated run. Metadata are stored |
|
95 * in the associated arena chunk header maps. |
|
96 * |
|
97 * Huge : Each allocation is backed by a dedicated contiguous set of chunks. |
|
98 * Metadata are stored in a separate red-black tree. |
|
99 * |
|
100 ******************************************************************************* |
|
101 */ |
|
102 |
|
103 #ifdef MOZ_MEMORY_ANDROID |
|
104 #define NO_TLS |
|
105 #define _pthread_self() pthread_self() |
|
106 #endif |
|
107 |
|
108 /* |
|
109 * On Linux, we use madvise(MADV_DONTNEED) to release memory back to the |
|
110 * operating system. If we release 1MB of live pages with MADV_DONTNEED, our |
|
111 * RSS will decrease by 1MB (almost) immediately. |
|
112 * |
|
113 * On Mac, we use madvise(MADV_FREE). Unlike MADV_DONTNEED on Linux, MADV_FREE |
|
114 * on Mac doesn't cause the OS to release the specified pages immediately; the |
|
115 * OS keeps them in our process until the machine comes under memory pressure. |
|
116 * |
|
117 * It's therefore difficult to measure the process's RSS on Mac, since, in the |
|
118 * absence of memory pressure, the contribution from the heap to RSS will not |
|
119 * decrease due to our madvise calls. |
|
120 * |
|
121 * We therefore define MALLOC_DOUBLE_PURGE on Mac. This causes jemalloc to |
|
122 * track which pages have been MADV_FREE'd. You can then call |
|
123 * jemalloc_purge_freed_pages(), which will force the OS to release those |
|
124 * MADV_FREE'd pages, making the process's RSS reflect its true memory usage. |
|
125 * |
|
126 * The jemalloc_purge_freed_pages definition in memory/build/mozmemory.h needs |
|
127 * to be adjusted if MALLOC_DOUBLE_PURGE is ever enabled on Linux. |
|
128 */ |
|
129 #ifdef MOZ_MEMORY_DARWIN |
|
130 #define MALLOC_DOUBLE_PURGE |
|
131 #endif |
|
132 |
|
133 /* |
|
134 * MALLOC_PRODUCTION disables assertions and statistics gathering. It also |
|
135 * defaults the A and J runtime options to off. These settings are appropriate |
|
136 * for production systems. |
|
137 */ |
|
138 #ifndef MOZ_MEMORY_DEBUG |
|
139 # define MALLOC_PRODUCTION |
|
140 #endif |
|
141 |
|
142 /* |
|
143 * Use only one arena by default. Mozilla does not currently make extensive |
|
144 * use of concurrent allocation, so the increased fragmentation associated with |
|
145 * multiple arenas is not warranted. |
|
146 */ |
|
147 #define MOZ_MEMORY_NARENAS_DEFAULT_ONE |
|
148 |
|
149 /* |
|
150 * Pass this set of options to jemalloc as its default. It does not override |
|
151 * the options passed via the MALLOC_OPTIONS environment variable but is |
|
152 * applied in addition to them. |
|
153 */ |
|
154 #ifdef MOZ_B2G |
|
155 /* Reduce the amount of unused dirty pages to 1MiB on B2G */ |
|
156 # define MOZ_MALLOC_OPTIONS "ff" |
|
157 #else |
|
158 # define MOZ_MALLOC_OPTIONS "" |
|
159 #endif |
|
160 |
|
161 /* |
|
162 * MALLOC_STATS enables statistics calculation, and is required for |
|
163 * jemalloc_stats(). |
|
164 */ |
|
165 #define MALLOC_STATS |
|
166 |
|
167 /* Memory filling (junk/poison/zero). */ |
|
168 #define MALLOC_FILL |
|
169 |
|
170 #ifndef MALLOC_PRODUCTION |
|
171 /* |
|
172 * MALLOC_DEBUG enables assertions and other sanity checks, and disables |
|
173 * inline functions. |
|
174 */ |
|
175 # define MALLOC_DEBUG |
|
176 |
|
177 /* Allocation tracing. */ |
|
178 # ifndef MOZ_MEMORY_WINDOWS |
|
179 # define MALLOC_UTRACE |
|
180 # endif |
|
181 |
|
182 /* Support optional abort() on OOM. */ |
|
183 # define MALLOC_XMALLOC |
|
184 |
|
185 /* Support SYSV semantics. */ |
|
186 # define MALLOC_SYSV |
|
187 #endif |
|
188 |
|
189 /* |
|
190 * MALLOC_VALIDATE causes malloc_usable_size() to perform some pointer |
|
191 * validation. There are many possible errors that validation does not even |
|
192 * attempt to detect. |
|
193 */ |
|
194 #define MALLOC_VALIDATE |
|
195 |
|
196 /* Embed no-op macros that support memory allocation tracking via valgrind. */ |
|
197 #ifdef MOZ_VALGRIND |
|
198 # define MALLOC_VALGRIND |
|
199 #endif |
|
200 #ifdef MALLOC_VALGRIND |
|
201 # include <valgrind/valgrind.h> |
|
202 #else |
|
203 # define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) |
|
204 # define VALGRIND_FREELIKE_BLOCK(addr, rzB) |
|
205 #endif |
|
206 |
|
207 /* |
|
208 * MALLOC_BALANCE enables monitoring of arena lock contention and dynamically |
|
209 * re-balances arena load if exponentially averaged contention exceeds a |
|
210 * certain threshold. |
|
211 */ |
|
212 /* #define MALLOC_BALANCE */ |
|
213 |
|
214 /* |
|
215 * MALLOC_PAGEFILE causes all mmap()ed memory to be backed by temporary |
|
216 * files, so that if a chunk is mapped, it is guaranteed to be swappable. |
|
217 * This avoids asynchronous OOM failures that are due to VM over-commit. |
|
218 */ |
|
219 /* #define MALLOC_PAGEFILE */ |
|
220 |
|
221 #ifdef MALLOC_PAGEFILE |
|
222 /* Write size when initializing a page file. */ |
|
223 # define MALLOC_PAGEFILE_WRITE_SIZE 512 |
|
224 #endif |
|
225 |
|
226 #if defined(MOZ_MEMORY_LINUX) && !defined(MOZ_MEMORY_ANDROID) |
|
227 #define _GNU_SOURCE /* For mremap(2). */ |
|
228 #define issetugid() 0 |
|
229 #if 0 /* Enable in order to test decommit code on Linux. */ |
|
230 # define MALLOC_DECOMMIT |
|
231 #endif |
|
232 #endif |
|
233 |
|
234 #include <sys/types.h> |
|
235 |
|
236 #include <errno.h> |
|
237 #include <stdlib.h> |
|
238 #include <limits.h> |
|
239 #include <stdarg.h> |
|
240 #include <stdio.h> |
|
241 #include <string.h> |
|
242 |
|
243 #ifdef MOZ_MEMORY_WINDOWS |
|
244 |
|
245 /* Some defines from the CRT internal headers that we need here. */ |
|
246 #define _CRT_SPINCOUNT 5000 |
|
247 #define __crtInitCritSecAndSpinCount InitializeCriticalSectionAndSpinCount |
|
248 #include <io.h> |
|
249 #include <windows.h> |
|
250 |
|
251 #pragma warning( disable: 4267 4996 4146 ) |
|
252 |
|
253 #define bool BOOL |
|
254 #define false FALSE |
|
255 #define true TRUE |
|
256 #define inline __inline |
|
257 #define SIZE_T_MAX SIZE_MAX |
|
258 #define STDERR_FILENO 2 |
|
259 #define PATH_MAX MAX_PATH |
|
260 #define vsnprintf _vsnprintf |
|
261 |
|
262 #ifndef NO_TLS |
|
263 static unsigned long tlsIndex = 0xffffffff; |
|
264 #endif |
|
265 |
|
266 #define __thread |
|
267 #define _pthread_self() __threadid() |
|
268 #define issetugid() 0 |
|
269 |
|
270 /* use MSVC intrinsics */ |
|
271 #pragma intrinsic(_BitScanForward) |
|
272 static __forceinline int |
|
273 ffs(int x) |
|
274 { |
|
275 unsigned long i; |
|
276 |
|
277 if (_BitScanForward(&i, x) != 0) |
|
278 return (i + 1); |
|
279 |
|
280 return (0); |
|
281 } |
|
282 |
|
283 /* Implement getenv without using malloc */ |
|
284 static char mozillaMallocOptionsBuf[64]; |
|
285 |
|
286 #define getenv xgetenv |
|
287 static char * |
|
288 getenv(const char *name) |
|
289 { |
|
290 |
|
291 if (GetEnvironmentVariableA(name, (LPSTR)&mozillaMallocOptionsBuf, |
|
292 sizeof(mozillaMallocOptionsBuf)) > 0) |
|
293 return (mozillaMallocOptionsBuf); |
|
294 |
|
295 return (NULL); |
|
296 } |
|
297 |
|
298 typedef unsigned char uint8_t; |
|
299 typedef unsigned uint32_t; |
|
300 typedef unsigned long long uint64_t; |
|
301 typedef unsigned long long uintmax_t; |
|
302 #if defined(_WIN64) |
|
303 typedef long long ssize_t; |
|
304 #else |
|
305 typedef long ssize_t; |
|
306 #endif |
|
307 |
|
308 #define MALLOC_DECOMMIT |
|
309 #endif |
|
310 |
|
311 #ifndef MOZ_MEMORY_WINDOWS |
|
312 #ifndef MOZ_MEMORY_SOLARIS |
|
313 #include <sys/cdefs.h> |
|
314 #endif |
|
315 #ifndef __DECONST |
|
316 # define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var)) |
|
317 #endif |
|
318 #ifndef MOZ_MEMORY |
|
319 __FBSDID("$FreeBSD: head/lib/libc/stdlib/malloc.c 180599 2008-07-18 19:35:44Z jasone $"); |
|
320 #include "libc_private.h" |
|
321 #ifdef MALLOC_DEBUG |
|
322 # define _LOCK_DEBUG |
|
323 #endif |
|
324 #include "spinlock.h" |
|
325 #include "namespace.h" |
|
326 #endif |
|
327 #include <sys/mman.h> |
|
328 #ifndef MADV_FREE |
|
329 # define MADV_FREE MADV_DONTNEED |
|
330 #endif |
|
331 #ifndef MAP_NOSYNC |
|
332 # define MAP_NOSYNC 0 |
|
333 #endif |
|
334 #include <sys/param.h> |
|
335 #ifndef MOZ_MEMORY |
|
336 #include <sys/stddef.h> |
|
337 #endif |
|
338 #include <sys/time.h> |
|
339 #include <sys/types.h> |
|
340 #if !defined(MOZ_MEMORY_SOLARIS) && !defined(MOZ_MEMORY_ANDROID) |
|
341 #include <sys/sysctl.h> |
|
342 #endif |
|
343 #include <sys/uio.h> |
|
344 #ifndef MOZ_MEMORY |
|
345 #include <sys/ktrace.h> /* Must come after several other sys/ includes. */ |
|
346 |
|
347 #include <machine/atomic.h> |
|
348 #include <machine/cpufunc.h> |
|
349 #include <machine/vmparam.h> |
|
350 #endif |
|
351 |
|
352 #include <errno.h> |
|
353 #include <limits.h> |
|
354 #ifndef SIZE_T_MAX |
|
355 # define SIZE_T_MAX SIZE_MAX |
|
356 #endif |
|
357 #include <pthread.h> |
|
358 #ifdef MOZ_MEMORY_DARWIN |
|
359 #define _pthread_self pthread_self |
|
360 #define _pthread_mutex_init pthread_mutex_init |
|
361 #define _pthread_mutex_trylock pthread_mutex_trylock |
|
362 #define _pthread_mutex_lock pthread_mutex_lock |
|
363 #define _pthread_mutex_unlock pthread_mutex_unlock |
|
364 #endif |
|
365 #include <sched.h> |
|
366 #include <stdarg.h> |
|
367 #include <stdio.h> |
|
368 #include <stdbool.h> |
|
369 #include <stdint.h> |
|
370 #include <stdlib.h> |
|
371 #include <string.h> |
|
372 #ifndef MOZ_MEMORY_DARWIN |
|
373 #include <strings.h> |
|
374 #endif |
|
375 #include <unistd.h> |
|
376 |
|
377 #ifdef MOZ_MEMORY_DARWIN |
|
378 #include <libkern/OSAtomic.h> |
|
379 #include <mach/mach_error.h> |
|
380 #include <mach/mach_init.h> |
|
381 #include <mach/vm_map.h> |
|
382 #include <malloc/malloc.h> |
|
383 #endif |
|
384 |
|
385 #ifndef MOZ_MEMORY |
|
386 #include "un-namespace.h" |
|
387 #endif |
|
388 |
|
389 #endif |
|
390 |
|
391 #include "jemalloc_types.h" |
|
392 #include "linkedlist.h" |
|
393 #include "mozmemory_wrap.h" |
|
394 |
|
395 /* Some tools, such as /dev/dsp wrappers, LD_PRELOAD libraries that |
|
396 * happen to override mmap() and call dlsym() from their overridden |
|
397 * mmap(). The problem is that dlsym() calls malloc(), and this ends |
|
398 * up in a dead lock in jemalloc. |
|
399 * On these systems, we prefer to directly use the system call. |
|
400 * We do that for Linux systems and kfreebsd with GNU userland. |
|
401 * Note sanity checks are not done (alignment of offset, ...) because |
|
402 * the uses of mmap are pretty limited, in jemalloc. |
|
403 * |
|
404 * On Alpha, glibc has a bug that prevents syscall() to work for system |
|
405 * calls with 6 arguments |
|
406 */ |
|
407 #if (defined(MOZ_MEMORY_LINUX) && !defined(__alpha__)) || \ |
|
408 (defined(MOZ_MEMORY_BSD) && defined(__GLIBC__)) |
|
409 #include <sys/syscall.h> |
|
410 #if defined(SYS_mmap) || defined(SYS_mmap2) |
|
411 static inline |
|
412 void *_mmap(void *addr, size_t length, int prot, int flags, |
|
413 int fd, off_t offset) |
|
414 { |
|
415 /* S390 only passes one argument to the mmap system call, which is a |
|
416 * pointer to a structure containing the arguments */ |
|
417 #ifdef __s390__ |
|
418 struct { |
|
419 void *addr; |
|
420 size_t length; |
|
421 long prot; |
|
422 long flags; |
|
423 long fd; |
|
424 off_t offset; |
|
425 } args = { addr, length, prot, flags, fd, offset }; |
|
426 return (void *) syscall(SYS_mmap, &args); |
|
427 #else |
|
428 #ifdef SYS_mmap2 |
|
429 return (void *) syscall(SYS_mmap2, addr, length, prot, flags, |
|
430 fd, offset >> 12); |
|
431 #else |
|
432 return (void *) syscall(SYS_mmap, addr, length, prot, flags, |
|
433 fd, offset); |
|
434 #endif |
|
435 #endif |
|
436 } |
|
437 #define mmap _mmap |
|
438 #define munmap(a, l) syscall(SYS_munmap, a, l) |
|
439 #endif |
|
440 #endif |
|
441 |
|
442 #ifdef MOZ_MEMORY_DARWIN |
|
443 static const bool isthreaded = true; |
|
444 #endif |
|
445 |
|
446 #if defined(MOZ_MEMORY_SOLARIS) && defined(MAP_ALIGN) && !defined(JEMALLOC_NEVER_USES_MAP_ALIGN) |
|
447 #define JEMALLOC_USES_MAP_ALIGN /* Required on Solaris 10. Might improve performance elsewhere. */ |
|
448 #endif |
|
449 |
|
450 #define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var)) |
|
451 |
|
452 #ifdef MOZ_MEMORY_WINDOWS |
|
453 /* MSVC++ does not support C99 variable-length arrays. */ |
|
454 # define RB_NO_C99_VARARRAYS |
|
455 #endif |
|
456 #include "rb.h" |
|
457 |
|
458 #ifdef MALLOC_DEBUG |
|
459 /* Disable inlining to make debugging easier. */ |
|
460 #ifdef inline |
|
461 #undef inline |
|
462 #endif |
|
463 |
|
464 # define inline |
|
465 #endif |
|
466 |
|
467 /* Size of stack-allocated buffer passed to strerror_r(). */ |
|
468 #define STRERROR_BUF 64 |
|
469 |
|
470 /* Minimum alignment of non-tiny allocations is 2^QUANTUM_2POW_MIN bytes. */ |
|
471 # define QUANTUM_2POW_MIN 4 |
|
472 #if defined(_WIN64) || defined(__LP64__) |
|
473 # define SIZEOF_PTR_2POW 3 |
|
474 #else |
|
475 # define SIZEOF_PTR_2POW 2 |
|
476 #endif |
|
477 #define PIC |
|
478 #ifndef MOZ_MEMORY_DARWIN |
|
479 static const bool isthreaded = true; |
|
480 #else |
|
481 # define NO_TLS |
|
482 #endif |
|
483 #if 0 |
|
484 #ifdef __i386__ |
|
485 # define QUANTUM_2POW_MIN 4 |
|
486 # define SIZEOF_PTR_2POW 2 |
|
487 # define CPU_SPINWAIT __asm__ volatile("pause") |
|
488 #endif |
|
489 #ifdef __ia64__ |
|
490 # define QUANTUM_2POW_MIN 4 |
|
491 # define SIZEOF_PTR_2POW 3 |
|
492 #endif |
|
493 #ifdef __alpha__ |
|
494 # define QUANTUM_2POW_MIN 4 |
|
495 # define SIZEOF_PTR_2POW 3 |
|
496 # define NO_TLS |
|
497 #endif |
|
498 #ifdef __sparc64__ |
|
499 # define QUANTUM_2POW_MIN 4 |
|
500 # define SIZEOF_PTR_2POW 3 |
|
501 # define NO_TLS |
|
502 #endif |
|
503 #ifdef __amd64__ |
|
504 # define QUANTUM_2POW_MIN 4 |
|
505 # define SIZEOF_PTR_2POW 3 |
|
506 # define CPU_SPINWAIT __asm__ volatile("pause") |
|
507 #endif |
|
508 #ifdef __arm__ |
|
509 # define QUANTUM_2POW_MIN 3 |
|
510 # define SIZEOF_PTR_2POW 2 |
|
511 # define NO_TLS |
|
512 #endif |
|
513 #ifdef __mips__ |
|
514 # define QUANTUM_2POW_MIN 3 |
|
515 # define SIZEOF_PTR_2POW 2 |
|
516 # define NO_TLS |
|
517 #endif |
|
518 #ifdef __powerpc__ |
|
519 # define QUANTUM_2POW_MIN 4 |
|
520 # define SIZEOF_PTR_2POW 2 |
|
521 #endif |
|
522 #endif |
|
523 |
|
524 #define SIZEOF_PTR (1U << SIZEOF_PTR_2POW) |
|
525 |
|
526 /* sizeof(int) == (1U << SIZEOF_INT_2POW). */ |
|
527 #ifndef SIZEOF_INT_2POW |
|
528 # define SIZEOF_INT_2POW 2 |
|
529 #endif |
|
530 |
|
531 /* We can't use TLS in non-PIC programs, since TLS relies on loader magic. */ |
|
532 #if (!defined(PIC) && !defined(NO_TLS)) |
|
533 # define NO_TLS |
|
534 #endif |
|
535 |
|
536 #ifdef NO_TLS |
|
537 /* MALLOC_BALANCE requires TLS. */ |
|
538 # ifdef MALLOC_BALANCE |
|
539 # undef MALLOC_BALANCE |
|
540 # endif |
|
541 #endif |
|
542 |
|
543 /* |
|
544 * Size and alignment of memory chunks that are allocated by the OS's virtual |
|
545 * memory system. |
|
546 */ |
|
547 #define CHUNK_2POW_DEFAULT 20 |
|
548 /* Maximum number of dirty pages per arena. */ |
|
549 #define DIRTY_MAX_DEFAULT (1U << 10) |
|
550 |
|
551 /* |
|
552 * Maximum size of L1 cache line. This is used to avoid cache line aliasing, |
|
553 * so over-estimates are okay (up to a point), but under-estimates will |
|
554 * negatively affect performance. |
|
555 */ |
|
556 #define CACHELINE_2POW 6 |
|
557 #define CACHELINE ((size_t)(1U << CACHELINE_2POW)) |
|
558 |
|
559 /* |
|
560 * Smallest size class to support. On Linux and Mac, even malloc(1) must |
|
561 * reserve a word's worth of memory (see Mozilla bug 691003). |
|
562 */ |
|
563 #ifdef MOZ_MEMORY_WINDOWS |
|
564 #define TINY_MIN_2POW 1 |
|
565 #else |
|
566 #define TINY_MIN_2POW (sizeof(void*) == 8 ? 3 : 2) |
|
567 #endif |
|
568 |
|
569 /* |
|
570 * Maximum size class that is a multiple of the quantum, but not (necessarily) |
|
571 * a power of 2. Above this size, allocations are rounded up to the nearest |
|
572 * power of 2. |
|
573 */ |
|
574 #define SMALL_MAX_2POW_DEFAULT 9 |
|
575 #define SMALL_MAX_DEFAULT (1U << SMALL_MAX_2POW_DEFAULT) |
|
576 |
|
577 /* |
|
578 * RUN_MAX_OVRHD indicates maximum desired run header overhead. Runs are sized |
|
579 * as small as possible such that this setting is still honored, without |
|
580 * violating other constraints. The goal is to make runs as small as possible |
|
581 * without exceeding a per run external fragmentation threshold. |
|
582 * |
|
583 * We use binary fixed point math for overhead computations, where the binary |
|
584 * point is implicitly RUN_BFP bits to the left. |
|
585 * |
|
586 * Note that it is possible to set RUN_MAX_OVRHD low enough that it cannot be |
|
587 * honored for some/all object sizes, since there is one bit of header overhead |
|
588 * per object (plus a constant). This constraint is relaxed (ignored) for runs |
|
589 * that are so small that the per-region overhead is greater than: |
|
590 * |
|
591 * (RUN_MAX_OVRHD / (reg_size << (3+RUN_BFP)) |
|
592 */ |
|
593 #define RUN_BFP 12 |
|
594 /* \/ Implicit binary fixed point. */ |
|
595 #define RUN_MAX_OVRHD 0x0000003dU |
|
596 #define RUN_MAX_OVRHD_RELAX 0x00001800U |
|
597 |
|
598 /* Put a cap on small object run size. This overrides RUN_MAX_OVRHD. */ |
|
599 #define RUN_MAX_SMALL_2POW 15 |
|
600 #define RUN_MAX_SMALL (1U << RUN_MAX_SMALL_2POW) |
|
601 |
|
602 /* |
|
603 * Hyper-threaded CPUs may need a special instruction inside spin loops in |
|
604 * order to yield to another virtual CPU. If no such instruction is defined |
|
605 * above, make CPU_SPINWAIT a no-op. |
|
606 */ |
|
607 #ifndef CPU_SPINWAIT |
|
608 # define CPU_SPINWAIT |
|
609 #endif |
|
610 |
|
611 /* |
|
612 * Adaptive spinning must eventually switch to blocking, in order to avoid the |
|
613 * potential for priority inversion deadlock. Backing off past a certain point |
|
614 * can actually waste time. |
|
615 */ |
|
616 #define SPIN_LIMIT_2POW 11 |
|
617 |
|
618 /* |
|
619 * Conversion from spinning to blocking is expensive; we use (1U << |
|
620 * BLOCK_COST_2POW) to estimate how many more times costly blocking is than |
|
621 * worst-case spinning. |
|
622 */ |
|
623 #define BLOCK_COST_2POW 4 |
|
624 |
|
625 #ifdef MALLOC_BALANCE |
|
626 /* |
|
627 * We use an exponential moving average to track recent lock contention, |
|
628 * where the size of the history window is N, and alpha=2/(N+1). |
|
629 * |
|
630 * Due to integer math rounding, very small values here can cause |
|
631 * substantial degradation in accuracy, thus making the moving average decay |
|
632 * faster than it would with precise calculation. |
|
633 */ |
|
634 # define BALANCE_ALPHA_INV_2POW 9 |
|
635 |
|
636 /* |
|
637 * Threshold value for the exponential moving contention average at which to |
|
638 * re-assign a thread. |
|
639 */ |
|
640 # define BALANCE_THRESHOLD_DEFAULT (1U << (SPIN_LIMIT_2POW-4)) |
|
641 #endif |
|
642 |
|
643 /******************************************************************************/ |
|
644 |
|
645 /* MALLOC_DECOMMIT and MALLOC_DOUBLE_PURGE are mutually exclusive. */ |
|
646 #if defined(MALLOC_DECOMMIT) && defined(MALLOC_DOUBLE_PURGE) |
|
647 #error MALLOC_DECOMMIT and MALLOC_DOUBLE_PURGE are mutually exclusive. |
|
648 #endif |
|
649 |
|
650 /* |
|
651 * Mutexes based on spinlocks. We can't use normal pthread spinlocks in all |
|
652 * places, because they require malloc()ed memory, which causes bootstrapping |
|
653 * issues in some cases. |
|
654 */ |
|
655 #if defined(MOZ_MEMORY_WINDOWS) |
|
656 #define malloc_mutex_t CRITICAL_SECTION |
|
657 #define malloc_spinlock_t CRITICAL_SECTION |
|
658 #elif defined(MOZ_MEMORY_DARWIN) |
|
659 typedef struct { |
|
660 OSSpinLock lock; |
|
661 } malloc_mutex_t; |
|
662 typedef struct { |
|
663 OSSpinLock lock; |
|
664 } malloc_spinlock_t; |
|
665 #elif defined(MOZ_MEMORY) |
|
666 typedef pthread_mutex_t malloc_mutex_t; |
|
667 typedef pthread_mutex_t malloc_spinlock_t; |
|
668 #else |
|
669 /* XXX these should #ifdef these for freebsd (and linux?) only */ |
|
670 typedef struct { |
|
671 spinlock_t lock; |
|
672 } malloc_mutex_t; |
|
673 typedef malloc_spinlock_t malloc_mutex_t; |
|
674 #endif |
|
675 |
|
676 /* Set to true once the allocator has been initialized. */ |
|
677 static bool malloc_initialized = false; |
|
678 |
|
679 #if defined(MOZ_MEMORY_WINDOWS) |
|
680 /* No init lock for Windows. */ |
|
681 #elif defined(MOZ_MEMORY_DARWIN) |
|
682 static malloc_mutex_t init_lock = {OS_SPINLOCK_INIT}; |
|
683 #elif defined(MOZ_MEMORY_LINUX) && !defined(MOZ_MEMORY_ANDROID) |
|
684 static malloc_mutex_t init_lock = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP; |
|
685 #elif defined(MOZ_MEMORY) |
|
686 static malloc_mutex_t init_lock = PTHREAD_MUTEX_INITIALIZER; |
|
687 #else |
|
688 static malloc_mutex_t init_lock = {_SPINLOCK_INITIALIZER}; |
|
689 #endif |
|
690 |
|
691 /******************************************************************************/ |
|
692 /* |
|
693 * Statistics data structures. |
|
694 */ |
|
695 |
|
696 #ifdef MALLOC_STATS |
|
697 |
|
698 typedef struct malloc_bin_stats_s malloc_bin_stats_t; |
|
699 struct malloc_bin_stats_s { |
|
700 /* |
|
701 * Number of allocation requests that corresponded to the size of this |
|
702 * bin. |
|
703 */ |
|
704 uint64_t nrequests; |
|
705 |
|
706 /* Total number of runs created for this bin's size class. */ |
|
707 uint64_t nruns; |
|
708 |
|
709 /* |
|
710 * Total number of runs reused by extracting them from the runs tree for |
|
711 * this bin's size class. |
|
712 */ |
|
713 uint64_t reruns; |
|
714 |
|
715 /* High-water mark for this bin. */ |
|
716 unsigned long highruns; |
|
717 |
|
718 /* Current number of runs in this bin. */ |
|
719 unsigned long curruns; |
|
720 }; |
|
721 |
|
722 typedef struct arena_stats_s arena_stats_t; |
|
723 struct arena_stats_s { |
|
724 /* Number of bytes currently mapped. */ |
|
725 size_t mapped; |
|
726 |
|
727 /* |
|
728 * Total number of purge sweeps, total number of madvise calls made, |
|
729 * and total pages purged in order to keep dirty unused memory under |
|
730 * control. |
|
731 */ |
|
732 uint64_t npurge; |
|
733 uint64_t nmadvise; |
|
734 uint64_t purged; |
|
735 #ifdef MALLOC_DECOMMIT |
|
736 /* |
|
737 * Total number of decommit/commit operations, and total number of |
|
738 * pages decommitted. |
|
739 */ |
|
740 uint64_t ndecommit; |
|
741 uint64_t ncommit; |
|
742 uint64_t decommitted; |
|
743 #endif |
|
744 |
|
745 /* Current number of committed pages. */ |
|
746 size_t committed; |
|
747 |
|
748 /* Per-size-category statistics. */ |
|
749 size_t allocated_small; |
|
750 uint64_t nmalloc_small; |
|
751 uint64_t ndalloc_small; |
|
752 |
|
753 size_t allocated_large; |
|
754 uint64_t nmalloc_large; |
|
755 uint64_t ndalloc_large; |
|
756 |
|
757 #ifdef MALLOC_BALANCE |
|
758 /* Number of times this arena reassigned a thread due to contention. */ |
|
759 uint64_t nbalance; |
|
760 #endif |
|
761 }; |
|
762 |
|
763 #endif /* #ifdef MALLOC_STATS */ |
|
764 |
|
765 /******************************************************************************/ |
|
766 /* |
|
767 * Extent data structures. |
|
768 */ |
|
769 |
|
770 /* Tree of extents. */ |
|
771 typedef struct extent_node_s extent_node_t; |
|
772 struct extent_node_s { |
|
773 /* Linkage for the size/address-ordered tree. */ |
|
774 rb_node(extent_node_t) link_szad; |
|
775 |
|
776 /* Linkage for the address-ordered tree. */ |
|
777 rb_node(extent_node_t) link_ad; |
|
778 |
|
779 /* Pointer to the extent that this tree node is responsible for. */ |
|
780 void *addr; |
|
781 |
|
782 /* Total region size. */ |
|
783 size_t size; |
|
784 }; |
|
785 typedef rb_tree(extent_node_t) extent_tree_t; |
|
786 |
|
787 /******************************************************************************/ |
|
788 /* |
|
789 * Radix tree data structures. |
|
790 */ |
|
791 |
|
792 #ifdef MALLOC_VALIDATE |
|
793 /* |
|
794 * Size of each radix tree node (must be a power of 2). This impacts tree |
|
795 * depth. |
|
796 */ |
|
797 # if (SIZEOF_PTR == 4) |
|
798 # define MALLOC_RTREE_NODESIZE (1U << 14) |
|
799 # else |
|
800 # define MALLOC_RTREE_NODESIZE CACHELINE |
|
801 # endif |
|
802 |
|
803 typedef struct malloc_rtree_s malloc_rtree_t; |
|
804 struct malloc_rtree_s { |
|
805 malloc_spinlock_t lock; |
|
806 void **root; |
|
807 unsigned height; |
|
808 unsigned level2bits[1]; /* Dynamically sized. */ |
|
809 }; |
|
810 #endif |
|
811 |
|
812 /******************************************************************************/ |
|
813 /* |
|
814 * Arena data structures. |
|
815 */ |
|
816 |
|
817 typedef struct arena_s arena_t; |
|
818 typedef struct arena_bin_s arena_bin_t; |
|
819 |
|
820 /* Each element of the chunk map corresponds to one page within the chunk. */ |
|
821 typedef struct arena_chunk_map_s arena_chunk_map_t; |
|
822 struct arena_chunk_map_s { |
|
823 /* |
|
824 * Linkage for run trees. There are two disjoint uses: |
|
825 * |
|
826 * 1) arena_t's runs_avail tree. |
|
827 * 2) arena_run_t conceptually uses this linkage for in-use non-full |
|
828 * runs, rather than directly embedding linkage. |
|
829 */ |
|
830 rb_node(arena_chunk_map_t) link; |
|
831 |
|
832 /* |
|
833 * Run address (or size) and various flags are stored together. The bit |
|
834 * layout looks like (assuming 32-bit system): |
|
835 * |
|
836 * ???????? ???????? ????---- -mckdzla |
|
837 * |
|
838 * ? : Unallocated: Run address for first/last pages, unset for internal |
|
839 * pages. |
|
840 * Small: Run address. |
|
841 * Large: Run size for first page, unset for trailing pages. |
|
842 * - : Unused. |
|
843 * m : MADV_FREE/MADV_DONTNEED'ed? |
|
844 * c : decommitted? |
|
845 * k : key? |
|
846 * d : dirty? |
|
847 * z : zeroed? |
|
848 * l : large? |
|
849 * a : allocated? |
|
850 * |
|
851 * Following are example bit patterns for the three types of runs. |
|
852 * |
|
853 * r : run address |
|
854 * s : run size |
|
855 * x : don't care |
|
856 * - : 0 |
|
857 * [cdzla] : bit set |
|
858 * |
|
859 * Unallocated: |
|
860 * ssssssss ssssssss ssss---- --c----- |
|
861 * xxxxxxxx xxxxxxxx xxxx---- ----d--- |
|
862 * ssssssss ssssssss ssss---- -----z-- |
|
863 * |
|
864 * Small: |
|
865 * rrrrrrrr rrrrrrrr rrrr---- -------a |
|
866 * rrrrrrrr rrrrrrrr rrrr---- -------a |
|
867 * rrrrrrrr rrrrrrrr rrrr---- -------a |
|
868 * |
|
869 * Large: |
|
870 * ssssssss ssssssss ssss---- ------la |
|
871 * -------- -------- -------- ------la |
|
872 * -------- -------- -------- ------la |
|
873 */ |
|
874 size_t bits; |
|
875 |
|
876 /* Note that CHUNK_MAP_DECOMMITTED's meaning varies depending on whether |
|
877 * MALLOC_DECOMMIT and MALLOC_DOUBLE_PURGE are defined. |
|
878 * |
|
879 * If MALLOC_DECOMMIT is defined, a page which is CHUNK_MAP_DECOMMITTED must be |
|
880 * re-committed with pages_commit() before it may be touched. If |
|
881 * MALLOC_DECOMMIT is defined, MALLOC_DOUBLE_PURGE may not be defined. |
|
882 * |
|
883 * If neither MALLOC_DECOMMIT nor MALLOC_DOUBLE_PURGE is defined, pages which |
|
884 * are madvised (with either MADV_DONTNEED or MADV_FREE) are marked with |
|
885 * CHUNK_MAP_MADVISED. |
|
886 * |
|
887 * Otherwise, if MALLOC_DECOMMIT is not defined and MALLOC_DOUBLE_PURGE is |
|
888 * defined, then a page which is madvised is marked as CHUNK_MAP_MADVISED. |
|
889 * When it's finally freed with jemalloc_purge_freed_pages, the page is marked |
|
890 * as CHUNK_MAP_DECOMMITTED. |
|
891 */ |
|
892 #if defined(MALLOC_DECOMMIT) || defined(MALLOC_STATS) || defined(MALLOC_DOUBLE_PURGE) |
|
893 #define CHUNK_MAP_MADVISED ((size_t)0x40U) |
|
894 #define CHUNK_MAP_DECOMMITTED ((size_t)0x20U) |
|
895 #define CHUNK_MAP_MADVISED_OR_DECOMMITTED (CHUNK_MAP_MADVISED | CHUNK_MAP_DECOMMITTED) |
|
896 #endif |
|
897 #define CHUNK_MAP_KEY ((size_t)0x10U) |
|
898 #define CHUNK_MAP_DIRTY ((size_t)0x08U) |
|
899 #define CHUNK_MAP_ZEROED ((size_t)0x04U) |
|
900 #define CHUNK_MAP_LARGE ((size_t)0x02U) |
|
901 #define CHUNK_MAP_ALLOCATED ((size_t)0x01U) |
|
902 }; |
|
903 typedef rb_tree(arena_chunk_map_t) arena_avail_tree_t; |
|
904 typedef rb_tree(arena_chunk_map_t) arena_run_tree_t; |
|
905 |
|
906 /* Arena chunk header. */ |
|
907 typedef struct arena_chunk_s arena_chunk_t; |
|
908 struct arena_chunk_s { |
|
909 /* Arena that owns the chunk. */ |
|
910 arena_t *arena; |
|
911 |
|
912 /* Linkage for the arena's chunks_dirty tree. */ |
|
913 rb_node(arena_chunk_t) link_dirty; |
|
914 |
|
915 #ifdef MALLOC_DOUBLE_PURGE |
|
916 /* If we're double-purging, we maintain a linked list of chunks which |
|
917 * have pages which have been madvise(MADV_FREE)'d but not explicitly |
|
918 * purged. |
|
919 * |
|
920 * We're currently lazy and don't remove a chunk from this list when |
|
921 * all its madvised pages are recommitted. */ |
|
922 LinkedList chunks_madvised_elem; |
|
923 #endif |
|
924 |
|
925 /* Number of dirty pages. */ |
|
926 size_t ndirty; |
|
927 |
|
928 /* Map of pages within chunk that keeps track of free/large/small. */ |
|
929 arena_chunk_map_t map[1]; /* Dynamically sized. */ |
|
930 }; |
|
931 typedef rb_tree(arena_chunk_t) arena_chunk_tree_t; |
|
932 |
|
933 typedef struct arena_run_s arena_run_t; |
|
934 struct arena_run_s { |
|
935 #if defined(MALLOC_DEBUG) || defined(MOZ_JEMALLOC_HARD_ASSERTS) |
|
936 uint32_t magic; |
|
937 # define ARENA_RUN_MAGIC 0x384adf93 |
|
938 #endif |
|
939 |
|
940 /* Bin this run is associated with. */ |
|
941 arena_bin_t *bin; |
|
942 |
|
943 /* Index of first element that might have a free region. */ |
|
944 unsigned regs_minelm; |
|
945 |
|
946 /* Number of free regions in run. */ |
|
947 unsigned nfree; |
|
948 |
|
949 /* Bitmask of in-use regions (0: in use, 1: free). */ |
|
950 unsigned regs_mask[1]; /* Dynamically sized. */ |
|
951 }; |
|
952 |
|
953 struct arena_bin_s { |
|
954 /* |
|
955 * Current run being used to service allocations of this bin's size |
|
956 * class. |
|
957 */ |
|
958 arena_run_t *runcur; |
|
959 |
|
960 /* |
|
961 * Tree of non-full runs. This tree is used when looking for an |
|
962 * existing run when runcur is no longer usable. We choose the |
|
963 * non-full run that is lowest in memory; this policy tends to keep |
|
964 * objects packed well, and it can also help reduce the number of |
|
965 * almost-empty chunks. |
|
966 */ |
|
967 arena_run_tree_t runs; |
|
968 |
|
969 /* Size of regions in a run for this bin's size class. */ |
|
970 size_t reg_size; |
|
971 |
|
972 /* Total size of a run for this bin's size class. */ |
|
973 size_t run_size; |
|
974 |
|
975 /* Total number of regions in a run for this bin's size class. */ |
|
976 uint32_t nregs; |
|
977 |
|
978 /* Number of elements in a run's regs_mask for this bin's size class. */ |
|
979 uint32_t regs_mask_nelms; |
|
980 |
|
981 /* Offset of first region in a run for this bin's size class. */ |
|
982 uint32_t reg0_offset; |
|
983 |
|
984 #ifdef MALLOC_STATS |
|
985 /* Bin statistics. */ |
|
986 malloc_bin_stats_t stats; |
|
987 #endif |
|
988 }; |
|
989 |
|
990 struct arena_s { |
|
991 #if defined(MALLOC_DEBUG) || defined(MOZ_JEMALLOC_HARD_ASSERTS) |
|
992 uint32_t magic; |
|
993 # define ARENA_MAGIC 0x947d3d24 |
|
994 #endif |
|
995 |
|
996 /* All operations on this arena require that lock be locked. */ |
|
997 #ifdef MOZ_MEMORY |
|
998 malloc_spinlock_t lock; |
|
999 #else |
|
1000 pthread_mutex_t lock; |
|
1001 #endif |
|
1002 |
|
1003 #ifdef MALLOC_STATS |
|
1004 arena_stats_t stats; |
|
1005 #endif |
|
1006 |
|
1007 /* Tree of dirty-page-containing chunks this arena manages. */ |
|
1008 arena_chunk_tree_t chunks_dirty; |
|
1009 |
|
1010 #ifdef MALLOC_DOUBLE_PURGE |
|
1011 /* Head of a linked list of MADV_FREE'd-page-containing chunks this |
|
1012 * arena manages. */ |
|
1013 LinkedList chunks_madvised; |
|
1014 #endif |
|
1015 |
|
1016 /* |
|
1017 * In order to avoid rapid chunk allocation/deallocation when an arena |
|
1018 * oscillates right on the cusp of needing a new chunk, cache the most |
|
1019 * recently freed chunk. The spare is left in the arena's chunk trees |
|
1020 * until it is deleted. |
|
1021 * |
|
1022 * There is one spare chunk per arena, rather than one spare total, in |
|
1023 * order to avoid interactions between multiple threads that could make |
|
1024 * a single spare inadequate. |
|
1025 */ |
|
1026 arena_chunk_t *spare; |
|
1027 |
|
1028 /* |
|
1029 * Current count of pages within unused runs that are potentially |
|
1030 * dirty, and for which madvise(... MADV_FREE) has not been called. By |
|
1031 * tracking this, we can institute a limit on how much dirty unused |
|
1032 * memory is mapped for each arena. |
|
1033 */ |
|
1034 size_t ndirty; |
|
1035 |
|
1036 /* |
|
1037 * Size/address-ordered tree of this arena's available runs. This tree |
|
1038 * is used for first-best-fit run allocation. |
|
1039 */ |
|
1040 arena_avail_tree_t runs_avail; |
|
1041 |
|
1042 #ifdef MALLOC_BALANCE |
|
1043 /* |
|
1044 * The arena load balancing machinery needs to keep track of how much |
|
1045 * lock contention there is. This value is exponentially averaged. |
|
1046 */ |
|
1047 uint32_t contention; |
|
1048 #endif |
|
1049 |
|
1050 /* |
|
1051 * bins is used to store rings of free regions of the following sizes, |
|
1052 * assuming a 16-byte quantum, 4kB pagesize, and default MALLOC_OPTIONS. |
|
1053 * |
|
1054 * bins[i] | size | |
|
1055 * --------+------+ |
|
1056 * 0 | 2 | |
|
1057 * 1 | 4 | |
|
1058 * 2 | 8 | |
|
1059 * --------+------+ |
|
1060 * 3 | 16 | |
|
1061 * 4 | 32 | |
|
1062 * 5 | 48 | |
|
1063 * 6 | 64 | |
|
1064 * : : |
|
1065 * : : |
|
1066 * 33 | 496 | |
|
1067 * 34 | 512 | |
|
1068 * --------+------+ |
|
1069 * 35 | 1024 | |
|
1070 * 36 | 2048 | |
|
1071 * --------+------+ |
|
1072 */ |
|
1073 arena_bin_t bins[1]; /* Dynamically sized. */ |
|
1074 }; |
|
1075 |
|
1076 /******************************************************************************/ |
|
1077 /* |
|
1078 * Data. |
|
1079 */ |
|
1080 |
|
1081 #ifndef MOZ_MEMORY_NARENAS_DEFAULT_ONE |
|
1082 /* Number of CPUs. */ |
|
1083 static unsigned ncpus; |
|
1084 #endif |
|
1085 |
|
1086 /* |
|
1087 * When MALLOC_STATIC_SIZES is defined most of the parameters |
|
1088 * controlling the malloc behavior are defined as compile-time constants |
|
1089 * for best performance and cannot be altered at runtime. |
|
1090 */ |
|
1091 #if !defined(__ia64__) && !defined(__sparc__) && !defined(__mips__) |
|
1092 #define MALLOC_STATIC_SIZES 1 |
|
1093 #endif |
|
1094 |
|
1095 #ifdef MALLOC_STATIC_SIZES |
|
1096 |
|
1097 /* |
|
1098 * VM page size. It must divide the runtime CPU page size or the code |
|
1099 * will abort. |
|
1100 * Platform specific page size conditions copied from js/public/HeapAPI.h |
|
1101 */ |
|
1102 #if (defined(SOLARIS) || defined(__FreeBSD__)) && \ |
|
1103 (defined(__sparc) || defined(__sparcv9) || defined(__ia64)) |
|
1104 #define pagesize_2pow ((size_t) 13) |
|
1105 #elif defined(__powerpc64__) || defined(__aarch64__) |
|
1106 #define pagesize_2pow ((size_t) 16) |
|
1107 #else |
|
1108 #define pagesize_2pow ((size_t) 12) |
|
1109 #endif |
|
1110 #define pagesize ((size_t) 1 << pagesize_2pow) |
|
1111 #define pagesize_mask (pagesize - 1) |
|
1112 |
|
1113 /* Various quantum-related settings. */ |
|
1114 |
|
1115 #define QUANTUM_DEFAULT ((size_t) 1 << QUANTUM_2POW_MIN) |
|
1116 static const size_t quantum = QUANTUM_DEFAULT; |
|
1117 static const size_t quantum_mask = QUANTUM_DEFAULT - 1; |
|
1118 |
|
1119 /* Various bin-related settings. */ |
|
1120 |
|
1121 static const size_t small_min = (QUANTUM_DEFAULT >> 1) + 1; |
|
1122 static const size_t small_max = (size_t) SMALL_MAX_DEFAULT; |
|
1123 |
|
1124 /* Max size class for bins. */ |
|
1125 static const size_t bin_maxclass = pagesize >> 1; |
|
1126 |
|
1127 /* Number of (2^n)-spaced tiny bins. */ |
|
1128 static const unsigned ntbins = (unsigned) |
|
1129 (QUANTUM_2POW_MIN - TINY_MIN_2POW); |
|
1130 |
|
1131 /* Number of quantum-spaced bins. */ |
|
1132 static const unsigned nqbins = (unsigned) |
|
1133 (SMALL_MAX_DEFAULT >> QUANTUM_2POW_MIN); |
|
1134 |
|
1135 /* Number of (2^n)-spaced sub-page bins. */ |
|
1136 static const unsigned nsbins = (unsigned) |
|
1137 (pagesize_2pow - |
|
1138 SMALL_MAX_2POW_DEFAULT - 1); |
|
1139 |
|
1140 #else /* !MALLOC_STATIC_SIZES */ |
|
1141 |
|
1142 /* VM page size. */ |
|
1143 static size_t pagesize; |
|
1144 static size_t pagesize_mask; |
|
1145 static size_t pagesize_2pow; |
|
1146 |
|
1147 /* Various bin-related settings. */ |
|
1148 static size_t bin_maxclass; /* Max size class for bins. */ |
|
1149 static unsigned ntbins; /* Number of (2^n)-spaced tiny bins. */ |
|
1150 static unsigned nqbins; /* Number of quantum-spaced bins. */ |
|
1151 static unsigned nsbins; /* Number of (2^n)-spaced sub-page bins. */ |
|
1152 static size_t small_min; |
|
1153 static size_t small_max; |
|
1154 |
|
1155 /* Various quantum-related settings. */ |
|
1156 static size_t quantum; |
|
1157 static size_t quantum_mask; /* (quantum - 1). */ |
|
1158 |
|
1159 #endif |
|
1160 |
|
1161 /* Various chunk-related settings. */ |
|
1162 |
|
1163 /* |
|
1164 * Compute the header size such that it is large enough to contain the page map |
|
1165 * and enough nodes for the worst case: one node per non-header page plus one |
|
1166 * extra for situations where we briefly have one more node allocated than we |
|
1167 * will need. |
|
1168 */ |
|
1169 #define calculate_arena_header_size() \ |
|
1170 (sizeof(arena_chunk_t) + sizeof(arena_chunk_map_t) * (chunk_npages - 1)) |
|
1171 |
|
1172 #define calculate_arena_header_pages() \ |
|
1173 ((calculate_arena_header_size() >> pagesize_2pow) + \ |
|
1174 ((calculate_arena_header_size() & pagesize_mask) ? 1 : 0)) |
|
1175 |
|
1176 /* Max size class for arenas. */ |
|
1177 #define calculate_arena_maxclass() \ |
|
1178 (chunksize - (arena_chunk_header_npages << pagesize_2pow)) |
|
1179 |
|
1180 #ifdef MALLOC_STATIC_SIZES |
|
1181 #define CHUNKSIZE_DEFAULT ((size_t) 1 << CHUNK_2POW_DEFAULT) |
|
1182 static const size_t chunksize = CHUNKSIZE_DEFAULT; |
|
1183 static const size_t chunksize_mask =CHUNKSIZE_DEFAULT - 1; |
|
1184 static const size_t chunk_npages = CHUNKSIZE_DEFAULT >> pagesize_2pow; |
|
1185 #define arena_chunk_header_npages calculate_arena_header_pages() |
|
1186 #define arena_maxclass calculate_arena_maxclass() |
|
1187 #else |
|
1188 static size_t chunksize; |
|
1189 static size_t chunksize_mask; /* (chunksize - 1). */ |
|
1190 static size_t chunk_npages; |
|
1191 static size_t arena_chunk_header_npages; |
|
1192 static size_t arena_maxclass; /* Max size class for arenas. */ |
|
1193 #endif |
|
1194 |
|
1195 /********/ |
|
1196 /* |
|
1197 * Chunks. |
|
1198 */ |
|
1199 |
|
1200 #ifdef MALLOC_VALIDATE |
|
1201 static malloc_rtree_t *chunk_rtree; |
|
1202 #endif |
|
1203 |
|
1204 /* Protects chunk-related data structures. */ |
|
1205 static malloc_mutex_t huge_mtx; |
|
1206 |
|
1207 /* Tree of chunks that are stand-alone huge allocations. */ |
|
1208 static extent_tree_t huge; |
|
1209 |
|
1210 #ifdef MALLOC_STATS |
|
1211 /* Huge allocation statistics. */ |
|
1212 static uint64_t huge_nmalloc; |
|
1213 static uint64_t huge_ndalloc; |
|
1214 static size_t huge_allocated; |
|
1215 static size_t huge_mapped; |
|
1216 #endif |
|
1217 |
|
1218 #ifdef MALLOC_PAGEFILE |
|
1219 static char pagefile_templ[PATH_MAX]; |
|
1220 #endif |
|
1221 |
|
1222 /****************************/ |
|
1223 /* |
|
1224 * base (internal allocation). |
|
1225 */ |
|
1226 |
|
1227 /* |
|
1228 * Current pages that are being used for internal memory allocations. These |
|
1229 * pages are carved up in cacheline-size quanta, so that there is no chance of |
|
1230 * false cache line sharing. |
|
1231 */ |
|
1232 static void *base_pages; |
|
1233 static void *base_next_addr; |
|
1234 #if defined(MALLOC_DECOMMIT) || defined(MALLOC_STATS) |
|
1235 static void *base_next_decommitted; |
|
1236 #endif |
|
1237 static void *base_past_addr; /* Addr immediately past base_pages. */ |
|
1238 static extent_node_t *base_nodes; |
|
1239 static malloc_mutex_t base_mtx; |
|
1240 #ifdef MALLOC_STATS |
|
1241 static size_t base_mapped; |
|
1242 static size_t base_committed; |
|
1243 #endif |
|
1244 |
|
1245 /********/ |
|
1246 /* |
|
1247 * Arenas. |
|
1248 */ |
|
1249 |
|
1250 /* |
|
1251 * Arenas that are used to service external requests. Not all elements of the |
|
1252 * arenas array are necessarily used; arenas are created lazily as needed. |
|
1253 */ |
|
1254 static arena_t **arenas; |
|
1255 static unsigned narenas; |
|
1256 #ifndef NO_TLS |
|
1257 # ifdef MALLOC_BALANCE |
|
1258 static unsigned narenas_2pow; |
|
1259 # else |
|
1260 static unsigned next_arena; |
|
1261 # endif |
|
1262 #endif |
|
1263 #ifdef MOZ_MEMORY |
|
1264 static malloc_spinlock_t arenas_lock; /* Protects arenas initialization. */ |
|
1265 #else |
|
1266 static pthread_mutex_t arenas_lock; /* Protects arenas initialization. */ |
|
1267 #endif |
|
1268 |
|
1269 #ifndef NO_TLS |
|
1270 /* |
|
1271 * Map of pthread_self() --> arenas[???], used for selecting an arena to use |
|
1272 * for allocations. |
|
1273 */ |
|
1274 #ifndef MOZ_MEMORY_WINDOWS |
|
1275 static __thread arena_t *arenas_map; |
|
1276 #endif |
|
1277 #endif |
|
1278 |
|
1279 /*******************************/ |
|
1280 /* |
|
1281 * Runtime configuration options. |
|
1282 */ |
|
1283 MOZ_JEMALLOC_API |
|
1284 const char *_malloc_options = MOZ_MALLOC_OPTIONS; |
|
1285 |
|
1286 #ifndef MALLOC_PRODUCTION |
|
1287 static bool opt_abort = true; |
|
1288 #ifdef MALLOC_FILL |
|
1289 static bool opt_junk = true; |
|
1290 static bool opt_poison = true; |
|
1291 static bool opt_zero = false; |
|
1292 #endif |
|
1293 #else |
|
1294 static bool opt_abort = false; |
|
1295 #ifdef MALLOC_FILL |
|
1296 static const bool opt_junk = false; |
|
1297 static const bool opt_poison = true; |
|
1298 static const bool opt_zero = false; |
|
1299 #endif |
|
1300 #endif |
|
1301 |
|
1302 static size_t opt_dirty_max = DIRTY_MAX_DEFAULT; |
|
1303 #ifdef MALLOC_BALANCE |
|
1304 static uint64_t opt_balance_threshold = BALANCE_THRESHOLD_DEFAULT; |
|
1305 #endif |
|
1306 static bool opt_print_stats = false; |
|
1307 #ifdef MALLOC_STATIC_SIZES |
|
1308 #define opt_quantum_2pow QUANTUM_2POW_MIN |
|
1309 #define opt_small_max_2pow SMALL_MAX_2POW_DEFAULT |
|
1310 #define opt_chunk_2pow CHUNK_2POW_DEFAULT |
|
1311 #else |
|
1312 static size_t opt_quantum_2pow = QUANTUM_2POW_MIN; |
|
1313 static size_t opt_small_max_2pow = SMALL_MAX_2POW_DEFAULT; |
|
1314 static size_t opt_chunk_2pow = CHUNK_2POW_DEFAULT; |
|
1315 #endif |
|
1316 #ifdef MALLOC_PAGEFILE |
|
1317 static bool opt_pagefile = false; |
|
1318 #endif |
|
1319 #ifdef MALLOC_UTRACE |
|
1320 static bool opt_utrace = false; |
|
1321 #endif |
|
1322 #ifdef MALLOC_SYSV |
|
1323 static bool opt_sysv = false; |
|
1324 #endif |
|
1325 #ifdef MALLOC_XMALLOC |
|
1326 static bool opt_xmalloc = false; |
|
1327 #endif |
|
1328 static int opt_narenas_lshift = 0; |
|
1329 |
|
1330 #ifdef MALLOC_UTRACE |
|
1331 typedef struct { |
|
1332 void *p; |
|
1333 size_t s; |
|
1334 void *r; |
|
1335 } malloc_utrace_t; |
|
1336 |
|
1337 #define UTRACE(a, b, c) \ |
|
1338 if (opt_utrace) { \ |
|
1339 malloc_utrace_t ut; \ |
|
1340 ut.p = (a); \ |
|
1341 ut.s = (b); \ |
|
1342 ut.r = (c); \ |
|
1343 utrace(&ut, sizeof(ut)); \ |
|
1344 } |
|
1345 #else |
|
1346 #define UTRACE(a, b, c) |
|
1347 #endif |
|
1348 |
|
1349 /******************************************************************************/ |
|
1350 /* |
|
1351 * Begin function prototypes for non-inline static functions. |
|
1352 */ |
|
1353 |
|
1354 static char *umax2s(uintmax_t x, unsigned base, char *s); |
|
1355 static bool malloc_mutex_init(malloc_mutex_t *mutex); |
|
1356 static bool malloc_spin_init(malloc_spinlock_t *lock); |
|
1357 static void wrtmessage(const char *p1, const char *p2, const char *p3, |
|
1358 const char *p4); |
|
1359 #ifdef MALLOC_STATS |
|
1360 #ifdef MOZ_MEMORY_DARWIN |
|
1361 /* Avoid namespace collision with OS X's malloc APIs. */ |
|
1362 #define malloc_printf moz_malloc_printf |
|
1363 #endif |
|
1364 static void malloc_printf(const char *format, ...); |
|
1365 #endif |
|
1366 static bool base_pages_alloc_mmap(size_t minsize); |
|
1367 static bool base_pages_alloc(size_t minsize); |
|
1368 static void *base_alloc(size_t size); |
|
1369 static void *base_calloc(size_t number, size_t size); |
|
1370 static extent_node_t *base_node_alloc(void); |
|
1371 static void base_node_dealloc(extent_node_t *node); |
|
1372 #ifdef MALLOC_STATS |
|
1373 static void stats_print(arena_t *arena); |
|
1374 #endif |
|
1375 static void *pages_map(void *addr, size_t size, int pfd); |
|
1376 static void pages_unmap(void *addr, size_t size); |
|
1377 static void *chunk_alloc_mmap(size_t size, bool pagefile); |
|
1378 #ifdef MALLOC_PAGEFILE |
|
1379 static int pagefile_init(size_t size); |
|
1380 static void pagefile_close(int pfd); |
|
1381 #endif |
|
1382 static void *chunk_alloc(size_t size, bool zero, bool pagefile); |
|
1383 static void chunk_dealloc_mmap(void *chunk, size_t size); |
|
1384 static void chunk_dealloc(void *chunk, size_t size); |
|
1385 #ifndef NO_TLS |
|
1386 static arena_t *choose_arena_hard(void); |
|
1387 #endif |
|
1388 static void arena_run_split(arena_t *arena, arena_run_t *run, size_t size, |
|
1389 bool large, bool zero); |
|
1390 static void arena_chunk_init(arena_t *arena, arena_chunk_t *chunk); |
|
1391 static void arena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk); |
|
1392 static arena_run_t *arena_run_alloc(arena_t *arena, arena_bin_t *bin, |
|
1393 size_t size, bool large, bool zero); |
|
1394 static void arena_purge(arena_t *arena, bool all); |
|
1395 static void arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty); |
|
1396 static void arena_run_trim_head(arena_t *arena, arena_chunk_t *chunk, |
|
1397 arena_run_t *run, size_t oldsize, size_t newsize); |
|
1398 static void arena_run_trim_tail(arena_t *arena, arena_chunk_t *chunk, |
|
1399 arena_run_t *run, size_t oldsize, size_t newsize, bool dirty); |
|
1400 static arena_run_t *arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin); |
|
1401 static void *arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin); |
|
1402 static size_t arena_bin_run_size_calc(arena_bin_t *bin, size_t min_run_size); |
|
1403 #ifdef MALLOC_BALANCE |
|
1404 static void arena_lock_balance_hard(arena_t *arena); |
|
1405 #endif |
|
1406 static void *arena_malloc_large(arena_t *arena, size_t size, bool zero); |
|
1407 static void *arena_palloc(arena_t *arena, size_t alignment, size_t size, |
|
1408 size_t alloc_size); |
|
1409 static size_t arena_salloc(const void *ptr); |
|
1410 static void arena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, |
|
1411 void *ptr); |
|
1412 static void arena_ralloc_large_shrink(arena_t *arena, arena_chunk_t *chunk, |
|
1413 void *ptr, size_t size, size_t oldsize); |
|
1414 static bool arena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk, |
|
1415 void *ptr, size_t size, size_t oldsize); |
|
1416 static bool arena_ralloc_large(void *ptr, size_t size, size_t oldsize); |
|
1417 static void *arena_ralloc(void *ptr, size_t size, size_t oldsize); |
|
1418 static bool arena_new(arena_t *arena); |
|
1419 static arena_t *arenas_extend(unsigned ind); |
|
1420 static void *huge_malloc(size_t size, bool zero); |
|
1421 static void *huge_palloc(size_t alignment, size_t size); |
|
1422 static void *huge_ralloc(void *ptr, size_t size, size_t oldsize); |
|
1423 static void huge_dalloc(void *ptr); |
|
1424 static void malloc_print_stats(void); |
|
1425 #ifndef MOZ_MEMORY_WINDOWS |
|
1426 static |
|
1427 #endif |
|
1428 bool malloc_init_hard(void); |
|
1429 |
|
1430 static void _malloc_prefork(void); |
|
1431 static void _malloc_postfork(void); |
|
1432 |
|
1433 #ifdef MOZ_MEMORY_DARWIN |
|
1434 /* |
|
1435 * MALLOC_ZONE_T_NOTE |
|
1436 * |
|
1437 * On Darwin, we hook into the memory allocator using a malloc_zone_t struct. |
|
1438 * We must be very careful around this struct because of different behaviour on |
|
1439 * different versions of OSX. |
|
1440 * |
|
1441 * Each of OSX 10.5, 10.6 and 10.7 use different versions of the struct |
|
1442 * (with version numbers 3, 6 and 8 respectively). The binary we use on each of |
|
1443 * these platforms will not necessarily be built using the correct SDK [1]. |
|
1444 * This means we need to statically know the correct struct size to use on all |
|
1445 * OSX releases, and have a fallback for unknown future versions. The struct |
|
1446 * sizes defined in osx_zone_types.h. |
|
1447 * |
|
1448 * For OSX 10.8 and later, we may expect the malloc_zone_t struct to change |
|
1449 * again, and need to dynamically account for this. By simply leaving |
|
1450 * malloc_zone_t alone, we don't quite deal with the problem, because there |
|
1451 * remain calls to jemalloc through the mozalloc interface. We check this |
|
1452 * dynamically on each allocation, using the CHECK_DARWIN macro and |
|
1453 * osx_use_jemalloc. |
|
1454 * |
|
1455 * |
|
1456 * [1] Mozilla is built as a universal binary on Mac, supporting i386 and |
|
1457 * x86_64. The i386 target is built using the 10.5 SDK, even if it runs on |
|
1458 * 10.6. The x86_64 target is built using the 10.6 SDK, even if it runs on |
|
1459 * 10.7 or later, or 10.5. |
|
1460 * |
|
1461 * FIXME: |
|
1462 * When later versions of OSX come out (10.8 and up), we need to check their |
|
1463 * malloc_zone_t versions. If they're greater than 8, we need a new version |
|
1464 * of malloc_zone_t adapted into osx_zone_types.h. |
|
1465 */ |
|
1466 |
|
1467 #ifndef MOZ_REPLACE_MALLOC |
|
1468 #include "osx_zone_types.h" |
|
1469 |
|
1470 #define LEOPARD_MALLOC_ZONE_T_VERSION 3 |
|
1471 #define SNOW_LEOPARD_MALLOC_ZONE_T_VERSION 6 |
|
1472 #define LION_MALLOC_ZONE_T_VERSION 8 |
|
1473 |
|
1474 static bool osx_use_jemalloc = false; |
|
1475 |
|
1476 |
|
1477 static lion_malloc_zone l_szone; |
|
1478 static malloc_zone_t * szone = (malloc_zone_t*)(&l_szone); |
|
1479 |
|
1480 static lion_malloc_introspection l_ozone_introspect; |
|
1481 static malloc_introspection_t * const ozone_introspect = |
|
1482 (malloc_introspection_t*)(&l_ozone_introspect); |
|
1483 static void szone2ozone(malloc_zone_t *zone, size_t size); |
|
1484 static size_t zone_version_size(int version); |
|
1485 #else |
|
1486 static const bool osx_use_jemalloc = true; |
|
1487 #endif |
|
1488 |
|
1489 #endif |
|
1490 |
|
1491 /* |
|
1492 * End function prototypes. |
|
1493 */ |
|
1494 /******************************************************************************/ |
|
1495 |
|
1496 /* |
|
1497 * umax2s() provides minimal integer printing functionality, which is |
|
1498 * especially useful for situations where allocation in vsnprintf() calls would |
|
1499 * potentially cause deadlock. |
|
1500 */ |
|
1501 #define UMAX2S_BUFSIZE 65 |
|
1502 char * |
|
1503 umax2s(uintmax_t x, unsigned base, char *s) |
|
1504 { |
|
1505 unsigned i; |
|
1506 |
|
1507 i = UMAX2S_BUFSIZE - 1; |
|
1508 s[i] = '\0'; |
|
1509 switch (base) { |
|
1510 case 10: |
|
1511 do { |
|
1512 i--; |
|
1513 s[i] = "0123456789"[x % 10]; |
|
1514 x /= 10; |
|
1515 } while (x > 0); |
|
1516 break; |
|
1517 case 16: |
|
1518 do { |
|
1519 i--; |
|
1520 s[i] = "0123456789abcdef"[x & 0xf]; |
|
1521 x >>= 4; |
|
1522 } while (x > 0); |
|
1523 break; |
|
1524 default: |
|
1525 do { |
|
1526 i--; |
|
1527 s[i] = "0123456789abcdefghijklmnopqrstuvwxyz"[x % base]; |
|
1528 x /= base; |
|
1529 } while (x > 0); |
|
1530 } |
|
1531 |
|
1532 return (&s[i]); |
|
1533 } |
|
1534 |
|
1535 static void |
|
1536 wrtmessage(const char *p1, const char *p2, const char *p3, const char *p4) |
|
1537 { |
|
1538 #if defined(MOZ_MEMORY) && !defined(MOZ_MEMORY_WINDOWS) |
|
1539 #define _write write |
|
1540 #endif |
|
1541 _write(STDERR_FILENO, p1, (unsigned int) strlen(p1)); |
|
1542 _write(STDERR_FILENO, p2, (unsigned int) strlen(p2)); |
|
1543 _write(STDERR_FILENO, p3, (unsigned int) strlen(p3)); |
|
1544 _write(STDERR_FILENO, p4, (unsigned int) strlen(p4)); |
|
1545 } |
|
1546 |
|
1547 MOZ_JEMALLOC_API |
|
1548 void (*_malloc_message)(const char *p1, const char *p2, const char *p3, |
|
1549 const char *p4) = wrtmessage; |
|
1550 |
|
1551 #ifdef MALLOC_DEBUG |
|
1552 # define assert(e) do { \ |
|
1553 if (!(e)) { \ |
|
1554 char line_buf[UMAX2S_BUFSIZE]; \ |
|
1555 _malloc_message(__FILE__, ":", umax2s(__LINE__, 10, \ |
|
1556 line_buf), ": Failed assertion: "); \ |
|
1557 _malloc_message("\"", #e, "\"\n", ""); \ |
|
1558 abort(); \ |
|
1559 } \ |
|
1560 } while (0) |
|
1561 #else |
|
1562 #define assert(e) |
|
1563 #endif |
|
1564 |
|
1565 #include <mozilla/Assertions.h> |
|
1566 #include <mozilla/Attributes.h> |
|
1567 |
|
1568 /* RELEASE_ASSERT calls jemalloc_crash() instead of calling MOZ_CRASH() |
|
1569 * directly because we want crashing to add a frame to the stack. This makes |
|
1570 * it easier to find the failing assertion in crash stacks. */ |
|
1571 MOZ_NEVER_INLINE static void |
|
1572 jemalloc_crash() |
|
1573 { |
|
1574 MOZ_CRASH(); |
|
1575 } |
|
1576 |
|
1577 #if defined(MOZ_JEMALLOC_HARD_ASSERTS) |
|
1578 # define RELEASE_ASSERT(assertion) do { \ |
|
1579 if (!(assertion)) { \ |
|
1580 jemalloc_crash(); \ |
|
1581 } \ |
|
1582 } while (0) |
|
1583 #else |
|
1584 # define RELEASE_ASSERT(assertion) assert(assertion) |
|
1585 #endif |
|
1586 |
|
1587 /******************************************************************************/ |
|
1588 /* |
|
1589 * Begin mutex. We can't use normal pthread mutexes in all places, because |
|
1590 * they require malloc()ed memory, which causes bootstrapping issues in some |
|
1591 * cases. |
|
1592 */ |
|
1593 |
|
1594 static bool |
|
1595 malloc_mutex_init(malloc_mutex_t *mutex) |
|
1596 { |
|
1597 #if defined(MOZ_MEMORY_WINDOWS) |
|
1598 if (isthreaded) |
|
1599 if (! __crtInitCritSecAndSpinCount(mutex, _CRT_SPINCOUNT)) |
|
1600 return (true); |
|
1601 #elif defined(MOZ_MEMORY_DARWIN) |
|
1602 mutex->lock = OS_SPINLOCK_INIT; |
|
1603 #elif defined(MOZ_MEMORY_LINUX) && !defined(MOZ_MEMORY_ANDROID) |
|
1604 pthread_mutexattr_t attr; |
|
1605 if (pthread_mutexattr_init(&attr) != 0) |
|
1606 return (true); |
|
1607 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP); |
|
1608 if (pthread_mutex_init(mutex, &attr) != 0) { |
|
1609 pthread_mutexattr_destroy(&attr); |
|
1610 return (true); |
|
1611 } |
|
1612 pthread_mutexattr_destroy(&attr); |
|
1613 #elif defined(MOZ_MEMORY) |
|
1614 if (pthread_mutex_init(mutex, NULL) != 0) |
|
1615 return (true); |
|
1616 #else |
|
1617 static const spinlock_t lock = _SPINLOCK_INITIALIZER; |
|
1618 |
|
1619 mutex->lock = lock; |
|
1620 #endif |
|
1621 return (false); |
|
1622 } |
|
1623 |
|
1624 static inline void |
|
1625 malloc_mutex_lock(malloc_mutex_t *mutex) |
|
1626 { |
|
1627 |
|
1628 #if defined(MOZ_MEMORY_WINDOWS) |
|
1629 EnterCriticalSection(mutex); |
|
1630 #elif defined(MOZ_MEMORY_DARWIN) |
|
1631 OSSpinLockLock(&mutex->lock); |
|
1632 #elif defined(MOZ_MEMORY) |
|
1633 pthread_mutex_lock(mutex); |
|
1634 #else |
|
1635 if (isthreaded) |
|
1636 _SPINLOCK(&mutex->lock); |
|
1637 #endif |
|
1638 } |
|
1639 |
|
1640 static inline void |
|
1641 malloc_mutex_unlock(malloc_mutex_t *mutex) |
|
1642 { |
|
1643 |
|
1644 #if defined(MOZ_MEMORY_WINDOWS) |
|
1645 LeaveCriticalSection(mutex); |
|
1646 #elif defined(MOZ_MEMORY_DARWIN) |
|
1647 OSSpinLockUnlock(&mutex->lock); |
|
1648 #elif defined(MOZ_MEMORY) |
|
1649 pthread_mutex_unlock(mutex); |
|
1650 #else |
|
1651 if (isthreaded) |
|
1652 _SPINUNLOCK(&mutex->lock); |
|
1653 #endif |
|
1654 } |
|
1655 |
|
1656 static bool |
|
1657 malloc_spin_init(malloc_spinlock_t *lock) |
|
1658 { |
|
1659 #if defined(MOZ_MEMORY_WINDOWS) |
|
1660 if (isthreaded) |
|
1661 if (! __crtInitCritSecAndSpinCount(lock, _CRT_SPINCOUNT)) |
|
1662 return (true); |
|
1663 #elif defined(MOZ_MEMORY_DARWIN) |
|
1664 lock->lock = OS_SPINLOCK_INIT; |
|
1665 #elif defined(MOZ_MEMORY_LINUX) && !defined(MOZ_MEMORY_ANDROID) |
|
1666 pthread_mutexattr_t attr; |
|
1667 if (pthread_mutexattr_init(&attr) != 0) |
|
1668 return (true); |
|
1669 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP); |
|
1670 if (pthread_mutex_init(lock, &attr) != 0) { |
|
1671 pthread_mutexattr_destroy(&attr); |
|
1672 return (true); |
|
1673 } |
|
1674 pthread_mutexattr_destroy(&attr); |
|
1675 #elif defined(MOZ_MEMORY) |
|
1676 if (pthread_mutex_init(lock, NULL) != 0) |
|
1677 return (true); |
|
1678 #else |
|
1679 lock->lock = _SPINLOCK_INITIALIZER; |
|
1680 #endif |
|
1681 return (false); |
|
1682 } |
|
1683 |
|
1684 static inline void |
|
1685 malloc_spin_lock(malloc_spinlock_t *lock) |
|
1686 { |
|
1687 |
|
1688 #if defined(MOZ_MEMORY_WINDOWS) |
|
1689 EnterCriticalSection(lock); |
|
1690 #elif defined(MOZ_MEMORY_DARWIN) |
|
1691 OSSpinLockLock(&lock->lock); |
|
1692 #elif defined(MOZ_MEMORY) |
|
1693 pthread_mutex_lock(lock); |
|
1694 #else |
|
1695 if (isthreaded) |
|
1696 _SPINLOCK(&lock->lock); |
|
1697 #endif |
|
1698 } |
|
1699 |
|
1700 static inline void |
|
1701 malloc_spin_unlock(malloc_spinlock_t *lock) |
|
1702 { |
|
1703 #if defined(MOZ_MEMORY_WINDOWS) |
|
1704 LeaveCriticalSection(lock); |
|
1705 #elif defined(MOZ_MEMORY_DARWIN) |
|
1706 OSSpinLockUnlock(&lock->lock); |
|
1707 #elif defined(MOZ_MEMORY) |
|
1708 pthread_mutex_unlock(lock); |
|
1709 #else |
|
1710 if (isthreaded) |
|
1711 _SPINUNLOCK(&lock->lock); |
|
1712 #endif |
|
1713 } |
|
1714 |
|
1715 /* |
|
1716 * End mutex. |
|
1717 */ |
|
1718 /******************************************************************************/ |
|
1719 /* |
|
1720 * Begin spin lock. Spin locks here are actually adaptive mutexes that block |
|
1721 * after a period of spinning, because unbounded spinning would allow for |
|
1722 * priority inversion. |
|
1723 */ |
|
1724 |
|
1725 #if defined(MOZ_MEMORY) && !defined(MOZ_MEMORY_DARWIN) |
|
1726 # define malloc_spin_init malloc_mutex_init |
|
1727 # define malloc_spin_lock malloc_mutex_lock |
|
1728 # define malloc_spin_unlock malloc_mutex_unlock |
|
1729 #endif |
|
1730 |
|
1731 #ifndef MOZ_MEMORY |
|
1732 /* |
|
1733 * We use an unpublished interface to initialize pthread mutexes with an |
|
1734 * allocation callback, in order to avoid infinite recursion. |
|
1735 */ |
|
1736 int _pthread_mutex_init_calloc_cb(pthread_mutex_t *mutex, |
|
1737 void *(calloc_cb)(size_t, size_t)); |
|
1738 |
|
1739 __weak_reference(_pthread_mutex_init_calloc_cb_stub, |
|
1740 _pthread_mutex_init_calloc_cb); |
|
1741 |
|
1742 int |
|
1743 _pthread_mutex_init_calloc_cb_stub(pthread_mutex_t *mutex, |
|
1744 void *(calloc_cb)(size_t, size_t)) |
|
1745 { |
|
1746 |
|
1747 return (0); |
|
1748 } |
|
1749 |
|
1750 static bool |
|
1751 malloc_spin_init(pthread_mutex_t *lock) |
|
1752 { |
|
1753 |
|
1754 if (_pthread_mutex_init_calloc_cb(lock, base_calloc) != 0) |
|
1755 return (true); |
|
1756 |
|
1757 return (false); |
|
1758 } |
|
1759 |
|
1760 static inline unsigned |
|
1761 malloc_spin_lock(pthread_mutex_t *lock) |
|
1762 { |
|
1763 unsigned ret = 0; |
|
1764 |
|
1765 if (isthreaded) { |
|
1766 if (_pthread_mutex_trylock(lock) != 0) { |
|
1767 unsigned i; |
|
1768 volatile unsigned j; |
|
1769 |
|
1770 /* Exponentially back off. */ |
|
1771 for (i = 1; i <= SPIN_LIMIT_2POW; i++) { |
|
1772 for (j = 0; j < (1U << i); j++) |
|
1773 ret++; |
|
1774 |
|
1775 CPU_SPINWAIT; |
|
1776 if (_pthread_mutex_trylock(lock) == 0) |
|
1777 return (ret); |
|
1778 } |
|
1779 |
|
1780 /* |
|
1781 * Spinning failed. Block until the lock becomes |
|
1782 * available, in order to avoid indefinite priority |
|
1783 * inversion. |
|
1784 */ |
|
1785 _pthread_mutex_lock(lock); |
|
1786 assert((ret << BLOCK_COST_2POW) != 0); |
|
1787 return (ret << BLOCK_COST_2POW); |
|
1788 } |
|
1789 } |
|
1790 |
|
1791 return (ret); |
|
1792 } |
|
1793 |
|
1794 static inline void |
|
1795 malloc_spin_unlock(pthread_mutex_t *lock) |
|
1796 { |
|
1797 |
|
1798 if (isthreaded) |
|
1799 _pthread_mutex_unlock(lock); |
|
1800 } |
|
1801 #endif |
|
1802 |
|
1803 /* |
|
1804 * End spin lock. |
|
1805 */ |
|
1806 /******************************************************************************/ |
|
1807 /* |
|
1808 * Begin Utility functions/macros. |
|
1809 */ |
|
1810 |
|
1811 /* Return the chunk address for allocation address a. */ |
|
1812 #define CHUNK_ADDR2BASE(a) \ |
|
1813 ((void *)((uintptr_t)(a) & ~chunksize_mask)) |
|
1814 |
|
1815 /* Return the chunk offset of address a. */ |
|
1816 #define CHUNK_ADDR2OFFSET(a) \ |
|
1817 ((size_t)((uintptr_t)(a) & chunksize_mask)) |
|
1818 |
|
1819 /* Return the smallest chunk multiple that is >= s. */ |
|
1820 #define CHUNK_CEILING(s) \ |
|
1821 (((s) + chunksize_mask) & ~chunksize_mask) |
|
1822 |
|
1823 /* Return the smallest cacheline multiple that is >= s. */ |
|
1824 #define CACHELINE_CEILING(s) \ |
|
1825 (((s) + (CACHELINE - 1)) & ~(CACHELINE - 1)) |
|
1826 |
|
1827 /* Return the smallest quantum multiple that is >= a. */ |
|
1828 #define QUANTUM_CEILING(a) \ |
|
1829 (((a) + quantum_mask) & ~quantum_mask) |
|
1830 |
|
1831 /* Return the smallest pagesize multiple that is >= s. */ |
|
1832 #define PAGE_CEILING(s) \ |
|
1833 (((s) + pagesize_mask) & ~pagesize_mask) |
|
1834 |
|
1835 /* Compute the smallest power of 2 that is >= x. */ |
|
1836 static inline size_t |
|
1837 pow2_ceil(size_t x) |
|
1838 { |
|
1839 |
|
1840 x--; |
|
1841 x |= x >> 1; |
|
1842 x |= x >> 2; |
|
1843 x |= x >> 4; |
|
1844 x |= x >> 8; |
|
1845 x |= x >> 16; |
|
1846 #if (SIZEOF_PTR == 8) |
|
1847 x |= x >> 32; |
|
1848 #endif |
|
1849 x++; |
|
1850 return (x); |
|
1851 } |
|
1852 |
|
1853 #ifdef MALLOC_BALANCE |
|
1854 /* |
|
1855 * Use a simple linear congruential pseudo-random number generator: |
|
1856 * |
|
1857 * prn(y) = (a*x + c) % m |
|
1858 * |
|
1859 * where the following constants ensure maximal period: |
|
1860 * |
|
1861 * a == Odd number (relatively prime to 2^n), and (a-1) is a multiple of 4. |
|
1862 * c == Odd number (relatively prime to 2^n). |
|
1863 * m == 2^32 |
|
1864 * |
|
1865 * See Knuth's TAOCP 3rd Ed., Vol. 2, pg. 17 for details on these constraints. |
|
1866 * |
|
1867 * This choice of m has the disadvantage that the quality of the bits is |
|
1868 * proportional to bit position. For example. the lowest bit has a cycle of 2, |
|
1869 * the next has a cycle of 4, etc. For this reason, we prefer to use the upper |
|
1870 * bits. |
|
1871 */ |
|
1872 # define PRN_DEFINE(suffix, var, a, c) \ |
|
1873 static inline void \ |
|
1874 sprn_##suffix(uint32_t seed) \ |
|
1875 { \ |
|
1876 var = seed; \ |
|
1877 } \ |
|
1878 \ |
|
1879 static inline uint32_t \ |
|
1880 prn_##suffix(uint32_t lg_range) \ |
|
1881 { \ |
|
1882 uint32_t ret, x; \ |
|
1883 \ |
|
1884 assert(lg_range > 0); \ |
|
1885 assert(lg_range <= 32); \ |
|
1886 \ |
|
1887 x = (var * (a)) + (c); \ |
|
1888 var = x; \ |
|
1889 ret = x >> (32 - lg_range); \ |
|
1890 \ |
|
1891 return (ret); \ |
|
1892 } |
|
1893 # define SPRN(suffix, seed) sprn_##suffix(seed) |
|
1894 # define PRN(suffix, lg_range) prn_##suffix(lg_range) |
|
1895 #endif |
|
1896 |
|
1897 #ifdef MALLOC_BALANCE |
|
1898 /* Define the PRNG used for arena assignment. */ |
|
1899 static __thread uint32_t balance_x; |
|
1900 PRN_DEFINE(balance, balance_x, 1297, 1301) |
|
1901 #endif |
|
1902 |
|
1903 #ifdef MALLOC_UTRACE |
|
1904 static int |
|
1905 utrace(const void *addr, size_t len) |
|
1906 { |
|
1907 malloc_utrace_t *ut = (malloc_utrace_t *)addr; |
|
1908 char buf_a[UMAX2S_BUFSIZE]; |
|
1909 char buf_b[UMAX2S_BUFSIZE]; |
|
1910 |
|
1911 assert(len == sizeof(malloc_utrace_t)); |
|
1912 |
|
1913 if (ut->p == NULL && ut->s == 0 && ut->r == NULL) { |
|
1914 _malloc_message( |
|
1915 umax2s(getpid(), 10, buf_a), |
|
1916 " x USER malloc_init()\n", "", ""); |
|
1917 } else if (ut->p == NULL && ut->r != NULL) { |
|
1918 _malloc_message( |
|
1919 umax2s(getpid(), 10, buf_a), |
|
1920 " x USER 0x", |
|
1921 umax2s((uintptr_t)ut->r, 16, buf_b), |
|
1922 " = malloc("); |
|
1923 _malloc_message( |
|
1924 umax2s(ut->s, 10, buf_a), |
|
1925 ")\n", "", ""); |
|
1926 } else if (ut->p != NULL && ut->r != NULL) { |
|
1927 _malloc_message( |
|
1928 umax2s(getpid(), 10, buf_a), |
|
1929 " x USER 0x", |
|
1930 umax2s((uintptr_t)ut->r, 16, buf_b), |
|
1931 " = realloc(0x"); |
|
1932 _malloc_message( |
|
1933 umax2s((uintptr_t)ut->p, 16, buf_a), |
|
1934 ", ", |
|
1935 umax2s(ut->s, 10, buf_b), |
|
1936 ")\n"); |
|
1937 } else { |
|
1938 _malloc_message( |
|
1939 umax2s(getpid(), 10, buf_a), |
|
1940 " x USER free(0x", |
|
1941 umax2s((uintptr_t)ut->p, 16, buf_b), |
|
1942 ")\n"); |
|
1943 } |
|
1944 |
|
1945 return (0); |
|
1946 } |
|
1947 #endif |
|
1948 |
|
1949 static inline const char * |
|
1950 _getprogname(void) |
|
1951 { |
|
1952 |
|
1953 return ("<jemalloc>"); |
|
1954 } |
|
1955 |
|
1956 #ifdef MALLOC_STATS |
|
1957 /* |
|
1958 * Print to stderr in such a way as to (hopefully) avoid memory allocation. |
|
1959 */ |
|
1960 static void |
|
1961 malloc_printf(const char *format, ...) |
|
1962 { |
|
1963 char buf[4096]; |
|
1964 va_list ap; |
|
1965 |
|
1966 va_start(ap, format); |
|
1967 vsnprintf(buf, sizeof(buf), format, ap); |
|
1968 va_end(ap); |
|
1969 _malloc_message(buf, "", "", ""); |
|
1970 } |
|
1971 #endif |
|
1972 |
|
1973 /******************************************************************************/ |
|
1974 |
|
1975 static inline void |
|
1976 pages_decommit(void *addr, size_t size) |
|
1977 { |
|
1978 |
|
1979 #ifdef MOZ_MEMORY_WINDOWS |
|
1980 VirtualFree(addr, size, MEM_DECOMMIT); |
|
1981 #else |
|
1982 if (mmap(addr, size, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, |
|
1983 0) == MAP_FAILED) |
|
1984 abort(); |
|
1985 #endif |
|
1986 } |
|
1987 |
|
1988 static inline void |
|
1989 pages_commit(void *addr, size_t size) |
|
1990 { |
|
1991 |
|
1992 # ifdef MOZ_MEMORY_WINDOWS |
|
1993 if (!VirtualAlloc(addr, size, MEM_COMMIT, PAGE_READWRITE)) |
|
1994 abort(); |
|
1995 # else |
|
1996 if (mmap(addr, size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | |
|
1997 MAP_ANON, -1, 0) == MAP_FAILED) |
|
1998 abort(); |
|
1999 # endif |
|
2000 } |
|
2001 |
|
2002 static bool |
|
2003 base_pages_alloc_mmap(size_t minsize) |
|
2004 { |
|
2005 bool ret; |
|
2006 size_t csize; |
|
2007 #if defined(MALLOC_DECOMMIT) || defined(MALLOC_STATS) |
|
2008 size_t pminsize; |
|
2009 #endif |
|
2010 int pfd; |
|
2011 |
|
2012 assert(minsize != 0); |
|
2013 csize = CHUNK_CEILING(minsize); |
|
2014 #ifdef MALLOC_PAGEFILE |
|
2015 if (opt_pagefile) { |
|
2016 pfd = pagefile_init(csize); |
|
2017 if (pfd == -1) |
|
2018 return (true); |
|
2019 } else |
|
2020 #endif |
|
2021 pfd = -1; |
|
2022 base_pages = pages_map(NULL, csize, pfd); |
|
2023 if (base_pages == NULL) { |
|
2024 ret = true; |
|
2025 goto RETURN; |
|
2026 } |
|
2027 base_next_addr = base_pages; |
|
2028 base_past_addr = (void *)((uintptr_t)base_pages + csize); |
|
2029 #if defined(MALLOC_DECOMMIT) || defined(MALLOC_STATS) |
|
2030 /* |
|
2031 * Leave enough pages for minsize committed, since otherwise they would |
|
2032 * have to be immediately recommitted. |
|
2033 */ |
|
2034 pminsize = PAGE_CEILING(minsize); |
|
2035 base_next_decommitted = (void *)((uintptr_t)base_pages + pminsize); |
|
2036 # if defined(MALLOC_DECOMMIT) |
|
2037 if (pminsize < csize) |
|
2038 pages_decommit(base_next_decommitted, csize - pminsize); |
|
2039 # endif |
|
2040 # ifdef MALLOC_STATS |
|
2041 base_mapped += csize; |
|
2042 base_committed += pminsize; |
|
2043 # endif |
|
2044 #endif |
|
2045 |
|
2046 ret = false; |
|
2047 RETURN: |
|
2048 #ifdef MALLOC_PAGEFILE |
|
2049 if (pfd != -1) |
|
2050 pagefile_close(pfd); |
|
2051 #endif |
|
2052 return (false); |
|
2053 } |
|
2054 |
|
2055 static bool |
|
2056 base_pages_alloc(size_t minsize) |
|
2057 { |
|
2058 |
|
2059 if (base_pages_alloc_mmap(minsize) == false) |
|
2060 return (false); |
|
2061 |
|
2062 return (true); |
|
2063 } |
|
2064 |
|
2065 static void * |
|
2066 base_alloc(size_t size) |
|
2067 { |
|
2068 void *ret; |
|
2069 size_t csize; |
|
2070 |
|
2071 /* Round size up to nearest multiple of the cacheline size. */ |
|
2072 csize = CACHELINE_CEILING(size); |
|
2073 |
|
2074 malloc_mutex_lock(&base_mtx); |
|
2075 /* Make sure there's enough space for the allocation. */ |
|
2076 if ((uintptr_t)base_next_addr + csize > (uintptr_t)base_past_addr) { |
|
2077 if (base_pages_alloc(csize)) { |
|
2078 malloc_mutex_unlock(&base_mtx); |
|
2079 return (NULL); |
|
2080 } |
|
2081 } |
|
2082 /* Allocate. */ |
|
2083 ret = base_next_addr; |
|
2084 base_next_addr = (void *)((uintptr_t)base_next_addr + csize); |
|
2085 #if defined(MALLOC_DECOMMIT) || defined(MALLOC_STATS) |
|
2086 /* Make sure enough pages are committed for the new allocation. */ |
|
2087 if ((uintptr_t)base_next_addr > (uintptr_t)base_next_decommitted) { |
|
2088 void *pbase_next_addr = |
|
2089 (void *)(PAGE_CEILING((uintptr_t)base_next_addr)); |
|
2090 |
|
2091 # ifdef MALLOC_DECOMMIT |
|
2092 pages_commit(base_next_decommitted, (uintptr_t)pbase_next_addr - |
|
2093 (uintptr_t)base_next_decommitted); |
|
2094 # endif |
|
2095 base_next_decommitted = pbase_next_addr; |
|
2096 # ifdef MALLOC_STATS |
|
2097 base_committed += (uintptr_t)pbase_next_addr - |
|
2098 (uintptr_t)base_next_decommitted; |
|
2099 # endif |
|
2100 } |
|
2101 #endif |
|
2102 malloc_mutex_unlock(&base_mtx); |
|
2103 VALGRIND_MALLOCLIKE_BLOCK(ret, size, 0, false); |
|
2104 |
|
2105 return (ret); |
|
2106 } |
|
2107 |
|
2108 static void * |
|
2109 base_calloc(size_t number, size_t size) |
|
2110 { |
|
2111 void *ret; |
|
2112 |
|
2113 ret = base_alloc(number * size); |
|
2114 #ifdef MALLOC_VALGRIND |
|
2115 if (ret != NULL) { |
|
2116 VALGRIND_FREELIKE_BLOCK(ret, 0); |
|
2117 VALGRIND_MALLOCLIKE_BLOCK(ret, size, 0, true); |
|
2118 } |
|
2119 #endif |
|
2120 memset(ret, 0, number * size); |
|
2121 |
|
2122 return (ret); |
|
2123 } |
|
2124 |
|
2125 static extent_node_t * |
|
2126 base_node_alloc(void) |
|
2127 { |
|
2128 extent_node_t *ret; |
|
2129 |
|
2130 malloc_mutex_lock(&base_mtx); |
|
2131 if (base_nodes != NULL) { |
|
2132 ret = base_nodes; |
|
2133 base_nodes = *(extent_node_t **)ret; |
|
2134 VALGRIND_FREELIKE_BLOCK(ret, 0); |
|
2135 VALGRIND_MALLOCLIKE_BLOCK(ret, sizeof(extent_node_t), 0, false); |
|
2136 malloc_mutex_unlock(&base_mtx); |
|
2137 } else { |
|
2138 malloc_mutex_unlock(&base_mtx); |
|
2139 ret = (extent_node_t *)base_alloc(sizeof(extent_node_t)); |
|
2140 } |
|
2141 |
|
2142 return (ret); |
|
2143 } |
|
2144 |
|
2145 static void |
|
2146 base_node_dealloc(extent_node_t *node) |
|
2147 { |
|
2148 |
|
2149 malloc_mutex_lock(&base_mtx); |
|
2150 VALGRIND_FREELIKE_BLOCK(node, 0); |
|
2151 VALGRIND_MALLOCLIKE_BLOCK(node, sizeof(extent_node_t *), 0, false); |
|
2152 *(extent_node_t **)node = base_nodes; |
|
2153 base_nodes = node; |
|
2154 malloc_mutex_unlock(&base_mtx); |
|
2155 } |
|
2156 |
|
2157 /******************************************************************************/ |
|
2158 |
|
2159 #ifdef MALLOC_STATS |
|
2160 static void |
|
2161 stats_print(arena_t *arena) |
|
2162 { |
|
2163 unsigned i, gap_start; |
|
2164 |
|
2165 #ifdef MOZ_MEMORY_WINDOWS |
|
2166 malloc_printf("dirty: %Iu page%s dirty, %I64u sweep%s," |
|
2167 " %I64u madvise%s, %I64u page%s purged\n", |
|
2168 arena->ndirty, arena->ndirty == 1 ? "" : "s", |
|
2169 arena->stats.npurge, arena->stats.npurge == 1 ? "" : "s", |
|
2170 arena->stats.nmadvise, arena->stats.nmadvise == 1 ? "" : "s", |
|
2171 arena->stats.purged, arena->stats.purged == 1 ? "" : "s"); |
|
2172 # ifdef MALLOC_DECOMMIT |
|
2173 malloc_printf("decommit: %I64u decommit%s, %I64u commit%s," |
|
2174 " %I64u page%s decommitted\n", |
|
2175 arena->stats.ndecommit, (arena->stats.ndecommit == 1) ? "" : "s", |
|
2176 arena->stats.ncommit, (arena->stats.ncommit == 1) ? "" : "s", |
|
2177 arena->stats.decommitted, |
|
2178 (arena->stats.decommitted == 1) ? "" : "s"); |
|
2179 # endif |
|
2180 |
|
2181 malloc_printf(" allocated nmalloc ndalloc\n"); |
|
2182 malloc_printf("small: %12Iu %12I64u %12I64u\n", |
|
2183 arena->stats.allocated_small, arena->stats.nmalloc_small, |
|
2184 arena->stats.ndalloc_small); |
|
2185 malloc_printf("large: %12Iu %12I64u %12I64u\n", |
|
2186 arena->stats.allocated_large, arena->stats.nmalloc_large, |
|
2187 arena->stats.ndalloc_large); |
|
2188 malloc_printf("total: %12Iu %12I64u %12I64u\n", |
|
2189 arena->stats.allocated_small + arena->stats.allocated_large, |
|
2190 arena->stats.nmalloc_small + arena->stats.nmalloc_large, |
|
2191 arena->stats.ndalloc_small + arena->stats.ndalloc_large); |
|
2192 malloc_printf("mapped: %12Iu\n", arena->stats.mapped); |
|
2193 #else |
|
2194 malloc_printf("dirty: %zu page%s dirty, %llu sweep%s," |
|
2195 " %llu madvise%s, %llu page%s purged\n", |
|
2196 arena->ndirty, arena->ndirty == 1 ? "" : "s", |
|
2197 arena->stats.npurge, arena->stats.npurge == 1 ? "" : "s", |
|
2198 arena->stats.nmadvise, arena->stats.nmadvise == 1 ? "" : "s", |
|
2199 arena->stats.purged, arena->stats.purged == 1 ? "" : "s"); |
|
2200 # ifdef MALLOC_DECOMMIT |
|
2201 malloc_printf("decommit: %llu decommit%s, %llu commit%s," |
|
2202 " %llu page%s decommitted\n", |
|
2203 arena->stats.ndecommit, (arena->stats.ndecommit == 1) ? "" : "s", |
|
2204 arena->stats.ncommit, (arena->stats.ncommit == 1) ? "" : "s", |
|
2205 arena->stats.decommitted, |
|
2206 (arena->stats.decommitted == 1) ? "" : "s"); |
|
2207 # endif |
|
2208 |
|
2209 malloc_printf(" allocated nmalloc ndalloc\n"); |
|
2210 malloc_printf("small: %12zu %12llu %12llu\n", |
|
2211 arena->stats.allocated_small, arena->stats.nmalloc_small, |
|
2212 arena->stats.ndalloc_small); |
|
2213 malloc_printf("large: %12zu %12llu %12llu\n", |
|
2214 arena->stats.allocated_large, arena->stats.nmalloc_large, |
|
2215 arena->stats.ndalloc_large); |
|
2216 malloc_printf("total: %12zu %12llu %12llu\n", |
|
2217 arena->stats.allocated_small + arena->stats.allocated_large, |
|
2218 arena->stats.nmalloc_small + arena->stats.nmalloc_large, |
|
2219 arena->stats.ndalloc_small + arena->stats.ndalloc_large); |
|
2220 malloc_printf("mapped: %12zu\n", arena->stats.mapped); |
|
2221 #endif |
|
2222 malloc_printf("bins: bin size regs pgs requests newruns" |
|
2223 " reruns maxruns curruns\n"); |
|
2224 for (i = 0, gap_start = UINT_MAX; i < ntbins + nqbins + nsbins; i++) { |
|
2225 if (arena->bins[i].stats.nrequests == 0) { |
|
2226 if (gap_start == UINT_MAX) |
|
2227 gap_start = i; |
|
2228 } else { |
|
2229 if (gap_start != UINT_MAX) { |
|
2230 if (i > gap_start + 1) { |
|
2231 /* Gap of more than one size class. */ |
|
2232 malloc_printf("[%u..%u]\n", |
|
2233 gap_start, i - 1); |
|
2234 } else { |
|
2235 /* Gap of one size class. */ |
|
2236 malloc_printf("[%u]\n", gap_start); |
|
2237 } |
|
2238 gap_start = UINT_MAX; |
|
2239 } |
|
2240 malloc_printf( |
|
2241 #if defined(MOZ_MEMORY_WINDOWS) |
|
2242 "%13u %1s %4u %4u %3u %9I64u %9I64u" |
|
2243 " %9I64u %7u %7u\n", |
|
2244 #else |
|
2245 "%13u %1s %4u %4u %3u %9llu %9llu" |
|
2246 " %9llu %7lu %7lu\n", |
|
2247 #endif |
|
2248 i, |
|
2249 i < ntbins ? "T" : i < ntbins + nqbins ? "Q" : "S", |
|
2250 arena->bins[i].reg_size, |
|
2251 arena->bins[i].nregs, |
|
2252 arena->bins[i].run_size >> pagesize_2pow, |
|
2253 arena->bins[i].stats.nrequests, |
|
2254 arena->bins[i].stats.nruns, |
|
2255 arena->bins[i].stats.reruns, |
|
2256 arena->bins[i].stats.highruns, |
|
2257 arena->bins[i].stats.curruns); |
|
2258 } |
|
2259 } |
|
2260 if (gap_start != UINT_MAX) { |
|
2261 if (i > gap_start + 1) { |
|
2262 /* Gap of more than one size class. */ |
|
2263 malloc_printf("[%u..%u]\n", gap_start, i - 1); |
|
2264 } else { |
|
2265 /* Gap of one size class. */ |
|
2266 malloc_printf("[%u]\n", gap_start); |
|
2267 } |
|
2268 } |
|
2269 } |
|
2270 #endif |
|
2271 |
|
2272 /* |
|
2273 * End Utility functions/macros. |
|
2274 */ |
|
2275 /******************************************************************************/ |
|
2276 /* |
|
2277 * Begin extent tree code. |
|
2278 */ |
|
2279 |
|
2280 static inline int |
|
2281 extent_szad_comp(extent_node_t *a, extent_node_t *b) |
|
2282 { |
|
2283 int ret; |
|
2284 size_t a_size = a->size; |
|
2285 size_t b_size = b->size; |
|
2286 |
|
2287 ret = (a_size > b_size) - (a_size < b_size); |
|
2288 if (ret == 0) { |
|
2289 uintptr_t a_addr = (uintptr_t)a->addr; |
|
2290 uintptr_t b_addr = (uintptr_t)b->addr; |
|
2291 |
|
2292 ret = (a_addr > b_addr) - (a_addr < b_addr); |
|
2293 } |
|
2294 |
|
2295 return (ret); |
|
2296 } |
|
2297 |
|
2298 /* Wrap red-black tree macros in functions. */ |
|
2299 rb_wrap(static, extent_tree_szad_, extent_tree_t, extent_node_t, |
|
2300 link_szad, extent_szad_comp) |
|
2301 |
|
2302 static inline int |
|
2303 extent_ad_comp(extent_node_t *a, extent_node_t *b) |
|
2304 { |
|
2305 uintptr_t a_addr = (uintptr_t)a->addr; |
|
2306 uintptr_t b_addr = (uintptr_t)b->addr; |
|
2307 |
|
2308 return ((a_addr > b_addr) - (a_addr < b_addr)); |
|
2309 } |
|
2310 |
|
2311 /* Wrap red-black tree macros in functions. */ |
|
2312 rb_wrap(static, extent_tree_ad_, extent_tree_t, extent_node_t, link_ad, |
|
2313 extent_ad_comp) |
|
2314 |
|
2315 /* |
|
2316 * End extent tree code. |
|
2317 */ |
|
2318 /******************************************************************************/ |
|
2319 /* |
|
2320 * Begin chunk management functions. |
|
2321 */ |
|
2322 |
|
2323 #ifdef MOZ_MEMORY_WINDOWS |
|
2324 |
|
2325 static void * |
|
2326 pages_map(void *addr, size_t size, int pfd) |
|
2327 { |
|
2328 void *ret = NULL; |
|
2329 ret = VirtualAlloc(addr, size, MEM_COMMIT | MEM_RESERVE, |
|
2330 PAGE_READWRITE); |
|
2331 return (ret); |
|
2332 } |
|
2333 |
|
2334 static void |
|
2335 pages_unmap(void *addr, size_t size) |
|
2336 { |
|
2337 if (VirtualFree(addr, 0, MEM_RELEASE) == 0) { |
|
2338 _malloc_message(_getprogname(), |
|
2339 ": (malloc) Error in VirtualFree()\n", "", ""); |
|
2340 if (opt_abort) |
|
2341 abort(); |
|
2342 } |
|
2343 } |
|
2344 #else |
|
2345 #ifdef JEMALLOC_USES_MAP_ALIGN |
|
2346 static void * |
|
2347 pages_map_align(size_t size, int pfd, size_t alignment) |
|
2348 { |
|
2349 void *ret; |
|
2350 |
|
2351 /* |
|
2352 * We don't use MAP_FIXED here, because it can cause the *replacement* |
|
2353 * of existing mappings, and we only want to create new mappings. |
|
2354 */ |
|
2355 #ifdef MALLOC_PAGEFILE |
|
2356 if (pfd != -1) { |
|
2357 ret = mmap((void *)alignment, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | |
|
2358 MAP_NOSYNC | MAP_ALIGN, pfd, 0); |
|
2359 } else |
|
2360 #endif |
|
2361 { |
|
2362 ret = mmap((void *)alignment, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | |
|
2363 MAP_NOSYNC | MAP_ALIGN | MAP_ANON, -1, 0); |
|
2364 } |
|
2365 assert(ret != NULL); |
|
2366 |
|
2367 if (ret == MAP_FAILED) |
|
2368 ret = NULL; |
|
2369 return (ret); |
|
2370 } |
|
2371 #endif |
|
2372 |
|
2373 static void * |
|
2374 pages_map(void *addr, size_t size, int pfd) |
|
2375 { |
|
2376 void *ret; |
|
2377 #if defined(__ia64__) |
|
2378 /* |
|
2379 * The JS engine assumes that all allocated pointers have their high 17 bits clear, |
|
2380 * which ia64's mmap doesn't support directly. However, we can emulate it by passing |
|
2381 * mmap an "addr" parameter with those bits clear. The mmap will return that address, |
|
2382 * or the nearest available memory above that address, providing a near-guarantee |
|
2383 * that those bits are clear. If they are not, we return NULL below to indicate |
|
2384 * out-of-memory. |
|
2385 * |
|
2386 * The addr is chosen as 0x0000070000000000, which still allows about 120TB of virtual |
|
2387 * address space. |
|
2388 * |
|
2389 * See Bug 589735 for more information. |
|
2390 */ |
|
2391 bool check_placement = true; |
|
2392 if (addr == NULL) { |
|
2393 addr = (void*)0x0000070000000000; |
|
2394 check_placement = false; |
|
2395 } |
|
2396 #endif |
|
2397 |
|
2398 /* |
|
2399 * We don't use MAP_FIXED here, because it can cause the *replacement* |
|
2400 * of existing mappings, and we only want to create new mappings. |
|
2401 */ |
|
2402 #ifdef MALLOC_PAGEFILE |
|
2403 if (pfd != -1) { |
|
2404 ret = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | |
|
2405 MAP_NOSYNC, pfd, 0); |
|
2406 } else |
|
2407 #endif |
|
2408 { |
|
2409 ret = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | |
|
2410 MAP_ANON, -1, 0); |
|
2411 } |
|
2412 assert(ret != NULL); |
|
2413 |
|
2414 if (ret == MAP_FAILED) { |
|
2415 ret = NULL; |
|
2416 } |
|
2417 #if defined(__ia64__) |
|
2418 /* |
|
2419 * If the allocated memory doesn't have its upper 17 bits clear, consider it |
|
2420 * as out of memory. |
|
2421 */ |
|
2422 else if ((long long)ret & 0xffff800000000000) { |
|
2423 munmap(ret, size); |
|
2424 ret = NULL; |
|
2425 } |
|
2426 /* If the caller requested a specific memory location, verify that's what mmap returned. */ |
|
2427 else if (check_placement && ret != addr) { |
|
2428 #else |
|
2429 else if (addr != NULL && ret != addr) { |
|
2430 #endif |
|
2431 /* |
|
2432 * We succeeded in mapping memory, but not in the right place. |
|
2433 */ |
|
2434 if (munmap(ret, size) == -1) { |
|
2435 char buf[STRERROR_BUF]; |
|
2436 |
|
2437 strerror_r(errno, buf, sizeof(buf)); |
|
2438 _malloc_message(_getprogname(), |
|
2439 ": (malloc) Error in munmap(): ", buf, "\n"); |
|
2440 if (opt_abort) |
|
2441 abort(); |
|
2442 } |
|
2443 ret = NULL; |
|
2444 } |
|
2445 |
|
2446 #if defined(__ia64__) |
|
2447 assert(ret == NULL || (!check_placement && ret != NULL) |
|
2448 || (check_placement && ret == addr)); |
|
2449 #else |
|
2450 assert(ret == NULL || (addr == NULL && ret != addr) |
|
2451 || (addr != NULL && ret == addr)); |
|
2452 #endif |
|
2453 return (ret); |
|
2454 } |
|
2455 |
|
2456 static void |
|
2457 pages_unmap(void *addr, size_t size) |
|
2458 { |
|
2459 |
|
2460 if (munmap(addr, size) == -1) { |
|
2461 char buf[STRERROR_BUF]; |
|
2462 |
|
2463 strerror_r(errno, buf, sizeof(buf)); |
|
2464 _malloc_message(_getprogname(), |
|
2465 ": (malloc) Error in munmap(): ", buf, "\n"); |
|
2466 if (opt_abort) |
|
2467 abort(); |
|
2468 } |
|
2469 } |
|
2470 #endif |
|
2471 |
|
2472 #ifdef MOZ_MEMORY_DARWIN |
|
2473 #define VM_COPY_MIN (pagesize << 5) |
|
2474 static inline void |
|
2475 pages_copy(void *dest, const void *src, size_t n) |
|
2476 { |
|
2477 |
|
2478 assert((void *)((uintptr_t)dest & ~pagesize_mask) == dest); |
|
2479 assert(n >= VM_COPY_MIN); |
|
2480 assert((void *)((uintptr_t)src & ~pagesize_mask) == src); |
|
2481 |
|
2482 vm_copy(mach_task_self(), (vm_address_t)src, (vm_size_t)n, |
|
2483 (vm_address_t)dest); |
|
2484 } |
|
2485 #endif |
|
2486 |
|
2487 #ifdef MALLOC_VALIDATE |
|
2488 static inline malloc_rtree_t * |
|
2489 malloc_rtree_new(unsigned bits) |
|
2490 { |
|
2491 malloc_rtree_t *ret; |
|
2492 unsigned bits_per_level, height, i; |
|
2493 |
|
2494 bits_per_level = ffs(pow2_ceil((MALLOC_RTREE_NODESIZE / |
|
2495 sizeof(void *)))) - 1; |
|
2496 height = bits / bits_per_level; |
|
2497 if (height * bits_per_level != bits) |
|
2498 height++; |
|
2499 RELEASE_ASSERT(height * bits_per_level >= bits); |
|
2500 |
|
2501 ret = (malloc_rtree_t*)base_calloc(1, sizeof(malloc_rtree_t) + |
|
2502 (sizeof(unsigned) * (height - 1))); |
|
2503 if (ret == NULL) |
|
2504 return (NULL); |
|
2505 |
|
2506 malloc_spin_init(&ret->lock); |
|
2507 ret->height = height; |
|
2508 if (bits_per_level * height > bits) |
|
2509 ret->level2bits[0] = bits % bits_per_level; |
|
2510 else |
|
2511 ret->level2bits[0] = bits_per_level; |
|
2512 for (i = 1; i < height; i++) |
|
2513 ret->level2bits[i] = bits_per_level; |
|
2514 |
|
2515 ret->root = (void**)base_calloc(1, sizeof(void *) << ret->level2bits[0]); |
|
2516 if (ret->root == NULL) { |
|
2517 /* |
|
2518 * We leak the rtree here, since there's no generic base |
|
2519 * deallocation. |
|
2520 */ |
|
2521 return (NULL); |
|
2522 } |
|
2523 |
|
2524 return (ret); |
|
2525 } |
|
2526 |
|
2527 #define MALLOC_RTREE_GET_GENERATE(f) \ |
|
2528 /* The least significant bits of the key are ignored. */ \ |
|
2529 static inline void * \ |
|
2530 f(malloc_rtree_t *rtree, uintptr_t key) \ |
|
2531 { \ |
|
2532 void *ret; \ |
|
2533 uintptr_t subkey; \ |
|
2534 unsigned i, lshift, height, bits; \ |
|
2535 void **node, **child; \ |
|
2536 \ |
|
2537 MALLOC_RTREE_LOCK(&rtree->lock); \ |
|
2538 for (i = lshift = 0, height = rtree->height, node = rtree->root;\ |
|
2539 i < height - 1; \ |
|
2540 i++, lshift += bits, node = child) { \ |
|
2541 bits = rtree->level2bits[i]; \ |
|
2542 subkey = (key << lshift) >> ((SIZEOF_PTR << 3) - bits); \ |
|
2543 child = (void**)node[subkey]; \ |
|
2544 if (child == NULL) { \ |
|
2545 MALLOC_RTREE_UNLOCK(&rtree->lock); \ |
|
2546 return (NULL); \ |
|
2547 } \ |
|
2548 } \ |
|
2549 \ |
|
2550 /* \ |
|
2551 * node is a leaf, so it contains values rather than node \ |
|
2552 * pointers. \ |
|
2553 */ \ |
|
2554 bits = rtree->level2bits[i]; \ |
|
2555 subkey = (key << lshift) >> ((SIZEOF_PTR << 3) - bits); \ |
|
2556 ret = node[subkey]; \ |
|
2557 MALLOC_RTREE_UNLOCK(&rtree->lock); \ |
|
2558 \ |
|
2559 MALLOC_RTREE_GET_VALIDATE \ |
|
2560 return (ret); \ |
|
2561 } |
|
2562 |
|
2563 #ifdef MALLOC_DEBUG |
|
2564 # define MALLOC_RTREE_LOCK(l) malloc_spin_lock(l) |
|
2565 # define MALLOC_RTREE_UNLOCK(l) malloc_spin_unlock(l) |
|
2566 # define MALLOC_RTREE_GET_VALIDATE |
|
2567 MALLOC_RTREE_GET_GENERATE(malloc_rtree_get_locked) |
|
2568 # undef MALLOC_RTREE_LOCK |
|
2569 # undef MALLOC_RTREE_UNLOCK |
|
2570 # undef MALLOC_RTREE_GET_VALIDATE |
|
2571 #endif |
|
2572 |
|
2573 #define MALLOC_RTREE_LOCK(l) |
|
2574 #define MALLOC_RTREE_UNLOCK(l) |
|
2575 #ifdef MALLOC_DEBUG |
|
2576 /* |
|
2577 * Suppose that it were possible for a jemalloc-allocated chunk to be |
|
2578 * munmap()ped, followed by a different allocator in another thread re-using |
|
2579 * overlapping virtual memory, all without invalidating the cached rtree |
|
2580 * value. The result would be a false positive (the rtree would claim that |
|
2581 * jemalloc owns memory that it had actually discarded). I don't think this |
|
2582 * scenario is possible, but the following assertion is a prudent sanity |
|
2583 * check. |
|
2584 */ |
|
2585 # define MALLOC_RTREE_GET_VALIDATE \ |
|
2586 assert(malloc_rtree_get_locked(rtree, key) == ret); |
|
2587 #else |
|
2588 # define MALLOC_RTREE_GET_VALIDATE |
|
2589 #endif |
|
2590 MALLOC_RTREE_GET_GENERATE(malloc_rtree_get) |
|
2591 #undef MALLOC_RTREE_LOCK |
|
2592 #undef MALLOC_RTREE_UNLOCK |
|
2593 #undef MALLOC_RTREE_GET_VALIDATE |
|
2594 |
|
2595 static inline bool |
|
2596 malloc_rtree_set(malloc_rtree_t *rtree, uintptr_t key, void *val) |
|
2597 { |
|
2598 uintptr_t subkey; |
|
2599 unsigned i, lshift, height, bits; |
|
2600 void **node, **child; |
|
2601 |
|
2602 malloc_spin_lock(&rtree->lock); |
|
2603 for (i = lshift = 0, height = rtree->height, node = rtree->root; |
|
2604 i < height - 1; |
|
2605 i++, lshift += bits, node = child) { |
|
2606 bits = rtree->level2bits[i]; |
|
2607 subkey = (key << lshift) >> ((SIZEOF_PTR << 3) - bits); |
|
2608 child = (void**)node[subkey]; |
|
2609 if (child == NULL) { |
|
2610 child = (void**)base_calloc(1, sizeof(void *) << |
|
2611 rtree->level2bits[i+1]); |
|
2612 if (child == NULL) { |
|
2613 malloc_spin_unlock(&rtree->lock); |
|
2614 return (true); |
|
2615 } |
|
2616 node[subkey] = child; |
|
2617 } |
|
2618 } |
|
2619 |
|
2620 /* node is a leaf, so it contains values rather than node pointers. */ |
|
2621 bits = rtree->level2bits[i]; |
|
2622 subkey = (key << lshift) >> ((SIZEOF_PTR << 3) - bits); |
|
2623 node[subkey] = val; |
|
2624 malloc_spin_unlock(&rtree->lock); |
|
2625 |
|
2626 return (false); |
|
2627 } |
|
2628 #endif |
|
2629 |
|
2630 #if defined(MOZ_MEMORY_WINDOWS) || defined(JEMALLOC_USES_MAP_ALIGN) || defined(MALLOC_PAGEFILE) |
|
2631 |
|
2632 /* Allocate an aligned chunk while maintaining a 1:1 correspondence between |
|
2633 * mmap and unmap calls. This is important on Windows, but not elsewhere. */ |
|
2634 static void * |
|
2635 chunk_alloc_mmap(size_t size, bool pagefile) |
|
2636 { |
|
2637 void *ret; |
|
2638 #ifndef JEMALLOC_USES_MAP_ALIGN |
|
2639 size_t offset; |
|
2640 #endif |
|
2641 int pfd; |
|
2642 |
|
2643 #ifdef MALLOC_PAGEFILE |
|
2644 if (opt_pagefile && pagefile) { |
|
2645 pfd = pagefile_init(size); |
|
2646 if (pfd == -1) |
|
2647 return (NULL); |
|
2648 } else |
|
2649 #endif |
|
2650 pfd = -1; |
|
2651 |
|
2652 #ifdef JEMALLOC_USES_MAP_ALIGN |
|
2653 ret = pages_map_align(size, pfd, chunksize); |
|
2654 #else |
|
2655 ret = pages_map(NULL, size, pfd); |
|
2656 if (ret == NULL) |
|
2657 goto RETURN; |
|
2658 |
|
2659 offset = CHUNK_ADDR2OFFSET(ret); |
|
2660 if (offset != 0) { |
|
2661 /* Deallocate, then try to allocate at (ret + size - offset). */ |
|
2662 pages_unmap(ret, size); |
|
2663 ret = pages_map((void *)((uintptr_t)ret + size - offset), size, |
|
2664 pfd); |
|
2665 while (ret == NULL) { |
|
2666 /* |
|
2667 * Over-allocate in order to map a memory region that |
|
2668 * is definitely large enough. |
|
2669 */ |
|
2670 ret = pages_map(NULL, size + chunksize, -1); |
|
2671 if (ret == NULL) |
|
2672 goto RETURN; |
|
2673 /* |
|
2674 * Deallocate, then allocate the correct size, within |
|
2675 * the over-sized mapping. |
|
2676 */ |
|
2677 offset = CHUNK_ADDR2OFFSET(ret); |
|
2678 pages_unmap(ret, size + chunksize); |
|
2679 if (offset == 0) |
|
2680 ret = pages_map(ret, size, pfd); |
|
2681 else { |
|
2682 ret = pages_map((void *)((uintptr_t)ret + |
|
2683 chunksize - offset), size, pfd); |
|
2684 } |
|
2685 /* |
|
2686 * Failure here indicates a race with another thread, so |
|
2687 * try again. |
|
2688 */ |
|
2689 } |
|
2690 } |
|
2691 RETURN: |
|
2692 #endif |
|
2693 #ifdef MALLOC_PAGEFILE |
|
2694 if (pfd != -1) |
|
2695 pagefile_close(pfd); |
|
2696 #endif |
|
2697 |
|
2698 return (ret); |
|
2699 } |
|
2700 |
|
2701 #else /* ! (defined(MOZ_MEMORY_WINDOWS) || defined(JEMALLOC_USES_MAP_ALIGN) || defined(MALLOC_PAGEFILE)) */ |
|
2702 |
|
2703 /* pages_trim, chunk_alloc_mmap_slow and chunk_alloc_mmap were cherry-picked |
|
2704 * from upstream jemalloc 3.4.1 to fix Mozilla bug 956501. */ |
|
2705 |
|
2706 /* Return the offset between a and the nearest aligned address at or below a. */ |
|
2707 #define ALIGNMENT_ADDR2OFFSET(a, alignment) \ |
|
2708 ((size_t)((uintptr_t)(a) & (alignment - 1))) |
|
2709 |
|
2710 /* Return the smallest alignment multiple that is >= s. */ |
|
2711 #define ALIGNMENT_CEILING(s, alignment) \ |
|
2712 (((s) + (alignment - 1)) & (-(alignment))) |
|
2713 |
|
2714 static void * |
|
2715 pages_trim(void *addr, size_t alloc_size, size_t leadsize, size_t size) |
|
2716 { |
|
2717 size_t trailsize; |
|
2718 void *ret = (void *)((uintptr_t)addr + leadsize); |
|
2719 |
|
2720 assert(alloc_size >= leadsize + size); |
|
2721 trailsize = alloc_size - leadsize - size; |
|
2722 |
|
2723 if (leadsize != 0) |
|
2724 pages_unmap(addr, leadsize); |
|
2725 if (trailsize != 0) |
|
2726 pages_unmap((void *)((uintptr_t)ret + size), trailsize); |
|
2727 return (ret); |
|
2728 } |
|
2729 |
|
2730 static void * |
|
2731 chunk_alloc_mmap_slow(size_t size, size_t alignment) |
|
2732 { |
|
2733 void *ret, *pages; |
|
2734 size_t alloc_size, leadsize; |
|
2735 |
|
2736 alloc_size = size + alignment - pagesize; |
|
2737 /* Beware size_t wrap-around. */ |
|
2738 if (alloc_size < size) |
|
2739 return (NULL); |
|
2740 do { |
|
2741 pages = pages_map(NULL, alloc_size, -1); |
|
2742 if (pages == NULL) |
|
2743 return (NULL); |
|
2744 leadsize = ALIGNMENT_CEILING((uintptr_t)pages, alignment) - |
|
2745 (uintptr_t)pages; |
|
2746 ret = pages_trim(pages, alloc_size, leadsize, size); |
|
2747 } while (ret == NULL); |
|
2748 |
|
2749 assert(ret != NULL); |
|
2750 return (ret); |
|
2751 } |
|
2752 |
|
2753 static void * |
|
2754 chunk_alloc_mmap(size_t size, bool pagefile) |
|
2755 { |
|
2756 void *ret; |
|
2757 size_t offset; |
|
2758 |
|
2759 /* |
|
2760 * Ideally, there would be a way to specify alignment to mmap() (like |
|
2761 * NetBSD has), but in the absence of such a feature, we have to work |
|
2762 * hard to efficiently create aligned mappings. The reliable, but |
|
2763 * slow method is to create a mapping that is over-sized, then trim the |
|
2764 * excess. However, that always results in one or two calls to |
|
2765 * pages_unmap(). |
|
2766 * |
|
2767 * Optimistically try mapping precisely the right amount before falling |
|
2768 * back to the slow method, with the expectation that the optimistic |
|
2769 * approach works most of the time. |
|
2770 */ |
|
2771 |
|
2772 ret = pages_map(NULL, size, -1); |
|
2773 if (ret == NULL) |
|
2774 return (NULL); |
|
2775 offset = ALIGNMENT_ADDR2OFFSET(ret, chunksize); |
|
2776 if (offset != 0) { |
|
2777 pages_unmap(ret, size); |
|
2778 return (chunk_alloc_mmap_slow(size, chunksize)); |
|
2779 } |
|
2780 |
|
2781 assert(ret != NULL); |
|
2782 return (ret); |
|
2783 } |
|
2784 |
|
2785 #endif /* defined(MOZ_MEMORY_WINDOWS) || defined(JEMALLOC_USES_MAP_ALIGN) || defined(MALLOC_PAGEFILE) */ |
|
2786 |
|
2787 #ifdef MALLOC_PAGEFILE |
|
2788 static int |
|
2789 pagefile_init(size_t size) |
|
2790 { |
|
2791 int ret; |
|
2792 size_t i; |
|
2793 char pagefile_path[PATH_MAX]; |
|
2794 char zbuf[MALLOC_PAGEFILE_WRITE_SIZE]; |
|
2795 |
|
2796 /* |
|
2797 * Create a temporary file, then immediately unlink it so that it will |
|
2798 * not persist. |
|
2799 */ |
|
2800 strcpy(pagefile_path, pagefile_templ); |
|
2801 ret = mkstemp(pagefile_path); |
|
2802 if (ret == -1) |
|
2803 return (ret); |
|
2804 if (unlink(pagefile_path)) { |
|
2805 char buf[STRERROR_BUF]; |
|
2806 |
|
2807 strerror_r(errno, buf, sizeof(buf)); |
|
2808 _malloc_message(_getprogname(), ": (malloc) Error in unlink(\"", |
|
2809 pagefile_path, "\"):"); |
|
2810 _malloc_message(buf, "\n", "", ""); |
|
2811 if (opt_abort) |
|
2812 abort(); |
|
2813 } |
|
2814 |
|
2815 /* |
|
2816 * Write sequential zeroes to the file in order to assure that disk |
|
2817 * space is committed, with minimal fragmentation. It would be |
|
2818 * sufficient to write one zero per disk block, but that potentially |
|
2819 * results in more system calls, for no real gain. |
|
2820 */ |
|
2821 memset(zbuf, 0, sizeof(zbuf)); |
|
2822 for (i = 0; i < size; i += sizeof(zbuf)) { |
|
2823 if (write(ret, zbuf, sizeof(zbuf)) != sizeof(zbuf)) { |
|
2824 if (errno != ENOSPC) { |
|
2825 char buf[STRERROR_BUF]; |
|
2826 |
|
2827 strerror_r(errno, buf, sizeof(buf)); |
|
2828 _malloc_message(_getprogname(), |
|
2829 ": (malloc) Error in write(): ", buf, "\n"); |
|
2830 if (opt_abort) |
|
2831 abort(); |
|
2832 } |
|
2833 pagefile_close(ret); |
|
2834 return (-1); |
|
2835 } |
|
2836 } |
|
2837 |
|
2838 return (ret); |
|
2839 } |
|
2840 |
|
2841 static void |
|
2842 pagefile_close(int pfd) |
|
2843 { |
|
2844 |
|
2845 if (close(pfd)) { |
|
2846 char buf[STRERROR_BUF]; |
|
2847 |
|
2848 strerror_r(errno, buf, sizeof(buf)); |
|
2849 _malloc_message(_getprogname(), |
|
2850 ": (malloc) Error in close(): ", buf, "\n"); |
|
2851 if (opt_abort) |
|
2852 abort(); |
|
2853 } |
|
2854 } |
|
2855 #endif |
|
2856 |
|
2857 static void * |
|
2858 chunk_alloc(size_t size, bool zero, bool pagefile) |
|
2859 { |
|
2860 void *ret; |
|
2861 |
|
2862 assert(size != 0); |
|
2863 assert((size & chunksize_mask) == 0); |
|
2864 |
|
2865 ret = chunk_alloc_mmap(size, pagefile); |
|
2866 if (ret != NULL) { |
|
2867 goto RETURN; |
|
2868 } |
|
2869 |
|
2870 /* All strategies for allocation failed. */ |
|
2871 ret = NULL; |
|
2872 RETURN: |
|
2873 |
|
2874 #ifdef MALLOC_VALIDATE |
|
2875 if (ret != NULL) { |
|
2876 if (malloc_rtree_set(chunk_rtree, (uintptr_t)ret, ret)) { |
|
2877 chunk_dealloc(ret, size); |
|
2878 return (NULL); |
|
2879 } |
|
2880 } |
|
2881 #endif |
|
2882 |
|
2883 assert(CHUNK_ADDR2BASE(ret) == ret); |
|
2884 return (ret); |
|
2885 } |
|
2886 |
|
2887 static void |
|
2888 chunk_dealloc_mmap(void *chunk, size_t size) |
|
2889 { |
|
2890 |
|
2891 pages_unmap(chunk, size); |
|
2892 } |
|
2893 |
|
2894 static void |
|
2895 chunk_dealloc(void *chunk, size_t size) |
|
2896 { |
|
2897 |
|
2898 assert(chunk != NULL); |
|
2899 assert(CHUNK_ADDR2BASE(chunk) == chunk); |
|
2900 assert(size != 0); |
|
2901 assert((size & chunksize_mask) == 0); |
|
2902 |
|
2903 #ifdef MALLOC_VALIDATE |
|
2904 malloc_rtree_set(chunk_rtree, (uintptr_t)chunk, NULL); |
|
2905 #endif |
|
2906 |
|
2907 chunk_dealloc_mmap(chunk, size); |
|
2908 } |
|
2909 |
|
2910 /* |
|
2911 * End chunk management functions. |
|
2912 */ |
|
2913 /******************************************************************************/ |
|
2914 /* |
|
2915 * Begin arena. |
|
2916 */ |
|
2917 |
|
2918 /* |
|
2919 * Choose an arena based on a per-thread value (fast-path code, calls slow-path |
|
2920 * code if necessary). |
|
2921 */ |
|
2922 static inline arena_t * |
|
2923 choose_arena(void) |
|
2924 { |
|
2925 arena_t *ret; |
|
2926 |
|
2927 /* |
|
2928 * We can only use TLS if this is a PIC library, since for the static |
|
2929 * library version, libc's malloc is used by TLS allocation, which |
|
2930 * introduces a bootstrapping issue. |
|
2931 */ |
|
2932 #ifndef NO_TLS |
|
2933 if (isthreaded == false) { |
|
2934 /* Avoid the overhead of TLS for single-threaded operation. */ |
|
2935 return (arenas[0]); |
|
2936 } |
|
2937 |
|
2938 # ifdef MOZ_MEMORY_WINDOWS |
|
2939 ret = (arena_t*)TlsGetValue(tlsIndex); |
|
2940 # else |
|
2941 ret = arenas_map; |
|
2942 # endif |
|
2943 |
|
2944 if (ret == NULL) { |
|
2945 ret = choose_arena_hard(); |
|
2946 RELEASE_ASSERT(ret != NULL); |
|
2947 } |
|
2948 #else |
|
2949 if (isthreaded && narenas > 1) { |
|
2950 unsigned long ind; |
|
2951 |
|
2952 /* |
|
2953 * Hash _pthread_self() to one of the arenas. There is a prime |
|
2954 * number of arenas, so this has a reasonable chance of |
|
2955 * working. Even so, the hashing can be easily thwarted by |
|
2956 * inconvenient _pthread_self() values. Without specific |
|
2957 * knowledge of how _pthread_self() calculates values, we can't |
|
2958 * easily do much better than this. |
|
2959 */ |
|
2960 ind = (unsigned long) _pthread_self() % narenas; |
|
2961 |
|
2962 /* |
|
2963 * Optimistially assume that arenas[ind] has been initialized. |
|
2964 * At worst, we find out that some other thread has already |
|
2965 * done so, after acquiring the lock in preparation. Note that |
|
2966 * this lazy locking also has the effect of lazily forcing |
|
2967 * cache coherency; without the lock acquisition, there's no |
|
2968 * guarantee that modification of arenas[ind] by another thread |
|
2969 * would be seen on this CPU for an arbitrary amount of time. |
|
2970 * |
|
2971 * In general, this approach to modifying a synchronized value |
|
2972 * isn't a good idea, but in this case we only ever modify the |
|
2973 * value once, so things work out well. |
|
2974 */ |
|
2975 ret = arenas[ind]; |
|
2976 if (ret == NULL) { |
|
2977 /* |
|
2978 * Avoid races with another thread that may have already |
|
2979 * initialized arenas[ind]. |
|
2980 */ |
|
2981 malloc_spin_lock(&arenas_lock); |
|
2982 if (arenas[ind] == NULL) |
|
2983 ret = arenas_extend((unsigned)ind); |
|
2984 else |
|
2985 ret = arenas[ind]; |
|
2986 malloc_spin_unlock(&arenas_lock); |
|
2987 } |
|
2988 } else |
|
2989 ret = arenas[0]; |
|
2990 #endif |
|
2991 |
|
2992 RELEASE_ASSERT(ret != NULL); |
|
2993 return (ret); |
|
2994 } |
|
2995 |
|
2996 #ifndef NO_TLS |
|
2997 /* |
|
2998 * Choose an arena based on a per-thread value (slow-path code only, called |
|
2999 * only by choose_arena()). |
|
3000 */ |
|
3001 static arena_t * |
|
3002 choose_arena_hard(void) |
|
3003 { |
|
3004 arena_t *ret; |
|
3005 |
|
3006 assert(isthreaded); |
|
3007 |
|
3008 #ifdef MALLOC_BALANCE |
|
3009 /* Seed the PRNG used for arena load balancing. */ |
|
3010 SPRN(balance, (uint32_t)(uintptr_t)(_pthread_self())); |
|
3011 #endif |
|
3012 |
|
3013 if (narenas > 1) { |
|
3014 #ifdef MALLOC_BALANCE |
|
3015 unsigned ind; |
|
3016 |
|
3017 ind = PRN(balance, narenas_2pow); |
|
3018 if ((ret = arenas[ind]) == NULL) { |
|
3019 malloc_spin_lock(&arenas_lock); |
|
3020 if ((ret = arenas[ind]) == NULL) |
|
3021 ret = arenas_extend(ind); |
|
3022 malloc_spin_unlock(&arenas_lock); |
|
3023 } |
|
3024 #else |
|
3025 malloc_spin_lock(&arenas_lock); |
|
3026 if ((ret = arenas[next_arena]) == NULL) |
|
3027 ret = arenas_extend(next_arena); |
|
3028 next_arena = (next_arena + 1) % narenas; |
|
3029 malloc_spin_unlock(&arenas_lock); |
|
3030 #endif |
|
3031 } else |
|
3032 ret = arenas[0]; |
|
3033 |
|
3034 #ifdef MOZ_MEMORY_WINDOWS |
|
3035 TlsSetValue(tlsIndex, ret); |
|
3036 #else |
|
3037 arenas_map = ret; |
|
3038 #endif |
|
3039 |
|
3040 return (ret); |
|
3041 } |
|
3042 #endif |
|
3043 |
|
3044 static inline int |
|
3045 arena_chunk_comp(arena_chunk_t *a, arena_chunk_t *b) |
|
3046 { |
|
3047 uintptr_t a_chunk = (uintptr_t)a; |
|
3048 uintptr_t b_chunk = (uintptr_t)b; |
|
3049 |
|
3050 assert(a != NULL); |
|
3051 assert(b != NULL); |
|
3052 |
|
3053 return ((a_chunk > b_chunk) - (a_chunk < b_chunk)); |
|
3054 } |
|
3055 |
|
3056 /* Wrap red-black tree macros in functions. */ |
|
3057 rb_wrap(static, arena_chunk_tree_dirty_, arena_chunk_tree_t, |
|
3058 arena_chunk_t, link_dirty, arena_chunk_comp) |
|
3059 |
|
3060 static inline int |
|
3061 arena_run_comp(arena_chunk_map_t *a, arena_chunk_map_t *b) |
|
3062 { |
|
3063 uintptr_t a_mapelm = (uintptr_t)a; |
|
3064 uintptr_t b_mapelm = (uintptr_t)b; |
|
3065 |
|
3066 assert(a != NULL); |
|
3067 assert(b != NULL); |
|
3068 |
|
3069 return ((a_mapelm > b_mapelm) - (a_mapelm < b_mapelm)); |
|
3070 } |
|
3071 |
|
3072 /* Wrap red-black tree macros in functions. */ |
|
3073 rb_wrap(static, arena_run_tree_, arena_run_tree_t, arena_chunk_map_t, link, |
|
3074 arena_run_comp) |
|
3075 |
|
3076 static inline int |
|
3077 arena_avail_comp(arena_chunk_map_t *a, arena_chunk_map_t *b) |
|
3078 { |
|
3079 int ret; |
|
3080 size_t a_size = a->bits & ~pagesize_mask; |
|
3081 size_t b_size = b->bits & ~pagesize_mask; |
|
3082 |
|
3083 ret = (a_size > b_size) - (a_size < b_size); |
|
3084 if (ret == 0) { |
|
3085 uintptr_t a_mapelm, b_mapelm; |
|
3086 |
|
3087 if ((a->bits & CHUNK_MAP_KEY) == 0) |
|
3088 a_mapelm = (uintptr_t)a; |
|
3089 else { |
|
3090 /* |
|
3091 * Treat keys as though they are lower than anything |
|
3092 * else. |
|
3093 */ |
|
3094 a_mapelm = 0; |
|
3095 } |
|
3096 b_mapelm = (uintptr_t)b; |
|
3097 |
|
3098 ret = (a_mapelm > b_mapelm) - (a_mapelm < b_mapelm); |
|
3099 } |
|
3100 |
|
3101 return (ret); |
|
3102 } |
|
3103 |
|
3104 /* Wrap red-black tree macros in functions. */ |
|
3105 rb_wrap(static, arena_avail_tree_, arena_avail_tree_t, arena_chunk_map_t, link, |
|
3106 arena_avail_comp) |
|
3107 |
|
3108 static inline void * |
|
3109 arena_run_reg_alloc(arena_run_t *run, arena_bin_t *bin) |
|
3110 { |
|
3111 void *ret; |
|
3112 unsigned i, mask, bit, regind; |
|
3113 |
|
3114 assert(run->magic == ARENA_RUN_MAGIC); |
|
3115 assert(run->regs_minelm < bin->regs_mask_nelms); |
|
3116 |
|
3117 /* |
|
3118 * Move the first check outside the loop, so that run->regs_minelm can |
|
3119 * be updated unconditionally, without the possibility of updating it |
|
3120 * multiple times. |
|
3121 */ |
|
3122 i = run->regs_minelm; |
|
3123 mask = run->regs_mask[i]; |
|
3124 if (mask != 0) { |
|
3125 /* Usable allocation found. */ |
|
3126 bit = ffs((int)mask) - 1; |
|
3127 |
|
3128 regind = ((i << (SIZEOF_INT_2POW + 3)) + bit); |
|
3129 assert(regind < bin->nregs); |
|
3130 ret = (void *)(((uintptr_t)run) + bin->reg0_offset |
|
3131 + (bin->reg_size * regind)); |
|
3132 |
|
3133 /* Clear bit. */ |
|
3134 mask ^= (1U << bit); |
|
3135 run->regs_mask[i] = mask; |
|
3136 |
|
3137 return (ret); |
|
3138 } |
|
3139 |
|
3140 for (i++; i < bin->regs_mask_nelms; i++) { |
|
3141 mask = run->regs_mask[i]; |
|
3142 if (mask != 0) { |
|
3143 /* Usable allocation found. */ |
|
3144 bit = ffs((int)mask) - 1; |
|
3145 |
|
3146 regind = ((i << (SIZEOF_INT_2POW + 3)) + bit); |
|
3147 assert(regind < bin->nregs); |
|
3148 ret = (void *)(((uintptr_t)run) + bin->reg0_offset |
|
3149 + (bin->reg_size * regind)); |
|
3150 |
|
3151 /* Clear bit. */ |
|
3152 mask ^= (1U << bit); |
|
3153 run->regs_mask[i] = mask; |
|
3154 |
|
3155 /* |
|
3156 * Make a note that nothing before this element |
|
3157 * contains a free region. |
|
3158 */ |
|
3159 run->regs_minelm = i; /* Low payoff: + (mask == 0); */ |
|
3160 |
|
3161 return (ret); |
|
3162 } |
|
3163 } |
|
3164 /* Not reached. */ |
|
3165 RELEASE_ASSERT(0); |
|
3166 return (NULL); |
|
3167 } |
|
3168 |
|
3169 static inline void |
|
3170 arena_run_reg_dalloc(arena_run_t *run, arena_bin_t *bin, void *ptr, size_t size) |
|
3171 { |
|
3172 /* |
|
3173 * To divide by a number D that is not a power of two we multiply |
|
3174 * by (2^21 / D) and then right shift by 21 positions. |
|
3175 * |
|
3176 * X / D |
|
3177 * |
|
3178 * becomes |
|
3179 * |
|
3180 * (X * size_invs[(D >> QUANTUM_2POW_MIN) - 3]) >> SIZE_INV_SHIFT |
|
3181 */ |
|
3182 #define SIZE_INV_SHIFT 21 |
|
3183 #define SIZE_INV(s) (((1U << SIZE_INV_SHIFT) / (s << QUANTUM_2POW_MIN)) + 1) |
|
3184 static const unsigned size_invs[] = { |
|
3185 SIZE_INV(3), |
|
3186 SIZE_INV(4), SIZE_INV(5), SIZE_INV(6), SIZE_INV(7), |
|
3187 SIZE_INV(8), SIZE_INV(9), SIZE_INV(10), SIZE_INV(11), |
|
3188 SIZE_INV(12),SIZE_INV(13), SIZE_INV(14), SIZE_INV(15), |
|
3189 SIZE_INV(16),SIZE_INV(17), SIZE_INV(18), SIZE_INV(19), |
|
3190 SIZE_INV(20),SIZE_INV(21), SIZE_INV(22), SIZE_INV(23), |
|
3191 SIZE_INV(24),SIZE_INV(25), SIZE_INV(26), SIZE_INV(27), |
|
3192 SIZE_INV(28),SIZE_INV(29), SIZE_INV(30), SIZE_INV(31) |
|
3193 #if (QUANTUM_2POW_MIN < 4) |
|
3194 , |
|
3195 SIZE_INV(32), SIZE_INV(33), SIZE_INV(34), SIZE_INV(35), |
|
3196 SIZE_INV(36), SIZE_INV(37), SIZE_INV(38), SIZE_INV(39), |
|
3197 SIZE_INV(40), SIZE_INV(41), SIZE_INV(42), SIZE_INV(43), |
|
3198 SIZE_INV(44), SIZE_INV(45), SIZE_INV(46), SIZE_INV(47), |
|
3199 SIZE_INV(48), SIZE_INV(49), SIZE_INV(50), SIZE_INV(51), |
|
3200 SIZE_INV(52), SIZE_INV(53), SIZE_INV(54), SIZE_INV(55), |
|
3201 SIZE_INV(56), SIZE_INV(57), SIZE_INV(58), SIZE_INV(59), |
|
3202 SIZE_INV(60), SIZE_INV(61), SIZE_INV(62), SIZE_INV(63) |
|
3203 #endif |
|
3204 }; |
|
3205 unsigned diff, regind, elm, bit; |
|
3206 |
|
3207 assert(run->magic == ARENA_RUN_MAGIC); |
|
3208 assert(((sizeof(size_invs)) / sizeof(unsigned)) + 3 |
|
3209 >= (SMALL_MAX_DEFAULT >> QUANTUM_2POW_MIN)); |
|
3210 |
|
3211 /* |
|
3212 * Avoid doing division with a variable divisor if possible. Using |
|
3213 * actual division here can reduce allocator throughput by over 20%! |
|
3214 */ |
|
3215 diff = (unsigned)((uintptr_t)ptr - (uintptr_t)run - bin->reg0_offset); |
|
3216 if ((size & (size - 1)) == 0) { |
|
3217 /* |
|
3218 * log2_table allows fast division of a power of two in the |
|
3219 * [1..128] range. |
|
3220 * |
|
3221 * (x / divisor) becomes (x >> log2_table[divisor - 1]). |
|
3222 */ |
|
3223 static const unsigned char log2_table[] = { |
|
3224 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, |
|
3225 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, |
|
3226 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
3227 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, |
|
3228 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
3229 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
3230 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
3231 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7 |
|
3232 }; |
|
3233 |
|
3234 if (size <= 128) |
|
3235 regind = (diff >> log2_table[size - 1]); |
|
3236 else if (size <= 32768) |
|
3237 regind = diff >> (8 + log2_table[(size >> 8) - 1]); |
|
3238 else { |
|
3239 /* |
|
3240 * The run size is too large for us to use the lookup |
|
3241 * table. Use real division. |
|
3242 */ |
|
3243 regind = diff / size; |
|
3244 } |
|
3245 } else if (size <= ((sizeof(size_invs) / sizeof(unsigned)) |
|
3246 << QUANTUM_2POW_MIN) + 2) { |
|
3247 regind = size_invs[(size >> QUANTUM_2POW_MIN) - 3] * diff; |
|
3248 regind >>= SIZE_INV_SHIFT; |
|
3249 } else { |
|
3250 /* |
|
3251 * size_invs isn't large enough to handle this size class, so |
|
3252 * calculate regind using actual division. This only happens |
|
3253 * if the user increases small_max via the 'S' runtime |
|
3254 * configuration option. |
|
3255 */ |
|
3256 regind = diff / size; |
|
3257 }; |
|
3258 RELEASE_ASSERT(diff == regind * size); |
|
3259 RELEASE_ASSERT(regind < bin->nregs); |
|
3260 |
|
3261 elm = regind >> (SIZEOF_INT_2POW + 3); |
|
3262 if (elm < run->regs_minelm) |
|
3263 run->regs_minelm = elm; |
|
3264 bit = regind - (elm << (SIZEOF_INT_2POW + 3)); |
|
3265 RELEASE_ASSERT((run->regs_mask[elm] & (1U << bit)) == 0); |
|
3266 run->regs_mask[elm] |= (1U << bit); |
|
3267 #undef SIZE_INV |
|
3268 #undef SIZE_INV_SHIFT |
|
3269 } |
|
3270 |
|
3271 static void |
|
3272 arena_run_split(arena_t *arena, arena_run_t *run, size_t size, bool large, |
|
3273 bool zero) |
|
3274 { |
|
3275 arena_chunk_t *chunk; |
|
3276 size_t old_ndirty, run_ind, total_pages, need_pages, rem_pages, i; |
|
3277 |
|
3278 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); |
|
3279 old_ndirty = chunk->ndirty; |
|
3280 run_ind = (unsigned)(((uintptr_t)run - (uintptr_t)chunk) |
|
3281 >> pagesize_2pow); |
|
3282 total_pages = (chunk->map[run_ind].bits & ~pagesize_mask) >> |
|
3283 pagesize_2pow; |
|
3284 need_pages = (size >> pagesize_2pow); |
|
3285 assert(need_pages > 0); |
|
3286 assert(need_pages <= total_pages); |
|
3287 rem_pages = total_pages - need_pages; |
|
3288 |
|
3289 arena_avail_tree_remove(&arena->runs_avail, &chunk->map[run_ind]); |
|
3290 |
|
3291 /* Keep track of trailing unused pages for later use. */ |
|
3292 if (rem_pages > 0) { |
|
3293 chunk->map[run_ind+need_pages].bits = (rem_pages << |
|
3294 pagesize_2pow) | (chunk->map[run_ind+need_pages].bits & |
|
3295 pagesize_mask); |
|
3296 chunk->map[run_ind+total_pages-1].bits = (rem_pages << |
|
3297 pagesize_2pow) | (chunk->map[run_ind+total_pages-1].bits & |
|
3298 pagesize_mask); |
|
3299 arena_avail_tree_insert(&arena->runs_avail, |
|
3300 &chunk->map[run_ind+need_pages]); |
|
3301 } |
|
3302 |
|
3303 for (i = 0; i < need_pages; i++) { |
|
3304 #if defined(MALLOC_DECOMMIT) || defined(MALLOC_STATS) || defined(MALLOC_DOUBLE_PURGE) |
|
3305 /* |
|
3306 * Commit decommitted pages if necessary. If a decommitted |
|
3307 * page is encountered, commit all needed adjacent decommitted |
|
3308 * pages in one operation, in order to reduce system call |
|
3309 * overhead. |
|
3310 */ |
|
3311 if (chunk->map[run_ind + i].bits & CHUNK_MAP_MADVISED_OR_DECOMMITTED) { |
|
3312 size_t j; |
|
3313 |
|
3314 /* |
|
3315 * Advance i+j to just past the index of the last page |
|
3316 * to commit. Clear CHUNK_MAP_DECOMMITTED and |
|
3317 * CHUNK_MAP_MADVISED along the way. |
|
3318 */ |
|
3319 for (j = 0; i + j < need_pages && (chunk->map[run_ind + |
|
3320 i + j].bits & CHUNK_MAP_MADVISED_OR_DECOMMITTED); j++) { |
|
3321 /* DECOMMITTED and MADVISED are mutually exclusive. */ |
|
3322 assert(!(chunk->map[run_ind + i + j].bits & CHUNK_MAP_DECOMMITTED && |
|
3323 chunk->map[run_ind + i + j].bits & CHUNK_MAP_MADVISED)); |
|
3324 |
|
3325 chunk->map[run_ind + i + j].bits &= |
|
3326 ~CHUNK_MAP_MADVISED_OR_DECOMMITTED; |
|
3327 } |
|
3328 |
|
3329 # ifdef MALLOC_DECOMMIT |
|
3330 pages_commit((void *)((uintptr_t)chunk + ((run_ind + i) |
|
3331 << pagesize_2pow)), (j << pagesize_2pow)); |
|
3332 # ifdef MALLOC_STATS |
|
3333 arena->stats.ncommit++; |
|
3334 # endif |
|
3335 # endif |
|
3336 |
|
3337 # ifdef MALLOC_STATS |
|
3338 arena->stats.committed += j; |
|
3339 # endif |
|
3340 |
|
3341 # ifndef MALLOC_DECOMMIT |
|
3342 } |
|
3343 # else |
|
3344 } else /* No need to zero since commit zeros. */ |
|
3345 # endif |
|
3346 |
|
3347 #endif |
|
3348 |
|
3349 /* Zero if necessary. */ |
|
3350 if (zero) { |
|
3351 if ((chunk->map[run_ind + i].bits & CHUNK_MAP_ZEROED) |
|
3352 == 0) { |
|
3353 VALGRIND_MALLOCLIKE_BLOCK((void *)((uintptr_t) |
|
3354 chunk + ((run_ind + i) << pagesize_2pow)), |
|
3355 pagesize, 0, false); |
|
3356 memset((void *)((uintptr_t)chunk + ((run_ind |
|
3357 + i) << pagesize_2pow)), 0, pagesize); |
|
3358 VALGRIND_FREELIKE_BLOCK((void *)((uintptr_t) |
|
3359 chunk + ((run_ind + i) << pagesize_2pow)), |
|
3360 0); |
|
3361 /* CHUNK_MAP_ZEROED is cleared below. */ |
|
3362 } |
|
3363 } |
|
3364 |
|
3365 /* Update dirty page accounting. */ |
|
3366 if (chunk->map[run_ind + i].bits & CHUNK_MAP_DIRTY) { |
|
3367 chunk->ndirty--; |
|
3368 arena->ndirty--; |
|
3369 /* CHUNK_MAP_DIRTY is cleared below. */ |
|
3370 } |
|
3371 |
|
3372 /* Initialize the chunk map. */ |
|
3373 if (large) { |
|
3374 chunk->map[run_ind + i].bits = CHUNK_MAP_LARGE |
|
3375 | CHUNK_MAP_ALLOCATED; |
|
3376 } else { |
|
3377 chunk->map[run_ind + i].bits = (size_t)run |
|
3378 | CHUNK_MAP_ALLOCATED; |
|
3379 } |
|
3380 } |
|
3381 |
|
3382 /* |
|
3383 * Set the run size only in the first element for large runs. This is |
|
3384 * primarily a debugging aid, since the lack of size info for trailing |
|
3385 * pages only matters if the application tries to operate on an |
|
3386 * interior pointer. |
|
3387 */ |
|
3388 if (large) |
|
3389 chunk->map[run_ind].bits |= size; |
|
3390 |
|
3391 if (chunk->ndirty == 0 && old_ndirty > 0) |
|
3392 arena_chunk_tree_dirty_remove(&arena->chunks_dirty, chunk); |
|
3393 } |
|
3394 |
|
3395 static void |
|
3396 arena_chunk_init(arena_t *arena, arena_chunk_t *chunk) |
|
3397 { |
|
3398 arena_run_t *run; |
|
3399 size_t i; |
|
3400 |
|
3401 VALGRIND_MALLOCLIKE_BLOCK(chunk, (arena_chunk_header_npages << |
|
3402 pagesize_2pow), 0, false); |
|
3403 #ifdef MALLOC_STATS |
|
3404 arena->stats.mapped += chunksize; |
|
3405 #endif |
|
3406 |
|
3407 chunk->arena = arena; |
|
3408 |
|
3409 /* |
|
3410 * Claim that no pages are in use, since the header is merely overhead. |
|
3411 */ |
|
3412 chunk->ndirty = 0; |
|
3413 |
|
3414 /* Initialize the map to contain one maximal free untouched run. */ |
|
3415 run = (arena_run_t *)((uintptr_t)chunk + (arena_chunk_header_npages << |
|
3416 pagesize_2pow)); |
|
3417 for (i = 0; i < arena_chunk_header_npages; i++) |
|
3418 chunk->map[i].bits = 0; |
|
3419 chunk->map[i].bits = arena_maxclass | CHUNK_MAP_DECOMMITTED | CHUNK_MAP_ZEROED; |
|
3420 for (i++; i < chunk_npages-1; i++) { |
|
3421 chunk->map[i].bits = CHUNK_MAP_DECOMMITTED | CHUNK_MAP_ZEROED; |
|
3422 } |
|
3423 chunk->map[chunk_npages-1].bits = arena_maxclass | CHUNK_MAP_DECOMMITTED | CHUNK_MAP_ZEROED; |
|
3424 |
|
3425 #ifdef MALLOC_DECOMMIT |
|
3426 /* |
|
3427 * Start out decommitted, in order to force a closer correspondence |
|
3428 * between dirty pages and committed untouched pages. |
|
3429 */ |
|
3430 pages_decommit(run, arena_maxclass); |
|
3431 # ifdef MALLOC_STATS |
|
3432 arena->stats.ndecommit++; |
|
3433 arena->stats.decommitted += (chunk_npages - arena_chunk_header_npages); |
|
3434 # endif |
|
3435 #endif |
|
3436 #ifdef MALLOC_STATS |
|
3437 arena->stats.committed += arena_chunk_header_npages; |
|
3438 #endif |
|
3439 |
|
3440 /* Insert the run into the runs_avail tree. */ |
|
3441 arena_avail_tree_insert(&arena->runs_avail, |
|
3442 &chunk->map[arena_chunk_header_npages]); |
|
3443 |
|
3444 #ifdef MALLOC_DOUBLE_PURGE |
|
3445 LinkedList_Init(&chunk->chunks_madvised_elem); |
|
3446 #endif |
|
3447 } |
|
3448 |
|
3449 static void |
|
3450 arena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk) |
|
3451 { |
|
3452 |
|
3453 if (arena->spare != NULL) { |
|
3454 if (arena->spare->ndirty > 0) { |
|
3455 arena_chunk_tree_dirty_remove( |
|
3456 &chunk->arena->chunks_dirty, arena->spare); |
|
3457 arena->ndirty -= arena->spare->ndirty; |
|
3458 #ifdef MALLOC_STATS |
|
3459 arena->stats.committed -= arena->spare->ndirty; |
|
3460 #endif |
|
3461 } |
|
3462 |
|
3463 #ifdef MALLOC_DOUBLE_PURGE |
|
3464 /* This is safe to do even if arena->spare is not in the list. */ |
|
3465 LinkedList_Remove(&arena->spare->chunks_madvised_elem); |
|
3466 #endif |
|
3467 |
|
3468 VALGRIND_FREELIKE_BLOCK(arena->spare, 0); |
|
3469 chunk_dealloc((void *)arena->spare, chunksize); |
|
3470 #ifdef MALLOC_STATS |
|
3471 arena->stats.mapped -= chunksize; |
|
3472 arena->stats.committed -= arena_chunk_header_npages; |
|
3473 #endif |
|
3474 } |
|
3475 |
|
3476 /* |
|
3477 * Remove run from runs_avail, so that the arena does not use it. |
|
3478 * Dirty page flushing only uses the chunks_dirty tree, so leaving this |
|
3479 * chunk in the chunks_* trees is sufficient for that purpose. |
|
3480 */ |
|
3481 arena_avail_tree_remove(&arena->runs_avail, |
|
3482 &chunk->map[arena_chunk_header_npages]); |
|
3483 |
|
3484 arena->spare = chunk; |
|
3485 } |
|
3486 |
|
3487 static arena_run_t * |
|
3488 arena_run_alloc(arena_t *arena, arena_bin_t *bin, size_t size, bool large, |
|
3489 bool zero) |
|
3490 { |
|
3491 arena_run_t *run; |
|
3492 arena_chunk_map_t *mapelm, key; |
|
3493 |
|
3494 assert(size <= arena_maxclass); |
|
3495 assert((size & pagesize_mask) == 0); |
|
3496 |
|
3497 /* Search the arena's chunks for the lowest best fit. */ |
|
3498 key.bits = size | CHUNK_MAP_KEY; |
|
3499 mapelm = arena_avail_tree_nsearch(&arena->runs_avail, &key); |
|
3500 if (mapelm != NULL) { |
|
3501 arena_chunk_t *chunk = |
|
3502 (arena_chunk_t*)CHUNK_ADDR2BASE(mapelm); |
|
3503 size_t pageind = ((uintptr_t)mapelm - |
|
3504 (uintptr_t)chunk->map) / |
|
3505 sizeof(arena_chunk_map_t); |
|
3506 |
|
3507 run = (arena_run_t *)((uintptr_t)chunk + (pageind |
|
3508 << pagesize_2pow)); |
|
3509 arena_run_split(arena, run, size, large, zero); |
|
3510 return (run); |
|
3511 } |
|
3512 |
|
3513 if (arena->spare != NULL) { |
|
3514 /* Use the spare. */ |
|
3515 arena_chunk_t *chunk = arena->spare; |
|
3516 arena->spare = NULL; |
|
3517 run = (arena_run_t *)((uintptr_t)chunk + |
|
3518 (arena_chunk_header_npages << pagesize_2pow)); |
|
3519 /* Insert the run into the runs_avail tree. */ |
|
3520 arena_avail_tree_insert(&arena->runs_avail, |
|
3521 &chunk->map[arena_chunk_header_npages]); |
|
3522 arena_run_split(arena, run, size, large, zero); |
|
3523 return (run); |
|
3524 } |
|
3525 |
|
3526 /* |
|
3527 * No usable runs. Create a new chunk from which to allocate |
|
3528 * the run. |
|
3529 */ |
|
3530 { |
|
3531 arena_chunk_t *chunk = (arena_chunk_t *) |
|
3532 chunk_alloc(chunksize, true, true); |
|
3533 if (chunk == NULL) |
|
3534 return (NULL); |
|
3535 |
|
3536 arena_chunk_init(arena, chunk); |
|
3537 run = (arena_run_t *)((uintptr_t)chunk + |
|
3538 (arena_chunk_header_npages << pagesize_2pow)); |
|
3539 } |
|
3540 /* Update page map. */ |
|
3541 arena_run_split(arena, run, size, large, zero); |
|
3542 return (run); |
|
3543 } |
|
3544 |
|
3545 static void |
|
3546 arena_purge(arena_t *arena, bool all) |
|
3547 { |
|
3548 arena_chunk_t *chunk; |
|
3549 size_t i, npages; |
|
3550 /* If all is set purge all dirty pages. */ |
|
3551 size_t dirty_max = all ? 1 : opt_dirty_max; |
|
3552 #ifdef MALLOC_DEBUG |
|
3553 size_t ndirty = 0; |
|
3554 rb_foreach_begin(arena_chunk_t, link_dirty, &arena->chunks_dirty, |
|
3555 chunk) { |
|
3556 ndirty += chunk->ndirty; |
|
3557 } rb_foreach_end(arena_chunk_t, link_dirty, &arena->chunks_dirty, chunk) |
|
3558 assert(ndirty == arena->ndirty); |
|
3559 #endif |
|
3560 RELEASE_ASSERT(all || (arena->ndirty > opt_dirty_max)); |
|
3561 |
|
3562 #ifdef MALLOC_STATS |
|
3563 arena->stats.npurge++; |
|
3564 #endif |
|
3565 |
|
3566 /* |
|
3567 * Iterate downward through chunks until enough dirty memory has been |
|
3568 * purged. Terminate as soon as possible in order to minimize the |
|
3569 * number of system calls, even if a chunk has only been partially |
|
3570 * purged. |
|
3571 */ |
|
3572 while (arena->ndirty > (dirty_max >> 1)) { |
|
3573 #ifdef MALLOC_DOUBLE_PURGE |
|
3574 bool madvised = false; |
|
3575 #endif |
|
3576 chunk = arena_chunk_tree_dirty_last(&arena->chunks_dirty); |
|
3577 RELEASE_ASSERT(chunk != NULL); |
|
3578 |
|
3579 for (i = chunk_npages - 1; chunk->ndirty > 0; i--) { |
|
3580 RELEASE_ASSERT(i >= arena_chunk_header_npages); |
|
3581 |
|
3582 if (chunk->map[i].bits & CHUNK_MAP_DIRTY) { |
|
3583 #ifdef MALLOC_DECOMMIT |
|
3584 const size_t free_operation = CHUNK_MAP_DECOMMITTED; |
|
3585 #else |
|
3586 const size_t free_operation = CHUNK_MAP_MADVISED; |
|
3587 #endif |
|
3588 assert((chunk->map[i].bits & |
|
3589 CHUNK_MAP_MADVISED_OR_DECOMMITTED) == 0); |
|
3590 chunk->map[i].bits ^= free_operation | CHUNK_MAP_DIRTY; |
|
3591 /* Find adjacent dirty run(s). */ |
|
3592 for (npages = 1; |
|
3593 i > arena_chunk_header_npages && |
|
3594 (chunk->map[i - 1].bits & CHUNK_MAP_DIRTY); |
|
3595 npages++) { |
|
3596 i--; |
|
3597 assert((chunk->map[i].bits & |
|
3598 CHUNK_MAP_MADVISED_OR_DECOMMITTED) == 0); |
|
3599 chunk->map[i].bits ^= free_operation | CHUNK_MAP_DIRTY; |
|
3600 } |
|
3601 chunk->ndirty -= npages; |
|
3602 arena->ndirty -= npages; |
|
3603 |
|
3604 #ifdef MALLOC_DECOMMIT |
|
3605 pages_decommit((void *)((uintptr_t) |
|
3606 chunk + (i << pagesize_2pow)), |
|
3607 (npages << pagesize_2pow)); |
|
3608 # ifdef MALLOC_STATS |
|
3609 arena->stats.ndecommit++; |
|
3610 arena->stats.decommitted += npages; |
|
3611 # endif |
|
3612 #endif |
|
3613 #ifdef MALLOC_STATS |
|
3614 arena->stats.committed -= npages; |
|
3615 #endif |
|
3616 |
|
3617 #ifndef MALLOC_DECOMMIT |
|
3618 madvise((void *)((uintptr_t)chunk + (i << |
|
3619 pagesize_2pow)), (npages << pagesize_2pow), |
|
3620 MADV_FREE); |
|
3621 # ifdef MALLOC_DOUBLE_PURGE |
|
3622 madvised = true; |
|
3623 # endif |
|
3624 #endif |
|
3625 #ifdef MALLOC_STATS |
|
3626 arena->stats.nmadvise++; |
|
3627 arena->stats.purged += npages; |
|
3628 #endif |
|
3629 if (arena->ndirty <= (dirty_max >> 1)) |
|
3630 break; |
|
3631 } |
|
3632 } |
|
3633 |
|
3634 if (chunk->ndirty == 0) { |
|
3635 arena_chunk_tree_dirty_remove(&arena->chunks_dirty, |
|
3636 chunk); |
|
3637 } |
|
3638 #ifdef MALLOC_DOUBLE_PURGE |
|
3639 if (madvised) { |
|
3640 /* The chunk might already be in the list, but this |
|
3641 * makes sure it's at the front. */ |
|
3642 LinkedList_Remove(&chunk->chunks_madvised_elem); |
|
3643 LinkedList_InsertHead(&arena->chunks_madvised, &chunk->chunks_madvised_elem); |
|
3644 } |
|
3645 #endif |
|
3646 } |
|
3647 } |
|
3648 |
|
3649 static void |
|
3650 arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty) |
|
3651 { |
|
3652 arena_chunk_t *chunk; |
|
3653 size_t size, run_ind, run_pages; |
|
3654 |
|
3655 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); |
|
3656 run_ind = (size_t)(((uintptr_t)run - (uintptr_t)chunk) |
|
3657 >> pagesize_2pow); |
|
3658 RELEASE_ASSERT(run_ind >= arena_chunk_header_npages); |
|
3659 RELEASE_ASSERT(run_ind < chunk_npages); |
|
3660 if ((chunk->map[run_ind].bits & CHUNK_MAP_LARGE) != 0) |
|
3661 size = chunk->map[run_ind].bits & ~pagesize_mask; |
|
3662 else |
|
3663 size = run->bin->run_size; |
|
3664 run_pages = (size >> pagesize_2pow); |
|
3665 |
|
3666 /* Mark pages as unallocated in the chunk map. */ |
|
3667 if (dirty) { |
|
3668 size_t i; |
|
3669 |
|
3670 for (i = 0; i < run_pages; i++) { |
|
3671 RELEASE_ASSERT((chunk->map[run_ind + i].bits & CHUNK_MAP_DIRTY) |
|
3672 == 0); |
|
3673 chunk->map[run_ind + i].bits = CHUNK_MAP_DIRTY; |
|
3674 } |
|
3675 |
|
3676 if (chunk->ndirty == 0) { |
|
3677 arena_chunk_tree_dirty_insert(&arena->chunks_dirty, |
|
3678 chunk); |
|
3679 } |
|
3680 chunk->ndirty += run_pages; |
|
3681 arena->ndirty += run_pages; |
|
3682 } else { |
|
3683 size_t i; |
|
3684 |
|
3685 for (i = 0; i < run_pages; i++) { |
|
3686 chunk->map[run_ind + i].bits &= ~(CHUNK_MAP_LARGE | |
|
3687 CHUNK_MAP_ALLOCATED); |
|
3688 } |
|
3689 } |
|
3690 chunk->map[run_ind].bits = size | (chunk->map[run_ind].bits & |
|
3691 pagesize_mask); |
|
3692 chunk->map[run_ind+run_pages-1].bits = size | |
|
3693 (chunk->map[run_ind+run_pages-1].bits & pagesize_mask); |
|
3694 |
|
3695 /* Try to coalesce forward. */ |
|
3696 if (run_ind + run_pages < chunk_npages && |
|
3697 (chunk->map[run_ind+run_pages].bits & CHUNK_MAP_ALLOCATED) == 0) { |
|
3698 size_t nrun_size = chunk->map[run_ind+run_pages].bits & |
|
3699 ~pagesize_mask; |
|
3700 |
|
3701 /* |
|
3702 * Remove successor from runs_avail; the coalesced run is |
|
3703 * inserted later. |
|
3704 */ |
|
3705 arena_avail_tree_remove(&arena->runs_avail, |
|
3706 &chunk->map[run_ind+run_pages]); |
|
3707 |
|
3708 size += nrun_size; |
|
3709 run_pages = size >> pagesize_2pow; |
|
3710 |
|
3711 RELEASE_ASSERT((chunk->map[run_ind+run_pages-1].bits & ~pagesize_mask) |
|
3712 == nrun_size); |
|
3713 chunk->map[run_ind].bits = size | (chunk->map[run_ind].bits & |
|
3714 pagesize_mask); |
|
3715 chunk->map[run_ind+run_pages-1].bits = size | |
|
3716 (chunk->map[run_ind+run_pages-1].bits & pagesize_mask); |
|
3717 } |
|
3718 |
|
3719 /* Try to coalesce backward. */ |
|
3720 if (run_ind > arena_chunk_header_npages && (chunk->map[run_ind-1].bits & |
|
3721 CHUNK_MAP_ALLOCATED) == 0) { |
|
3722 size_t prun_size = chunk->map[run_ind-1].bits & ~pagesize_mask; |
|
3723 |
|
3724 run_ind -= prun_size >> pagesize_2pow; |
|
3725 |
|
3726 /* |
|
3727 * Remove predecessor from runs_avail; the coalesced run is |
|
3728 * inserted later. |
|
3729 */ |
|
3730 arena_avail_tree_remove(&arena->runs_avail, |
|
3731 &chunk->map[run_ind]); |
|
3732 |
|
3733 size += prun_size; |
|
3734 run_pages = size >> pagesize_2pow; |
|
3735 |
|
3736 RELEASE_ASSERT((chunk->map[run_ind].bits & ~pagesize_mask) == |
|
3737 prun_size); |
|
3738 chunk->map[run_ind].bits = size | (chunk->map[run_ind].bits & |
|
3739 pagesize_mask); |
|
3740 chunk->map[run_ind+run_pages-1].bits = size | |
|
3741 (chunk->map[run_ind+run_pages-1].bits & pagesize_mask); |
|
3742 } |
|
3743 |
|
3744 /* Insert into runs_avail, now that coalescing is complete. */ |
|
3745 arena_avail_tree_insert(&arena->runs_avail, &chunk->map[run_ind]); |
|
3746 |
|
3747 /* Deallocate chunk if it is now completely unused. */ |
|
3748 if ((chunk->map[arena_chunk_header_npages].bits & (~pagesize_mask | |
|
3749 CHUNK_MAP_ALLOCATED)) == arena_maxclass) |
|
3750 arena_chunk_dealloc(arena, chunk); |
|
3751 |
|
3752 /* Enforce opt_dirty_max. */ |
|
3753 if (arena->ndirty > opt_dirty_max) |
|
3754 arena_purge(arena, false); |
|
3755 } |
|
3756 |
|
3757 static void |
|
3758 arena_run_trim_head(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, |
|
3759 size_t oldsize, size_t newsize) |
|
3760 { |
|
3761 size_t pageind = ((uintptr_t)run - (uintptr_t)chunk) >> pagesize_2pow; |
|
3762 size_t head_npages = (oldsize - newsize) >> pagesize_2pow; |
|
3763 |
|
3764 assert(oldsize > newsize); |
|
3765 |
|
3766 /* |
|
3767 * Update the chunk map so that arena_run_dalloc() can treat the |
|
3768 * leading run as separately allocated. |
|
3769 */ |
|
3770 chunk->map[pageind].bits = (oldsize - newsize) | CHUNK_MAP_LARGE | |
|
3771 CHUNK_MAP_ALLOCATED; |
|
3772 chunk->map[pageind+head_npages].bits = newsize | CHUNK_MAP_LARGE | |
|
3773 CHUNK_MAP_ALLOCATED; |
|
3774 |
|
3775 arena_run_dalloc(arena, run, false); |
|
3776 } |
|
3777 |
|
3778 static void |
|
3779 arena_run_trim_tail(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, |
|
3780 size_t oldsize, size_t newsize, bool dirty) |
|
3781 { |
|
3782 size_t pageind = ((uintptr_t)run - (uintptr_t)chunk) >> pagesize_2pow; |
|
3783 size_t npages = newsize >> pagesize_2pow; |
|
3784 |
|
3785 assert(oldsize > newsize); |
|
3786 |
|
3787 /* |
|
3788 * Update the chunk map so that arena_run_dalloc() can treat the |
|
3789 * trailing run as separately allocated. |
|
3790 */ |
|
3791 chunk->map[pageind].bits = newsize | CHUNK_MAP_LARGE | |
|
3792 CHUNK_MAP_ALLOCATED; |
|
3793 chunk->map[pageind+npages].bits = (oldsize - newsize) | CHUNK_MAP_LARGE |
|
3794 | CHUNK_MAP_ALLOCATED; |
|
3795 |
|
3796 arena_run_dalloc(arena, (arena_run_t *)((uintptr_t)run + newsize), |
|
3797 dirty); |
|
3798 } |
|
3799 |
|
3800 static arena_run_t * |
|
3801 arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin) |
|
3802 { |
|
3803 arena_chunk_map_t *mapelm; |
|
3804 arena_run_t *run; |
|
3805 unsigned i, remainder; |
|
3806 |
|
3807 /* Look for a usable run. */ |
|
3808 mapelm = arena_run_tree_first(&bin->runs); |
|
3809 if (mapelm != NULL) { |
|
3810 /* run is guaranteed to have available space. */ |
|
3811 arena_run_tree_remove(&bin->runs, mapelm); |
|
3812 run = (arena_run_t *)(mapelm->bits & ~pagesize_mask); |
|
3813 #ifdef MALLOC_STATS |
|
3814 bin->stats.reruns++; |
|
3815 #endif |
|
3816 return (run); |
|
3817 } |
|
3818 /* No existing runs have any space available. */ |
|
3819 |
|
3820 /* Allocate a new run. */ |
|
3821 run = arena_run_alloc(arena, bin, bin->run_size, false, false); |
|
3822 if (run == NULL) |
|
3823 return (NULL); |
|
3824 /* |
|
3825 * Don't initialize if a race in arena_run_alloc() allowed an existing |
|
3826 * run to become usable. |
|
3827 */ |
|
3828 if (run == bin->runcur) |
|
3829 return (run); |
|
3830 |
|
3831 VALGRIND_MALLOCLIKE_BLOCK(run, sizeof(arena_run_t) + (sizeof(unsigned) * |
|
3832 (bin->regs_mask_nelms - 1)), 0, false); |
|
3833 |
|
3834 /* Initialize run internals. */ |
|
3835 run->bin = bin; |
|
3836 |
|
3837 for (i = 0; i < bin->regs_mask_nelms - 1; i++) |
|
3838 run->regs_mask[i] = UINT_MAX; |
|
3839 remainder = bin->nregs & ((1U << (SIZEOF_INT_2POW + 3)) - 1); |
|
3840 if (remainder == 0) |
|
3841 run->regs_mask[i] = UINT_MAX; |
|
3842 else { |
|
3843 /* The last element has spare bits that need to be unset. */ |
|
3844 run->regs_mask[i] = (UINT_MAX >> ((1U << (SIZEOF_INT_2POW + 3)) |
|
3845 - remainder)); |
|
3846 } |
|
3847 |
|
3848 run->regs_minelm = 0; |
|
3849 |
|
3850 run->nfree = bin->nregs; |
|
3851 #if defined(MALLOC_DEBUG) || defined(MOZ_JEMALLOC_HARD_ASSERTS) |
|
3852 run->magic = ARENA_RUN_MAGIC; |
|
3853 #endif |
|
3854 |
|
3855 #ifdef MALLOC_STATS |
|
3856 bin->stats.nruns++; |
|
3857 bin->stats.curruns++; |
|
3858 if (bin->stats.curruns > bin->stats.highruns) |
|
3859 bin->stats.highruns = bin->stats.curruns; |
|
3860 #endif |
|
3861 return (run); |
|
3862 } |
|
3863 |
|
3864 /* bin->runcur must have space available before this function is called. */ |
|
3865 static inline void * |
|
3866 arena_bin_malloc_easy(arena_t *arena, arena_bin_t *bin, arena_run_t *run) |
|
3867 { |
|
3868 void *ret; |
|
3869 |
|
3870 RELEASE_ASSERT(run->magic == ARENA_RUN_MAGIC); |
|
3871 RELEASE_ASSERT(run->nfree > 0); |
|
3872 |
|
3873 ret = arena_run_reg_alloc(run, bin); |
|
3874 RELEASE_ASSERT(ret != NULL); |
|
3875 run->nfree--; |
|
3876 |
|
3877 return (ret); |
|
3878 } |
|
3879 |
|
3880 /* Re-fill bin->runcur, then call arena_bin_malloc_easy(). */ |
|
3881 static void * |
|
3882 arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin) |
|
3883 { |
|
3884 |
|
3885 bin->runcur = arena_bin_nonfull_run_get(arena, bin); |
|
3886 if (bin->runcur == NULL) |
|
3887 return (NULL); |
|
3888 RELEASE_ASSERT(bin->runcur->magic == ARENA_RUN_MAGIC); |
|
3889 RELEASE_ASSERT(bin->runcur->nfree > 0); |
|
3890 |
|
3891 return (arena_bin_malloc_easy(arena, bin, bin->runcur)); |
|
3892 } |
|
3893 |
|
3894 /* |
|
3895 * Calculate bin->run_size such that it meets the following constraints: |
|
3896 * |
|
3897 * *) bin->run_size >= min_run_size |
|
3898 * *) bin->run_size <= arena_maxclass |
|
3899 * *) bin->run_size <= RUN_MAX_SMALL |
|
3900 * *) run header overhead <= RUN_MAX_OVRHD (or header overhead relaxed). |
|
3901 * |
|
3902 * bin->nregs, bin->regs_mask_nelms, and bin->reg0_offset are |
|
3903 * also calculated here, since these settings are all interdependent. |
|
3904 */ |
|
3905 static size_t |
|
3906 arena_bin_run_size_calc(arena_bin_t *bin, size_t min_run_size) |
|
3907 { |
|
3908 size_t try_run_size, good_run_size; |
|
3909 unsigned good_nregs, good_mask_nelms, good_reg0_offset; |
|
3910 unsigned try_nregs, try_mask_nelms, try_reg0_offset; |
|
3911 |
|
3912 assert(min_run_size >= pagesize); |
|
3913 assert(min_run_size <= arena_maxclass); |
|
3914 assert(min_run_size <= RUN_MAX_SMALL); |
|
3915 |
|
3916 /* |
|
3917 * Calculate known-valid settings before entering the run_size |
|
3918 * expansion loop, so that the first part of the loop always copies |
|
3919 * valid settings. |
|
3920 * |
|
3921 * The do..while loop iteratively reduces the number of regions until |
|
3922 * the run header and the regions no longer overlap. A closed formula |
|
3923 * would be quite messy, since there is an interdependency between the |
|
3924 * header's mask length and the number of regions. |
|
3925 */ |
|
3926 try_run_size = min_run_size; |
|
3927 try_nregs = ((try_run_size - sizeof(arena_run_t)) / bin->reg_size) |
|
3928 + 1; /* Counter-act try_nregs-- in loop. */ |
|
3929 do { |
|
3930 try_nregs--; |
|
3931 try_mask_nelms = (try_nregs >> (SIZEOF_INT_2POW + 3)) + |
|
3932 ((try_nregs & ((1U << (SIZEOF_INT_2POW + 3)) - 1)) ? 1 : 0); |
|
3933 try_reg0_offset = try_run_size - (try_nregs * bin->reg_size); |
|
3934 } while (sizeof(arena_run_t) + (sizeof(unsigned) * (try_mask_nelms - 1)) |
|
3935 > try_reg0_offset); |
|
3936 |
|
3937 /* run_size expansion loop. */ |
|
3938 do { |
|
3939 /* |
|
3940 * Copy valid settings before trying more aggressive settings. |
|
3941 */ |
|
3942 good_run_size = try_run_size; |
|
3943 good_nregs = try_nregs; |
|
3944 good_mask_nelms = try_mask_nelms; |
|
3945 good_reg0_offset = try_reg0_offset; |
|
3946 |
|
3947 /* Try more aggressive settings. */ |
|
3948 try_run_size += pagesize; |
|
3949 try_nregs = ((try_run_size - sizeof(arena_run_t)) / |
|
3950 bin->reg_size) + 1; /* Counter-act try_nregs-- in loop. */ |
|
3951 do { |
|
3952 try_nregs--; |
|
3953 try_mask_nelms = (try_nregs >> (SIZEOF_INT_2POW + 3)) + |
|
3954 ((try_nregs & ((1U << (SIZEOF_INT_2POW + 3)) - 1)) ? |
|
3955 1 : 0); |
|
3956 try_reg0_offset = try_run_size - (try_nregs * |
|
3957 bin->reg_size); |
|
3958 } while (sizeof(arena_run_t) + (sizeof(unsigned) * |
|
3959 (try_mask_nelms - 1)) > try_reg0_offset); |
|
3960 } while (try_run_size <= arena_maxclass && try_run_size <= RUN_MAX_SMALL |
|
3961 && RUN_MAX_OVRHD * (bin->reg_size << 3) > RUN_MAX_OVRHD_RELAX |
|
3962 && (try_reg0_offset << RUN_BFP) > RUN_MAX_OVRHD * try_run_size); |
|
3963 |
|
3964 assert(sizeof(arena_run_t) + (sizeof(unsigned) * (good_mask_nelms - 1)) |
|
3965 <= good_reg0_offset); |
|
3966 assert((good_mask_nelms << (SIZEOF_INT_2POW + 3)) >= good_nregs); |
|
3967 |
|
3968 /* Copy final settings. */ |
|
3969 bin->run_size = good_run_size; |
|
3970 bin->nregs = good_nregs; |
|
3971 bin->regs_mask_nelms = good_mask_nelms; |
|
3972 bin->reg0_offset = good_reg0_offset; |
|
3973 |
|
3974 return (good_run_size); |
|
3975 } |
|
3976 |
|
3977 #ifdef MALLOC_BALANCE |
|
3978 static inline void |
|
3979 arena_lock_balance(arena_t *arena) |
|
3980 { |
|
3981 unsigned contention; |
|
3982 |
|
3983 contention = malloc_spin_lock(&arena->lock); |
|
3984 if (narenas > 1) { |
|
3985 /* |
|
3986 * Calculate the exponentially averaged contention for this |
|
3987 * arena. Due to integer math always rounding down, this value |
|
3988 * decays somewhat faster then normal. |
|
3989 */ |
|
3990 arena->contention = (((uint64_t)arena->contention |
|
3991 * (uint64_t)((1U << BALANCE_ALPHA_INV_2POW)-1)) |
|
3992 + (uint64_t)contention) >> BALANCE_ALPHA_INV_2POW; |
|
3993 if (arena->contention >= opt_balance_threshold) |
|
3994 arena_lock_balance_hard(arena); |
|
3995 } |
|
3996 } |
|
3997 |
|
3998 static void |
|
3999 arena_lock_balance_hard(arena_t *arena) |
|
4000 { |
|
4001 uint32_t ind; |
|
4002 |
|
4003 arena->contention = 0; |
|
4004 #ifdef MALLOC_STATS |
|
4005 arena->stats.nbalance++; |
|
4006 #endif |
|
4007 ind = PRN(balance, narenas_2pow); |
|
4008 if (arenas[ind] != NULL) { |
|
4009 #ifdef MOZ_MEMORY_WINDOWS |
|
4010 TlsSetValue(tlsIndex, arenas[ind]); |
|
4011 #else |
|
4012 arenas_map = arenas[ind]; |
|
4013 #endif |
|
4014 } else { |
|
4015 malloc_spin_lock(&arenas_lock); |
|
4016 if (arenas[ind] != NULL) { |
|
4017 #ifdef MOZ_MEMORY_WINDOWS |
|
4018 TlsSetValue(tlsIndex, arenas[ind]); |
|
4019 #else |
|
4020 arenas_map = arenas[ind]; |
|
4021 #endif |
|
4022 } else { |
|
4023 #ifdef MOZ_MEMORY_WINDOWS |
|
4024 TlsSetValue(tlsIndex, arenas_extend(ind)); |
|
4025 #else |
|
4026 arenas_map = arenas_extend(ind); |
|
4027 #endif |
|
4028 } |
|
4029 malloc_spin_unlock(&arenas_lock); |
|
4030 } |
|
4031 } |
|
4032 #endif |
|
4033 |
|
4034 static inline void * |
|
4035 arena_malloc_small(arena_t *arena, size_t size, bool zero) |
|
4036 { |
|
4037 void *ret; |
|
4038 arena_bin_t *bin; |
|
4039 arena_run_t *run; |
|
4040 |
|
4041 if (size < small_min) { |
|
4042 /* Tiny. */ |
|
4043 size = pow2_ceil(size); |
|
4044 bin = &arena->bins[ffs((int)(size >> (TINY_MIN_2POW + |
|
4045 1)))]; |
|
4046 #if (!defined(NDEBUG) || defined(MALLOC_STATS)) |
|
4047 /* |
|
4048 * Bin calculation is always correct, but we may need |
|
4049 * to fix size for the purposes of assertions and/or |
|
4050 * stats accuracy. |
|
4051 */ |
|
4052 if (size < (1U << TINY_MIN_2POW)) |
|
4053 size = (1U << TINY_MIN_2POW); |
|
4054 #endif |
|
4055 } else if (size <= small_max) { |
|
4056 /* Quantum-spaced. */ |
|
4057 size = QUANTUM_CEILING(size); |
|
4058 bin = &arena->bins[ntbins + (size >> opt_quantum_2pow) |
|
4059 - 1]; |
|
4060 } else { |
|
4061 /* Sub-page. */ |
|
4062 size = pow2_ceil(size); |
|
4063 bin = &arena->bins[ntbins + nqbins |
|
4064 + (ffs((int)(size >> opt_small_max_2pow)) - 2)]; |
|
4065 } |
|
4066 RELEASE_ASSERT(size == bin->reg_size); |
|
4067 |
|
4068 #ifdef MALLOC_BALANCE |
|
4069 arena_lock_balance(arena); |
|
4070 #else |
|
4071 malloc_spin_lock(&arena->lock); |
|
4072 #endif |
|
4073 if ((run = bin->runcur) != NULL && run->nfree > 0) |
|
4074 ret = arena_bin_malloc_easy(arena, bin, run); |
|
4075 else |
|
4076 ret = arena_bin_malloc_hard(arena, bin); |
|
4077 |
|
4078 if (ret == NULL) { |
|
4079 malloc_spin_unlock(&arena->lock); |
|
4080 return (NULL); |
|
4081 } |
|
4082 |
|
4083 #ifdef MALLOC_STATS |
|
4084 bin->stats.nrequests++; |
|
4085 arena->stats.nmalloc_small++; |
|
4086 arena->stats.allocated_small += size; |
|
4087 #endif |
|
4088 malloc_spin_unlock(&arena->lock); |
|
4089 |
|
4090 VALGRIND_MALLOCLIKE_BLOCK(ret, size, 0, zero); |
|
4091 if (zero == false) { |
|
4092 #ifdef MALLOC_FILL |
|
4093 if (opt_junk) |
|
4094 memset(ret, 0xa5, size); |
|
4095 else if (opt_zero) |
|
4096 memset(ret, 0, size); |
|
4097 #endif |
|
4098 } else |
|
4099 memset(ret, 0, size); |
|
4100 |
|
4101 return (ret); |
|
4102 } |
|
4103 |
|
4104 static void * |
|
4105 arena_malloc_large(arena_t *arena, size_t size, bool zero) |
|
4106 { |
|
4107 void *ret; |
|
4108 |
|
4109 /* Large allocation. */ |
|
4110 size = PAGE_CEILING(size); |
|
4111 #ifdef MALLOC_BALANCE |
|
4112 arena_lock_balance(arena); |
|
4113 #else |
|
4114 malloc_spin_lock(&arena->lock); |
|
4115 #endif |
|
4116 ret = (void *)arena_run_alloc(arena, NULL, size, true, zero); |
|
4117 if (ret == NULL) { |
|
4118 malloc_spin_unlock(&arena->lock); |
|
4119 return (NULL); |
|
4120 } |
|
4121 #ifdef MALLOC_STATS |
|
4122 arena->stats.nmalloc_large++; |
|
4123 arena->stats.allocated_large += size; |
|
4124 #endif |
|
4125 malloc_spin_unlock(&arena->lock); |
|
4126 |
|
4127 VALGRIND_MALLOCLIKE_BLOCK(ret, size, 0, zero); |
|
4128 if (zero == false) { |
|
4129 #ifdef MALLOC_FILL |
|
4130 if (opt_junk) |
|
4131 memset(ret, 0xa5, size); |
|
4132 else if (opt_zero) |
|
4133 memset(ret, 0, size); |
|
4134 #endif |
|
4135 } |
|
4136 |
|
4137 return (ret); |
|
4138 } |
|
4139 |
|
4140 static inline void * |
|
4141 arena_malloc(arena_t *arena, size_t size, bool zero) |
|
4142 { |
|
4143 |
|
4144 assert(arena != NULL); |
|
4145 RELEASE_ASSERT(arena->magic == ARENA_MAGIC); |
|
4146 assert(size != 0); |
|
4147 assert(QUANTUM_CEILING(size) <= arena_maxclass); |
|
4148 |
|
4149 if (size <= bin_maxclass) { |
|
4150 return (arena_malloc_small(arena, size, zero)); |
|
4151 } else |
|
4152 return (arena_malloc_large(arena, size, zero)); |
|
4153 } |
|
4154 |
|
4155 static inline void * |
|
4156 imalloc(size_t size) |
|
4157 { |
|
4158 |
|
4159 assert(size != 0); |
|
4160 |
|
4161 if (size <= arena_maxclass) |
|
4162 return (arena_malloc(choose_arena(), size, false)); |
|
4163 else |
|
4164 return (huge_malloc(size, false)); |
|
4165 } |
|
4166 |
|
4167 static inline void * |
|
4168 icalloc(size_t size) |
|
4169 { |
|
4170 |
|
4171 if (size <= arena_maxclass) |
|
4172 return (arena_malloc(choose_arena(), size, true)); |
|
4173 else |
|
4174 return (huge_malloc(size, true)); |
|
4175 } |
|
4176 |
|
4177 /* Only handles large allocations that require more than page alignment. */ |
|
4178 static void * |
|
4179 arena_palloc(arena_t *arena, size_t alignment, size_t size, size_t alloc_size) |
|
4180 { |
|
4181 void *ret; |
|
4182 size_t offset; |
|
4183 arena_chunk_t *chunk; |
|
4184 |
|
4185 assert((size & pagesize_mask) == 0); |
|
4186 assert((alignment & pagesize_mask) == 0); |
|
4187 |
|
4188 #ifdef MALLOC_BALANCE |
|
4189 arena_lock_balance(arena); |
|
4190 #else |
|
4191 malloc_spin_lock(&arena->lock); |
|
4192 #endif |
|
4193 ret = (void *)arena_run_alloc(arena, NULL, alloc_size, true, false); |
|
4194 if (ret == NULL) { |
|
4195 malloc_spin_unlock(&arena->lock); |
|
4196 return (NULL); |
|
4197 } |
|
4198 |
|
4199 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ret); |
|
4200 |
|
4201 offset = (uintptr_t)ret & (alignment - 1); |
|
4202 assert((offset & pagesize_mask) == 0); |
|
4203 assert(offset < alloc_size); |
|
4204 if (offset == 0) |
|
4205 arena_run_trim_tail(arena, chunk, (arena_run_t*)ret, alloc_size, size, false); |
|
4206 else { |
|
4207 size_t leadsize, trailsize; |
|
4208 |
|
4209 leadsize = alignment - offset; |
|
4210 if (leadsize > 0) { |
|
4211 arena_run_trim_head(arena, chunk, (arena_run_t*)ret, alloc_size, |
|
4212 alloc_size - leadsize); |
|
4213 ret = (void *)((uintptr_t)ret + leadsize); |
|
4214 } |
|
4215 |
|
4216 trailsize = alloc_size - leadsize - size; |
|
4217 if (trailsize != 0) { |
|
4218 /* Trim trailing space. */ |
|
4219 assert(trailsize < alloc_size); |
|
4220 arena_run_trim_tail(arena, chunk, (arena_run_t*)ret, size + trailsize, |
|
4221 size, false); |
|
4222 } |
|
4223 } |
|
4224 |
|
4225 #ifdef MALLOC_STATS |
|
4226 arena->stats.nmalloc_large++; |
|
4227 arena->stats.allocated_large += size; |
|
4228 #endif |
|
4229 malloc_spin_unlock(&arena->lock); |
|
4230 |
|
4231 VALGRIND_MALLOCLIKE_BLOCK(ret, size, 0, false); |
|
4232 #ifdef MALLOC_FILL |
|
4233 if (opt_junk) |
|
4234 memset(ret, 0xa5, size); |
|
4235 else if (opt_zero) |
|
4236 memset(ret, 0, size); |
|
4237 #endif |
|
4238 return (ret); |
|
4239 } |
|
4240 |
|
4241 static inline void * |
|
4242 ipalloc(size_t alignment, size_t size) |
|
4243 { |
|
4244 void *ret; |
|
4245 size_t ceil_size; |
|
4246 |
|
4247 /* |
|
4248 * Round size up to the nearest multiple of alignment. |
|
4249 * |
|
4250 * This done, we can take advantage of the fact that for each small |
|
4251 * size class, every object is aligned at the smallest power of two |
|
4252 * that is non-zero in the base two representation of the size. For |
|
4253 * example: |
|
4254 * |
|
4255 * Size | Base 2 | Minimum alignment |
|
4256 * -----+----------+------------------ |
|
4257 * 96 | 1100000 | 32 |
|
4258 * 144 | 10100000 | 32 |
|
4259 * 192 | 11000000 | 64 |
|
4260 * |
|
4261 * Depending on runtime settings, it is possible that arena_malloc() |
|
4262 * will further round up to a power of two, but that never causes |
|
4263 * correctness issues. |
|
4264 */ |
|
4265 ceil_size = (size + (alignment - 1)) & (-alignment); |
|
4266 /* |
|
4267 * (ceil_size < size) protects against the combination of maximal |
|
4268 * alignment and size greater than maximal alignment. |
|
4269 */ |
|
4270 if (ceil_size < size) { |
|
4271 /* size_t overflow. */ |
|
4272 return (NULL); |
|
4273 } |
|
4274 |
|
4275 if (ceil_size <= pagesize || (alignment <= pagesize |
|
4276 && ceil_size <= arena_maxclass)) |
|
4277 ret = arena_malloc(choose_arena(), ceil_size, false); |
|
4278 else { |
|
4279 size_t run_size; |
|
4280 |
|
4281 /* |
|
4282 * We can't achieve sub-page alignment, so round up alignment |
|
4283 * permanently; it makes later calculations simpler. |
|
4284 */ |
|
4285 alignment = PAGE_CEILING(alignment); |
|
4286 ceil_size = PAGE_CEILING(size); |
|
4287 /* |
|
4288 * (ceil_size < size) protects against very large sizes within |
|
4289 * pagesize of SIZE_T_MAX. |
|
4290 * |
|
4291 * (ceil_size + alignment < ceil_size) protects against the |
|
4292 * combination of maximal alignment and ceil_size large enough |
|
4293 * to cause overflow. This is similar to the first overflow |
|
4294 * check above, but it needs to be repeated due to the new |
|
4295 * ceil_size value, which may now be *equal* to maximal |
|
4296 * alignment, whereas before we only detected overflow if the |
|
4297 * original size was *greater* than maximal alignment. |
|
4298 */ |
|
4299 if (ceil_size < size || ceil_size + alignment < ceil_size) { |
|
4300 /* size_t overflow. */ |
|
4301 return (NULL); |
|
4302 } |
|
4303 |
|
4304 /* |
|
4305 * Calculate the size of the over-size run that arena_palloc() |
|
4306 * would need to allocate in order to guarantee the alignment. |
|
4307 */ |
|
4308 if (ceil_size >= alignment) |
|
4309 run_size = ceil_size + alignment - pagesize; |
|
4310 else { |
|
4311 /* |
|
4312 * It is possible that (alignment << 1) will cause |
|
4313 * overflow, but it doesn't matter because we also |
|
4314 * subtract pagesize, which in the case of overflow |
|
4315 * leaves us with a very large run_size. That causes |
|
4316 * the first conditional below to fail, which means |
|
4317 * that the bogus run_size value never gets used for |
|
4318 * anything important. |
|
4319 */ |
|
4320 run_size = (alignment << 1) - pagesize; |
|
4321 } |
|
4322 |
|
4323 if (run_size <= arena_maxclass) { |
|
4324 ret = arena_palloc(choose_arena(), alignment, ceil_size, |
|
4325 run_size); |
|
4326 } else if (alignment <= chunksize) |
|
4327 ret = huge_malloc(ceil_size, false); |
|
4328 else |
|
4329 ret = huge_palloc(alignment, ceil_size); |
|
4330 } |
|
4331 |
|
4332 assert(((uintptr_t)ret & (alignment - 1)) == 0); |
|
4333 return (ret); |
|
4334 } |
|
4335 |
|
4336 /* Return the size of the allocation pointed to by ptr. */ |
|
4337 static size_t |
|
4338 arena_salloc(const void *ptr) |
|
4339 { |
|
4340 size_t ret; |
|
4341 arena_chunk_t *chunk; |
|
4342 size_t pageind, mapbits; |
|
4343 |
|
4344 assert(ptr != NULL); |
|
4345 assert(CHUNK_ADDR2BASE(ptr) != ptr); |
|
4346 |
|
4347 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); |
|
4348 pageind = (((uintptr_t)ptr - (uintptr_t)chunk) >> pagesize_2pow); |
|
4349 mapbits = chunk->map[pageind].bits; |
|
4350 RELEASE_ASSERT((mapbits & CHUNK_MAP_ALLOCATED) != 0); |
|
4351 if ((mapbits & CHUNK_MAP_LARGE) == 0) { |
|
4352 arena_run_t *run = (arena_run_t *)(mapbits & ~pagesize_mask); |
|
4353 RELEASE_ASSERT(run->magic == ARENA_RUN_MAGIC); |
|
4354 ret = run->bin->reg_size; |
|
4355 } else { |
|
4356 ret = mapbits & ~pagesize_mask; |
|
4357 RELEASE_ASSERT(ret != 0); |
|
4358 } |
|
4359 |
|
4360 return (ret); |
|
4361 } |
|
4362 |
|
4363 #if (defined(MALLOC_VALIDATE) || defined(MOZ_MEMORY_DARWIN)) |
|
4364 /* |
|
4365 * Validate ptr before assuming that it points to an allocation. Currently, |
|
4366 * the following validation is performed: |
|
4367 * |
|
4368 * + Check that ptr is not NULL. |
|
4369 * |
|
4370 * + Check that ptr lies within a mapped chunk. |
|
4371 */ |
|
4372 static inline size_t |
|
4373 isalloc_validate(const void *ptr) |
|
4374 { |
|
4375 arena_chunk_t *chunk; |
|
4376 |
|
4377 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); |
|
4378 if (chunk == NULL) |
|
4379 return (0); |
|
4380 |
|
4381 if (malloc_rtree_get(chunk_rtree, (uintptr_t)chunk) == NULL) |
|
4382 return (0); |
|
4383 |
|
4384 if (chunk != ptr) { |
|
4385 RELEASE_ASSERT(chunk->arena->magic == ARENA_MAGIC); |
|
4386 return (arena_salloc(ptr)); |
|
4387 } else { |
|
4388 size_t ret; |
|
4389 extent_node_t *node; |
|
4390 extent_node_t key; |
|
4391 |
|
4392 /* Chunk. */ |
|
4393 key.addr = (void *)chunk; |
|
4394 malloc_mutex_lock(&huge_mtx); |
|
4395 node = extent_tree_ad_search(&huge, &key); |
|
4396 if (node != NULL) |
|
4397 ret = node->size; |
|
4398 else |
|
4399 ret = 0; |
|
4400 malloc_mutex_unlock(&huge_mtx); |
|
4401 return (ret); |
|
4402 } |
|
4403 } |
|
4404 #endif |
|
4405 |
|
4406 static inline size_t |
|
4407 isalloc(const void *ptr) |
|
4408 { |
|
4409 size_t ret; |
|
4410 arena_chunk_t *chunk; |
|
4411 |
|
4412 assert(ptr != NULL); |
|
4413 |
|
4414 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); |
|
4415 if (chunk != ptr) { |
|
4416 /* Region. */ |
|
4417 assert(chunk->arena->magic == ARENA_MAGIC); |
|
4418 |
|
4419 ret = arena_salloc(ptr); |
|
4420 } else { |
|
4421 extent_node_t *node, key; |
|
4422 |
|
4423 /* Chunk (huge allocation). */ |
|
4424 |
|
4425 malloc_mutex_lock(&huge_mtx); |
|
4426 |
|
4427 /* Extract from tree of huge allocations. */ |
|
4428 key.addr = __DECONST(void *, ptr); |
|
4429 node = extent_tree_ad_search(&huge, &key); |
|
4430 RELEASE_ASSERT(node != NULL); |
|
4431 |
|
4432 ret = node->size; |
|
4433 |
|
4434 malloc_mutex_unlock(&huge_mtx); |
|
4435 } |
|
4436 |
|
4437 return (ret); |
|
4438 } |
|
4439 |
|
4440 static inline void |
|
4441 arena_dalloc_small(arena_t *arena, arena_chunk_t *chunk, void *ptr, |
|
4442 arena_chunk_map_t *mapelm) |
|
4443 { |
|
4444 arena_run_t *run; |
|
4445 arena_bin_t *bin; |
|
4446 size_t size; |
|
4447 |
|
4448 run = (arena_run_t *)(mapelm->bits & ~pagesize_mask); |
|
4449 RELEASE_ASSERT(run->magic == ARENA_RUN_MAGIC); |
|
4450 bin = run->bin; |
|
4451 size = bin->reg_size; |
|
4452 |
|
4453 #ifdef MALLOC_FILL |
|
4454 if (opt_poison) |
|
4455 memset(ptr, 0x5a, size); |
|
4456 #endif |
|
4457 |
|
4458 arena_run_reg_dalloc(run, bin, ptr, size); |
|
4459 run->nfree++; |
|
4460 |
|
4461 if (run->nfree == bin->nregs) { |
|
4462 /* Deallocate run. */ |
|
4463 if (run == bin->runcur) |
|
4464 bin->runcur = NULL; |
|
4465 else if (bin->nregs != 1) { |
|
4466 size_t run_pageind = (((uintptr_t)run - |
|
4467 (uintptr_t)chunk)) >> pagesize_2pow; |
|
4468 arena_chunk_map_t *run_mapelm = |
|
4469 &chunk->map[run_pageind]; |
|
4470 /* |
|
4471 * This block's conditional is necessary because if the |
|
4472 * run only contains one region, then it never gets |
|
4473 * inserted into the non-full runs tree. |
|
4474 */ |
|
4475 RELEASE_ASSERT(arena_run_tree_search(&bin->runs, run_mapelm) == |
|
4476 run_mapelm); |
|
4477 arena_run_tree_remove(&bin->runs, run_mapelm); |
|
4478 } |
|
4479 #if defined(MALLOC_DEBUG) || defined(MOZ_JEMALLOC_HARD_ASSERTS) |
|
4480 run->magic = 0; |
|
4481 #endif |
|
4482 VALGRIND_FREELIKE_BLOCK(run, 0); |
|
4483 arena_run_dalloc(arena, run, true); |
|
4484 #ifdef MALLOC_STATS |
|
4485 bin->stats.curruns--; |
|
4486 #endif |
|
4487 } else if (run->nfree == 1 && run != bin->runcur) { |
|
4488 /* |
|
4489 * Make sure that bin->runcur always refers to the lowest |
|
4490 * non-full run, if one exists. |
|
4491 */ |
|
4492 if (bin->runcur == NULL) |
|
4493 bin->runcur = run; |
|
4494 else if ((uintptr_t)run < (uintptr_t)bin->runcur) { |
|
4495 /* Switch runcur. */ |
|
4496 if (bin->runcur->nfree > 0) { |
|
4497 arena_chunk_t *runcur_chunk = |
|
4498 (arena_chunk_t*)CHUNK_ADDR2BASE(bin->runcur); |
|
4499 size_t runcur_pageind = |
|
4500 (((uintptr_t)bin->runcur - |
|
4501 (uintptr_t)runcur_chunk)) >> pagesize_2pow; |
|
4502 arena_chunk_map_t *runcur_mapelm = |
|
4503 &runcur_chunk->map[runcur_pageind]; |
|
4504 |
|
4505 /* Insert runcur. */ |
|
4506 RELEASE_ASSERT(arena_run_tree_search(&bin->runs, |
|
4507 runcur_mapelm) == NULL); |
|
4508 arena_run_tree_insert(&bin->runs, |
|
4509 runcur_mapelm); |
|
4510 } |
|
4511 bin->runcur = run; |
|
4512 } else { |
|
4513 size_t run_pageind = (((uintptr_t)run - |
|
4514 (uintptr_t)chunk)) >> pagesize_2pow; |
|
4515 arena_chunk_map_t *run_mapelm = |
|
4516 &chunk->map[run_pageind]; |
|
4517 |
|
4518 RELEASE_ASSERT(arena_run_tree_search(&bin->runs, run_mapelm) == |
|
4519 NULL); |
|
4520 arena_run_tree_insert(&bin->runs, run_mapelm); |
|
4521 } |
|
4522 } |
|
4523 #ifdef MALLOC_STATS |
|
4524 arena->stats.allocated_small -= size; |
|
4525 arena->stats.ndalloc_small++; |
|
4526 #endif |
|
4527 } |
|
4528 |
|
4529 static void |
|
4530 arena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, void *ptr) |
|
4531 { |
|
4532 /* Large allocation. */ |
|
4533 malloc_spin_lock(&arena->lock); |
|
4534 |
|
4535 #ifdef MALLOC_FILL |
|
4536 #ifndef MALLOC_STATS |
|
4537 if (opt_poison) |
|
4538 #endif |
|
4539 #endif |
|
4540 { |
|
4541 size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> |
|
4542 pagesize_2pow; |
|
4543 size_t size = chunk->map[pageind].bits & ~pagesize_mask; |
|
4544 |
|
4545 #ifdef MALLOC_FILL |
|
4546 #ifdef MALLOC_STATS |
|
4547 if (opt_poison) |
|
4548 #endif |
|
4549 memset(ptr, 0x5a, size); |
|
4550 #endif |
|
4551 #ifdef MALLOC_STATS |
|
4552 arena->stats.allocated_large -= size; |
|
4553 #endif |
|
4554 } |
|
4555 #ifdef MALLOC_STATS |
|
4556 arena->stats.ndalloc_large++; |
|
4557 #endif |
|
4558 |
|
4559 arena_run_dalloc(arena, (arena_run_t *)ptr, true); |
|
4560 malloc_spin_unlock(&arena->lock); |
|
4561 } |
|
4562 |
|
4563 static inline void |
|
4564 arena_dalloc(void *ptr, size_t offset) |
|
4565 { |
|
4566 arena_chunk_t *chunk; |
|
4567 arena_t *arena; |
|
4568 size_t pageind; |
|
4569 arena_chunk_map_t *mapelm; |
|
4570 |
|
4571 assert(ptr != NULL); |
|
4572 assert(offset != 0); |
|
4573 assert(CHUNK_ADDR2OFFSET(ptr) == offset); |
|
4574 |
|
4575 chunk = (arena_chunk_t *) ((uintptr_t)ptr - offset); |
|
4576 arena = chunk->arena; |
|
4577 assert(arena != NULL); |
|
4578 RELEASE_ASSERT(arena->magic == ARENA_MAGIC); |
|
4579 |
|
4580 pageind = offset >> pagesize_2pow; |
|
4581 mapelm = &chunk->map[pageind]; |
|
4582 RELEASE_ASSERT((mapelm->bits & CHUNK_MAP_ALLOCATED) != 0); |
|
4583 if ((mapelm->bits & CHUNK_MAP_LARGE) == 0) { |
|
4584 /* Small allocation. */ |
|
4585 malloc_spin_lock(&arena->lock); |
|
4586 arena_dalloc_small(arena, chunk, ptr, mapelm); |
|
4587 malloc_spin_unlock(&arena->lock); |
|
4588 } else |
|
4589 arena_dalloc_large(arena, chunk, ptr); |
|
4590 VALGRIND_FREELIKE_BLOCK(ptr, 0); |
|
4591 } |
|
4592 |
|
4593 static inline void |
|
4594 idalloc(void *ptr) |
|
4595 { |
|
4596 size_t offset; |
|
4597 |
|
4598 assert(ptr != NULL); |
|
4599 |
|
4600 offset = CHUNK_ADDR2OFFSET(ptr); |
|
4601 if (offset != 0) |
|
4602 arena_dalloc(ptr, offset); |
|
4603 else |
|
4604 huge_dalloc(ptr); |
|
4605 } |
|
4606 |
|
4607 static void |
|
4608 arena_ralloc_large_shrink(arena_t *arena, arena_chunk_t *chunk, void *ptr, |
|
4609 size_t size, size_t oldsize) |
|
4610 { |
|
4611 |
|
4612 assert(size < oldsize); |
|
4613 |
|
4614 /* |
|
4615 * Shrink the run, and make trailing pages available for other |
|
4616 * allocations. |
|
4617 */ |
|
4618 #ifdef MALLOC_BALANCE |
|
4619 arena_lock_balance(arena); |
|
4620 #else |
|
4621 malloc_spin_lock(&arena->lock); |
|
4622 #endif |
|
4623 arena_run_trim_tail(arena, chunk, (arena_run_t *)ptr, oldsize, size, |
|
4624 true); |
|
4625 #ifdef MALLOC_STATS |
|
4626 arena->stats.allocated_large -= oldsize - size; |
|
4627 #endif |
|
4628 malloc_spin_unlock(&arena->lock); |
|
4629 } |
|
4630 |
|
4631 static bool |
|
4632 arena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk, void *ptr, |
|
4633 size_t size, size_t oldsize) |
|
4634 { |
|
4635 size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> pagesize_2pow; |
|
4636 size_t npages = oldsize >> pagesize_2pow; |
|
4637 |
|
4638 RELEASE_ASSERT(oldsize == (chunk->map[pageind].bits & ~pagesize_mask)); |
|
4639 |
|
4640 /* Try to extend the run. */ |
|
4641 assert(size > oldsize); |
|
4642 #ifdef MALLOC_BALANCE |
|
4643 arena_lock_balance(arena); |
|
4644 #else |
|
4645 malloc_spin_lock(&arena->lock); |
|
4646 #endif |
|
4647 if (pageind + npages < chunk_npages && (chunk->map[pageind+npages].bits |
|
4648 & CHUNK_MAP_ALLOCATED) == 0 && (chunk->map[pageind+npages].bits & |
|
4649 ~pagesize_mask) >= size - oldsize) { |
|
4650 /* |
|
4651 * The next run is available and sufficiently large. Split the |
|
4652 * following run, then merge the first part with the existing |
|
4653 * allocation. |
|
4654 */ |
|
4655 arena_run_split(arena, (arena_run_t *)((uintptr_t)chunk + |
|
4656 ((pageind+npages) << pagesize_2pow)), size - oldsize, true, |
|
4657 false); |
|
4658 |
|
4659 chunk->map[pageind].bits = size | CHUNK_MAP_LARGE | |
|
4660 CHUNK_MAP_ALLOCATED; |
|
4661 chunk->map[pageind+npages].bits = CHUNK_MAP_LARGE | |
|
4662 CHUNK_MAP_ALLOCATED; |
|
4663 |
|
4664 #ifdef MALLOC_STATS |
|
4665 arena->stats.allocated_large += size - oldsize; |
|
4666 #endif |
|
4667 malloc_spin_unlock(&arena->lock); |
|
4668 return (false); |
|
4669 } |
|
4670 malloc_spin_unlock(&arena->lock); |
|
4671 |
|
4672 return (true); |
|
4673 } |
|
4674 |
|
4675 /* |
|
4676 * Try to resize a large allocation, in order to avoid copying. This will |
|
4677 * always fail if growing an object, and the following run is already in use. |
|
4678 */ |
|
4679 static bool |
|
4680 arena_ralloc_large(void *ptr, size_t size, size_t oldsize) |
|
4681 { |
|
4682 size_t psize; |
|
4683 |
|
4684 psize = PAGE_CEILING(size); |
|
4685 if (psize == oldsize) { |
|
4686 /* Same size class. */ |
|
4687 #ifdef MALLOC_FILL |
|
4688 if (opt_poison && size < oldsize) { |
|
4689 memset((void *)((uintptr_t)ptr + size), 0x5a, oldsize - |
|
4690 size); |
|
4691 } |
|
4692 #endif |
|
4693 return (false); |
|
4694 } else { |
|
4695 arena_chunk_t *chunk; |
|
4696 arena_t *arena; |
|
4697 |
|
4698 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); |
|
4699 arena = chunk->arena; |
|
4700 RELEASE_ASSERT(arena->magic == ARENA_MAGIC); |
|
4701 |
|
4702 if (psize < oldsize) { |
|
4703 #ifdef MALLOC_FILL |
|
4704 /* Fill before shrinking in order avoid a race. */ |
|
4705 if (opt_poison) { |
|
4706 memset((void *)((uintptr_t)ptr + size), 0x5a, |
|
4707 oldsize - size); |
|
4708 } |
|
4709 #endif |
|
4710 arena_ralloc_large_shrink(arena, chunk, ptr, psize, |
|
4711 oldsize); |
|
4712 return (false); |
|
4713 } else { |
|
4714 bool ret = arena_ralloc_large_grow(arena, chunk, ptr, |
|
4715 psize, oldsize); |
|
4716 #ifdef MALLOC_FILL |
|
4717 if (ret == false && opt_zero) { |
|
4718 memset((void *)((uintptr_t)ptr + oldsize), 0, |
|
4719 size - oldsize); |
|
4720 } |
|
4721 #endif |
|
4722 return (ret); |
|
4723 } |
|
4724 } |
|
4725 } |
|
4726 |
|
4727 static void * |
|
4728 arena_ralloc(void *ptr, size_t size, size_t oldsize) |
|
4729 { |
|
4730 void *ret; |
|
4731 size_t copysize; |
|
4732 |
|
4733 /* Try to avoid moving the allocation. */ |
|
4734 if (size < small_min) { |
|
4735 if (oldsize < small_min && |
|
4736 ffs((int)(pow2_ceil(size) >> (TINY_MIN_2POW + 1))) |
|
4737 == ffs((int)(pow2_ceil(oldsize) >> (TINY_MIN_2POW + 1)))) |
|
4738 goto IN_PLACE; /* Same size class. */ |
|
4739 } else if (size <= small_max) { |
|
4740 if (oldsize >= small_min && oldsize <= small_max && |
|
4741 (QUANTUM_CEILING(size) >> opt_quantum_2pow) |
|
4742 == (QUANTUM_CEILING(oldsize) >> opt_quantum_2pow)) |
|
4743 goto IN_PLACE; /* Same size class. */ |
|
4744 } else if (size <= bin_maxclass) { |
|
4745 if (oldsize > small_max && oldsize <= bin_maxclass && |
|
4746 pow2_ceil(size) == pow2_ceil(oldsize)) |
|
4747 goto IN_PLACE; /* Same size class. */ |
|
4748 } else if (oldsize > bin_maxclass && oldsize <= arena_maxclass) { |
|
4749 assert(size > bin_maxclass); |
|
4750 if (arena_ralloc_large(ptr, size, oldsize) == false) |
|
4751 return (ptr); |
|
4752 } |
|
4753 |
|
4754 /* |
|
4755 * If we get here, then size and oldsize are different enough that we |
|
4756 * need to move the object. In that case, fall back to allocating new |
|
4757 * space and copying. |
|
4758 */ |
|
4759 ret = arena_malloc(choose_arena(), size, false); |
|
4760 if (ret == NULL) |
|
4761 return (NULL); |
|
4762 |
|
4763 /* Junk/zero-filling were already done by arena_malloc(). */ |
|
4764 copysize = (size < oldsize) ? size : oldsize; |
|
4765 #ifdef VM_COPY_MIN |
|
4766 if (copysize >= VM_COPY_MIN) |
|
4767 pages_copy(ret, ptr, copysize); |
|
4768 else |
|
4769 #endif |
|
4770 memcpy(ret, ptr, copysize); |
|
4771 idalloc(ptr); |
|
4772 return (ret); |
|
4773 IN_PLACE: |
|
4774 #ifdef MALLOC_FILL |
|
4775 if (opt_poison && size < oldsize) |
|
4776 memset((void *)((uintptr_t)ptr + size), 0x5a, oldsize - size); |
|
4777 else if (opt_zero && size > oldsize) |
|
4778 memset((void *)((uintptr_t)ptr + oldsize), 0, size - oldsize); |
|
4779 #endif |
|
4780 return (ptr); |
|
4781 } |
|
4782 |
|
4783 static inline void * |
|
4784 iralloc(void *ptr, size_t size) |
|
4785 { |
|
4786 size_t oldsize; |
|
4787 |
|
4788 assert(ptr != NULL); |
|
4789 assert(size != 0); |
|
4790 |
|
4791 oldsize = isalloc(ptr); |
|
4792 |
|
4793 #ifndef MALLOC_VALGRIND |
|
4794 if (size <= arena_maxclass) |
|
4795 return (arena_ralloc(ptr, size, oldsize)); |
|
4796 else |
|
4797 return (huge_ralloc(ptr, size, oldsize)); |
|
4798 #else |
|
4799 /* |
|
4800 * Valgrind does not provide a public interface for modifying an |
|
4801 * existing allocation, so use malloc/memcpy/free instead. |
|
4802 */ |
|
4803 { |
|
4804 void *ret = imalloc(size); |
|
4805 if (ret != NULL) { |
|
4806 if (oldsize < size) |
|
4807 memcpy(ret, ptr, oldsize); |
|
4808 else |
|
4809 memcpy(ret, ptr, size); |
|
4810 idalloc(ptr); |
|
4811 } |
|
4812 return (ret); |
|
4813 } |
|
4814 #endif |
|
4815 } |
|
4816 |
|
4817 static bool |
|
4818 arena_new(arena_t *arena) |
|
4819 { |
|
4820 unsigned i; |
|
4821 arena_bin_t *bin; |
|
4822 size_t pow2_size, prev_run_size; |
|
4823 |
|
4824 if (malloc_spin_init(&arena->lock)) |
|
4825 return (true); |
|
4826 |
|
4827 #ifdef MALLOC_STATS |
|
4828 memset(&arena->stats, 0, sizeof(arena_stats_t)); |
|
4829 #endif |
|
4830 |
|
4831 /* Initialize chunks. */ |
|
4832 arena_chunk_tree_dirty_new(&arena->chunks_dirty); |
|
4833 #ifdef MALLOC_DOUBLE_PURGE |
|
4834 LinkedList_Init(&arena->chunks_madvised); |
|
4835 #endif |
|
4836 arena->spare = NULL; |
|
4837 |
|
4838 arena->ndirty = 0; |
|
4839 |
|
4840 arena_avail_tree_new(&arena->runs_avail); |
|
4841 |
|
4842 #ifdef MALLOC_BALANCE |
|
4843 arena->contention = 0; |
|
4844 #endif |
|
4845 |
|
4846 /* Initialize bins. */ |
|
4847 prev_run_size = pagesize; |
|
4848 |
|
4849 /* (2^n)-spaced tiny bins. */ |
|
4850 for (i = 0; i < ntbins; i++) { |
|
4851 bin = &arena->bins[i]; |
|
4852 bin->runcur = NULL; |
|
4853 arena_run_tree_new(&bin->runs); |
|
4854 |
|
4855 bin->reg_size = (1U << (TINY_MIN_2POW + i)); |
|
4856 |
|
4857 prev_run_size = arena_bin_run_size_calc(bin, prev_run_size); |
|
4858 |
|
4859 #ifdef MALLOC_STATS |
|
4860 memset(&bin->stats, 0, sizeof(malloc_bin_stats_t)); |
|
4861 #endif |
|
4862 } |
|
4863 |
|
4864 /* Quantum-spaced bins. */ |
|
4865 for (; i < ntbins + nqbins; i++) { |
|
4866 bin = &arena->bins[i]; |
|
4867 bin->runcur = NULL; |
|
4868 arena_run_tree_new(&bin->runs); |
|
4869 |
|
4870 bin->reg_size = quantum * (i - ntbins + 1); |
|
4871 |
|
4872 pow2_size = pow2_ceil(quantum * (i - ntbins + 1)); |
|
4873 prev_run_size = arena_bin_run_size_calc(bin, prev_run_size); |
|
4874 |
|
4875 #ifdef MALLOC_STATS |
|
4876 memset(&bin->stats, 0, sizeof(malloc_bin_stats_t)); |
|
4877 #endif |
|
4878 } |
|
4879 |
|
4880 /* (2^n)-spaced sub-page bins. */ |
|
4881 for (; i < ntbins + nqbins + nsbins; i++) { |
|
4882 bin = &arena->bins[i]; |
|
4883 bin->runcur = NULL; |
|
4884 arena_run_tree_new(&bin->runs); |
|
4885 |
|
4886 bin->reg_size = (small_max << (i - (ntbins + nqbins) + 1)); |
|
4887 |
|
4888 prev_run_size = arena_bin_run_size_calc(bin, prev_run_size); |
|
4889 |
|
4890 #ifdef MALLOC_STATS |
|
4891 memset(&bin->stats, 0, sizeof(malloc_bin_stats_t)); |
|
4892 #endif |
|
4893 } |
|
4894 |
|
4895 #if defined(MALLOC_DEBUG) || defined(MOZ_JEMALLOC_HARD_ASSERTS) |
|
4896 arena->magic = ARENA_MAGIC; |
|
4897 #endif |
|
4898 |
|
4899 return (false); |
|
4900 } |
|
4901 |
|
4902 /* Create a new arena and insert it into the arenas array at index ind. */ |
|
4903 static arena_t * |
|
4904 arenas_extend(unsigned ind) |
|
4905 { |
|
4906 arena_t *ret; |
|
4907 |
|
4908 /* Allocate enough space for trailing bins. */ |
|
4909 ret = (arena_t *)base_alloc(sizeof(arena_t) |
|
4910 + (sizeof(arena_bin_t) * (ntbins + nqbins + nsbins - 1))); |
|
4911 if (ret != NULL && arena_new(ret) == false) { |
|
4912 arenas[ind] = ret; |
|
4913 return (ret); |
|
4914 } |
|
4915 /* Only reached if there is an OOM error. */ |
|
4916 |
|
4917 /* |
|
4918 * OOM here is quite inconvenient to propagate, since dealing with it |
|
4919 * would require a check for failure in the fast path. Instead, punt |
|
4920 * by using arenas[0]. In practice, this is an extremely unlikely |
|
4921 * failure. |
|
4922 */ |
|
4923 _malloc_message(_getprogname(), |
|
4924 ": (malloc) Error initializing arena\n", "", ""); |
|
4925 if (opt_abort) |
|
4926 abort(); |
|
4927 |
|
4928 return (arenas[0]); |
|
4929 } |
|
4930 |
|
4931 /* |
|
4932 * End arena. |
|
4933 */ |
|
4934 /******************************************************************************/ |
|
4935 /* |
|
4936 * Begin general internal functions. |
|
4937 */ |
|
4938 |
|
4939 static void * |
|
4940 huge_malloc(size_t size, bool zero) |
|
4941 { |
|
4942 void *ret; |
|
4943 size_t csize; |
|
4944 size_t psize; |
|
4945 extent_node_t *node; |
|
4946 |
|
4947 /* Allocate one or more contiguous chunks for this request. */ |
|
4948 |
|
4949 csize = CHUNK_CEILING(size); |
|
4950 if (csize == 0) { |
|
4951 /* size is large enough to cause size_t wrap-around. */ |
|
4952 return (NULL); |
|
4953 } |
|
4954 |
|
4955 /* Allocate an extent node with which to track the chunk. */ |
|
4956 node = base_node_alloc(); |
|
4957 if (node == NULL) |
|
4958 return (NULL); |
|
4959 |
|
4960 ret = chunk_alloc(csize, zero, true); |
|
4961 if (ret == NULL) { |
|
4962 base_node_dealloc(node); |
|
4963 return (NULL); |
|
4964 } |
|
4965 |
|
4966 /* Insert node into huge. */ |
|
4967 node->addr = ret; |
|
4968 psize = PAGE_CEILING(size); |
|
4969 node->size = psize; |
|
4970 |
|
4971 malloc_mutex_lock(&huge_mtx); |
|
4972 extent_tree_ad_insert(&huge, node); |
|
4973 #ifdef MALLOC_STATS |
|
4974 huge_nmalloc++; |
|
4975 |
|
4976 /* Although we allocated space for csize bytes, we indicate that we've |
|
4977 * allocated only psize bytes. |
|
4978 * |
|
4979 * If DECOMMIT is defined, this is a reasonable thing to do, since |
|
4980 * we'll explicitly decommit the bytes in excess of psize. |
|
4981 * |
|
4982 * If DECOMMIT is not defined, then we're relying on the OS to be lazy |
|
4983 * about how it allocates physical pages to mappings. If we never |
|
4984 * touch the pages in excess of psize, the OS won't allocate a physical |
|
4985 * page, and we won't use more than psize bytes of physical memory. |
|
4986 * |
|
4987 * A correct program will only touch memory in excess of how much it |
|
4988 * requested if it first calls malloc_usable_size and finds out how |
|
4989 * much space it has to play with. But because we set node->size = |
|
4990 * psize above, malloc_usable_size will return psize, not csize, and |
|
4991 * the program will (hopefully) never touch bytes in excess of psize. |
|
4992 * Thus those bytes won't take up space in physical memory, and we can |
|
4993 * reasonably claim we never "allocated" them in the first place. */ |
|
4994 huge_allocated += psize; |
|
4995 huge_mapped += csize; |
|
4996 #endif |
|
4997 malloc_mutex_unlock(&huge_mtx); |
|
4998 |
|
4999 #ifdef MALLOC_DECOMMIT |
|
5000 if (csize - psize > 0) |
|
5001 pages_decommit((void *)((uintptr_t)ret + psize), csize - psize); |
|
5002 #endif |
|
5003 |
|
5004 #ifdef MALLOC_DECOMMIT |
|
5005 VALGRIND_MALLOCLIKE_BLOCK(ret, psize, 0, zero); |
|
5006 #else |
|
5007 VALGRIND_MALLOCLIKE_BLOCK(ret, csize, 0, zero); |
|
5008 #endif |
|
5009 |
|
5010 #ifdef MALLOC_FILL |
|
5011 if (zero == false) { |
|
5012 if (opt_junk) |
|
5013 # ifdef MALLOC_DECOMMIT |
|
5014 memset(ret, 0xa5, psize); |
|
5015 # else |
|
5016 memset(ret, 0xa5, csize); |
|
5017 # endif |
|
5018 else if (opt_zero) |
|
5019 # ifdef MALLOC_DECOMMIT |
|
5020 memset(ret, 0, psize); |
|
5021 # else |
|
5022 memset(ret, 0, csize); |
|
5023 # endif |
|
5024 } |
|
5025 #endif |
|
5026 |
|
5027 return (ret); |
|
5028 } |
|
5029 |
|
5030 /* Only handles large allocations that require more than chunk alignment. */ |
|
5031 static void * |
|
5032 huge_palloc(size_t alignment, size_t size) |
|
5033 { |
|
5034 void *ret; |
|
5035 size_t alloc_size, chunk_size, offset; |
|
5036 size_t psize; |
|
5037 extent_node_t *node; |
|
5038 int pfd; |
|
5039 |
|
5040 /* |
|
5041 * This allocation requires alignment that is even larger than chunk |
|
5042 * alignment. This means that huge_malloc() isn't good enough. |
|
5043 * |
|
5044 * Allocate almost twice as many chunks as are demanded by the size or |
|
5045 * alignment, in order to assure the alignment can be achieved, then |
|
5046 * unmap leading and trailing chunks. |
|
5047 */ |
|
5048 assert(alignment >= chunksize); |
|
5049 |
|
5050 chunk_size = CHUNK_CEILING(size); |
|
5051 |
|
5052 if (size >= alignment) |
|
5053 alloc_size = chunk_size + alignment - chunksize; |
|
5054 else |
|
5055 alloc_size = (alignment << 1) - chunksize; |
|
5056 |
|
5057 /* Allocate an extent node with which to track the chunk. */ |
|
5058 node = base_node_alloc(); |
|
5059 if (node == NULL) |
|
5060 return (NULL); |
|
5061 |
|
5062 /* |
|
5063 * Windows requires that there be a 1:1 mapping between VM |
|
5064 * allocation/deallocation operations. Therefore, take care here to |
|
5065 * acquire the final result via one mapping operation. |
|
5066 * |
|
5067 * The MALLOC_PAGEFILE code also benefits from this mapping algorithm, |
|
5068 * since it reduces the number of page files. |
|
5069 */ |
|
5070 #ifdef MALLOC_PAGEFILE |
|
5071 if (opt_pagefile) { |
|
5072 pfd = pagefile_init(size); |
|
5073 if (pfd == -1) |
|
5074 return (NULL); |
|
5075 } else |
|
5076 #endif |
|
5077 pfd = -1; |
|
5078 #ifdef JEMALLOC_USES_MAP_ALIGN |
|
5079 ret = pages_map_align(chunk_size, pfd, alignment); |
|
5080 #else |
|
5081 do { |
|
5082 void *over; |
|
5083 |
|
5084 over = chunk_alloc(alloc_size, false, false); |
|
5085 if (over == NULL) { |
|
5086 base_node_dealloc(node); |
|
5087 ret = NULL; |
|
5088 goto RETURN; |
|
5089 } |
|
5090 |
|
5091 offset = (uintptr_t)over & (alignment - 1); |
|
5092 assert((offset & chunksize_mask) == 0); |
|
5093 assert(offset < alloc_size); |
|
5094 ret = (void *)((uintptr_t)over + offset); |
|
5095 chunk_dealloc(over, alloc_size); |
|
5096 ret = pages_map(ret, chunk_size, pfd); |
|
5097 /* |
|
5098 * Failure here indicates a race with another thread, so try |
|
5099 * again. |
|
5100 */ |
|
5101 } while (ret == NULL); |
|
5102 #endif |
|
5103 /* Insert node into huge. */ |
|
5104 node->addr = ret; |
|
5105 psize = PAGE_CEILING(size); |
|
5106 node->size = psize; |
|
5107 |
|
5108 malloc_mutex_lock(&huge_mtx); |
|
5109 extent_tree_ad_insert(&huge, node); |
|
5110 #ifdef MALLOC_STATS |
|
5111 huge_nmalloc++; |
|
5112 /* See note in huge_alloc() for why huge_allocated += psize is correct |
|
5113 * here even when DECOMMIT is not defined. */ |
|
5114 huge_allocated += psize; |
|
5115 huge_mapped += chunk_size; |
|
5116 #endif |
|
5117 malloc_mutex_unlock(&huge_mtx); |
|
5118 |
|
5119 #ifdef MALLOC_DECOMMIT |
|
5120 if (chunk_size - psize > 0) { |
|
5121 pages_decommit((void *)((uintptr_t)ret + psize), |
|
5122 chunk_size - psize); |
|
5123 } |
|
5124 #endif |
|
5125 |
|
5126 #ifdef MALLOC_DECOMMIT |
|
5127 VALGRIND_MALLOCLIKE_BLOCK(ret, psize, 0, false); |
|
5128 #else |
|
5129 VALGRIND_MALLOCLIKE_BLOCK(ret, chunk_size, 0, false); |
|
5130 #endif |
|
5131 |
|
5132 #ifdef MALLOC_FILL |
|
5133 if (opt_junk) |
|
5134 # ifdef MALLOC_DECOMMIT |
|
5135 memset(ret, 0xa5, psize); |
|
5136 # else |
|
5137 memset(ret, 0xa5, chunk_size); |
|
5138 # endif |
|
5139 else if (opt_zero) |
|
5140 # ifdef MALLOC_DECOMMIT |
|
5141 memset(ret, 0, psize); |
|
5142 # else |
|
5143 memset(ret, 0, chunk_size); |
|
5144 # endif |
|
5145 #endif |
|
5146 |
|
5147 RETURN: |
|
5148 #ifdef MALLOC_PAGEFILE |
|
5149 if (pfd != -1) |
|
5150 pagefile_close(pfd); |
|
5151 #endif |
|
5152 return (ret); |
|
5153 } |
|
5154 |
|
5155 static void * |
|
5156 huge_ralloc(void *ptr, size_t size, size_t oldsize) |
|
5157 { |
|
5158 void *ret; |
|
5159 size_t copysize; |
|
5160 |
|
5161 /* Avoid moving the allocation if the size class would not change. */ |
|
5162 |
|
5163 if (oldsize > arena_maxclass && |
|
5164 CHUNK_CEILING(size) == CHUNK_CEILING(oldsize)) { |
|
5165 size_t psize = PAGE_CEILING(size); |
|
5166 #ifdef MALLOC_FILL |
|
5167 if (opt_poison && size < oldsize) { |
|
5168 memset((void *)((uintptr_t)ptr + size), 0x5a, oldsize |
|
5169 - size); |
|
5170 } |
|
5171 #endif |
|
5172 #ifdef MALLOC_DECOMMIT |
|
5173 if (psize < oldsize) { |
|
5174 extent_node_t *node, key; |
|
5175 |
|
5176 pages_decommit((void *)((uintptr_t)ptr + psize), |
|
5177 oldsize - psize); |
|
5178 |
|
5179 /* Update recorded size. */ |
|
5180 malloc_mutex_lock(&huge_mtx); |
|
5181 key.addr = __DECONST(void *, ptr); |
|
5182 node = extent_tree_ad_search(&huge, &key); |
|
5183 assert(node != NULL); |
|
5184 assert(node->size == oldsize); |
|
5185 # ifdef MALLOC_STATS |
|
5186 huge_allocated -= oldsize - psize; |
|
5187 /* No need to change huge_mapped, because we didn't |
|
5188 * (un)map anything. */ |
|
5189 # endif |
|
5190 node->size = psize; |
|
5191 malloc_mutex_unlock(&huge_mtx); |
|
5192 } else if (psize > oldsize) { |
|
5193 pages_commit((void *)((uintptr_t)ptr + oldsize), |
|
5194 psize - oldsize); |
|
5195 } |
|
5196 #endif |
|
5197 |
|
5198 /* Although we don't have to commit or decommit anything if |
|
5199 * DECOMMIT is not defined and the size class didn't change, we |
|
5200 * do need to update the recorded size if the size increased, |
|
5201 * so malloc_usable_size doesn't return a value smaller than |
|
5202 * what was requested via realloc(). */ |
|
5203 |
|
5204 if (psize > oldsize) { |
|
5205 /* Update recorded size. */ |
|
5206 extent_node_t *node, key; |
|
5207 malloc_mutex_lock(&huge_mtx); |
|
5208 key.addr = __DECONST(void *, ptr); |
|
5209 node = extent_tree_ad_search(&huge, &key); |
|
5210 assert(node != NULL); |
|
5211 assert(node->size == oldsize); |
|
5212 # ifdef MALLOC_STATS |
|
5213 huge_allocated += psize - oldsize; |
|
5214 /* No need to change huge_mapped, because we didn't |
|
5215 * (un)map anything. */ |
|
5216 # endif |
|
5217 node->size = psize; |
|
5218 malloc_mutex_unlock(&huge_mtx); |
|
5219 } |
|
5220 |
|
5221 #ifdef MALLOC_FILL |
|
5222 if (opt_zero && size > oldsize) { |
|
5223 memset((void *)((uintptr_t)ptr + oldsize), 0, size |
|
5224 - oldsize); |
|
5225 } |
|
5226 #endif |
|
5227 return (ptr); |
|
5228 } |
|
5229 |
|
5230 /* |
|
5231 * If we get here, then size and oldsize are different enough that we |
|
5232 * need to use a different size class. In that case, fall back to |
|
5233 * allocating new space and copying. |
|
5234 */ |
|
5235 ret = huge_malloc(size, false); |
|
5236 if (ret == NULL) |
|
5237 return (NULL); |
|
5238 |
|
5239 copysize = (size < oldsize) ? size : oldsize; |
|
5240 #ifdef VM_COPY_MIN |
|
5241 if (copysize >= VM_COPY_MIN) |
|
5242 pages_copy(ret, ptr, copysize); |
|
5243 else |
|
5244 #endif |
|
5245 memcpy(ret, ptr, copysize); |
|
5246 idalloc(ptr); |
|
5247 return (ret); |
|
5248 } |
|
5249 |
|
5250 static void |
|
5251 huge_dalloc(void *ptr) |
|
5252 { |
|
5253 extent_node_t *node, key; |
|
5254 |
|
5255 malloc_mutex_lock(&huge_mtx); |
|
5256 |
|
5257 /* Extract from tree of huge allocations. */ |
|
5258 key.addr = ptr; |
|
5259 node = extent_tree_ad_search(&huge, &key); |
|
5260 assert(node != NULL); |
|
5261 assert(node->addr == ptr); |
|
5262 extent_tree_ad_remove(&huge, node); |
|
5263 |
|
5264 #ifdef MALLOC_STATS |
|
5265 huge_ndalloc++; |
|
5266 huge_allocated -= node->size; |
|
5267 huge_mapped -= CHUNK_CEILING(node->size); |
|
5268 #endif |
|
5269 |
|
5270 malloc_mutex_unlock(&huge_mtx); |
|
5271 |
|
5272 /* Unmap chunk. */ |
|
5273 #ifdef MALLOC_FILL |
|
5274 if (opt_junk) |
|
5275 memset(node->addr, 0x5a, node->size); |
|
5276 #endif |
|
5277 chunk_dealloc(node->addr, CHUNK_CEILING(node->size)); |
|
5278 VALGRIND_FREELIKE_BLOCK(node->addr, 0); |
|
5279 |
|
5280 base_node_dealloc(node); |
|
5281 } |
|
5282 |
|
5283 #ifndef MOZ_MEMORY_NARENAS_DEFAULT_ONE |
|
5284 #ifdef MOZ_MEMORY_BSD |
|
5285 static inline unsigned |
|
5286 malloc_ncpus(void) |
|
5287 { |
|
5288 unsigned ret; |
|
5289 int mib[2]; |
|
5290 size_t len; |
|
5291 |
|
5292 mib[0] = CTL_HW; |
|
5293 mib[1] = HW_NCPU; |
|
5294 len = sizeof(ret); |
|
5295 if (sysctl(mib, 2, &ret, &len, (void *) 0, 0) == -1) { |
|
5296 /* Error. */ |
|
5297 return (1); |
|
5298 } |
|
5299 |
|
5300 return (ret); |
|
5301 } |
|
5302 #elif (defined(MOZ_MEMORY_LINUX)) |
|
5303 #include <fcntl.h> |
|
5304 |
|
5305 static inline unsigned |
|
5306 malloc_ncpus(void) |
|
5307 { |
|
5308 unsigned ret; |
|
5309 int fd, nread, column; |
|
5310 char buf[1024]; |
|
5311 static const char matchstr[] = "processor\t:"; |
|
5312 int i; |
|
5313 |
|
5314 /* |
|
5315 * sysconf(3) would be the preferred method for determining the number |
|
5316 * of CPUs, but it uses malloc internally, which causes untennable |
|
5317 * recursion during malloc initialization. |
|
5318 */ |
|
5319 fd = open("/proc/cpuinfo", O_RDONLY); |
|
5320 if (fd == -1) |
|
5321 return (1); /* Error. */ |
|
5322 /* |
|
5323 * Count the number of occurrences of matchstr at the beginnings of |
|
5324 * lines. This treats hyperthreaded CPUs as multiple processors. |
|
5325 */ |
|
5326 column = 0; |
|
5327 ret = 0; |
|
5328 while (true) { |
|
5329 nread = read(fd, &buf, sizeof(buf)); |
|
5330 if (nread <= 0) |
|
5331 break; /* EOF or error. */ |
|
5332 for (i = 0;i < nread;i++) { |
|
5333 char c = buf[i]; |
|
5334 if (c == '\n') |
|
5335 column = 0; |
|
5336 else if (column != -1) { |
|
5337 if (c == matchstr[column]) { |
|
5338 column++; |
|
5339 if (column == sizeof(matchstr) - 1) { |
|
5340 column = -1; |
|
5341 ret++; |
|
5342 } |
|
5343 } else |
|
5344 column = -1; |
|
5345 } |
|
5346 } |
|
5347 } |
|
5348 |
|
5349 if (ret == 0) |
|
5350 ret = 1; /* Something went wrong in the parser. */ |
|
5351 close(fd); |
|
5352 |
|
5353 return (ret); |
|
5354 } |
|
5355 #elif (defined(MOZ_MEMORY_DARWIN)) |
|
5356 #include <mach/mach_init.h> |
|
5357 #include <mach/mach_host.h> |
|
5358 |
|
5359 static inline unsigned |
|
5360 malloc_ncpus(void) |
|
5361 { |
|
5362 kern_return_t error; |
|
5363 natural_t n; |
|
5364 processor_info_array_t pinfo; |
|
5365 mach_msg_type_number_t pinfocnt; |
|
5366 |
|
5367 error = host_processor_info(mach_host_self(), PROCESSOR_BASIC_INFO, |
|
5368 &n, &pinfo, &pinfocnt); |
|
5369 if (error != KERN_SUCCESS) |
|
5370 return (1); /* Error. */ |
|
5371 else |
|
5372 return (n); |
|
5373 } |
|
5374 #elif (defined(MOZ_MEMORY_SOLARIS)) |
|
5375 |
|
5376 static inline unsigned |
|
5377 malloc_ncpus(void) |
|
5378 { |
|
5379 return sysconf(_SC_NPROCESSORS_ONLN); |
|
5380 } |
|
5381 #else |
|
5382 static inline unsigned |
|
5383 malloc_ncpus(void) |
|
5384 { |
|
5385 |
|
5386 /* |
|
5387 * We lack a way to determine the number of CPUs on this platform, so |
|
5388 * assume 1 CPU. |
|
5389 */ |
|
5390 return (1); |
|
5391 } |
|
5392 #endif |
|
5393 #endif |
|
5394 |
|
5395 static void |
|
5396 malloc_print_stats(void) |
|
5397 { |
|
5398 |
|
5399 if (opt_print_stats) { |
|
5400 char s[UMAX2S_BUFSIZE]; |
|
5401 _malloc_message("___ Begin malloc statistics ___\n", "", "", |
|
5402 ""); |
|
5403 _malloc_message("Assertions ", |
|
5404 #ifdef NDEBUG |
|
5405 "disabled", |
|
5406 #else |
|
5407 "enabled", |
|
5408 #endif |
|
5409 "\n", ""); |
|
5410 _malloc_message("Boolean MALLOC_OPTIONS: ", |
|
5411 opt_abort ? "A" : "a", "", ""); |
|
5412 #ifdef MALLOC_FILL |
|
5413 _malloc_message(opt_poison ? "C" : "c", "", "", ""); |
|
5414 _malloc_message(opt_junk ? "J" : "j", "", "", ""); |
|
5415 #endif |
|
5416 #ifdef MALLOC_PAGEFILE |
|
5417 _malloc_message(opt_pagefile ? "o" : "O", "", "", ""); |
|
5418 #endif |
|
5419 _malloc_message("P", "", "", ""); |
|
5420 #ifdef MALLOC_UTRACE |
|
5421 _malloc_message(opt_utrace ? "U" : "u", "", "", ""); |
|
5422 #endif |
|
5423 #ifdef MALLOC_SYSV |
|
5424 _malloc_message(opt_sysv ? "V" : "v", "", "", ""); |
|
5425 #endif |
|
5426 #ifdef MALLOC_XMALLOC |
|
5427 _malloc_message(opt_xmalloc ? "X" : "x", "", "", ""); |
|
5428 #endif |
|
5429 #ifdef MALLOC_FILL |
|
5430 _malloc_message(opt_zero ? "Z" : "z", "", "", ""); |
|
5431 #endif |
|
5432 _malloc_message("\n", "", "", ""); |
|
5433 |
|
5434 #ifndef MOZ_MEMORY_NARENAS_DEFAULT_ONE |
|
5435 _malloc_message("CPUs: ", umax2s(ncpus, 10, s), "\n", ""); |
|
5436 #endif |
|
5437 _malloc_message("Max arenas: ", umax2s(narenas, 10, s), "\n", |
|
5438 ""); |
|
5439 #ifdef MALLOC_BALANCE |
|
5440 _malloc_message("Arena balance threshold: ", |
|
5441 umax2s(opt_balance_threshold, 10, s), "\n", ""); |
|
5442 #endif |
|
5443 _malloc_message("Pointer size: ", umax2s(sizeof(void *), 10, s), |
|
5444 "\n", ""); |
|
5445 _malloc_message("Quantum size: ", umax2s(quantum, 10, s), "\n", |
|
5446 ""); |
|
5447 _malloc_message("Max small size: ", umax2s(small_max, 10, s), |
|
5448 "\n", ""); |
|
5449 _malloc_message("Max dirty pages per arena: ", |
|
5450 umax2s(opt_dirty_max, 10, s), "\n", ""); |
|
5451 |
|
5452 _malloc_message("Chunk size: ", umax2s(chunksize, 10, s), "", |
|
5453 ""); |
|
5454 _malloc_message(" (2^", umax2s(opt_chunk_2pow, 10, s), ")\n", |
|
5455 ""); |
|
5456 |
|
5457 #ifdef MALLOC_STATS |
|
5458 { |
|
5459 size_t allocated, mapped = 0; |
|
5460 #ifdef MALLOC_BALANCE |
|
5461 uint64_t nbalance = 0; |
|
5462 #endif |
|
5463 unsigned i; |
|
5464 arena_t *arena; |
|
5465 |
|
5466 /* Calculate and print allocated/mapped stats. */ |
|
5467 |
|
5468 /* arenas. */ |
|
5469 for (i = 0, allocated = 0; i < narenas; i++) { |
|
5470 if (arenas[i] != NULL) { |
|
5471 malloc_spin_lock(&arenas[i]->lock); |
|
5472 allocated += |
|
5473 arenas[i]->stats.allocated_small; |
|
5474 allocated += |
|
5475 arenas[i]->stats.allocated_large; |
|
5476 mapped += arenas[i]->stats.mapped; |
|
5477 #ifdef MALLOC_BALANCE |
|
5478 nbalance += arenas[i]->stats.nbalance; |
|
5479 #endif |
|
5480 malloc_spin_unlock(&arenas[i]->lock); |
|
5481 } |
|
5482 } |
|
5483 |
|
5484 /* huge/base. */ |
|
5485 malloc_mutex_lock(&huge_mtx); |
|
5486 allocated += huge_allocated; |
|
5487 mapped += huge_mapped; |
|
5488 malloc_mutex_unlock(&huge_mtx); |
|
5489 |
|
5490 malloc_mutex_lock(&base_mtx); |
|
5491 mapped += base_mapped; |
|
5492 malloc_mutex_unlock(&base_mtx); |
|
5493 |
|
5494 #ifdef MOZ_MEMORY_WINDOWS |
|
5495 malloc_printf("Allocated: %lu, mapped: %lu\n", |
|
5496 allocated, mapped); |
|
5497 #else |
|
5498 malloc_printf("Allocated: %zu, mapped: %zu\n", |
|
5499 allocated, mapped); |
|
5500 #endif |
|
5501 |
|
5502 #ifdef MALLOC_BALANCE |
|
5503 malloc_printf("Arena balance reassignments: %llu\n", |
|
5504 nbalance); |
|
5505 #endif |
|
5506 |
|
5507 /* Print chunk stats. */ |
|
5508 malloc_printf( |
|
5509 "huge: nmalloc ndalloc allocated\n"); |
|
5510 #ifdef MOZ_MEMORY_WINDOWS |
|
5511 malloc_printf(" %12llu %12llu %12lu\n", |
|
5512 huge_nmalloc, huge_ndalloc, huge_allocated); |
|
5513 #else |
|
5514 malloc_printf(" %12llu %12llu %12zu\n", |
|
5515 huge_nmalloc, huge_ndalloc, huge_allocated); |
|
5516 #endif |
|
5517 /* Print stats for each arena. */ |
|
5518 for (i = 0; i < narenas; i++) { |
|
5519 arena = arenas[i]; |
|
5520 if (arena != NULL) { |
|
5521 malloc_printf( |
|
5522 "\narenas[%u]:\n", i); |
|
5523 malloc_spin_lock(&arena->lock); |
|
5524 stats_print(arena); |
|
5525 malloc_spin_unlock(&arena->lock); |
|
5526 } |
|
5527 } |
|
5528 } |
|
5529 #endif /* #ifdef MALLOC_STATS */ |
|
5530 _malloc_message("--- End malloc statistics ---\n", "", "", ""); |
|
5531 } |
|
5532 } |
|
5533 |
|
5534 /* |
|
5535 * FreeBSD's pthreads implementation calls malloc(3), so the malloc |
|
5536 * implementation has to take pains to avoid infinite recursion during |
|
5537 * initialization. |
|
5538 */ |
|
5539 #if (defined(MOZ_MEMORY_WINDOWS) || defined(MOZ_MEMORY_DARWIN)) |
|
5540 #define malloc_init() false |
|
5541 #else |
|
5542 static inline bool |
|
5543 malloc_init(void) |
|
5544 { |
|
5545 |
|
5546 if (malloc_initialized == false) |
|
5547 return (malloc_init_hard()); |
|
5548 |
|
5549 return (false); |
|
5550 } |
|
5551 #endif |
|
5552 |
|
5553 #if !defined(MOZ_MEMORY_WINDOWS) |
|
5554 static |
|
5555 #endif |
|
5556 bool |
|
5557 malloc_init_hard(void) |
|
5558 { |
|
5559 unsigned i; |
|
5560 char buf[PATH_MAX + 1]; |
|
5561 const char *opts; |
|
5562 long result; |
|
5563 #ifndef MOZ_MEMORY_WINDOWS |
|
5564 int linklen; |
|
5565 #endif |
|
5566 #ifdef MOZ_MEMORY_DARWIN |
|
5567 malloc_zone_t* default_zone; |
|
5568 #endif |
|
5569 |
|
5570 #ifndef MOZ_MEMORY_WINDOWS |
|
5571 malloc_mutex_lock(&init_lock); |
|
5572 #endif |
|
5573 |
|
5574 if (malloc_initialized) { |
|
5575 /* |
|
5576 * Another thread initialized the allocator before this one |
|
5577 * acquired init_lock. |
|
5578 */ |
|
5579 #ifndef MOZ_MEMORY_WINDOWS |
|
5580 malloc_mutex_unlock(&init_lock); |
|
5581 #endif |
|
5582 return (false); |
|
5583 } |
|
5584 |
|
5585 #ifdef MOZ_MEMORY_WINDOWS |
|
5586 /* get a thread local storage index */ |
|
5587 tlsIndex = TlsAlloc(); |
|
5588 #endif |
|
5589 |
|
5590 /* Get page size and number of CPUs */ |
|
5591 #ifdef MOZ_MEMORY_WINDOWS |
|
5592 { |
|
5593 SYSTEM_INFO info; |
|
5594 |
|
5595 GetSystemInfo(&info); |
|
5596 result = info.dwPageSize; |
|
5597 |
|
5598 #ifndef MOZ_MEMORY_NARENAS_DEFAULT_ONE |
|
5599 ncpus = info.dwNumberOfProcessors; |
|
5600 #endif |
|
5601 } |
|
5602 #else |
|
5603 #ifndef MOZ_MEMORY_NARENAS_DEFAULT_ONE |
|
5604 ncpus = malloc_ncpus(); |
|
5605 #endif |
|
5606 |
|
5607 result = sysconf(_SC_PAGESIZE); |
|
5608 assert(result != -1); |
|
5609 #endif |
|
5610 |
|
5611 /* We assume that the page size is a power of 2. */ |
|
5612 assert(((result - 1) & result) == 0); |
|
5613 #ifdef MALLOC_STATIC_SIZES |
|
5614 if (pagesize % (size_t) result) { |
|
5615 _malloc_message(_getprogname(), |
|
5616 "Compile-time page size does not divide the runtime one.\n", |
|
5617 "", ""); |
|
5618 abort(); |
|
5619 } |
|
5620 #else |
|
5621 pagesize = (size_t) result; |
|
5622 pagesize_mask = (size_t) result - 1; |
|
5623 pagesize_2pow = ffs((int)result) - 1; |
|
5624 #endif |
|
5625 |
|
5626 #ifdef MALLOC_PAGEFILE |
|
5627 /* |
|
5628 * Determine where to create page files. It is insufficient to |
|
5629 * unconditionally use P_tmpdir (typically "/tmp"), since for some |
|
5630 * operating systems /tmp is a separate filesystem that is rather small. |
|
5631 * Therefore prefer, in order, the following locations: |
|
5632 * |
|
5633 * 1) MALLOC_TMPDIR |
|
5634 * 2) TMPDIR |
|
5635 * 3) P_tmpdir |
|
5636 */ |
|
5637 { |
|
5638 char *s; |
|
5639 size_t slen; |
|
5640 static const char suffix[] = "/jemalloc.XXXXXX"; |
|
5641 |
|
5642 if ((s = getenv("MALLOC_TMPDIR")) == NULL && (s = |
|
5643 getenv("TMPDIR")) == NULL) |
|
5644 s = P_tmpdir; |
|
5645 slen = strlen(s); |
|
5646 if (slen + sizeof(suffix) > sizeof(pagefile_templ)) { |
|
5647 _malloc_message(_getprogname(), |
|
5648 ": (malloc) Page file path too long\n", |
|
5649 "", ""); |
|
5650 abort(); |
|
5651 } |
|
5652 memcpy(pagefile_templ, s, slen); |
|
5653 memcpy(&pagefile_templ[slen], suffix, sizeof(suffix)); |
|
5654 } |
|
5655 #endif |
|
5656 |
|
5657 for (i = 0; i < 3; i++) { |
|
5658 unsigned j; |
|
5659 |
|
5660 /* Get runtime configuration. */ |
|
5661 switch (i) { |
|
5662 case 0: |
|
5663 #ifndef MOZ_MEMORY_WINDOWS |
|
5664 if ((linklen = readlink("/etc/malloc.conf", buf, |
|
5665 sizeof(buf) - 1)) != -1) { |
|
5666 /* |
|
5667 * Use the contents of the "/etc/malloc.conf" |
|
5668 * symbolic link's name. |
|
5669 */ |
|
5670 buf[linklen] = '\0'; |
|
5671 opts = buf; |
|
5672 } else |
|
5673 #endif |
|
5674 { |
|
5675 /* No configuration specified. */ |
|
5676 buf[0] = '\0'; |
|
5677 opts = buf; |
|
5678 } |
|
5679 break; |
|
5680 case 1: |
|
5681 if (issetugid() == 0 && (opts = |
|
5682 getenv("MALLOC_OPTIONS")) != NULL) { |
|
5683 /* |
|
5684 * Do nothing; opts is already initialized to |
|
5685 * the value of the MALLOC_OPTIONS environment |
|
5686 * variable. |
|
5687 */ |
|
5688 } else { |
|
5689 /* No configuration specified. */ |
|
5690 buf[0] = '\0'; |
|
5691 opts = buf; |
|
5692 } |
|
5693 break; |
|
5694 case 2: |
|
5695 if (_malloc_options != NULL) { |
|
5696 /* |
|
5697 * Use options that were compiled into the |
|
5698 * program. |
|
5699 */ |
|
5700 opts = _malloc_options; |
|
5701 } else { |
|
5702 /* No configuration specified. */ |
|
5703 buf[0] = '\0'; |
|
5704 opts = buf; |
|
5705 } |
|
5706 break; |
|
5707 default: |
|
5708 /* NOTREACHED */ |
|
5709 buf[0] = '\0'; |
|
5710 opts = buf; |
|
5711 assert(false); |
|
5712 } |
|
5713 |
|
5714 for (j = 0; opts[j] != '\0'; j++) { |
|
5715 unsigned k, nreps; |
|
5716 bool nseen; |
|
5717 |
|
5718 /* Parse repetition count, if any. */ |
|
5719 for (nreps = 0, nseen = false;; j++, nseen = true) { |
|
5720 switch (opts[j]) { |
|
5721 case '0': case '1': case '2': case '3': |
|
5722 case '4': case '5': case '6': case '7': |
|
5723 case '8': case '9': |
|
5724 nreps *= 10; |
|
5725 nreps += opts[j] - '0'; |
|
5726 break; |
|
5727 default: |
|
5728 goto MALLOC_OUT; |
|
5729 } |
|
5730 } |
|
5731 MALLOC_OUT: |
|
5732 if (nseen == false) |
|
5733 nreps = 1; |
|
5734 |
|
5735 for (k = 0; k < nreps; k++) { |
|
5736 switch (opts[j]) { |
|
5737 case 'a': |
|
5738 opt_abort = false; |
|
5739 break; |
|
5740 case 'A': |
|
5741 opt_abort = true; |
|
5742 break; |
|
5743 case 'b': |
|
5744 #ifdef MALLOC_BALANCE |
|
5745 opt_balance_threshold >>= 1; |
|
5746 #endif |
|
5747 break; |
|
5748 case 'B': |
|
5749 #ifdef MALLOC_BALANCE |
|
5750 if (opt_balance_threshold == 0) |
|
5751 opt_balance_threshold = 1; |
|
5752 else if ((opt_balance_threshold << 1) |
|
5753 > opt_balance_threshold) |
|
5754 opt_balance_threshold <<= 1; |
|
5755 #endif |
|
5756 break; |
|
5757 #ifdef MALLOC_FILL |
|
5758 #ifndef MALLOC_PRODUCTION |
|
5759 case 'c': |
|
5760 opt_poison = false; |
|
5761 break; |
|
5762 case 'C': |
|
5763 opt_poison = true; |
|
5764 break; |
|
5765 #endif |
|
5766 #endif |
|
5767 case 'f': |
|
5768 opt_dirty_max >>= 1; |
|
5769 break; |
|
5770 case 'F': |
|
5771 if (opt_dirty_max == 0) |
|
5772 opt_dirty_max = 1; |
|
5773 else if ((opt_dirty_max << 1) != 0) |
|
5774 opt_dirty_max <<= 1; |
|
5775 break; |
|
5776 #ifdef MALLOC_FILL |
|
5777 #ifndef MALLOC_PRODUCTION |
|
5778 case 'j': |
|
5779 opt_junk = false; |
|
5780 break; |
|
5781 case 'J': |
|
5782 opt_junk = true; |
|
5783 break; |
|
5784 #endif |
|
5785 #endif |
|
5786 #ifndef MALLOC_STATIC_SIZES |
|
5787 case 'k': |
|
5788 /* |
|
5789 * Chunks always require at least one |
|
5790 * header page, so chunks can never be |
|
5791 * smaller than two pages. |
|
5792 */ |
|
5793 if (opt_chunk_2pow > pagesize_2pow + 1) |
|
5794 opt_chunk_2pow--; |
|
5795 break; |
|
5796 case 'K': |
|
5797 if (opt_chunk_2pow + 1 < |
|
5798 (sizeof(size_t) << 3)) |
|
5799 opt_chunk_2pow++; |
|
5800 break; |
|
5801 #endif |
|
5802 case 'n': |
|
5803 opt_narenas_lshift--; |
|
5804 break; |
|
5805 case 'N': |
|
5806 opt_narenas_lshift++; |
|
5807 break; |
|
5808 #ifdef MALLOC_PAGEFILE |
|
5809 case 'o': |
|
5810 /* Do not over-commit. */ |
|
5811 opt_pagefile = true; |
|
5812 break; |
|
5813 case 'O': |
|
5814 /* Allow over-commit. */ |
|
5815 opt_pagefile = false; |
|
5816 break; |
|
5817 #endif |
|
5818 case 'p': |
|
5819 opt_print_stats = false; |
|
5820 break; |
|
5821 case 'P': |
|
5822 opt_print_stats = true; |
|
5823 break; |
|
5824 #ifndef MALLOC_STATIC_SIZES |
|
5825 case 'q': |
|
5826 if (opt_quantum_2pow > QUANTUM_2POW_MIN) |
|
5827 opt_quantum_2pow--; |
|
5828 break; |
|
5829 case 'Q': |
|
5830 if (opt_quantum_2pow < pagesize_2pow - |
|
5831 1) |
|
5832 opt_quantum_2pow++; |
|
5833 break; |
|
5834 case 's': |
|
5835 if (opt_small_max_2pow > |
|
5836 QUANTUM_2POW_MIN) |
|
5837 opt_small_max_2pow--; |
|
5838 break; |
|
5839 case 'S': |
|
5840 if (opt_small_max_2pow < pagesize_2pow |
|
5841 - 1) |
|
5842 opt_small_max_2pow++; |
|
5843 break; |
|
5844 #endif |
|
5845 #ifdef MALLOC_UTRACE |
|
5846 case 'u': |
|
5847 opt_utrace = false; |
|
5848 break; |
|
5849 case 'U': |
|
5850 opt_utrace = true; |
|
5851 break; |
|
5852 #endif |
|
5853 #ifdef MALLOC_SYSV |
|
5854 case 'v': |
|
5855 opt_sysv = false; |
|
5856 break; |
|
5857 case 'V': |
|
5858 opt_sysv = true; |
|
5859 break; |
|
5860 #endif |
|
5861 #ifdef MALLOC_XMALLOC |
|
5862 case 'x': |
|
5863 opt_xmalloc = false; |
|
5864 break; |
|
5865 case 'X': |
|
5866 opt_xmalloc = true; |
|
5867 break; |
|
5868 #endif |
|
5869 #ifdef MALLOC_FILL |
|
5870 #ifndef MALLOC_PRODUCTION |
|
5871 case 'z': |
|
5872 opt_zero = false; |
|
5873 break; |
|
5874 case 'Z': |
|
5875 opt_zero = true; |
|
5876 break; |
|
5877 #endif |
|
5878 #endif |
|
5879 default: { |
|
5880 char cbuf[2]; |
|
5881 |
|
5882 cbuf[0] = opts[j]; |
|
5883 cbuf[1] = '\0'; |
|
5884 _malloc_message(_getprogname(), |
|
5885 ": (malloc) Unsupported character " |
|
5886 "in malloc options: '", cbuf, |
|
5887 "'\n"); |
|
5888 } |
|
5889 } |
|
5890 } |
|
5891 } |
|
5892 } |
|
5893 |
|
5894 /* Take care to call atexit() only once. */ |
|
5895 if (opt_print_stats) { |
|
5896 #ifndef MOZ_MEMORY_WINDOWS |
|
5897 /* Print statistics at exit. */ |
|
5898 atexit(malloc_print_stats); |
|
5899 #endif |
|
5900 } |
|
5901 |
|
5902 #if !defined(MOZ_MEMORY_WINDOWS) && !defined(MOZ_MEMORY_DARWIN) |
|
5903 /* Prevent potential deadlock on malloc locks after fork. */ |
|
5904 pthread_atfork(_malloc_prefork, _malloc_postfork, _malloc_postfork); |
|
5905 #endif |
|
5906 |
|
5907 #ifndef MALLOC_STATIC_SIZES |
|
5908 /* Set variables according to the value of opt_small_max_2pow. */ |
|
5909 if (opt_small_max_2pow < opt_quantum_2pow) |
|
5910 opt_small_max_2pow = opt_quantum_2pow; |
|
5911 small_max = (1U << opt_small_max_2pow); |
|
5912 |
|
5913 /* Set bin-related variables. */ |
|
5914 bin_maxclass = (pagesize >> 1); |
|
5915 assert(opt_quantum_2pow >= TINY_MIN_2POW); |
|
5916 ntbins = opt_quantum_2pow - TINY_MIN_2POW; |
|
5917 assert(ntbins <= opt_quantum_2pow); |
|
5918 nqbins = (small_max >> opt_quantum_2pow); |
|
5919 nsbins = pagesize_2pow - opt_small_max_2pow - 1; |
|
5920 |
|
5921 /* Set variables according to the value of opt_quantum_2pow. */ |
|
5922 quantum = (1U << opt_quantum_2pow); |
|
5923 quantum_mask = quantum - 1; |
|
5924 if (ntbins > 0) |
|
5925 small_min = (quantum >> 1) + 1; |
|
5926 else |
|
5927 small_min = 1; |
|
5928 assert(small_min <= quantum); |
|
5929 |
|
5930 /* Set variables according to the value of opt_chunk_2pow. */ |
|
5931 chunksize = (1LU << opt_chunk_2pow); |
|
5932 chunksize_mask = chunksize - 1; |
|
5933 chunk_npages = (chunksize >> pagesize_2pow); |
|
5934 |
|
5935 arena_chunk_header_npages = calculate_arena_header_pages(); |
|
5936 arena_maxclass = calculate_arena_maxclass(); |
|
5937 #endif |
|
5938 |
|
5939 #ifdef JEMALLOC_USES_MAP_ALIGN |
|
5940 /* |
|
5941 * When using MAP_ALIGN, the alignment parameter must be a power of two |
|
5942 * multiple of the system pagesize, or mmap will fail. |
|
5943 */ |
|
5944 assert((chunksize % pagesize) == 0); |
|
5945 assert((1 << (ffs(chunksize / pagesize) - 1)) == (chunksize/pagesize)); |
|
5946 #endif |
|
5947 |
|
5948 UTRACE(0, 0, 0); |
|
5949 |
|
5950 /* Various sanity checks that regard configuration. */ |
|
5951 assert(quantum >= sizeof(void *)); |
|
5952 assert(quantum <= pagesize); |
|
5953 assert(chunksize >= pagesize); |
|
5954 assert(quantum * 4 <= chunksize); |
|
5955 |
|
5956 /* Initialize chunks data. */ |
|
5957 malloc_mutex_init(&huge_mtx); |
|
5958 extent_tree_ad_new(&huge); |
|
5959 #ifdef MALLOC_STATS |
|
5960 huge_nmalloc = 0; |
|
5961 huge_ndalloc = 0; |
|
5962 huge_allocated = 0; |
|
5963 huge_mapped = 0; |
|
5964 #endif |
|
5965 |
|
5966 /* Initialize base allocation data structures. */ |
|
5967 #ifdef MALLOC_STATS |
|
5968 base_mapped = 0; |
|
5969 base_committed = 0; |
|
5970 #endif |
|
5971 base_nodes = NULL; |
|
5972 malloc_mutex_init(&base_mtx); |
|
5973 |
|
5974 #ifdef MOZ_MEMORY_NARENAS_DEFAULT_ONE |
|
5975 narenas = 1; |
|
5976 #else |
|
5977 if (ncpus > 1) { |
|
5978 /* |
|
5979 * For SMP systems, create four times as many arenas as there |
|
5980 * are CPUs by default. |
|
5981 */ |
|
5982 opt_narenas_lshift += 2; |
|
5983 } |
|
5984 |
|
5985 /* Determine how many arenas to use. */ |
|
5986 narenas = ncpus; |
|
5987 #endif |
|
5988 if (opt_narenas_lshift > 0) { |
|
5989 if ((narenas << opt_narenas_lshift) > narenas) |
|
5990 narenas <<= opt_narenas_lshift; |
|
5991 /* |
|
5992 * Make sure not to exceed the limits of what base_alloc() can |
|
5993 * handle. |
|
5994 */ |
|
5995 if (narenas * sizeof(arena_t *) > chunksize) |
|
5996 narenas = chunksize / sizeof(arena_t *); |
|
5997 } else if (opt_narenas_lshift < 0) { |
|
5998 if ((narenas >> -opt_narenas_lshift) < narenas) |
|
5999 narenas >>= -opt_narenas_lshift; |
|
6000 /* Make sure there is at least one arena. */ |
|
6001 if (narenas == 0) |
|
6002 narenas = 1; |
|
6003 } |
|
6004 #ifdef MALLOC_BALANCE |
|
6005 assert(narenas != 0); |
|
6006 for (narenas_2pow = 0; |
|
6007 (narenas >> (narenas_2pow + 1)) != 0; |
|
6008 narenas_2pow++); |
|
6009 #endif |
|
6010 |
|
6011 #ifdef NO_TLS |
|
6012 if (narenas > 1) { |
|
6013 static const unsigned primes[] = {1, 3, 5, 7, 11, 13, 17, 19, |
|
6014 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, |
|
6015 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, |
|
6016 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, |
|
6017 223, 227, 229, 233, 239, 241, 251, 257, 263}; |
|
6018 unsigned nprimes, parenas; |
|
6019 |
|
6020 /* |
|
6021 * Pick a prime number of hash arenas that is more than narenas |
|
6022 * so that direct hashing of pthread_self() pointers tends to |
|
6023 * spread allocations evenly among the arenas. |
|
6024 */ |
|
6025 assert((narenas & 1) == 0); /* narenas must be even. */ |
|
6026 nprimes = (sizeof(primes) >> SIZEOF_INT_2POW); |
|
6027 parenas = primes[nprimes - 1]; /* In case not enough primes. */ |
|
6028 for (i = 1; i < nprimes; i++) { |
|
6029 if (primes[i] > narenas) { |
|
6030 parenas = primes[i]; |
|
6031 break; |
|
6032 } |
|
6033 } |
|
6034 narenas = parenas; |
|
6035 } |
|
6036 #endif |
|
6037 |
|
6038 #ifndef NO_TLS |
|
6039 # ifndef MALLOC_BALANCE |
|
6040 next_arena = 0; |
|
6041 # endif |
|
6042 #endif |
|
6043 |
|
6044 /* Allocate and initialize arenas. */ |
|
6045 arenas = (arena_t **)base_alloc(sizeof(arena_t *) * narenas); |
|
6046 if (arenas == NULL) { |
|
6047 #ifndef MOZ_MEMORY_WINDOWS |
|
6048 malloc_mutex_unlock(&init_lock); |
|
6049 #endif |
|
6050 return (true); |
|
6051 } |
|
6052 /* |
|
6053 * Zero the array. In practice, this should always be pre-zeroed, |
|
6054 * since it was just mmap()ed, but let's be sure. |
|
6055 */ |
|
6056 memset(arenas, 0, sizeof(arena_t *) * narenas); |
|
6057 |
|
6058 /* |
|
6059 * Initialize one arena here. The rest are lazily created in |
|
6060 * choose_arena_hard(). |
|
6061 */ |
|
6062 arenas_extend(0); |
|
6063 if (arenas[0] == NULL) { |
|
6064 #ifndef MOZ_MEMORY_WINDOWS |
|
6065 malloc_mutex_unlock(&init_lock); |
|
6066 #endif |
|
6067 return (true); |
|
6068 } |
|
6069 #ifndef NO_TLS |
|
6070 /* |
|
6071 * Assign the initial arena to the initial thread, in order to avoid |
|
6072 * spurious creation of an extra arena if the application switches to |
|
6073 * threaded mode. |
|
6074 */ |
|
6075 #ifdef MOZ_MEMORY_WINDOWS |
|
6076 TlsSetValue(tlsIndex, arenas[0]); |
|
6077 #else |
|
6078 arenas_map = arenas[0]; |
|
6079 #endif |
|
6080 #endif |
|
6081 |
|
6082 /* |
|
6083 * Seed here for the initial thread, since choose_arena_hard() is only |
|
6084 * called for other threads. The seed value doesn't really matter. |
|
6085 */ |
|
6086 #ifdef MALLOC_BALANCE |
|
6087 SPRN(balance, 42); |
|
6088 #endif |
|
6089 |
|
6090 malloc_spin_init(&arenas_lock); |
|
6091 |
|
6092 #ifdef MALLOC_VALIDATE |
|
6093 chunk_rtree = malloc_rtree_new((SIZEOF_PTR << 3) - opt_chunk_2pow); |
|
6094 if (chunk_rtree == NULL) |
|
6095 return (true); |
|
6096 #endif |
|
6097 |
|
6098 malloc_initialized = true; |
|
6099 |
|
6100 #if defined(NEEDS_PTHREAD_MMAP_UNALIGNED_TSD) |
|
6101 if (pthread_key_create(&mmap_unaligned_tsd, NULL) != 0) { |
|
6102 malloc_printf("<jemalloc>: Error in pthread_key_create()\n"); |
|
6103 } |
|
6104 #endif |
|
6105 |
|
6106 #if defined(MOZ_MEMORY_DARWIN) && !defined(MOZ_REPLACE_MALLOC) |
|
6107 /* |
|
6108 * Overwrite the default memory allocator to use jemalloc everywhere. |
|
6109 */ |
|
6110 default_zone = malloc_default_zone(); |
|
6111 |
|
6112 /* |
|
6113 * We only use jemalloc with MacOS 10.6 and 10.7. jemalloc is disabled |
|
6114 * on 32-bit builds (10.5 and 32-bit 10.6) due to bug 702250, an |
|
6115 * apparent MacOS bug. In fact, this code isn't even compiled on |
|
6116 * 32-bit builds. |
|
6117 * |
|
6118 * We'll have to update our code to work with newer versions, because |
|
6119 * the malloc zone layout is likely to change. |
|
6120 */ |
|
6121 |
|
6122 osx_use_jemalloc = (default_zone->version == SNOW_LEOPARD_MALLOC_ZONE_T_VERSION || |
|
6123 default_zone->version == LION_MALLOC_ZONE_T_VERSION); |
|
6124 |
|
6125 /* Allow us dynamically turn off jemalloc for testing. */ |
|
6126 if (getenv("NO_MAC_JEMALLOC")) { |
|
6127 osx_use_jemalloc = false; |
|
6128 #ifdef __i386__ |
|
6129 malloc_printf("Warning: NO_MAC_JEMALLOC has no effect on " |
|
6130 "i386 machines (such as this one).\n"); |
|
6131 #endif |
|
6132 } |
|
6133 |
|
6134 if (osx_use_jemalloc) { |
|
6135 /* |
|
6136 * Convert the default szone to an "overlay zone" that is capable |
|
6137 * of deallocating szone-allocated objects, but allocating new |
|
6138 * objects from jemalloc. |
|
6139 */ |
|
6140 size_t size = zone_version_size(default_zone->version); |
|
6141 szone2ozone(default_zone, size); |
|
6142 } |
|
6143 else { |
|
6144 szone = default_zone; |
|
6145 } |
|
6146 #endif |
|
6147 |
|
6148 #ifndef MOZ_MEMORY_WINDOWS |
|
6149 malloc_mutex_unlock(&init_lock); |
|
6150 #endif |
|
6151 return (false); |
|
6152 } |
|
6153 |
|
6154 /* XXX Why not just expose malloc_print_stats()? */ |
|
6155 #ifdef MOZ_MEMORY_WINDOWS |
|
6156 void |
|
6157 malloc_shutdown() |
|
6158 { |
|
6159 |
|
6160 malloc_print_stats(); |
|
6161 } |
|
6162 #endif |
|
6163 |
|
6164 /* |
|
6165 * End general internal functions. |
|
6166 */ |
|
6167 /******************************************************************************/ |
|
6168 /* |
|
6169 * Begin malloc(3)-compatible functions. |
|
6170 */ |
|
6171 |
|
6172 /* |
|
6173 * Even though we compile with MOZ_MEMORY, we may have to dynamically decide |
|
6174 * not to use jemalloc, as discussed above. However, we call jemalloc |
|
6175 * functions directly from mozalloc. Since it's pretty dangerous to mix the |
|
6176 * allocators, we need to call the OSX allocators from the functions below, |
|
6177 * when osx_use_jemalloc is not (dynamically) set. |
|
6178 * |
|
6179 * Note that we assume jemalloc is enabled on i386. This is safe because the |
|
6180 * only i386 versions of MacOS are 10.5 and 10.6, which we support. We have to |
|
6181 * do this because madvise isn't in the malloc zone struct for 10.5. |
|
6182 * |
|
6183 * This means that NO_MAC_JEMALLOC doesn't work on i386. |
|
6184 */ |
|
6185 #if defined(MOZ_MEMORY_DARWIN) && !defined(__i386__) && !defined(MOZ_REPLACE_MALLOC) |
|
6186 #define DARWIN_ONLY(A) if (!osx_use_jemalloc) { A; } |
|
6187 #else |
|
6188 #define DARWIN_ONLY(A) |
|
6189 #endif |
|
6190 |
|
6191 MOZ_MEMORY_API void * |
|
6192 malloc_impl(size_t size) |
|
6193 { |
|
6194 void *ret; |
|
6195 |
|
6196 DARWIN_ONLY(return (szone->malloc)(szone, size)); |
|
6197 |
|
6198 if (malloc_init()) { |
|
6199 ret = NULL; |
|
6200 goto RETURN; |
|
6201 } |
|
6202 |
|
6203 if (size == 0) { |
|
6204 #ifdef MALLOC_SYSV |
|
6205 if (opt_sysv == false) |
|
6206 #endif |
|
6207 size = 1; |
|
6208 #ifdef MALLOC_SYSV |
|
6209 else { |
|
6210 ret = NULL; |
|
6211 goto RETURN; |
|
6212 } |
|
6213 #endif |
|
6214 } |
|
6215 |
|
6216 ret = imalloc(size); |
|
6217 |
|
6218 RETURN: |
|
6219 if (ret == NULL) { |
|
6220 #ifdef MALLOC_XMALLOC |
|
6221 if (opt_xmalloc) { |
|
6222 _malloc_message(_getprogname(), |
|
6223 ": (malloc) Error in malloc(): out of memory\n", "", |
|
6224 ""); |
|
6225 abort(); |
|
6226 } |
|
6227 #endif |
|
6228 errno = ENOMEM; |
|
6229 } |
|
6230 |
|
6231 UTRACE(0, size, ret); |
|
6232 return (ret); |
|
6233 } |
|
6234 |
|
6235 /* |
|
6236 * In ELF systems the default visibility allows symbols to be preempted at |
|
6237 * runtime. This in turn prevents the uses of memalign in this file from being |
|
6238 * optimized. What we do in here is define two aliasing symbols (they point to |
|
6239 * the same code): memalign and memalign_internal. The internal version has |
|
6240 * hidden visibility and is used in every reference from this file. |
|
6241 * |
|
6242 * For more information on this technique, see section 2.2.7 (Avoid Using |
|
6243 * Exported Symbols) in http://www.akkadia.org/drepper/dsohowto.pdf. |
|
6244 */ |
|
6245 |
|
6246 #ifndef MOZ_REPLACE_MALLOC |
|
6247 #if defined(__GNUC__) && !defined(MOZ_MEMORY_DARWIN) |
|
6248 #define MOZ_MEMORY_ELF |
|
6249 #endif |
|
6250 |
|
6251 #ifdef MOZ_MEMORY_SOLARIS |
|
6252 # ifdef __SUNPRO_C |
|
6253 void * |
|
6254 memalign_impl(size_t alignment, size_t size); |
|
6255 #pragma no_inline(memalign_impl) |
|
6256 # elif (defined(__GNUC__)) |
|
6257 __attribute__((noinline)) |
|
6258 # endif |
|
6259 #else |
|
6260 #if (defined(MOZ_MEMORY_ELF)) |
|
6261 __attribute__((visibility ("hidden"))) |
|
6262 #endif |
|
6263 #endif |
|
6264 #endif /* MOZ_REPLACE_MALLOC */ |
|
6265 |
|
6266 #ifdef MOZ_MEMORY_ELF |
|
6267 #define MEMALIGN memalign_internal |
|
6268 #else |
|
6269 #define MEMALIGN memalign_impl |
|
6270 #endif |
|
6271 |
|
6272 #ifndef MOZ_MEMORY_ELF |
|
6273 MOZ_MEMORY_API |
|
6274 #endif |
|
6275 void * |
|
6276 MEMALIGN(size_t alignment, size_t size) |
|
6277 { |
|
6278 void *ret; |
|
6279 |
|
6280 DARWIN_ONLY(return (szone->memalign)(szone, alignment, size)); |
|
6281 |
|
6282 assert(((alignment - 1) & alignment) == 0); |
|
6283 |
|
6284 if (malloc_init()) { |
|
6285 ret = NULL; |
|
6286 goto RETURN; |
|
6287 } |
|
6288 |
|
6289 if (size == 0) { |
|
6290 #ifdef MALLOC_SYSV |
|
6291 if (opt_sysv == false) |
|
6292 #endif |
|
6293 size = 1; |
|
6294 #ifdef MALLOC_SYSV |
|
6295 else { |
|
6296 ret = NULL; |
|
6297 goto RETURN; |
|
6298 } |
|
6299 #endif |
|
6300 } |
|
6301 |
|
6302 alignment = alignment < sizeof(void*) ? sizeof(void*) : alignment; |
|
6303 ret = ipalloc(alignment, size); |
|
6304 |
|
6305 RETURN: |
|
6306 #ifdef MALLOC_XMALLOC |
|
6307 if (opt_xmalloc && ret == NULL) { |
|
6308 _malloc_message(_getprogname(), |
|
6309 ": (malloc) Error in memalign(): out of memory\n", "", ""); |
|
6310 abort(); |
|
6311 } |
|
6312 #endif |
|
6313 UTRACE(0, size, ret); |
|
6314 return (ret); |
|
6315 } |
|
6316 |
|
6317 #ifdef MOZ_MEMORY_ELF |
|
6318 extern void * |
|
6319 memalign_impl(size_t alignment, size_t size) __attribute__((alias ("memalign_internal"), visibility ("default"))); |
|
6320 #endif |
|
6321 |
|
6322 MOZ_MEMORY_API int |
|
6323 posix_memalign_impl(void **memptr, size_t alignment, size_t size) |
|
6324 { |
|
6325 void *result; |
|
6326 |
|
6327 /* Make sure that alignment is a large enough power of 2. */ |
|
6328 if (((alignment - 1) & alignment) != 0 || alignment < sizeof(void *)) { |
|
6329 #ifdef MALLOC_XMALLOC |
|
6330 if (opt_xmalloc) { |
|
6331 _malloc_message(_getprogname(), |
|
6332 ": (malloc) Error in posix_memalign(): " |
|
6333 "invalid alignment\n", "", ""); |
|
6334 abort(); |
|
6335 } |
|
6336 #endif |
|
6337 return (EINVAL); |
|
6338 } |
|
6339 |
|
6340 /* The 0-->1 size promotion is done in the memalign() call below */ |
|
6341 |
|
6342 result = MEMALIGN(alignment, size); |
|
6343 |
|
6344 if (result == NULL) |
|
6345 return (ENOMEM); |
|
6346 |
|
6347 *memptr = result; |
|
6348 return (0); |
|
6349 } |
|
6350 |
|
6351 MOZ_MEMORY_API void * |
|
6352 aligned_alloc_impl(size_t alignment, size_t size) |
|
6353 { |
|
6354 if (size % alignment) { |
|
6355 #ifdef MALLOC_XMALLOC |
|
6356 if (opt_xmalloc) { |
|
6357 _malloc_message(_getprogname(), |
|
6358 ": (malloc) Error in aligned_alloc(): " |
|
6359 "size is not multiple of alignment\n", "", ""); |
|
6360 abort(); |
|
6361 } |
|
6362 #endif |
|
6363 return (NULL); |
|
6364 } |
|
6365 return MEMALIGN(alignment, size); |
|
6366 } |
|
6367 |
|
6368 MOZ_MEMORY_API void * |
|
6369 valloc_impl(size_t size) |
|
6370 { |
|
6371 return (MEMALIGN(pagesize, size)); |
|
6372 } |
|
6373 |
|
6374 MOZ_MEMORY_API void * |
|
6375 calloc_impl(size_t num, size_t size) |
|
6376 { |
|
6377 void *ret; |
|
6378 size_t num_size; |
|
6379 |
|
6380 DARWIN_ONLY(return (szone->calloc)(szone, num, size)); |
|
6381 |
|
6382 if (malloc_init()) { |
|
6383 num_size = 0; |
|
6384 ret = NULL; |
|
6385 goto RETURN; |
|
6386 } |
|
6387 |
|
6388 num_size = num * size; |
|
6389 if (num_size == 0) { |
|
6390 #ifdef MALLOC_SYSV |
|
6391 if ((opt_sysv == false) && ((num == 0) || (size == 0))) |
|
6392 #endif |
|
6393 num_size = 1; |
|
6394 #ifdef MALLOC_SYSV |
|
6395 else { |
|
6396 ret = NULL; |
|
6397 goto RETURN; |
|
6398 } |
|
6399 #endif |
|
6400 /* |
|
6401 * Try to avoid division here. We know that it isn't possible to |
|
6402 * overflow during multiplication if neither operand uses any of the |
|
6403 * most significant half of the bits in a size_t. |
|
6404 */ |
|
6405 } else if (((num | size) & (SIZE_T_MAX << (sizeof(size_t) << 2))) |
|
6406 && (num_size / size != num)) { |
|
6407 /* size_t overflow. */ |
|
6408 ret = NULL; |
|
6409 goto RETURN; |
|
6410 } |
|
6411 |
|
6412 ret = icalloc(num_size); |
|
6413 |
|
6414 RETURN: |
|
6415 if (ret == NULL) { |
|
6416 #ifdef MALLOC_XMALLOC |
|
6417 if (opt_xmalloc) { |
|
6418 _malloc_message(_getprogname(), |
|
6419 ": (malloc) Error in calloc(): out of memory\n", "", |
|
6420 ""); |
|
6421 abort(); |
|
6422 } |
|
6423 #endif |
|
6424 errno = ENOMEM; |
|
6425 } |
|
6426 |
|
6427 UTRACE(0, num_size, ret); |
|
6428 return (ret); |
|
6429 } |
|
6430 |
|
6431 MOZ_MEMORY_API void * |
|
6432 realloc_impl(void *ptr, size_t size) |
|
6433 { |
|
6434 void *ret; |
|
6435 |
|
6436 DARWIN_ONLY(return (szone->realloc)(szone, ptr, size)); |
|
6437 |
|
6438 if (size == 0) { |
|
6439 #ifdef MALLOC_SYSV |
|
6440 if (opt_sysv == false) |
|
6441 #endif |
|
6442 size = 1; |
|
6443 #ifdef MALLOC_SYSV |
|
6444 else { |
|
6445 if (ptr != NULL) |
|
6446 idalloc(ptr); |
|
6447 ret = NULL; |
|
6448 goto RETURN; |
|
6449 } |
|
6450 #endif |
|
6451 } |
|
6452 |
|
6453 if (ptr != NULL) { |
|
6454 assert(malloc_initialized); |
|
6455 |
|
6456 ret = iralloc(ptr, size); |
|
6457 |
|
6458 if (ret == NULL) { |
|
6459 #ifdef MALLOC_XMALLOC |
|
6460 if (opt_xmalloc) { |
|
6461 _malloc_message(_getprogname(), |
|
6462 ": (malloc) Error in realloc(): out of " |
|
6463 "memory\n", "", ""); |
|
6464 abort(); |
|
6465 } |
|
6466 #endif |
|
6467 errno = ENOMEM; |
|
6468 } |
|
6469 } else { |
|
6470 if (malloc_init()) |
|
6471 ret = NULL; |
|
6472 else |
|
6473 ret = imalloc(size); |
|
6474 |
|
6475 if (ret == NULL) { |
|
6476 #ifdef MALLOC_XMALLOC |
|
6477 if (opt_xmalloc) { |
|
6478 _malloc_message(_getprogname(), |
|
6479 ": (malloc) Error in realloc(): out of " |
|
6480 "memory\n", "", ""); |
|
6481 abort(); |
|
6482 } |
|
6483 #endif |
|
6484 errno = ENOMEM; |
|
6485 } |
|
6486 } |
|
6487 |
|
6488 #ifdef MALLOC_SYSV |
|
6489 RETURN: |
|
6490 #endif |
|
6491 UTRACE(ptr, size, ret); |
|
6492 return (ret); |
|
6493 } |
|
6494 |
|
6495 MOZ_MEMORY_API void |
|
6496 free_impl(void *ptr) |
|
6497 { |
|
6498 size_t offset; |
|
6499 |
|
6500 DARWIN_ONLY((szone->free)(szone, ptr); return); |
|
6501 |
|
6502 UTRACE(ptr, 0, 0); |
|
6503 |
|
6504 /* |
|
6505 * A version of idalloc that checks for NULL pointer but only for |
|
6506 * huge allocations assuming that CHUNK_ADDR2OFFSET(NULL) == 0. |
|
6507 */ |
|
6508 assert(CHUNK_ADDR2OFFSET(NULL) == 0); |
|
6509 offset = CHUNK_ADDR2OFFSET(ptr); |
|
6510 if (offset != 0) |
|
6511 arena_dalloc(ptr, offset); |
|
6512 else if (ptr != NULL) |
|
6513 huge_dalloc(ptr); |
|
6514 } |
|
6515 |
|
6516 /* |
|
6517 * End malloc(3)-compatible functions. |
|
6518 */ |
|
6519 /******************************************************************************/ |
|
6520 /* |
|
6521 * Begin non-standard functions. |
|
6522 */ |
|
6523 |
|
6524 /* This was added by Mozilla for use by SQLite. */ |
|
6525 #if defined(MOZ_MEMORY_DARWIN) && !defined(MOZ_REPLACE_MALLOC) |
|
6526 static |
|
6527 #else |
|
6528 MOZ_MEMORY_API |
|
6529 #endif |
|
6530 size_t |
|
6531 malloc_good_size_impl(size_t size) |
|
6532 { |
|
6533 /* |
|
6534 * This duplicates the logic in imalloc(), arena_malloc() and |
|
6535 * arena_malloc_small(). |
|
6536 */ |
|
6537 if (size < small_min) { |
|
6538 /* Small (tiny). */ |
|
6539 size = pow2_ceil(size); |
|
6540 /* |
|
6541 * We omit the #ifdefs from arena_malloc_small() -- |
|
6542 * it can be inaccurate with its size in some cases, but this |
|
6543 * function must be accurate. |
|
6544 */ |
|
6545 if (size < (1U << TINY_MIN_2POW)) |
|
6546 size = (1U << TINY_MIN_2POW); |
|
6547 } else if (size <= small_max) { |
|
6548 /* Small (quantum-spaced). */ |
|
6549 size = QUANTUM_CEILING(size); |
|
6550 } else if (size <= bin_maxclass) { |
|
6551 /* Small (sub-page). */ |
|
6552 size = pow2_ceil(size); |
|
6553 } else if (size <= arena_maxclass) { |
|
6554 /* Large. */ |
|
6555 size = PAGE_CEILING(size); |
|
6556 } else { |
|
6557 /* |
|
6558 * Huge. We use PAGE_CEILING to get psize, instead of using |
|
6559 * CHUNK_CEILING to get csize. This ensures that this |
|
6560 * malloc_usable_size(malloc(n)) always matches |
|
6561 * malloc_good_size(n). |
|
6562 */ |
|
6563 size = PAGE_CEILING(size); |
|
6564 } |
|
6565 return size; |
|
6566 } |
|
6567 |
|
6568 |
|
6569 #if defined(MOZ_MEMORY_ANDROID) && (ANDROID_VERSION < 19) |
|
6570 MOZ_MEMORY_API size_t |
|
6571 malloc_usable_size_impl(void *ptr) |
|
6572 #else |
|
6573 MOZ_MEMORY_API size_t |
|
6574 malloc_usable_size_impl(const void *ptr) |
|
6575 #endif |
|
6576 { |
|
6577 DARWIN_ONLY(return (szone->size)(szone, ptr)); |
|
6578 |
|
6579 #ifdef MALLOC_VALIDATE |
|
6580 return (isalloc_validate(ptr)); |
|
6581 #else |
|
6582 assert(ptr != NULL); |
|
6583 |
|
6584 return (isalloc(ptr)); |
|
6585 #endif |
|
6586 } |
|
6587 |
|
6588 MOZ_JEMALLOC_API void |
|
6589 jemalloc_stats_impl(jemalloc_stats_t *stats) |
|
6590 { |
|
6591 size_t i; |
|
6592 |
|
6593 assert(stats != NULL); |
|
6594 |
|
6595 /* |
|
6596 * Gather runtime settings. |
|
6597 */ |
|
6598 stats->opt_abort = opt_abort; |
|
6599 stats->opt_junk = |
|
6600 #ifdef MALLOC_FILL |
|
6601 opt_junk ? true : |
|
6602 #endif |
|
6603 false; |
|
6604 stats->opt_poison = |
|
6605 #ifdef MALLOC_FILL |
|
6606 opt_poison ? true : |
|
6607 #endif |
|
6608 false; |
|
6609 stats->opt_utrace = |
|
6610 #ifdef MALLOC_UTRACE |
|
6611 opt_utrace ? true : |
|
6612 #endif |
|
6613 false; |
|
6614 stats->opt_sysv = |
|
6615 #ifdef MALLOC_SYSV |
|
6616 opt_sysv ? true : |
|
6617 #endif |
|
6618 false; |
|
6619 stats->opt_xmalloc = |
|
6620 #ifdef MALLOC_XMALLOC |
|
6621 opt_xmalloc ? true : |
|
6622 #endif |
|
6623 false; |
|
6624 stats->opt_zero = |
|
6625 #ifdef MALLOC_FILL |
|
6626 opt_zero ? true : |
|
6627 #endif |
|
6628 false; |
|
6629 stats->narenas = narenas; |
|
6630 stats->balance_threshold = |
|
6631 #ifdef MALLOC_BALANCE |
|
6632 opt_balance_threshold |
|
6633 #else |
|
6634 SIZE_T_MAX |
|
6635 #endif |
|
6636 ; |
|
6637 stats->quantum = quantum; |
|
6638 stats->small_max = small_max; |
|
6639 stats->large_max = arena_maxclass; |
|
6640 stats->chunksize = chunksize; |
|
6641 stats->dirty_max = opt_dirty_max; |
|
6642 |
|
6643 /* |
|
6644 * Gather current memory usage statistics. |
|
6645 */ |
|
6646 stats->mapped = 0; |
|
6647 stats->allocated = 0; |
|
6648 stats->waste = 0; |
|
6649 stats->page_cache = 0; |
|
6650 stats->bookkeeping = 0; |
|
6651 |
|
6652 /* Get huge mapped/allocated. */ |
|
6653 malloc_mutex_lock(&huge_mtx); |
|
6654 stats->mapped += huge_mapped; |
|
6655 stats->allocated += huge_allocated; |
|
6656 assert(huge_mapped >= huge_allocated); |
|
6657 malloc_mutex_unlock(&huge_mtx); |
|
6658 |
|
6659 /* Get base mapped/allocated. */ |
|
6660 malloc_mutex_lock(&base_mtx); |
|
6661 stats->mapped += base_mapped; |
|
6662 stats->bookkeeping += base_committed; |
|
6663 assert(base_mapped >= base_committed); |
|
6664 malloc_mutex_unlock(&base_mtx); |
|
6665 |
|
6666 /* Iterate over arenas. */ |
|
6667 for (i = 0; i < narenas; i++) { |
|
6668 arena_t *arena = arenas[i]; |
|
6669 size_t arena_mapped, arena_allocated, arena_committed, arena_dirty; |
|
6670 |
|
6671 if (arena == NULL) { |
|
6672 continue; |
|
6673 } |
|
6674 |
|
6675 malloc_spin_lock(&arena->lock); |
|
6676 |
|
6677 arena_mapped = arena->stats.mapped; |
|
6678 |
|
6679 /* "committed" counts dirty and allocated memory. */ |
|
6680 arena_committed = arena->stats.committed << pagesize_2pow; |
|
6681 |
|
6682 arena_allocated = arena->stats.allocated_small + |
|
6683 arena->stats.allocated_large; |
|
6684 |
|
6685 arena_dirty = arena->ndirty << pagesize_2pow; |
|
6686 |
|
6687 malloc_spin_unlock(&arena->lock); |
|
6688 |
|
6689 assert(arena_mapped >= arena_committed); |
|
6690 assert(arena_committed >= arena_allocated + arena_dirty); |
|
6691 |
|
6692 /* "waste" is committed memory that is neither dirty nor |
|
6693 * allocated. */ |
|
6694 stats->mapped += arena_mapped; |
|
6695 stats->allocated += arena_allocated; |
|
6696 stats->page_cache += arena_dirty; |
|
6697 stats->waste += arena_committed - arena_allocated - arena_dirty; |
|
6698 } |
|
6699 |
|
6700 assert(stats->mapped >= stats->allocated + stats->waste + |
|
6701 stats->page_cache + stats->bookkeeping); |
|
6702 } |
|
6703 |
|
6704 #ifdef MALLOC_DOUBLE_PURGE |
|
6705 |
|
6706 /* Explicitly remove all of this chunk's MADV_FREE'd pages from memory. */ |
|
6707 static void |
|
6708 hard_purge_chunk(arena_chunk_t *chunk) |
|
6709 { |
|
6710 /* See similar logic in arena_purge(). */ |
|
6711 |
|
6712 size_t i; |
|
6713 for (i = arena_chunk_header_npages; i < chunk_npages; i++) { |
|
6714 /* Find all adjacent pages with CHUNK_MAP_MADVISED set. */ |
|
6715 size_t npages; |
|
6716 for (npages = 0; |
|
6717 chunk->map[i + npages].bits & CHUNK_MAP_MADVISED && i + npages < chunk_npages; |
|
6718 npages++) { |
|
6719 /* Turn off the chunk's MADV_FREED bit and turn on its |
|
6720 * DECOMMITTED bit. */ |
|
6721 RELEASE_ASSERT(!(chunk->map[i + npages].bits & CHUNK_MAP_DECOMMITTED)); |
|
6722 chunk->map[i + npages].bits ^= CHUNK_MAP_MADVISED_OR_DECOMMITTED; |
|
6723 } |
|
6724 |
|
6725 /* We could use mincore to find out which pages are actually |
|
6726 * present, but it's not clear that's better. */ |
|
6727 if (npages > 0) { |
|
6728 pages_decommit(((char*)chunk) + (i << pagesize_2pow), npages << pagesize_2pow); |
|
6729 pages_commit(((char*)chunk) + (i << pagesize_2pow), npages << pagesize_2pow); |
|
6730 } |
|
6731 i += npages; |
|
6732 } |
|
6733 } |
|
6734 |
|
6735 /* Explicitly remove all of this arena's MADV_FREE'd pages from memory. */ |
|
6736 static void |
|
6737 hard_purge_arena(arena_t *arena) |
|
6738 { |
|
6739 malloc_spin_lock(&arena->lock); |
|
6740 |
|
6741 while (!LinkedList_IsEmpty(&arena->chunks_madvised)) { |
|
6742 LinkedList* next = arena->chunks_madvised.next; |
|
6743 arena_chunk_t *chunk = |
|
6744 LinkedList_Get(arena->chunks_madvised.next, |
|
6745 arena_chunk_t, chunks_madvised_elem); |
|
6746 hard_purge_chunk(chunk); |
|
6747 LinkedList_Remove(&chunk->chunks_madvised_elem); |
|
6748 } |
|
6749 |
|
6750 malloc_spin_unlock(&arena->lock); |
|
6751 } |
|
6752 |
|
6753 MOZ_JEMALLOC_API void |
|
6754 jemalloc_purge_freed_pages_impl() |
|
6755 { |
|
6756 size_t i; |
|
6757 for (i = 0; i < narenas; i++) { |
|
6758 arena_t *arena = arenas[i]; |
|
6759 if (arena != NULL) |
|
6760 hard_purge_arena(arena); |
|
6761 } |
|
6762 } |
|
6763 |
|
6764 #else /* !defined MALLOC_DOUBLE_PURGE */ |
|
6765 |
|
6766 MOZ_JEMALLOC_API void |
|
6767 jemalloc_purge_freed_pages_impl() |
|
6768 { |
|
6769 /* Do nothing. */ |
|
6770 } |
|
6771 |
|
6772 #endif /* defined MALLOC_DOUBLE_PURGE */ |
|
6773 |
|
6774 |
|
6775 |
|
6776 #ifdef MOZ_MEMORY_WINDOWS |
|
6777 void* |
|
6778 _recalloc(void *ptr, size_t count, size_t size) |
|
6779 { |
|
6780 size_t oldsize = (ptr != NULL) ? isalloc(ptr) : 0; |
|
6781 size_t newsize = count * size; |
|
6782 |
|
6783 /* |
|
6784 * In order for all trailing bytes to be zeroed, the caller needs to |
|
6785 * use calloc(), followed by recalloc(). However, the current calloc() |
|
6786 * implementation only zeros the bytes requested, so if recalloc() is |
|
6787 * to work 100% correctly, calloc() will need to change to zero |
|
6788 * trailing bytes. |
|
6789 */ |
|
6790 |
|
6791 ptr = realloc(ptr, newsize); |
|
6792 if (ptr != NULL && oldsize < newsize) { |
|
6793 memset((void *)((uintptr_t)ptr + oldsize), 0, newsize - |
|
6794 oldsize); |
|
6795 } |
|
6796 |
|
6797 return ptr; |
|
6798 } |
|
6799 |
|
6800 /* |
|
6801 * This impl of _expand doesn't ever actually expand or shrink blocks: it |
|
6802 * simply replies that you may continue using a shrunk block. |
|
6803 */ |
|
6804 void* |
|
6805 _expand(void *ptr, size_t newsize) |
|
6806 { |
|
6807 if (isalloc(ptr) >= newsize) |
|
6808 return ptr; |
|
6809 |
|
6810 return NULL; |
|
6811 } |
|
6812 |
|
6813 size_t |
|
6814 _msize(const void *ptr) |
|
6815 { |
|
6816 |
|
6817 return malloc_usable_size_impl(ptr); |
|
6818 } |
|
6819 #endif |
|
6820 |
|
6821 MOZ_JEMALLOC_API void |
|
6822 jemalloc_free_dirty_pages_impl(void) |
|
6823 { |
|
6824 size_t i; |
|
6825 for (i = 0; i < narenas; i++) { |
|
6826 arena_t *arena = arenas[i]; |
|
6827 |
|
6828 if (arena != NULL) { |
|
6829 malloc_spin_lock(&arena->lock); |
|
6830 arena_purge(arena, true); |
|
6831 malloc_spin_unlock(&arena->lock); |
|
6832 } |
|
6833 } |
|
6834 } |
|
6835 |
|
6836 /* |
|
6837 * End non-standard functions. |
|
6838 */ |
|
6839 /******************************************************************************/ |
|
6840 /* |
|
6841 * Begin library-private functions, used by threading libraries for protection |
|
6842 * of malloc during fork(). These functions are only called if the program is |
|
6843 * running in threaded mode, so there is no need to check whether the program |
|
6844 * is threaded here. |
|
6845 */ |
|
6846 |
|
6847 static void |
|
6848 _malloc_prefork(void) |
|
6849 { |
|
6850 unsigned i; |
|
6851 |
|
6852 /* Acquire all mutexes in a safe order. */ |
|
6853 |
|
6854 malloc_spin_lock(&arenas_lock); |
|
6855 for (i = 0; i < narenas; i++) { |
|
6856 if (arenas[i] != NULL) |
|
6857 malloc_spin_lock(&arenas[i]->lock); |
|
6858 } |
|
6859 |
|
6860 malloc_mutex_lock(&base_mtx); |
|
6861 |
|
6862 malloc_mutex_lock(&huge_mtx); |
|
6863 } |
|
6864 |
|
6865 static void |
|
6866 _malloc_postfork(void) |
|
6867 { |
|
6868 unsigned i; |
|
6869 |
|
6870 /* Release all mutexes, now that fork() has completed. */ |
|
6871 |
|
6872 malloc_mutex_unlock(&huge_mtx); |
|
6873 |
|
6874 malloc_mutex_unlock(&base_mtx); |
|
6875 |
|
6876 for (i = 0; i < narenas; i++) { |
|
6877 if (arenas[i] != NULL) |
|
6878 malloc_spin_unlock(&arenas[i]->lock); |
|
6879 } |
|
6880 malloc_spin_unlock(&arenas_lock); |
|
6881 } |
|
6882 |
|
6883 /* |
|
6884 * End library-private functions. |
|
6885 */ |
|
6886 /******************************************************************************/ |
|
6887 |
|
6888 #ifdef HAVE_DLOPEN |
|
6889 # include <dlfcn.h> |
|
6890 #endif |
|
6891 |
|
6892 #if defined(MOZ_MEMORY_DARWIN) |
|
6893 |
|
6894 #if !defined(MOZ_REPLACE_MALLOC) |
|
6895 static void * |
|
6896 zone_malloc(malloc_zone_t *zone, size_t size) |
|
6897 { |
|
6898 |
|
6899 return (malloc_impl(size)); |
|
6900 } |
|
6901 |
|
6902 static void * |
|
6903 zone_calloc(malloc_zone_t *zone, size_t num, size_t size) |
|
6904 { |
|
6905 |
|
6906 return (calloc_impl(num, size)); |
|
6907 } |
|
6908 |
|
6909 static void * |
|
6910 zone_valloc(malloc_zone_t *zone, size_t size) |
|
6911 { |
|
6912 void *ret = NULL; /* Assignment avoids useless compiler warning. */ |
|
6913 |
|
6914 posix_memalign_impl(&ret, pagesize, size); |
|
6915 |
|
6916 return (ret); |
|
6917 } |
|
6918 |
|
6919 static void * |
|
6920 zone_memalign(malloc_zone_t *zone, size_t alignment, size_t size) |
|
6921 { |
|
6922 return (memalign_impl(alignment, size)); |
|
6923 } |
|
6924 |
|
6925 static void * |
|
6926 zone_destroy(malloc_zone_t *zone) |
|
6927 { |
|
6928 |
|
6929 /* This function should never be called. */ |
|
6930 assert(false); |
|
6931 return (NULL); |
|
6932 } |
|
6933 |
|
6934 static size_t |
|
6935 zone_good_size(malloc_zone_t *zone, size_t size) |
|
6936 { |
|
6937 return malloc_good_size_impl(size); |
|
6938 } |
|
6939 |
|
6940 static size_t |
|
6941 ozone_size(malloc_zone_t *zone, void *ptr) |
|
6942 { |
|
6943 size_t ret = isalloc_validate(ptr); |
|
6944 if (ret == 0) |
|
6945 ret = szone->size(zone, ptr); |
|
6946 |
|
6947 return ret; |
|
6948 } |
|
6949 |
|
6950 static void |
|
6951 ozone_free(malloc_zone_t *zone, void *ptr) |
|
6952 { |
|
6953 if (isalloc_validate(ptr) != 0) |
|
6954 free_impl(ptr); |
|
6955 else { |
|
6956 size_t size = szone->size(zone, ptr); |
|
6957 if (size != 0) |
|
6958 (szone->free)(zone, ptr); |
|
6959 /* Otherwise we leak. */ |
|
6960 } |
|
6961 } |
|
6962 |
|
6963 static void * |
|
6964 ozone_realloc(malloc_zone_t *zone, void *ptr, size_t size) |
|
6965 { |
|
6966 size_t oldsize; |
|
6967 if (ptr == NULL) |
|
6968 return (malloc_impl(size)); |
|
6969 |
|
6970 oldsize = isalloc_validate(ptr); |
|
6971 if (oldsize != 0) |
|
6972 return (realloc_impl(ptr, size)); |
|
6973 else { |
|
6974 oldsize = szone->size(zone, ptr); |
|
6975 if (oldsize == 0) |
|
6976 return (malloc_impl(size)); |
|
6977 else { |
|
6978 void *ret = malloc_impl(size); |
|
6979 if (ret != NULL) { |
|
6980 memcpy(ret, ptr, (oldsize < size) ? oldsize : |
|
6981 size); |
|
6982 (szone->free)(zone, ptr); |
|
6983 } |
|
6984 return (ret); |
|
6985 } |
|
6986 } |
|
6987 } |
|
6988 |
|
6989 static unsigned |
|
6990 ozone_batch_malloc(malloc_zone_t *zone, size_t size, void **results, |
|
6991 unsigned num_requested) |
|
6992 { |
|
6993 /* Don't bother implementing this interface, since it isn't required. */ |
|
6994 return 0; |
|
6995 } |
|
6996 |
|
6997 static void |
|
6998 ozone_batch_free(malloc_zone_t *zone, void **to_be_freed, unsigned num) |
|
6999 { |
|
7000 unsigned i; |
|
7001 |
|
7002 for (i = 0; i < num; i++) |
|
7003 ozone_free(zone, to_be_freed[i]); |
|
7004 } |
|
7005 |
|
7006 static void |
|
7007 ozone_free_definite_size(malloc_zone_t *zone, void *ptr, size_t size) |
|
7008 { |
|
7009 if (isalloc_validate(ptr) != 0) { |
|
7010 assert(isalloc_validate(ptr) == size); |
|
7011 free_impl(ptr); |
|
7012 } else { |
|
7013 assert(size == szone->size(zone, ptr)); |
|
7014 l_szone.m16(zone, ptr, size); |
|
7015 } |
|
7016 } |
|
7017 |
|
7018 static void |
|
7019 ozone_force_lock(malloc_zone_t *zone) |
|
7020 { |
|
7021 _malloc_prefork(); |
|
7022 szone->introspect->force_lock(zone); |
|
7023 } |
|
7024 |
|
7025 static void |
|
7026 ozone_force_unlock(malloc_zone_t *zone) |
|
7027 { |
|
7028 szone->introspect->force_unlock(zone); |
|
7029 _malloc_postfork(); |
|
7030 } |
|
7031 |
|
7032 static size_t |
|
7033 zone_version_size(int version) |
|
7034 { |
|
7035 switch (version) |
|
7036 { |
|
7037 case SNOW_LEOPARD_MALLOC_ZONE_T_VERSION: |
|
7038 return sizeof(snow_leopard_malloc_zone); |
|
7039 case LEOPARD_MALLOC_ZONE_T_VERSION: |
|
7040 return sizeof(leopard_malloc_zone); |
|
7041 default: |
|
7042 case LION_MALLOC_ZONE_T_VERSION: |
|
7043 return sizeof(lion_malloc_zone); |
|
7044 } |
|
7045 } |
|
7046 |
|
7047 /* |
|
7048 * Overlay the default scalable zone (szone) such that existing allocations are |
|
7049 * drained, and further allocations come from jemalloc. This is necessary |
|
7050 * because Core Foundation directly accesses and uses the szone before the |
|
7051 * jemalloc library is even loaded. |
|
7052 */ |
|
7053 static void |
|
7054 szone2ozone(malloc_zone_t *default_zone, size_t size) |
|
7055 { |
|
7056 lion_malloc_zone *l_zone; |
|
7057 assert(malloc_initialized); |
|
7058 |
|
7059 /* |
|
7060 * Stash a copy of the original szone so that we can call its |
|
7061 * functions as needed. Note that internally, the szone stores its |
|
7062 * bookkeeping data structures immediately following the malloc_zone_t |
|
7063 * header, so when calling szone functions, we need to pass a pointer to |
|
7064 * the original zone structure. |
|
7065 */ |
|
7066 memcpy(szone, default_zone, size); |
|
7067 |
|
7068 /* OSX 10.7 allocates the default zone in protected memory. */ |
|
7069 if (default_zone->version >= LION_MALLOC_ZONE_T_VERSION) { |
|
7070 void* start_of_page = (void*)((size_t)(default_zone) & ~pagesize_mask); |
|
7071 mprotect (start_of_page, size, PROT_READ | PROT_WRITE); |
|
7072 } |
|
7073 |
|
7074 default_zone->size = (void *)ozone_size; |
|
7075 default_zone->malloc = (void *)zone_malloc; |
|
7076 default_zone->calloc = (void *)zone_calloc; |
|
7077 default_zone->valloc = (void *)zone_valloc; |
|
7078 default_zone->free = (void *)ozone_free; |
|
7079 default_zone->realloc = (void *)ozone_realloc; |
|
7080 default_zone->destroy = (void *)zone_destroy; |
|
7081 default_zone->batch_malloc = NULL; |
|
7082 default_zone->batch_free = ozone_batch_free; |
|
7083 default_zone->introspect = ozone_introspect; |
|
7084 |
|
7085 /* Don't modify default_zone->zone_name; Mac libc may rely on the name |
|
7086 * being unchanged. See Mozilla bug 694896. */ |
|
7087 |
|
7088 ozone_introspect->enumerator = NULL; |
|
7089 ozone_introspect->good_size = (void *)zone_good_size; |
|
7090 ozone_introspect->check = NULL; |
|
7091 ozone_introspect->print = NULL; |
|
7092 ozone_introspect->log = NULL; |
|
7093 ozone_introspect->force_lock = (void *)ozone_force_lock; |
|
7094 ozone_introspect->force_unlock = (void *)ozone_force_unlock; |
|
7095 ozone_introspect->statistics = NULL; |
|
7096 |
|
7097 /* Platform-dependent structs */ |
|
7098 l_zone = (lion_malloc_zone*)(default_zone); |
|
7099 |
|
7100 if (default_zone->version >= SNOW_LEOPARD_MALLOC_ZONE_T_VERSION) { |
|
7101 l_zone->m15 = (void (*)())zone_memalign; |
|
7102 l_zone->m16 = (void (*)())ozone_free_definite_size; |
|
7103 l_ozone_introspect.m9 = NULL; |
|
7104 } |
|
7105 |
|
7106 if (default_zone->version >= LION_MALLOC_ZONE_T_VERSION) { |
|
7107 l_zone->m17 = NULL; |
|
7108 l_ozone_introspect.m10 = NULL; |
|
7109 l_ozone_introspect.m11 = NULL; |
|
7110 l_ozone_introspect.m12 = NULL; |
|
7111 l_ozone_introspect.m13 = NULL; |
|
7112 } |
|
7113 } |
|
7114 #endif |
|
7115 |
|
7116 __attribute__((constructor)) |
|
7117 void |
|
7118 jemalloc_darwin_init(void) |
|
7119 { |
|
7120 if (malloc_init_hard()) |
|
7121 abort(); |
|
7122 } |
|
7123 |
|
7124 #endif |
|
7125 |
|
7126 /* |
|
7127 * is_malloc(malloc_impl) is some macro magic to detect if malloc_impl is |
|
7128 * defined as "malloc" in mozmemory_wrap.h |
|
7129 */ |
|
7130 #define malloc_is_malloc 1 |
|
7131 #define is_malloc_(a) malloc_is_ ## a |
|
7132 #define is_malloc(a) is_malloc_(a) |
|
7133 |
|
7134 #if !defined(MOZ_MEMORY_DARWIN) && (is_malloc(malloc_impl) == 1) |
|
7135 # if defined(__GLIBC__) && !defined(__UCLIBC__) |
|
7136 /* |
|
7137 * glibc provides the RTLD_DEEPBIND flag for dlopen which can make it possible |
|
7138 * to inconsistently reference libc's malloc(3)-compatible functions |
|
7139 * (bug 493541). |
|
7140 * |
|
7141 * These definitions interpose hooks in glibc. The functions are actually |
|
7142 * passed an extra argument for the caller return address, which will be |
|
7143 * ignored. |
|
7144 */ |
|
7145 MOZ_MEMORY_API void (*__free_hook)(void *ptr) = free_impl; |
|
7146 MOZ_MEMORY_API void *(*__malloc_hook)(size_t size) = malloc_impl; |
|
7147 MOZ_MEMORY_API void *(*__realloc_hook)(void *ptr, size_t size) = realloc_impl; |
|
7148 MOZ_MEMORY_API void *(*__memalign_hook)(size_t alignment, size_t size) = MEMALIGN; |
|
7149 |
|
7150 # elif defined(RTLD_DEEPBIND) |
|
7151 /* |
|
7152 * XXX On systems that support RTLD_GROUP or DF_1_GROUP, do their |
|
7153 * implementations permit similar inconsistencies? Should STV_SINGLETON |
|
7154 * visibility be used for interposition where available? |
|
7155 */ |
|
7156 # error "Interposing malloc is unsafe on this system without libc malloc hooks." |
|
7157 # endif |
|
7158 #endif |
|
7159 |
|
7160 #ifdef MOZ_MEMORY_WINDOWS |
|
7161 /* |
|
7162 * In the new style jemalloc integration jemalloc is built as a separate |
|
7163 * shared library. Since we're no longer hooking into the CRT binary, |
|
7164 * we need to initialize the heap at the first opportunity we get. |
|
7165 * DLL_PROCESS_ATTACH in DllMain is that opportunity. |
|
7166 */ |
|
7167 BOOL APIENTRY DllMain(HINSTANCE hModule, |
|
7168 DWORD reason, |
|
7169 LPVOID lpReserved) |
|
7170 { |
|
7171 switch (reason) { |
|
7172 case DLL_PROCESS_ATTACH: |
|
7173 /* Don't force the system to page DllMain back in every time |
|
7174 * we create/destroy a thread */ |
|
7175 DisableThreadLibraryCalls(hModule); |
|
7176 /* Initialize the heap */ |
|
7177 malloc_init_hard(); |
|
7178 break; |
|
7179 |
|
7180 case DLL_PROCESS_DETACH: |
|
7181 break; |
|
7182 |
|
7183 } |
|
7184 |
|
7185 return TRUE; |
|
7186 } |
|
7187 #endif |