1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/memory/mozjemalloc/jemalloc.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,7187 @@ 1.4 +/* -*- Mode: C; tab-width: 8; c-basic-offset: 8; indent-tabs-mode: t -*- */ 1.5 +/* vim:set softtabstop=8 shiftwidth=8 noet: */ 1.6 +/*- 1.7 + * Copyright (C) 2006-2008 Jason Evans <jasone@FreeBSD.org>. 1.8 + * All rights reserved. 1.9 + * 1.10 + * Redistribution and use in source and binary forms, with or without 1.11 + * modification, are permitted provided that the following conditions 1.12 + * are met: 1.13 + * 1. Redistributions of source code must retain the above copyright 1.14 + * notice(s), this list of conditions and the following disclaimer as 1.15 + * the first lines of this file unmodified other than the possible 1.16 + * addition of one or more copyright notices. 1.17 + * 2. Redistributions in binary form must reproduce the above copyright 1.18 + * notice(s), this list of conditions and the following disclaimer in 1.19 + * the documentation and/or other materials provided with the 1.20 + * distribution. 1.21 + * 1.22 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY 1.23 + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1.24 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 1.25 + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE 1.26 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 1.27 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 1.28 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 1.29 + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 1.30 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 1.31 + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 1.32 + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.33 + * 1.34 + ******************************************************************************* 1.35 + * 1.36 + * This allocator implementation is designed to provide scalable performance 1.37 + * for multi-threaded programs on multi-processor systems. The following 1.38 + * features are included for this purpose: 1.39 + * 1.40 + * + Multiple arenas are used if there are multiple CPUs, which reduces lock 1.41 + * contention and cache sloshing. 1.42 + * 1.43 + * + Cache line sharing between arenas is avoided for internal data 1.44 + * structures. 1.45 + * 1.46 + * + Memory is managed in chunks and runs (chunks can be split into runs), 1.47 + * rather than as individual pages. This provides a constant-time 1.48 + * mechanism for associating allocations with particular arenas. 1.49 + * 1.50 + * Allocation requests are rounded up to the nearest size class, and no record 1.51 + * of the original request size is maintained. Allocations are broken into 1.52 + * categories according to size class. Assuming runtime defaults, 4 kB pages 1.53 + * and a 16 byte quantum on a 32-bit system, the size classes in each category 1.54 + * are as follows: 1.55 + * 1.56 + * |=====================================| 1.57 + * | Category | Subcategory | Size | 1.58 + * |=====================================| 1.59 + * | Small | Tiny | 2 | 1.60 + * | | | 4 | 1.61 + * | | | 8 | 1.62 + * | |----------------+---------| 1.63 + * | | Quantum-spaced | 16 | 1.64 + * | | | 32 | 1.65 + * | | | 48 | 1.66 + * | | | ... | 1.67 + * | | | 480 | 1.68 + * | | | 496 | 1.69 + * | | | 512 | 1.70 + * | |----------------+---------| 1.71 + * | | Sub-page | 1 kB | 1.72 + * | | | 2 kB | 1.73 + * |=====================================| 1.74 + * | Large | 4 kB | 1.75 + * | | 8 kB | 1.76 + * | | 12 kB | 1.77 + * | | ... | 1.78 + * | | 1012 kB | 1.79 + * | | 1016 kB | 1.80 + * | | 1020 kB | 1.81 + * |=====================================| 1.82 + * | Huge | 1 MB | 1.83 + * | | 2 MB | 1.84 + * | | 3 MB | 1.85 + * | | ... | 1.86 + * |=====================================| 1.87 + * 1.88 + * NOTE: Due to Mozilla bug 691003, we cannot reserve less than one word for an 1.89 + * allocation on Linux or Mac. So on 32-bit *nix, the smallest bucket size is 1.90 + * 4 bytes, and on 64-bit, the smallest bucket size is 8 bytes. 1.91 + * 1.92 + * A different mechanism is used for each category: 1.93 + * 1.94 + * Small : Each size class is segregated into its own set of runs. Each run 1.95 + * maintains a bitmap of which regions are free/allocated. 1.96 + * 1.97 + * Large : Each allocation is backed by a dedicated run. Metadata are stored 1.98 + * in the associated arena chunk header maps. 1.99 + * 1.100 + * Huge : Each allocation is backed by a dedicated contiguous set of chunks. 1.101 + * Metadata are stored in a separate red-black tree. 1.102 + * 1.103 + ******************************************************************************* 1.104 + */ 1.105 + 1.106 +#ifdef MOZ_MEMORY_ANDROID 1.107 +#define NO_TLS 1.108 +#define _pthread_self() pthread_self() 1.109 +#endif 1.110 + 1.111 +/* 1.112 + * On Linux, we use madvise(MADV_DONTNEED) to release memory back to the 1.113 + * operating system. If we release 1MB of live pages with MADV_DONTNEED, our 1.114 + * RSS will decrease by 1MB (almost) immediately. 1.115 + * 1.116 + * On Mac, we use madvise(MADV_FREE). Unlike MADV_DONTNEED on Linux, MADV_FREE 1.117 + * on Mac doesn't cause the OS to release the specified pages immediately; the 1.118 + * OS keeps them in our process until the machine comes under memory pressure. 1.119 + * 1.120 + * It's therefore difficult to measure the process's RSS on Mac, since, in the 1.121 + * absence of memory pressure, the contribution from the heap to RSS will not 1.122 + * decrease due to our madvise calls. 1.123 + * 1.124 + * We therefore define MALLOC_DOUBLE_PURGE on Mac. This causes jemalloc to 1.125 + * track which pages have been MADV_FREE'd. You can then call 1.126 + * jemalloc_purge_freed_pages(), which will force the OS to release those 1.127 + * MADV_FREE'd pages, making the process's RSS reflect its true memory usage. 1.128 + * 1.129 + * The jemalloc_purge_freed_pages definition in memory/build/mozmemory.h needs 1.130 + * to be adjusted if MALLOC_DOUBLE_PURGE is ever enabled on Linux. 1.131 + */ 1.132 +#ifdef MOZ_MEMORY_DARWIN 1.133 +#define MALLOC_DOUBLE_PURGE 1.134 +#endif 1.135 + 1.136 +/* 1.137 + * MALLOC_PRODUCTION disables assertions and statistics gathering. It also 1.138 + * defaults the A and J runtime options to off. These settings are appropriate 1.139 + * for production systems. 1.140 + */ 1.141 +#ifndef MOZ_MEMORY_DEBUG 1.142 +# define MALLOC_PRODUCTION 1.143 +#endif 1.144 + 1.145 +/* 1.146 + * Use only one arena by default. Mozilla does not currently make extensive 1.147 + * use of concurrent allocation, so the increased fragmentation associated with 1.148 + * multiple arenas is not warranted. 1.149 + */ 1.150 +#define MOZ_MEMORY_NARENAS_DEFAULT_ONE 1.151 + 1.152 +/* 1.153 + * Pass this set of options to jemalloc as its default. It does not override 1.154 + * the options passed via the MALLOC_OPTIONS environment variable but is 1.155 + * applied in addition to them. 1.156 + */ 1.157 +#ifdef MOZ_B2G 1.158 + /* Reduce the amount of unused dirty pages to 1MiB on B2G */ 1.159 +# define MOZ_MALLOC_OPTIONS "ff" 1.160 +#else 1.161 +# define MOZ_MALLOC_OPTIONS "" 1.162 +#endif 1.163 + 1.164 +/* 1.165 + * MALLOC_STATS enables statistics calculation, and is required for 1.166 + * jemalloc_stats(). 1.167 + */ 1.168 +#define MALLOC_STATS 1.169 + 1.170 +/* Memory filling (junk/poison/zero). */ 1.171 +#define MALLOC_FILL 1.172 + 1.173 +#ifndef MALLOC_PRODUCTION 1.174 + /* 1.175 + * MALLOC_DEBUG enables assertions and other sanity checks, and disables 1.176 + * inline functions. 1.177 + */ 1.178 +# define MALLOC_DEBUG 1.179 + 1.180 + /* Allocation tracing. */ 1.181 +# ifndef MOZ_MEMORY_WINDOWS 1.182 +# define MALLOC_UTRACE 1.183 +# endif 1.184 + 1.185 + /* Support optional abort() on OOM. */ 1.186 +# define MALLOC_XMALLOC 1.187 + 1.188 + /* Support SYSV semantics. */ 1.189 +# define MALLOC_SYSV 1.190 +#endif 1.191 + 1.192 +/* 1.193 + * MALLOC_VALIDATE causes malloc_usable_size() to perform some pointer 1.194 + * validation. There are many possible errors that validation does not even 1.195 + * attempt to detect. 1.196 + */ 1.197 +#define MALLOC_VALIDATE 1.198 + 1.199 +/* Embed no-op macros that support memory allocation tracking via valgrind. */ 1.200 +#ifdef MOZ_VALGRIND 1.201 +# define MALLOC_VALGRIND 1.202 +#endif 1.203 +#ifdef MALLOC_VALGRIND 1.204 +# include <valgrind/valgrind.h> 1.205 +#else 1.206 +# define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) 1.207 +# define VALGRIND_FREELIKE_BLOCK(addr, rzB) 1.208 +#endif 1.209 + 1.210 +/* 1.211 + * MALLOC_BALANCE enables monitoring of arena lock contention and dynamically 1.212 + * re-balances arena load if exponentially averaged contention exceeds a 1.213 + * certain threshold. 1.214 + */ 1.215 +/* #define MALLOC_BALANCE */ 1.216 + 1.217 +/* 1.218 + * MALLOC_PAGEFILE causes all mmap()ed memory to be backed by temporary 1.219 + * files, so that if a chunk is mapped, it is guaranteed to be swappable. 1.220 + * This avoids asynchronous OOM failures that are due to VM over-commit. 1.221 + */ 1.222 +/* #define MALLOC_PAGEFILE */ 1.223 + 1.224 +#ifdef MALLOC_PAGEFILE 1.225 +/* Write size when initializing a page file. */ 1.226 +# define MALLOC_PAGEFILE_WRITE_SIZE 512 1.227 +#endif 1.228 + 1.229 +#if defined(MOZ_MEMORY_LINUX) && !defined(MOZ_MEMORY_ANDROID) 1.230 +#define _GNU_SOURCE /* For mremap(2). */ 1.231 +#define issetugid() 0 1.232 +#if 0 /* Enable in order to test decommit code on Linux. */ 1.233 +# define MALLOC_DECOMMIT 1.234 +#endif 1.235 +#endif 1.236 + 1.237 +#include <sys/types.h> 1.238 + 1.239 +#include <errno.h> 1.240 +#include <stdlib.h> 1.241 +#include <limits.h> 1.242 +#include <stdarg.h> 1.243 +#include <stdio.h> 1.244 +#include <string.h> 1.245 + 1.246 +#ifdef MOZ_MEMORY_WINDOWS 1.247 + 1.248 +/* Some defines from the CRT internal headers that we need here. */ 1.249 +#define _CRT_SPINCOUNT 5000 1.250 +#define __crtInitCritSecAndSpinCount InitializeCriticalSectionAndSpinCount 1.251 +#include <io.h> 1.252 +#include <windows.h> 1.253 + 1.254 +#pragma warning( disable: 4267 4996 4146 ) 1.255 + 1.256 +#define bool BOOL 1.257 +#define false FALSE 1.258 +#define true TRUE 1.259 +#define inline __inline 1.260 +#define SIZE_T_MAX SIZE_MAX 1.261 +#define STDERR_FILENO 2 1.262 +#define PATH_MAX MAX_PATH 1.263 +#define vsnprintf _vsnprintf 1.264 + 1.265 +#ifndef NO_TLS 1.266 +static unsigned long tlsIndex = 0xffffffff; 1.267 +#endif 1.268 + 1.269 +#define __thread 1.270 +#define _pthread_self() __threadid() 1.271 +#define issetugid() 0 1.272 + 1.273 +/* use MSVC intrinsics */ 1.274 +#pragma intrinsic(_BitScanForward) 1.275 +static __forceinline int 1.276 +ffs(int x) 1.277 +{ 1.278 + unsigned long i; 1.279 + 1.280 + if (_BitScanForward(&i, x) != 0) 1.281 + return (i + 1); 1.282 + 1.283 + return (0); 1.284 +} 1.285 + 1.286 +/* Implement getenv without using malloc */ 1.287 +static char mozillaMallocOptionsBuf[64]; 1.288 + 1.289 +#define getenv xgetenv 1.290 +static char * 1.291 +getenv(const char *name) 1.292 +{ 1.293 + 1.294 + if (GetEnvironmentVariableA(name, (LPSTR)&mozillaMallocOptionsBuf, 1.295 + sizeof(mozillaMallocOptionsBuf)) > 0) 1.296 + return (mozillaMallocOptionsBuf); 1.297 + 1.298 + return (NULL); 1.299 +} 1.300 + 1.301 +typedef unsigned char uint8_t; 1.302 +typedef unsigned uint32_t; 1.303 +typedef unsigned long long uint64_t; 1.304 +typedef unsigned long long uintmax_t; 1.305 +#if defined(_WIN64) 1.306 +typedef long long ssize_t; 1.307 +#else 1.308 +typedef long ssize_t; 1.309 +#endif 1.310 + 1.311 +#define MALLOC_DECOMMIT 1.312 +#endif 1.313 + 1.314 +#ifndef MOZ_MEMORY_WINDOWS 1.315 +#ifndef MOZ_MEMORY_SOLARIS 1.316 +#include <sys/cdefs.h> 1.317 +#endif 1.318 +#ifndef __DECONST 1.319 +# define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var)) 1.320 +#endif 1.321 +#ifndef MOZ_MEMORY 1.322 +__FBSDID("$FreeBSD: head/lib/libc/stdlib/malloc.c 180599 2008-07-18 19:35:44Z jasone $"); 1.323 +#include "libc_private.h" 1.324 +#ifdef MALLOC_DEBUG 1.325 +# define _LOCK_DEBUG 1.326 +#endif 1.327 +#include "spinlock.h" 1.328 +#include "namespace.h" 1.329 +#endif 1.330 +#include <sys/mman.h> 1.331 +#ifndef MADV_FREE 1.332 +# define MADV_FREE MADV_DONTNEED 1.333 +#endif 1.334 +#ifndef MAP_NOSYNC 1.335 +# define MAP_NOSYNC 0 1.336 +#endif 1.337 +#include <sys/param.h> 1.338 +#ifndef MOZ_MEMORY 1.339 +#include <sys/stddef.h> 1.340 +#endif 1.341 +#include <sys/time.h> 1.342 +#include <sys/types.h> 1.343 +#if !defined(MOZ_MEMORY_SOLARIS) && !defined(MOZ_MEMORY_ANDROID) 1.344 +#include <sys/sysctl.h> 1.345 +#endif 1.346 +#include <sys/uio.h> 1.347 +#ifndef MOZ_MEMORY 1.348 +#include <sys/ktrace.h> /* Must come after several other sys/ includes. */ 1.349 + 1.350 +#include <machine/atomic.h> 1.351 +#include <machine/cpufunc.h> 1.352 +#include <machine/vmparam.h> 1.353 +#endif 1.354 + 1.355 +#include <errno.h> 1.356 +#include <limits.h> 1.357 +#ifndef SIZE_T_MAX 1.358 +# define SIZE_T_MAX SIZE_MAX 1.359 +#endif 1.360 +#include <pthread.h> 1.361 +#ifdef MOZ_MEMORY_DARWIN 1.362 +#define _pthread_self pthread_self 1.363 +#define _pthread_mutex_init pthread_mutex_init 1.364 +#define _pthread_mutex_trylock pthread_mutex_trylock 1.365 +#define _pthread_mutex_lock pthread_mutex_lock 1.366 +#define _pthread_mutex_unlock pthread_mutex_unlock 1.367 +#endif 1.368 +#include <sched.h> 1.369 +#include <stdarg.h> 1.370 +#include <stdio.h> 1.371 +#include <stdbool.h> 1.372 +#include <stdint.h> 1.373 +#include <stdlib.h> 1.374 +#include <string.h> 1.375 +#ifndef MOZ_MEMORY_DARWIN 1.376 +#include <strings.h> 1.377 +#endif 1.378 +#include <unistd.h> 1.379 + 1.380 +#ifdef MOZ_MEMORY_DARWIN 1.381 +#include <libkern/OSAtomic.h> 1.382 +#include <mach/mach_error.h> 1.383 +#include <mach/mach_init.h> 1.384 +#include <mach/vm_map.h> 1.385 +#include <malloc/malloc.h> 1.386 +#endif 1.387 + 1.388 +#ifndef MOZ_MEMORY 1.389 +#include "un-namespace.h" 1.390 +#endif 1.391 + 1.392 +#endif 1.393 + 1.394 +#include "jemalloc_types.h" 1.395 +#include "linkedlist.h" 1.396 +#include "mozmemory_wrap.h" 1.397 + 1.398 +/* Some tools, such as /dev/dsp wrappers, LD_PRELOAD libraries that 1.399 + * happen to override mmap() and call dlsym() from their overridden 1.400 + * mmap(). The problem is that dlsym() calls malloc(), and this ends 1.401 + * up in a dead lock in jemalloc. 1.402 + * On these systems, we prefer to directly use the system call. 1.403 + * We do that for Linux systems and kfreebsd with GNU userland. 1.404 + * Note sanity checks are not done (alignment of offset, ...) because 1.405 + * the uses of mmap are pretty limited, in jemalloc. 1.406 + * 1.407 + * On Alpha, glibc has a bug that prevents syscall() to work for system 1.408 + * calls with 6 arguments 1.409 + */ 1.410 +#if (defined(MOZ_MEMORY_LINUX) && !defined(__alpha__)) || \ 1.411 + (defined(MOZ_MEMORY_BSD) && defined(__GLIBC__)) 1.412 +#include <sys/syscall.h> 1.413 +#if defined(SYS_mmap) || defined(SYS_mmap2) 1.414 +static inline 1.415 +void *_mmap(void *addr, size_t length, int prot, int flags, 1.416 + int fd, off_t offset) 1.417 +{ 1.418 +/* S390 only passes one argument to the mmap system call, which is a 1.419 + * pointer to a structure containing the arguments */ 1.420 +#ifdef __s390__ 1.421 + struct { 1.422 + void *addr; 1.423 + size_t length; 1.424 + long prot; 1.425 + long flags; 1.426 + long fd; 1.427 + off_t offset; 1.428 + } args = { addr, length, prot, flags, fd, offset }; 1.429 + return (void *) syscall(SYS_mmap, &args); 1.430 +#else 1.431 +#ifdef SYS_mmap2 1.432 + return (void *) syscall(SYS_mmap2, addr, length, prot, flags, 1.433 + fd, offset >> 12); 1.434 +#else 1.435 + return (void *) syscall(SYS_mmap, addr, length, prot, flags, 1.436 + fd, offset); 1.437 +#endif 1.438 +#endif 1.439 +} 1.440 +#define mmap _mmap 1.441 +#define munmap(a, l) syscall(SYS_munmap, a, l) 1.442 +#endif 1.443 +#endif 1.444 + 1.445 +#ifdef MOZ_MEMORY_DARWIN 1.446 +static const bool isthreaded = true; 1.447 +#endif 1.448 + 1.449 +#if defined(MOZ_MEMORY_SOLARIS) && defined(MAP_ALIGN) && !defined(JEMALLOC_NEVER_USES_MAP_ALIGN) 1.450 +#define JEMALLOC_USES_MAP_ALIGN /* Required on Solaris 10. Might improve performance elsewhere. */ 1.451 +#endif 1.452 + 1.453 +#define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var)) 1.454 + 1.455 +#ifdef MOZ_MEMORY_WINDOWS 1.456 + /* MSVC++ does not support C99 variable-length arrays. */ 1.457 +# define RB_NO_C99_VARARRAYS 1.458 +#endif 1.459 +#include "rb.h" 1.460 + 1.461 +#ifdef MALLOC_DEBUG 1.462 + /* Disable inlining to make debugging easier. */ 1.463 +#ifdef inline 1.464 +#undef inline 1.465 +#endif 1.466 + 1.467 +# define inline 1.468 +#endif 1.469 + 1.470 +/* Size of stack-allocated buffer passed to strerror_r(). */ 1.471 +#define STRERROR_BUF 64 1.472 + 1.473 +/* Minimum alignment of non-tiny allocations is 2^QUANTUM_2POW_MIN bytes. */ 1.474 +# define QUANTUM_2POW_MIN 4 1.475 +#if defined(_WIN64) || defined(__LP64__) 1.476 +# define SIZEOF_PTR_2POW 3 1.477 +#else 1.478 +# define SIZEOF_PTR_2POW 2 1.479 +#endif 1.480 +#define PIC 1.481 +#ifndef MOZ_MEMORY_DARWIN 1.482 +static const bool isthreaded = true; 1.483 +#else 1.484 +# define NO_TLS 1.485 +#endif 1.486 +#if 0 1.487 +#ifdef __i386__ 1.488 +# define QUANTUM_2POW_MIN 4 1.489 +# define SIZEOF_PTR_2POW 2 1.490 +# define CPU_SPINWAIT __asm__ volatile("pause") 1.491 +#endif 1.492 +#ifdef __ia64__ 1.493 +# define QUANTUM_2POW_MIN 4 1.494 +# define SIZEOF_PTR_2POW 3 1.495 +#endif 1.496 +#ifdef __alpha__ 1.497 +# define QUANTUM_2POW_MIN 4 1.498 +# define SIZEOF_PTR_2POW 3 1.499 +# define NO_TLS 1.500 +#endif 1.501 +#ifdef __sparc64__ 1.502 +# define QUANTUM_2POW_MIN 4 1.503 +# define SIZEOF_PTR_2POW 3 1.504 +# define NO_TLS 1.505 +#endif 1.506 +#ifdef __amd64__ 1.507 +# define QUANTUM_2POW_MIN 4 1.508 +# define SIZEOF_PTR_2POW 3 1.509 +# define CPU_SPINWAIT __asm__ volatile("pause") 1.510 +#endif 1.511 +#ifdef __arm__ 1.512 +# define QUANTUM_2POW_MIN 3 1.513 +# define SIZEOF_PTR_2POW 2 1.514 +# define NO_TLS 1.515 +#endif 1.516 +#ifdef __mips__ 1.517 +# define QUANTUM_2POW_MIN 3 1.518 +# define SIZEOF_PTR_2POW 2 1.519 +# define NO_TLS 1.520 +#endif 1.521 +#ifdef __powerpc__ 1.522 +# define QUANTUM_2POW_MIN 4 1.523 +# define SIZEOF_PTR_2POW 2 1.524 +#endif 1.525 +#endif 1.526 + 1.527 +#define SIZEOF_PTR (1U << SIZEOF_PTR_2POW) 1.528 + 1.529 +/* sizeof(int) == (1U << SIZEOF_INT_2POW). */ 1.530 +#ifndef SIZEOF_INT_2POW 1.531 +# define SIZEOF_INT_2POW 2 1.532 +#endif 1.533 + 1.534 +/* We can't use TLS in non-PIC programs, since TLS relies on loader magic. */ 1.535 +#if (!defined(PIC) && !defined(NO_TLS)) 1.536 +# define NO_TLS 1.537 +#endif 1.538 + 1.539 +#ifdef NO_TLS 1.540 + /* MALLOC_BALANCE requires TLS. */ 1.541 +# ifdef MALLOC_BALANCE 1.542 +# undef MALLOC_BALANCE 1.543 +# endif 1.544 +#endif 1.545 + 1.546 +/* 1.547 + * Size and alignment of memory chunks that are allocated by the OS's virtual 1.548 + * memory system. 1.549 + */ 1.550 +#define CHUNK_2POW_DEFAULT 20 1.551 +/* Maximum number of dirty pages per arena. */ 1.552 +#define DIRTY_MAX_DEFAULT (1U << 10) 1.553 + 1.554 +/* 1.555 + * Maximum size of L1 cache line. This is used to avoid cache line aliasing, 1.556 + * so over-estimates are okay (up to a point), but under-estimates will 1.557 + * negatively affect performance. 1.558 + */ 1.559 +#define CACHELINE_2POW 6 1.560 +#define CACHELINE ((size_t)(1U << CACHELINE_2POW)) 1.561 + 1.562 +/* 1.563 + * Smallest size class to support. On Linux and Mac, even malloc(1) must 1.564 + * reserve a word's worth of memory (see Mozilla bug 691003). 1.565 + */ 1.566 +#ifdef MOZ_MEMORY_WINDOWS 1.567 +#define TINY_MIN_2POW 1 1.568 +#else 1.569 +#define TINY_MIN_2POW (sizeof(void*) == 8 ? 3 : 2) 1.570 +#endif 1.571 + 1.572 +/* 1.573 + * Maximum size class that is a multiple of the quantum, but not (necessarily) 1.574 + * a power of 2. Above this size, allocations are rounded up to the nearest 1.575 + * power of 2. 1.576 + */ 1.577 +#define SMALL_MAX_2POW_DEFAULT 9 1.578 +#define SMALL_MAX_DEFAULT (1U << SMALL_MAX_2POW_DEFAULT) 1.579 + 1.580 +/* 1.581 + * RUN_MAX_OVRHD indicates maximum desired run header overhead. Runs are sized 1.582 + * as small as possible such that this setting is still honored, without 1.583 + * violating other constraints. The goal is to make runs as small as possible 1.584 + * without exceeding a per run external fragmentation threshold. 1.585 + * 1.586 + * We use binary fixed point math for overhead computations, where the binary 1.587 + * point is implicitly RUN_BFP bits to the left. 1.588 + * 1.589 + * Note that it is possible to set RUN_MAX_OVRHD low enough that it cannot be 1.590 + * honored for some/all object sizes, since there is one bit of header overhead 1.591 + * per object (plus a constant). This constraint is relaxed (ignored) for runs 1.592 + * that are so small that the per-region overhead is greater than: 1.593 + * 1.594 + * (RUN_MAX_OVRHD / (reg_size << (3+RUN_BFP)) 1.595 + */ 1.596 +#define RUN_BFP 12 1.597 +/* \/ Implicit binary fixed point. */ 1.598 +#define RUN_MAX_OVRHD 0x0000003dU 1.599 +#define RUN_MAX_OVRHD_RELAX 0x00001800U 1.600 + 1.601 +/* Put a cap on small object run size. This overrides RUN_MAX_OVRHD. */ 1.602 +#define RUN_MAX_SMALL_2POW 15 1.603 +#define RUN_MAX_SMALL (1U << RUN_MAX_SMALL_2POW) 1.604 + 1.605 +/* 1.606 + * Hyper-threaded CPUs may need a special instruction inside spin loops in 1.607 + * order to yield to another virtual CPU. If no such instruction is defined 1.608 + * above, make CPU_SPINWAIT a no-op. 1.609 + */ 1.610 +#ifndef CPU_SPINWAIT 1.611 +# define CPU_SPINWAIT 1.612 +#endif 1.613 + 1.614 +/* 1.615 + * Adaptive spinning must eventually switch to blocking, in order to avoid the 1.616 + * potential for priority inversion deadlock. Backing off past a certain point 1.617 + * can actually waste time. 1.618 + */ 1.619 +#define SPIN_LIMIT_2POW 11 1.620 + 1.621 +/* 1.622 + * Conversion from spinning to blocking is expensive; we use (1U << 1.623 + * BLOCK_COST_2POW) to estimate how many more times costly blocking is than 1.624 + * worst-case spinning. 1.625 + */ 1.626 +#define BLOCK_COST_2POW 4 1.627 + 1.628 +#ifdef MALLOC_BALANCE 1.629 + /* 1.630 + * We use an exponential moving average to track recent lock contention, 1.631 + * where the size of the history window is N, and alpha=2/(N+1). 1.632 + * 1.633 + * Due to integer math rounding, very small values here can cause 1.634 + * substantial degradation in accuracy, thus making the moving average decay 1.635 + * faster than it would with precise calculation. 1.636 + */ 1.637 +# define BALANCE_ALPHA_INV_2POW 9 1.638 + 1.639 + /* 1.640 + * Threshold value for the exponential moving contention average at which to 1.641 + * re-assign a thread. 1.642 + */ 1.643 +# define BALANCE_THRESHOLD_DEFAULT (1U << (SPIN_LIMIT_2POW-4)) 1.644 +#endif 1.645 + 1.646 +/******************************************************************************/ 1.647 + 1.648 +/* MALLOC_DECOMMIT and MALLOC_DOUBLE_PURGE are mutually exclusive. */ 1.649 +#if defined(MALLOC_DECOMMIT) && defined(MALLOC_DOUBLE_PURGE) 1.650 +#error MALLOC_DECOMMIT and MALLOC_DOUBLE_PURGE are mutually exclusive. 1.651 +#endif 1.652 + 1.653 +/* 1.654 + * Mutexes based on spinlocks. We can't use normal pthread spinlocks in all 1.655 + * places, because they require malloc()ed memory, which causes bootstrapping 1.656 + * issues in some cases. 1.657 + */ 1.658 +#if defined(MOZ_MEMORY_WINDOWS) 1.659 +#define malloc_mutex_t CRITICAL_SECTION 1.660 +#define malloc_spinlock_t CRITICAL_SECTION 1.661 +#elif defined(MOZ_MEMORY_DARWIN) 1.662 +typedef struct { 1.663 + OSSpinLock lock; 1.664 +} malloc_mutex_t; 1.665 +typedef struct { 1.666 + OSSpinLock lock; 1.667 +} malloc_spinlock_t; 1.668 +#elif defined(MOZ_MEMORY) 1.669 +typedef pthread_mutex_t malloc_mutex_t; 1.670 +typedef pthread_mutex_t malloc_spinlock_t; 1.671 +#else 1.672 +/* XXX these should #ifdef these for freebsd (and linux?) only */ 1.673 +typedef struct { 1.674 + spinlock_t lock; 1.675 +} malloc_mutex_t; 1.676 +typedef malloc_spinlock_t malloc_mutex_t; 1.677 +#endif 1.678 + 1.679 +/* Set to true once the allocator has been initialized. */ 1.680 +static bool malloc_initialized = false; 1.681 + 1.682 +#if defined(MOZ_MEMORY_WINDOWS) 1.683 +/* No init lock for Windows. */ 1.684 +#elif defined(MOZ_MEMORY_DARWIN) 1.685 +static malloc_mutex_t init_lock = {OS_SPINLOCK_INIT}; 1.686 +#elif defined(MOZ_MEMORY_LINUX) && !defined(MOZ_MEMORY_ANDROID) 1.687 +static malloc_mutex_t init_lock = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP; 1.688 +#elif defined(MOZ_MEMORY) 1.689 +static malloc_mutex_t init_lock = PTHREAD_MUTEX_INITIALIZER; 1.690 +#else 1.691 +static malloc_mutex_t init_lock = {_SPINLOCK_INITIALIZER}; 1.692 +#endif 1.693 + 1.694 +/******************************************************************************/ 1.695 +/* 1.696 + * Statistics data structures. 1.697 + */ 1.698 + 1.699 +#ifdef MALLOC_STATS 1.700 + 1.701 +typedef struct malloc_bin_stats_s malloc_bin_stats_t; 1.702 +struct malloc_bin_stats_s { 1.703 + /* 1.704 + * Number of allocation requests that corresponded to the size of this 1.705 + * bin. 1.706 + */ 1.707 + uint64_t nrequests; 1.708 + 1.709 + /* Total number of runs created for this bin's size class. */ 1.710 + uint64_t nruns; 1.711 + 1.712 + /* 1.713 + * Total number of runs reused by extracting them from the runs tree for 1.714 + * this bin's size class. 1.715 + */ 1.716 + uint64_t reruns; 1.717 + 1.718 + /* High-water mark for this bin. */ 1.719 + unsigned long highruns; 1.720 + 1.721 + /* Current number of runs in this bin. */ 1.722 + unsigned long curruns; 1.723 +}; 1.724 + 1.725 +typedef struct arena_stats_s arena_stats_t; 1.726 +struct arena_stats_s { 1.727 + /* Number of bytes currently mapped. */ 1.728 + size_t mapped; 1.729 + 1.730 + /* 1.731 + * Total number of purge sweeps, total number of madvise calls made, 1.732 + * and total pages purged in order to keep dirty unused memory under 1.733 + * control. 1.734 + */ 1.735 + uint64_t npurge; 1.736 + uint64_t nmadvise; 1.737 + uint64_t purged; 1.738 +#ifdef MALLOC_DECOMMIT 1.739 + /* 1.740 + * Total number of decommit/commit operations, and total number of 1.741 + * pages decommitted. 1.742 + */ 1.743 + uint64_t ndecommit; 1.744 + uint64_t ncommit; 1.745 + uint64_t decommitted; 1.746 +#endif 1.747 + 1.748 + /* Current number of committed pages. */ 1.749 + size_t committed; 1.750 + 1.751 + /* Per-size-category statistics. */ 1.752 + size_t allocated_small; 1.753 + uint64_t nmalloc_small; 1.754 + uint64_t ndalloc_small; 1.755 + 1.756 + size_t allocated_large; 1.757 + uint64_t nmalloc_large; 1.758 + uint64_t ndalloc_large; 1.759 + 1.760 +#ifdef MALLOC_BALANCE 1.761 + /* Number of times this arena reassigned a thread due to contention. */ 1.762 + uint64_t nbalance; 1.763 +#endif 1.764 +}; 1.765 + 1.766 +#endif /* #ifdef MALLOC_STATS */ 1.767 + 1.768 +/******************************************************************************/ 1.769 +/* 1.770 + * Extent data structures. 1.771 + */ 1.772 + 1.773 +/* Tree of extents. */ 1.774 +typedef struct extent_node_s extent_node_t; 1.775 +struct extent_node_s { 1.776 + /* Linkage for the size/address-ordered tree. */ 1.777 + rb_node(extent_node_t) link_szad; 1.778 + 1.779 + /* Linkage for the address-ordered tree. */ 1.780 + rb_node(extent_node_t) link_ad; 1.781 + 1.782 + /* Pointer to the extent that this tree node is responsible for. */ 1.783 + void *addr; 1.784 + 1.785 + /* Total region size. */ 1.786 + size_t size; 1.787 +}; 1.788 +typedef rb_tree(extent_node_t) extent_tree_t; 1.789 + 1.790 +/******************************************************************************/ 1.791 +/* 1.792 + * Radix tree data structures. 1.793 + */ 1.794 + 1.795 +#ifdef MALLOC_VALIDATE 1.796 + /* 1.797 + * Size of each radix tree node (must be a power of 2). This impacts tree 1.798 + * depth. 1.799 + */ 1.800 +# if (SIZEOF_PTR == 4) 1.801 +# define MALLOC_RTREE_NODESIZE (1U << 14) 1.802 +# else 1.803 +# define MALLOC_RTREE_NODESIZE CACHELINE 1.804 +# endif 1.805 + 1.806 +typedef struct malloc_rtree_s malloc_rtree_t; 1.807 +struct malloc_rtree_s { 1.808 + malloc_spinlock_t lock; 1.809 + void **root; 1.810 + unsigned height; 1.811 + unsigned level2bits[1]; /* Dynamically sized. */ 1.812 +}; 1.813 +#endif 1.814 + 1.815 +/******************************************************************************/ 1.816 +/* 1.817 + * Arena data structures. 1.818 + */ 1.819 + 1.820 +typedef struct arena_s arena_t; 1.821 +typedef struct arena_bin_s arena_bin_t; 1.822 + 1.823 +/* Each element of the chunk map corresponds to one page within the chunk. */ 1.824 +typedef struct arena_chunk_map_s arena_chunk_map_t; 1.825 +struct arena_chunk_map_s { 1.826 + /* 1.827 + * Linkage for run trees. There are two disjoint uses: 1.828 + * 1.829 + * 1) arena_t's runs_avail tree. 1.830 + * 2) arena_run_t conceptually uses this linkage for in-use non-full 1.831 + * runs, rather than directly embedding linkage. 1.832 + */ 1.833 + rb_node(arena_chunk_map_t) link; 1.834 + 1.835 + /* 1.836 + * Run address (or size) and various flags are stored together. The bit 1.837 + * layout looks like (assuming 32-bit system): 1.838 + * 1.839 + * ???????? ???????? ????---- -mckdzla 1.840 + * 1.841 + * ? : Unallocated: Run address for first/last pages, unset for internal 1.842 + * pages. 1.843 + * Small: Run address. 1.844 + * Large: Run size for first page, unset for trailing pages. 1.845 + * - : Unused. 1.846 + * m : MADV_FREE/MADV_DONTNEED'ed? 1.847 + * c : decommitted? 1.848 + * k : key? 1.849 + * d : dirty? 1.850 + * z : zeroed? 1.851 + * l : large? 1.852 + * a : allocated? 1.853 + * 1.854 + * Following are example bit patterns for the three types of runs. 1.855 + * 1.856 + * r : run address 1.857 + * s : run size 1.858 + * x : don't care 1.859 + * - : 0 1.860 + * [cdzla] : bit set 1.861 + * 1.862 + * Unallocated: 1.863 + * ssssssss ssssssss ssss---- --c----- 1.864 + * xxxxxxxx xxxxxxxx xxxx---- ----d--- 1.865 + * ssssssss ssssssss ssss---- -----z-- 1.866 + * 1.867 + * Small: 1.868 + * rrrrrrrr rrrrrrrr rrrr---- -------a 1.869 + * rrrrrrrr rrrrrrrr rrrr---- -------a 1.870 + * rrrrrrrr rrrrrrrr rrrr---- -------a 1.871 + * 1.872 + * Large: 1.873 + * ssssssss ssssssss ssss---- ------la 1.874 + * -------- -------- -------- ------la 1.875 + * -------- -------- -------- ------la 1.876 + */ 1.877 + size_t bits; 1.878 + 1.879 +/* Note that CHUNK_MAP_DECOMMITTED's meaning varies depending on whether 1.880 + * MALLOC_DECOMMIT and MALLOC_DOUBLE_PURGE are defined. 1.881 + * 1.882 + * If MALLOC_DECOMMIT is defined, a page which is CHUNK_MAP_DECOMMITTED must be 1.883 + * re-committed with pages_commit() before it may be touched. If 1.884 + * MALLOC_DECOMMIT is defined, MALLOC_DOUBLE_PURGE may not be defined. 1.885 + * 1.886 + * If neither MALLOC_DECOMMIT nor MALLOC_DOUBLE_PURGE is defined, pages which 1.887 + * are madvised (with either MADV_DONTNEED or MADV_FREE) are marked with 1.888 + * CHUNK_MAP_MADVISED. 1.889 + * 1.890 + * Otherwise, if MALLOC_DECOMMIT is not defined and MALLOC_DOUBLE_PURGE is 1.891 + * defined, then a page which is madvised is marked as CHUNK_MAP_MADVISED. 1.892 + * When it's finally freed with jemalloc_purge_freed_pages, the page is marked 1.893 + * as CHUNK_MAP_DECOMMITTED. 1.894 + */ 1.895 +#if defined(MALLOC_DECOMMIT) || defined(MALLOC_STATS) || defined(MALLOC_DOUBLE_PURGE) 1.896 +#define CHUNK_MAP_MADVISED ((size_t)0x40U) 1.897 +#define CHUNK_MAP_DECOMMITTED ((size_t)0x20U) 1.898 +#define CHUNK_MAP_MADVISED_OR_DECOMMITTED (CHUNK_MAP_MADVISED | CHUNK_MAP_DECOMMITTED) 1.899 +#endif 1.900 +#define CHUNK_MAP_KEY ((size_t)0x10U) 1.901 +#define CHUNK_MAP_DIRTY ((size_t)0x08U) 1.902 +#define CHUNK_MAP_ZEROED ((size_t)0x04U) 1.903 +#define CHUNK_MAP_LARGE ((size_t)0x02U) 1.904 +#define CHUNK_MAP_ALLOCATED ((size_t)0x01U) 1.905 +}; 1.906 +typedef rb_tree(arena_chunk_map_t) arena_avail_tree_t; 1.907 +typedef rb_tree(arena_chunk_map_t) arena_run_tree_t; 1.908 + 1.909 +/* Arena chunk header. */ 1.910 +typedef struct arena_chunk_s arena_chunk_t; 1.911 +struct arena_chunk_s { 1.912 + /* Arena that owns the chunk. */ 1.913 + arena_t *arena; 1.914 + 1.915 + /* Linkage for the arena's chunks_dirty tree. */ 1.916 + rb_node(arena_chunk_t) link_dirty; 1.917 + 1.918 +#ifdef MALLOC_DOUBLE_PURGE 1.919 + /* If we're double-purging, we maintain a linked list of chunks which 1.920 + * have pages which have been madvise(MADV_FREE)'d but not explicitly 1.921 + * purged. 1.922 + * 1.923 + * We're currently lazy and don't remove a chunk from this list when 1.924 + * all its madvised pages are recommitted. */ 1.925 + LinkedList chunks_madvised_elem; 1.926 +#endif 1.927 + 1.928 + /* Number of dirty pages. */ 1.929 + size_t ndirty; 1.930 + 1.931 + /* Map of pages within chunk that keeps track of free/large/small. */ 1.932 + arena_chunk_map_t map[1]; /* Dynamically sized. */ 1.933 +}; 1.934 +typedef rb_tree(arena_chunk_t) arena_chunk_tree_t; 1.935 + 1.936 +typedef struct arena_run_s arena_run_t; 1.937 +struct arena_run_s { 1.938 +#if defined(MALLOC_DEBUG) || defined(MOZ_JEMALLOC_HARD_ASSERTS) 1.939 + uint32_t magic; 1.940 +# define ARENA_RUN_MAGIC 0x384adf93 1.941 +#endif 1.942 + 1.943 + /* Bin this run is associated with. */ 1.944 + arena_bin_t *bin; 1.945 + 1.946 + /* Index of first element that might have a free region. */ 1.947 + unsigned regs_minelm; 1.948 + 1.949 + /* Number of free regions in run. */ 1.950 + unsigned nfree; 1.951 + 1.952 + /* Bitmask of in-use regions (0: in use, 1: free). */ 1.953 + unsigned regs_mask[1]; /* Dynamically sized. */ 1.954 +}; 1.955 + 1.956 +struct arena_bin_s { 1.957 + /* 1.958 + * Current run being used to service allocations of this bin's size 1.959 + * class. 1.960 + */ 1.961 + arena_run_t *runcur; 1.962 + 1.963 + /* 1.964 + * Tree of non-full runs. This tree is used when looking for an 1.965 + * existing run when runcur is no longer usable. We choose the 1.966 + * non-full run that is lowest in memory; this policy tends to keep 1.967 + * objects packed well, and it can also help reduce the number of 1.968 + * almost-empty chunks. 1.969 + */ 1.970 + arena_run_tree_t runs; 1.971 + 1.972 + /* Size of regions in a run for this bin's size class. */ 1.973 + size_t reg_size; 1.974 + 1.975 + /* Total size of a run for this bin's size class. */ 1.976 + size_t run_size; 1.977 + 1.978 + /* Total number of regions in a run for this bin's size class. */ 1.979 + uint32_t nregs; 1.980 + 1.981 + /* Number of elements in a run's regs_mask for this bin's size class. */ 1.982 + uint32_t regs_mask_nelms; 1.983 + 1.984 + /* Offset of first region in a run for this bin's size class. */ 1.985 + uint32_t reg0_offset; 1.986 + 1.987 +#ifdef MALLOC_STATS 1.988 + /* Bin statistics. */ 1.989 + malloc_bin_stats_t stats; 1.990 +#endif 1.991 +}; 1.992 + 1.993 +struct arena_s { 1.994 +#if defined(MALLOC_DEBUG) || defined(MOZ_JEMALLOC_HARD_ASSERTS) 1.995 + uint32_t magic; 1.996 +# define ARENA_MAGIC 0x947d3d24 1.997 +#endif 1.998 + 1.999 + /* All operations on this arena require that lock be locked. */ 1.1000 +#ifdef MOZ_MEMORY 1.1001 + malloc_spinlock_t lock; 1.1002 +#else 1.1003 + pthread_mutex_t lock; 1.1004 +#endif 1.1005 + 1.1006 +#ifdef MALLOC_STATS 1.1007 + arena_stats_t stats; 1.1008 +#endif 1.1009 + 1.1010 + /* Tree of dirty-page-containing chunks this arena manages. */ 1.1011 + arena_chunk_tree_t chunks_dirty; 1.1012 + 1.1013 +#ifdef MALLOC_DOUBLE_PURGE 1.1014 + /* Head of a linked list of MADV_FREE'd-page-containing chunks this 1.1015 + * arena manages. */ 1.1016 + LinkedList chunks_madvised; 1.1017 +#endif 1.1018 + 1.1019 + /* 1.1020 + * In order to avoid rapid chunk allocation/deallocation when an arena 1.1021 + * oscillates right on the cusp of needing a new chunk, cache the most 1.1022 + * recently freed chunk. The spare is left in the arena's chunk trees 1.1023 + * until it is deleted. 1.1024 + * 1.1025 + * There is one spare chunk per arena, rather than one spare total, in 1.1026 + * order to avoid interactions between multiple threads that could make 1.1027 + * a single spare inadequate. 1.1028 + */ 1.1029 + arena_chunk_t *spare; 1.1030 + 1.1031 + /* 1.1032 + * Current count of pages within unused runs that are potentially 1.1033 + * dirty, and for which madvise(... MADV_FREE) has not been called. By 1.1034 + * tracking this, we can institute a limit on how much dirty unused 1.1035 + * memory is mapped for each arena. 1.1036 + */ 1.1037 + size_t ndirty; 1.1038 + 1.1039 + /* 1.1040 + * Size/address-ordered tree of this arena's available runs. This tree 1.1041 + * is used for first-best-fit run allocation. 1.1042 + */ 1.1043 + arena_avail_tree_t runs_avail; 1.1044 + 1.1045 +#ifdef MALLOC_BALANCE 1.1046 + /* 1.1047 + * The arena load balancing machinery needs to keep track of how much 1.1048 + * lock contention there is. This value is exponentially averaged. 1.1049 + */ 1.1050 + uint32_t contention; 1.1051 +#endif 1.1052 + 1.1053 + /* 1.1054 + * bins is used to store rings of free regions of the following sizes, 1.1055 + * assuming a 16-byte quantum, 4kB pagesize, and default MALLOC_OPTIONS. 1.1056 + * 1.1057 + * bins[i] | size | 1.1058 + * --------+------+ 1.1059 + * 0 | 2 | 1.1060 + * 1 | 4 | 1.1061 + * 2 | 8 | 1.1062 + * --------+------+ 1.1063 + * 3 | 16 | 1.1064 + * 4 | 32 | 1.1065 + * 5 | 48 | 1.1066 + * 6 | 64 | 1.1067 + * : : 1.1068 + * : : 1.1069 + * 33 | 496 | 1.1070 + * 34 | 512 | 1.1071 + * --------+------+ 1.1072 + * 35 | 1024 | 1.1073 + * 36 | 2048 | 1.1074 + * --------+------+ 1.1075 + */ 1.1076 + arena_bin_t bins[1]; /* Dynamically sized. */ 1.1077 +}; 1.1078 + 1.1079 +/******************************************************************************/ 1.1080 +/* 1.1081 + * Data. 1.1082 + */ 1.1083 + 1.1084 +#ifndef MOZ_MEMORY_NARENAS_DEFAULT_ONE 1.1085 +/* Number of CPUs. */ 1.1086 +static unsigned ncpus; 1.1087 +#endif 1.1088 + 1.1089 +/* 1.1090 + * When MALLOC_STATIC_SIZES is defined most of the parameters 1.1091 + * controlling the malloc behavior are defined as compile-time constants 1.1092 + * for best performance and cannot be altered at runtime. 1.1093 + */ 1.1094 +#if !defined(__ia64__) && !defined(__sparc__) && !defined(__mips__) 1.1095 +#define MALLOC_STATIC_SIZES 1 1.1096 +#endif 1.1097 + 1.1098 +#ifdef MALLOC_STATIC_SIZES 1.1099 + 1.1100 +/* 1.1101 + * VM page size. It must divide the runtime CPU page size or the code 1.1102 + * will abort. 1.1103 + * Platform specific page size conditions copied from js/public/HeapAPI.h 1.1104 + */ 1.1105 +#if (defined(SOLARIS) || defined(__FreeBSD__)) && \ 1.1106 + (defined(__sparc) || defined(__sparcv9) || defined(__ia64)) 1.1107 +#define pagesize_2pow ((size_t) 13) 1.1108 +#elif defined(__powerpc64__) || defined(__aarch64__) 1.1109 +#define pagesize_2pow ((size_t) 16) 1.1110 +#else 1.1111 +#define pagesize_2pow ((size_t) 12) 1.1112 +#endif 1.1113 +#define pagesize ((size_t) 1 << pagesize_2pow) 1.1114 +#define pagesize_mask (pagesize - 1) 1.1115 + 1.1116 +/* Various quantum-related settings. */ 1.1117 + 1.1118 +#define QUANTUM_DEFAULT ((size_t) 1 << QUANTUM_2POW_MIN) 1.1119 +static const size_t quantum = QUANTUM_DEFAULT; 1.1120 +static const size_t quantum_mask = QUANTUM_DEFAULT - 1; 1.1121 + 1.1122 +/* Various bin-related settings. */ 1.1123 + 1.1124 +static const size_t small_min = (QUANTUM_DEFAULT >> 1) + 1; 1.1125 +static const size_t small_max = (size_t) SMALL_MAX_DEFAULT; 1.1126 + 1.1127 +/* Max size class for bins. */ 1.1128 +static const size_t bin_maxclass = pagesize >> 1; 1.1129 + 1.1130 + /* Number of (2^n)-spaced tiny bins. */ 1.1131 +static const unsigned ntbins = (unsigned) 1.1132 + (QUANTUM_2POW_MIN - TINY_MIN_2POW); 1.1133 + 1.1134 + /* Number of quantum-spaced bins. */ 1.1135 +static const unsigned nqbins = (unsigned) 1.1136 + (SMALL_MAX_DEFAULT >> QUANTUM_2POW_MIN); 1.1137 + 1.1138 +/* Number of (2^n)-spaced sub-page bins. */ 1.1139 +static const unsigned nsbins = (unsigned) 1.1140 + (pagesize_2pow - 1.1141 + SMALL_MAX_2POW_DEFAULT - 1); 1.1142 + 1.1143 +#else /* !MALLOC_STATIC_SIZES */ 1.1144 + 1.1145 +/* VM page size. */ 1.1146 +static size_t pagesize; 1.1147 +static size_t pagesize_mask; 1.1148 +static size_t pagesize_2pow; 1.1149 + 1.1150 +/* Various bin-related settings. */ 1.1151 +static size_t bin_maxclass; /* Max size class for bins. */ 1.1152 +static unsigned ntbins; /* Number of (2^n)-spaced tiny bins. */ 1.1153 +static unsigned nqbins; /* Number of quantum-spaced bins. */ 1.1154 +static unsigned nsbins; /* Number of (2^n)-spaced sub-page bins. */ 1.1155 +static size_t small_min; 1.1156 +static size_t small_max; 1.1157 + 1.1158 +/* Various quantum-related settings. */ 1.1159 +static size_t quantum; 1.1160 +static size_t quantum_mask; /* (quantum - 1). */ 1.1161 + 1.1162 +#endif 1.1163 + 1.1164 +/* Various chunk-related settings. */ 1.1165 + 1.1166 +/* 1.1167 + * Compute the header size such that it is large enough to contain the page map 1.1168 + * and enough nodes for the worst case: one node per non-header page plus one 1.1169 + * extra for situations where we briefly have one more node allocated than we 1.1170 + * will need. 1.1171 + */ 1.1172 +#define calculate_arena_header_size() \ 1.1173 + (sizeof(arena_chunk_t) + sizeof(arena_chunk_map_t) * (chunk_npages - 1)) 1.1174 + 1.1175 +#define calculate_arena_header_pages() \ 1.1176 + ((calculate_arena_header_size() >> pagesize_2pow) + \ 1.1177 + ((calculate_arena_header_size() & pagesize_mask) ? 1 : 0)) 1.1178 + 1.1179 +/* Max size class for arenas. */ 1.1180 +#define calculate_arena_maxclass() \ 1.1181 + (chunksize - (arena_chunk_header_npages << pagesize_2pow)) 1.1182 + 1.1183 +#ifdef MALLOC_STATIC_SIZES 1.1184 +#define CHUNKSIZE_DEFAULT ((size_t) 1 << CHUNK_2POW_DEFAULT) 1.1185 +static const size_t chunksize = CHUNKSIZE_DEFAULT; 1.1186 +static const size_t chunksize_mask =CHUNKSIZE_DEFAULT - 1; 1.1187 +static const size_t chunk_npages = CHUNKSIZE_DEFAULT >> pagesize_2pow; 1.1188 +#define arena_chunk_header_npages calculate_arena_header_pages() 1.1189 +#define arena_maxclass calculate_arena_maxclass() 1.1190 +#else 1.1191 +static size_t chunksize; 1.1192 +static size_t chunksize_mask; /* (chunksize - 1). */ 1.1193 +static size_t chunk_npages; 1.1194 +static size_t arena_chunk_header_npages; 1.1195 +static size_t arena_maxclass; /* Max size class for arenas. */ 1.1196 +#endif 1.1197 + 1.1198 +/********/ 1.1199 +/* 1.1200 + * Chunks. 1.1201 + */ 1.1202 + 1.1203 +#ifdef MALLOC_VALIDATE 1.1204 +static malloc_rtree_t *chunk_rtree; 1.1205 +#endif 1.1206 + 1.1207 +/* Protects chunk-related data structures. */ 1.1208 +static malloc_mutex_t huge_mtx; 1.1209 + 1.1210 +/* Tree of chunks that are stand-alone huge allocations. */ 1.1211 +static extent_tree_t huge; 1.1212 + 1.1213 +#ifdef MALLOC_STATS 1.1214 +/* Huge allocation statistics. */ 1.1215 +static uint64_t huge_nmalloc; 1.1216 +static uint64_t huge_ndalloc; 1.1217 +static size_t huge_allocated; 1.1218 +static size_t huge_mapped; 1.1219 +#endif 1.1220 + 1.1221 +#ifdef MALLOC_PAGEFILE 1.1222 +static char pagefile_templ[PATH_MAX]; 1.1223 +#endif 1.1224 + 1.1225 +/****************************/ 1.1226 +/* 1.1227 + * base (internal allocation). 1.1228 + */ 1.1229 + 1.1230 +/* 1.1231 + * Current pages that are being used for internal memory allocations. These 1.1232 + * pages are carved up in cacheline-size quanta, so that there is no chance of 1.1233 + * false cache line sharing. 1.1234 + */ 1.1235 +static void *base_pages; 1.1236 +static void *base_next_addr; 1.1237 +#if defined(MALLOC_DECOMMIT) || defined(MALLOC_STATS) 1.1238 +static void *base_next_decommitted; 1.1239 +#endif 1.1240 +static void *base_past_addr; /* Addr immediately past base_pages. */ 1.1241 +static extent_node_t *base_nodes; 1.1242 +static malloc_mutex_t base_mtx; 1.1243 +#ifdef MALLOC_STATS 1.1244 +static size_t base_mapped; 1.1245 +static size_t base_committed; 1.1246 +#endif 1.1247 + 1.1248 +/********/ 1.1249 +/* 1.1250 + * Arenas. 1.1251 + */ 1.1252 + 1.1253 +/* 1.1254 + * Arenas that are used to service external requests. Not all elements of the 1.1255 + * arenas array are necessarily used; arenas are created lazily as needed. 1.1256 + */ 1.1257 +static arena_t **arenas; 1.1258 +static unsigned narenas; 1.1259 +#ifndef NO_TLS 1.1260 +# ifdef MALLOC_BALANCE 1.1261 +static unsigned narenas_2pow; 1.1262 +# else 1.1263 +static unsigned next_arena; 1.1264 +# endif 1.1265 +#endif 1.1266 +#ifdef MOZ_MEMORY 1.1267 +static malloc_spinlock_t arenas_lock; /* Protects arenas initialization. */ 1.1268 +#else 1.1269 +static pthread_mutex_t arenas_lock; /* Protects arenas initialization. */ 1.1270 +#endif 1.1271 + 1.1272 +#ifndef NO_TLS 1.1273 +/* 1.1274 + * Map of pthread_self() --> arenas[???], used for selecting an arena to use 1.1275 + * for allocations. 1.1276 + */ 1.1277 +#ifndef MOZ_MEMORY_WINDOWS 1.1278 +static __thread arena_t *arenas_map; 1.1279 +#endif 1.1280 +#endif 1.1281 + 1.1282 +/*******************************/ 1.1283 +/* 1.1284 + * Runtime configuration options. 1.1285 + */ 1.1286 +MOZ_JEMALLOC_API 1.1287 +const char *_malloc_options = MOZ_MALLOC_OPTIONS; 1.1288 + 1.1289 +#ifndef MALLOC_PRODUCTION 1.1290 +static bool opt_abort = true; 1.1291 +#ifdef MALLOC_FILL 1.1292 +static bool opt_junk = true; 1.1293 +static bool opt_poison = true; 1.1294 +static bool opt_zero = false; 1.1295 +#endif 1.1296 +#else 1.1297 +static bool opt_abort = false; 1.1298 +#ifdef MALLOC_FILL 1.1299 +static const bool opt_junk = false; 1.1300 +static const bool opt_poison = true; 1.1301 +static const bool opt_zero = false; 1.1302 +#endif 1.1303 +#endif 1.1304 + 1.1305 +static size_t opt_dirty_max = DIRTY_MAX_DEFAULT; 1.1306 +#ifdef MALLOC_BALANCE 1.1307 +static uint64_t opt_balance_threshold = BALANCE_THRESHOLD_DEFAULT; 1.1308 +#endif 1.1309 +static bool opt_print_stats = false; 1.1310 +#ifdef MALLOC_STATIC_SIZES 1.1311 +#define opt_quantum_2pow QUANTUM_2POW_MIN 1.1312 +#define opt_small_max_2pow SMALL_MAX_2POW_DEFAULT 1.1313 +#define opt_chunk_2pow CHUNK_2POW_DEFAULT 1.1314 +#else 1.1315 +static size_t opt_quantum_2pow = QUANTUM_2POW_MIN; 1.1316 +static size_t opt_small_max_2pow = SMALL_MAX_2POW_DEFAULT; 1.1317 +static size_t opt_chunk_2pow = CHUNK_2POW_DEFAULT; 1.1318 +#endif 1.1319 +#ifdef MALLOC_PAGEFILE 1.1320 +static bool opt_pagefile = false; 1.1321 +#endif 1.1322 +#ifdef MALLOC_UTRACE 1.1323 +static bool opt_utrace = false; 1.1324 +#endif 1.1325 +#ifdef MALLOC_SYSV 1.1326 +static bool opt_sysv = false; 1.1327 +#endif 1.1328 +#ifdef MALLOC_XMALLOC 1.1329 +static bool opt_xmalloc = false; 1.1330 +#endif 1.1331 +static int opt_narenas_lshift = 0; 1.1332 + 1.1333 +#ifdef MALLOC_UTRACE 1.1334 +typedef struct { 1.1335 + void *p; 1.1336 + size_t s; 1.1337 + void *r; 1.1338 +} malloc_utrace_t; 1.1339 + 1.1340 +#define UTRACE(a, b, c) \ 1.1341 + if (opt_utrace) { \ 1.1342 + malloc_utrace_t ut; \ 1.1343 + ut.p = (a); \ 1.1344 + ut.s = (b); \ 1.1345 + ut.r = (c); \ 1.1346 + utrace(&ut, sizeof(ut)); \ 1.1347 + } 1.1348 +#else 1.1349 +#define UTRACE(a, b, c) 1.1350 +#endif 1.1351 + 1.1352 +/******************************************************************************/ 1.1353 +/* 1.1354 + * Begin function prototypes for non-inline static functions. 1.1355 + */ 1.1356 + 1.1357 +static char *umax2s(uintmax_t x, unsigned base, char *s); 1.1358 +static bool malloc_mutex_init(malloc_mutex_t *mutex); 1.1359 +static bool malloc_spin_init(malloc_spinlock_t *lock); 1.1360 +static void wrtmessage(const char *p1, const char *p2, const char *p3, 1.1361 + const char *p4); 1.1362 +#ifdef MALLOC_STATS 1.1363 +#ifdef MOZ_MEMORY_DARWIN 1.1364 +/* Avoid namespace collision with OS X's malloc APIs. */ 1.1365 +#define malloc_printf moz_malloc_printf 1.1366 +#endif 1.1367 +static void malloc_printf(const char *format, ...); 1.1368 +#endif 1.1369 +static bool base_pages_alloc_mmap(size_t minsize); 1.1370 +static bool base_pages_alloc(size_t minsize); 1.1371 +static void *base_alloc(size_t size); 1.1372 +static void *base_calloc(size_t number, size_t size); 1.1373 +static extent_node_t *base_node_alloc(void); 1.1374 +static void base_node_dealloc(extent_node_t *node); 1.1375 +#ifdef MALLOC_STATS 1.1376 +static void stats_print(arena_t *arena); 1.1377 +#endif 1.1378 +static void *pages_map(void *addr, size_t size, int pfd); 1.1379 +static void pages_unmap(void *addr, size_t size); 1.1380 +static void *chunk_alloc_mmap(size_t size, bool pagefile); 1.1381 +#ifdef MALLOC_PAGEFILE 1.1382 +static int pagefile_init(size_t size); 1.1383 +static void pagefile_close(int pfd); 1.1384 +#endif 1.1385 +static void *chunk_alloc(size_t size, bool zero, bool pagefile); 1.1386 +static void chunk_dealloc_mmap(void *chunk, size_t size); 1.1387 +static void chunk_dealloc(void *chunk, size_t size); 1.1388 +#ifndef NO_TLS 1.1389 +static arena_t *choose_arena_hard(void); 1.1390 +#endif 1.1391 +static void arena_run_split(arena_t *arena, arena_run_t *run, size_t size, 1.1392 + bool large, bool zero); 1.1393 +static void arena_chunk_init(arena_t *arena, arena_chunk_t *chunk); 1.1394 +static void arena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk); 1.1395 +static arena_run_t *arena_run_alloc(arena_t *arena, arena_bin_t *bin, 1.1396 + size_t size, bool large, bool zero); 1.1397 +static void arena_purge(arena_t *arena, bool all); 1.1398 +static void arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty); 1.1399 +static void arena_run_trim_head(arena_t *arena, arena_chunk_t *chunk, 1.1400 + arena_run_t *run, size_t oldsize, size_t newsize); 1.1401 +static void arena_run_trim_tail(arena_t *arena, arena_chunk_t *chunk, 1.1402 + arena_run_t *run, size_t oldsize, size_t newsize, bool dirty); 1.1403 +static arena_run_t *arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin); 1.1404 +static void *arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin); 1.1405 +static size_t arena_bin_run_size_calc(arena_bin_t *bin, size_t min_run_size); 1.1406 +#ifdef MALLOC_BALANCE 1.1407 +static void arena_lock_balance_hard(arena_t *arena); 1.1408 +#endif 1.1409 +static void *arena_malloc_large(arena_t *arena, size_t size, bool zero); 1.1410 +static void *arena_palloc(arena_t *arena, size_t alignment, size_t size, 1.1411 + size_t alloc_size); 1.1412 +static size_t arena_salloc(const void *ptr); 1.1413 +static void arena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, 1.1414 + void *ptr); 1.1415 +static void arena_ralloc_large_shrink(arena_t *arena, arena_chunk_t *chunk, 1.1416 + void *ptr, size_t size, size_t oldsize); 1.1417 +static bool arena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk, 1.1418 + void *ptr, size_t size, size_t oldsize); 1.1419 +static bool arena_ralloc_large(void *ptr, size_t size, size_t oldsize); 1.1420 +static void *arena_ralloc(void *ptr, size_t size, size_t oldsize); 1.1421 +static bool arena_new(arena_t *arena); 1.1422 +static arena_t *arenas_extend(unsigned ind); 1.1423 +static void *huge_malloc(size_t size, bool zero); 1.1424 +static void *huge_palloc(size_t alignment, size_t size); 1.1425 +static void *huge_ralloc(void *ptr, size_t size, size_t oldsize); 1.1426 +static void huge_dalloc(void *ptr); 1.1427 +static void malloc_print_stats(void); 1.1428 +#ifndef MOZ_MEMORY_WINDOWS 1.1429 +static 1.1430 +#endif 1.1431 +bool malloc_init_hard(void); 1.1432 + 1.1433 +static void _malloc_prefork(void); 1.1434 +static void _malloc_postfork(void); 1.1435 + 1.1436 +#ifdef MOZ_MEMORY_DARWIN 1.1437 +/* 1.1438 + * MALLOC_ZONE_T_NOTE 1.1439 + * 1.1440 + * On Darwin, we hook into the memory allocator using a malloc_zone_t struct. 1.1441 + * We must be very careful around this struct because of different behaviour on 1.1442 + * different versions of OSX. 1.1443 + * 1.1444 + * Each of OSX 10.5, 10.6 and 10.7 use different versions of the struct 1.1445 + * (with version numbers 3, 6 and 8 respectively). The binary we use on each of 1.1446 + * these platforms will not necessarily be built using the correct SDK [1]. 1.1447 + * This means we need to statically know the correct struct size to use on all 1.1448 + * OSX releases, and have a fallback for unknown future versions. The struct 1.1449 + * sizes defined in osx_zone_types.h. 1.1450 + * 1.1451 + * For OSX 10.8 and later, we may expect the malloc_zone_t struct to change 1.1452 + * again, and need to dynamically account for this. By simply leaving 1.1453 + * malloc_zone_t alone, we don't quite deal with the problem, because there 1.1454 + * remain calls to jemalloc through the mozalloc interface. We check this 1.1455 + * dynamically on each allocation, using the CHECK_DARWIN macro and 1.1456 + * osx_use_jemalloc. 1.1457 + * 1.1458 + * 1.1459 + * [1] Mozilla is built as a universal binary on Mac, supporting i386 and 1.1460 + * x86_64. The i386 target is built using the 10.5 SDK, even if it runs on 1.1461 + * 10.6. The x86_64 target is built using the 10.6 SDK, even if it runs on 1.1462 + * 10.7 or later, or 10.5. 1.1463 + * 1.1464 + * FIXME: 1.1465 + * When later versions of OSX come out (10.8 and up), we need to check their 1.1466 + * malloc_zone_t versions. If they're greater than 8, we need a new version 1.1467 + * of malloc_zone_t adapted into osx_zone_types.h. 1.1468 + */ 1.1469 + 1.1470 +#ifndef MOZ_REPLACE_MALLOC 1.1471 +#include "osx_zone_types.h" 1.1472 + 1.1473 +#define LEOPARD_MALLOC_ZONE_T_VERSION 3 1.1474 +#define SNOW_LEOPARD_MALLOC_ZONE_T_VERSION 6 1.1475 +#define LION_MALLOC_ZONE_T_VERSION 8 1.1476 + 1.1477 +static bool osx_use_jemalloc = false; 1.1478 + 1.1479 + 1.1480 +static lion_malloc_zone l_szone; 1.1481 +static malloc_zone_t * szone = (malloc_zone_t*)(&l_szone); 1.1482 + 1.1483 +static lion_malloc_introspection l_ozone_introspect; 1.1484 +static malloc_introspection_t * const ozone_introspect = 1.1485 + (malloc_introspection_t*)(&l_ozone_introspect); 1.1486 +static void szone2ozone(malloc_zone_t *zone, size_t size); 1.1487 +static size_t zone_version_size(int version); 1.1488 +#else 1.1489 +static const bool osx_use_jemalloc = true; 1.1490 +#endif 1.1491 + 1.1492 +#endif 1.1493 + 1.1494 +/* 1.1495 + * End function prototypes. 1.1496 + */ 1.1497 +/******************************************************************************/ 1.1498 + 1.1499 +/* 1.1500 + * umax2s() provides minimal integer printing functionality, which is 1.1501 + * especially useful for situations where allocation in vsnprintf() calls would 1.1502 + * potentially cause deadlock. 1.1503 + */ 1.1504 +#define UMAX2S_BUFSIZE 65 1.1505 +char * 1.1506 +umax2s(uintmax_t x, unsigned base, char *s) 1.1507 +{ 1.1508 + unsigned i; 1.1509 + 1.1510 + i = UMAX2S_BUFSIZE - 1; 1.1511 + s[i] = '\0'; 1.1512 + switch (base) { 1.1513 + case 10: 1.1514 + do { 1.1515 + i--; 1.1516 + s[i] = "0123456789"[x % 10]; 1.1517 + x /= 10; 1.1518 + } while (x > 0); 1.1519 + break; 1.1520 + case 16: 1.1521 + do { 1.1522 + i--; 1.1523 + s[i] = "0123456789abcdef"[x & 0xf]; 1.1524 + x >>= 4; 1.1525 + } while (x > 0); 1.1526 + break; 1.1527 + default: 1.1528 + do { 1.1529 + i--; 1.1530 + s[i] = "0123456789abcdefghijklmnopqrstuvwxyz"[x % base]; 1.1531 + x /= base; 1.1532 + } while (x > 0); 1.1533 + } 1.1534 + 1.1535 + return (&s[i]); 1.1536 +} 1.1537 + 1.1538 +static void 1.1539 +wrtmessage(const char *p1, const char *p2, const char *p3, const char *p4) 1.1540 +{ 1.1541 +#if defined(MOZ_MEMORY) && !defined(MOZ_MEMORY_WINDOWS) 1.1542 +#define _write write 1.1543 +#endif 1.1544 + _write(STDERR_FILENO, p1, (unsigned int) strlen(p1)); 1.1545 + _write(STDERR_FILENO, p2, (unsigned int) strlen(p2)); 1.1546 + _write(STDERR_FILENO, p3, (unsigned int) strlen(p3)); 1.1547 + _write(STDERR_FILENO, p4, (unsigned int) strlen(p4)); 1.1548 +} 1.1549 + 1.1550 +MOZ_JEMALLOC_API 1.1551 +void (*_malloc_message)(const char *p1, const char *p2, const char *p3, 1.1552 + const char *p4) = wrtmessage; 1.1553 + 1.1554 +#ifdef MALLOC_DEBUG 1.1555 +# define assert(e) do { \ 1.1556 + if (!(e)) { \ 1.1557 + char line_buf[UMAX2S_BUFSIZE]; \ 1.1558 + _malloc_message(__FILE__, ":", umax2s(__LINE__, 10, \ 1.1559 + line_buf), ": Failed assertion: "); \ 1.1560 + _malloc_message("\"", #e, "\"\n", ""); \ 1.1561 + abort(); \ 1.1562 + } \ 1.1563 +} while (0) 1.1564 +#else 1.1565 +#define assert(e) 1.1566 +#endif 1.1567 + 1.1568 +#include <mozilla/Assertions.h> 1.1569 +#include <mozilla/Attributes.h> 1.1570 + 1.1571 +/* RELEASE_ASSERT calls jemalloc_crash() instead of calling MOZ_CRASH() 1.1572 + * directly because we want crashing to add a frame to the stack. This makes 1.1573 + * it easier to find the failing assertion in crash stacks. */ 1.1574 +MOZ_NEVER_INLINE static void 1.1575 +jemalloc_crash() 1.1576 +{ 1.1577 + MOZ_CRASH(); 1.1578 +} 1.1579 + 1.1580 +#if defined(MOZ_JEMALLOC_HARD_ASSERTS) 1.1581 +# define RELEASE_ASSERT(assertion) do { \ 1.1582 + if (!(assertion)) { \ 1.1583 + jemalloc_crash(); \ 1.1584 + } \ 1.1585 +} while (0) 1.1586 +#else 1.1587 +# define RELEASE_ASSERT(assertion) assert(assertion) 1.1588 +#endif 1.1589 + 1.1590 +/******************************************************************************/ 1.1591 +/* 1.1592 + * Begin mutex. We can't use normal pthread mutexes in all places, because 1.1593 + * they require malloc()ed memory, which causes bootstrapping issues in some 1.1594 + * cases. 1.1595 + */ 1.1596 + 1.1597 +static bool 1.1598 +malloc_mutex_init(malloc_mutex_t *mutex) 1.1599 +{ 1.1600 +#if defined(MOZ_MEMORY_WINDOWS) 1.1601 + if (isthreaded) 1.1602 + if (! __crtInitCritSecAndSpinCount(mutex, _CRT_SPINCOUNT)) 1.1603 + return (true); 1.1604 +#elif defined(MOZ_MEMORY_DARWIN) 1.1605 + mutex->lock = OS_SPINLOCK_INIT; 1.1606 +#elif defined(MOZ_MEMORY_LINUX) && !defined(MOZ_MEMORY_ANDROID) 1.1607 + pthread_mutexattr_t attr; 1.1608 + if (pthread_mutexattr_init(&attr) != 0) 1.1609 + return (true); 1.1610 + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP); 1.1611 + if (pthread_mutex_init(mutex, &attr) != 0) { 1.1612 + pthread_mutexattr_destroy(&attr); 1.1613 + return (true); 1.1614 + } 1.1615 + pthread_mutexattr_destroy(&attr); 1.1616 +#elif defined(MOZ_MEMORY) 1.1617 + if (pthread_mutex_init(mutex, NULL) != 0) 1.1618 + return (true); 1.1619 +#else 1.1620 + static const spinlock_t lock = _SPINLOCK_INITIALIZER; 1.1621 + 1.1622 + mutex->lock = lock; 1.1623 +#endif 1.1624 + return (false); 1.1625 +} 1.1626 + 1.1627 +static inline void 1.1628 +malloc_mutex_lock(malloc_mutex_t *mutex) 1.1629 +{ 1.1630 + 1.1631 +#if defined(MOZ_MEMORY_WINDOWS) 1.1632 + EnterCriticalSection(mutex); 1.1633 +#elif defined(MOZ_MEMORY_DARWIN) 1.1634 + OSSpinLockLock(&mutex->lock); 1.1635 +#elif defined(MOZ_MEMORY) 1.1636 + pthread_mutex_lock(mutex); 1.1637 +#else 1.1638 + if (isthreaded) 1.1639 + _SPINLOCK(&mutex->lock); 1.1640 +#endif 1.1641 +} 1.1642 + 1.1643 +static inline void 1.1644 +malloc_mutex_unlock(malloc_mutex_t *mutex) 1.1645 +{ 1.1646 + 1.1647 +#if defined(MOZ_MEMORY_WINDOWS) 1.1648 + LeaveCriticalSection(mutex); 1.1649 +#elif defined(MOZ_MEMORY_DARWIN) 1.1650 + OSSpinLockUnlock(&mutex->lock); 1.1651 +#elif defined(MOZ_MEMORY) 1.1652 + pthread_mutex_unlock(mutex); 1.1653 +#else 1.1654 + if (isthreaded) 1.1655 + _SPINUNLOCK(&mutex->lock); 1.1656 +#endif 1.1657 +} 1.1658 + 1.1659 +static bool 1.1660 +malloc_spin_init(malloc_spinlock_t *lock) 1.1661 +{ 1.1662 +#if defined(MOZ_MEMORY_WINDOWS) 1.1663 + if (isthreaded) 1.1664 + if (! __crtInitCritSecAndSpinCount(lock, _CRT_SPINCOUNT)) 1.1665 + return (true); 1.1666 +#elif defined(MOZ_MEMORY_DARWIN) 1.1667 + lock->lock = OS_SPINLOCK_INIT; 1.1668 +#elif defined(MOZ_MEMORY_LINUX) && !defined(MOZ_MEMORY_ANDROID) 1.1669 + pthread_mutexattr_t attr; 1.1670 + if (pthread_mutexattr_init(&attr) != 0) 1.1671 + return (true); 1.1672 + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP); 1.1673 + if (pthread_mutex_init(lock, &attr) != 0) { 1.1674 + pthread_mutexattr_destroy(&attr); 1.1675 + return (true); 1.1676 + } 1.1677 + pthread_mutexattr_destroy(&attr); 1.1678 +#elif defined(MOZ_MEMORY) 1.1679 + if (pthread_mutex_init(lock, NULL) != 0) 1.1680 + return (true); 1.1681 +#else 1.1682 + lock->lock = _SPINLOCK_INITIALIZER; 1.1683 +#endif 1.1684 + return (false); 1.1685 +} 1.1686 + 1.1687 +static inline void 1.1688 +malloc_spin_lock(malloc_spinlock_t *lock) 1.1689 +{ 1.1690 + 1.1691 +#if defined(MOZ_MEMORY_WINDOWS) 1.1692 + EnterCriticalSection(lock); 1.1693 +#elif defined(MOZ_MEMORY_DARWIN) 1.1694 + OSSpinLockLock(&lock->lock); 1.1695 +#elif defined(MOZ_MEMORY) 1.1696 + pthread_mutex_lock(lock); 1.1697 +#else 1.1698 + if (isthreaded) 1.1699 + _SPINLOCK(&lock->lock); 1.1700 +#endif 1.1701 +} 1.1702 + 1.1703 +static inline void 1.1704 +malloc_spin_unlock(malloc_spinlock_t *lock) 1.1705 +{ 1.1706 +#if defined(MOZ_MEMORY_WINDOWS) 1.1707 + LeaveCriticalSection(lock); 1.1708 +#elif defined(MOZ_MEMORY_DARWIN) 1.1709 + OSSpinLockUnlock(&lock->lock); 1.1710 +#elif defined(MOZ_MEMORY) 1.1711 + pthread_mutex_unlock(lock); 1.1712 +#else 1.1713 + if (isthreaded) 1.1714 + _SPINUNLOCK(&lock->lock); 1.1715 +#endif 1.1716 +} 1.1717 + 1.1718 +/* 1.1719 + * End mutex. 1.1720 + */ 1.1721 +/******************************************************************************/ 1.1722 +/* 1.1723 + * Begin spin lock. Spin locks here are actually adaptive mutexes that block 1.1724 + * after a period of spinning, because unbounded spinning would allow for 1.1725 + * priority inversion. 1.1726 + */ 1.1727 + 1.1728 +#if defined(MOZ_MEMORY) && !defined(MOZ_MEMORY_DARWIN) 1.1729 +# define malloc_spin_init malloc_mutex_init 1.1730 +# define malloc_spin_lock malloc_mutex_lock 1.1731 +# define malloc_spin_unlock malloc_mutex_unlock 1.1732 +#endif 1.1733 + 1.1734 +#ifndef MOZ_MEMORY 1.1735 +/* 1.1736 + * We use an unpublished interface to initialize pthread mutexes with an 1.1737 + * allocation callback, in order to avoid infinite recursion. 1.1738 + */ 1.1739 +int _pthread_mutex_init_calloc_cb(pthread_mutex_t *mutex, 1.1740 + void *(calloc_cb)(size_t, size_t)); 1.1741 + 1.1742 +__weak_reference(_pthread_mutex_init_calloc_cb_stub, 1.1743 + _pthread_mutex_init_calloc_cb); 1.1744 + 1.1745 +int 1.1746 +_pthread_mutex_init_calloc_cb_stub(pthread_mutex_t *mutex, 1.1747 + void *(calloc_cb)(size_t, size_t)) 1.1748 +{ 1.1749 + 1.1750 + return (0); 1.1751 +} 1.1752 + 1.1753 +static bool 1.1754 +malloc_spin_init(pthread_mutex_t *lock) 1.1755 +{ 1.1756 + 1.1757 + if (_pthread_mutex_init_calloc_cb(lock, base_calloc) != 0) 1.1758 + return (true); 1.1759 + 1.1760 + return (false); 1.1761 +} 1.1762 + 1.1763 +static inline unsigned 1.1764 +malloc_spin_lock(pthread_mutex_t *lock) 1.1765 +{ 1.1766 + unsigned ret = 0; 1.1767 + 1.1768 + if (isthreaded) { 1.1769 + if (_pthread_mutex_trylock(lock) != 0) { 1.1770 + unsigned i; 1.1771 + volatile unsigned j; 1.1772 + 1.1773 + /* Exponentially back off. */ 1.1774 + for (i = 1; i <= SPIN_LIMIT_2POW; i++) { 1.1775 + for (j = 0; j < (1U << i); j++) 1.1776 + ret++; 1.1777 + 1.1778 + CPU_SPINWAIT; 1.1779 + if (_pthread_mutex_trylock(lock) == 0) 1.1780 + return (ret); 1.1781 + } 1.1782 + 1.1783 + /* 1.1784 + * Spinning failed. Block until the lock becomes 1.1785 + * available, in order to avoid indefinite priority 1.1786 + * inversion. 1.1787 + */ 1.1788 + _pthread_mutex_lock(lock); 1.1789 + assert((ret << BLOCK_COST_2POW) != 0); 1.1790 + return (ret << BLOCK_COST_2POW); 1.1791 + } 1.1792 + } 1.1793 + 1.1794 + return (ret); 1.1795 +} 1.1796 + 1.1797 +static inline void 1.1798 +malloc_spin_unlock(pthread_mutex_t *lock) 1.1799 +{ 1.1800 + 1.1801 + if (isthreaded) 1.1802 + _pthread_mutex_unlock(lock); 1.1803 +} 1.1804 +#endif 1.1805 + 1.1806 +/* 1.1807 + * End spin lock. 1.1808 + */ 1.1809 +/******************************************************************************/ 1.1810 +/* 1.1811 + * Begin Utility functions/macros. 1.1812 + */ 1.1813 + 1.1814 +/* Return the chunk address for allocation address a. */ 1.1815 +#define CHUNK_ADDR2BASE(a) \ 1.1816 + ((void *)((uintptr_t)(a) & ~chunksize_mask)) 1.1817 + 1.1818 +/* Return the chunk offset of address a. */ 1.1819 +#define CHUNK_ADDR2OFFSET(a) \ 1.1820 + ((size_t)((uintptr_t)(a) & chunksize_mask)) 1.1821 + 1.1822 +/* Return the smallest chunk multiple that is >= s. */ 1.1823 +#define CHUNK_CEILING(s) \ 1.1824 + (((s) + chunksize_mask) & ~chunksize_mask) 1.1825 + 1.1826 +/* Return the smallest cacheline multiple that is >= s. */ 1.1827 +#define CACHELINE_CEILING(s) \ 1.1828 + (((s) + (CACHELINE - 1)) & ~(CACHELINE - 1)) 1.1829 + 1.1830 +/* Return the smallest quantum multiple that is >= a. */ 1.1831 +#define QUANTUM_CEILING(a) \ 1.1832 + (((a) + quantum_mask) & ~quantum_mask) 1.1833 + 1.1834 +/* Return the smallest pagesize multiple that is >= s. */ 1.1835 +#define PAGE_CEILING(s) \ 1.1836 + (((s) + pagesize_mask) & ~pagesize_mask) 1.1837 + 1.1838 +/* Compute the smallest power of 2 that is >= x. */ 1.1839 +static inline size_t 1.1840 +pow2_ceil(size_t x) 1.1841 +{ 1.1842 + 1.1843 + x--; 1.1844 + x |= x >> 1; 1.1845 + x |= x >> 2; 1.1846 + x |= x >> 4; 1.1847 + x |= x >> 8; 1.1848 + x |= x >> 16; 1.1849 +#if (SIZEOF_PTR == 8) 1.1850 + x |= x >> 32; 1.1851 +#endif 1.1852 + x++; 1.1853 + return (x); 1.1854 +} 1.1855 + 1.1856 +#ifdef MALLOC_BALANCE 1.1857 +/* 1.1858 + * Use a simple linear congruential pseudo-random number generator: 1.1859 + * 1.1860 + * prn(y) = (a*x + c) % m 1.1861 + * 1.1862 + * where the following constants ensure maximal period: 1.1863 + * 1.1864 + * a == Odd number (relatively prime to 2^n), and (a-1) is a multiple of 4. 1.1865 + * c == Odd number (relatively prime to 2^n). 1.1866 + * m == 2^32 1.1867 + * 1.1868 + * See Knuth's TAOCP 3rd Ed., Vol. 2, pg. 17 for details on these constraints. 1.1869 + * 1.1870 + * This choice of m has the disadvantage that the quality of the bits is 1.1871 + * proportional to bit position. For example. the lowest bit has a cycle of 2, 1.1872 + * the next has a cycle of 4, etc. For this reason, we prefer to use the upper 1.1873 + * bits. 1.1874 + */ 1.1875 +# define PRN_DEFINE(suffix, var, a, c) \ 1.1876 +static inline void \ 1.1877 +sprn_##suffix(uint32_t seed) \ 1.1878 +{ \ 1.1879 + var = seed; \ 1.1880 +} \ 1.1881 + \ 1.1882 +static inline uint32_t \ 1.1883 +prn_##suffix(uint32_t lg_range) \ 1.1884 +{ \ 1.1885 + uint32_t ret, x; \ 1.1886 + \ 1.1887 + assert(lg_range > 0); \ 1.1888 + assert(lg_range <= 32); \ 1.1889 + \ 1.1890 + x = (var * (a)) + (c); \ 1.1891 + var = x; \ 1.1892 + ret = x >> (32 - lg_range); \ 1.1893 + \ 1.1894 + return (ret); \ 1.1895 +} 1.1896 +# define SPRN(suffix, seed) sprn_##suffix(seed) 1.1897 +# define PRN(suffix, lg_range) prn_##suffix(lg_range) 1.1898 +#endif 1.1899 + 1.1900 +#ifdef MALLOC_BALANCE 1.1901 +/* Define the PRNG used for arena assignment. */ 1.1902 +static __thread uint32_t balance_x; 1.1903 +PRN_DEFINE(balance, balance_x, 1297, 1301) 1.1904 +#endif 1.1905 + 1.1906 +#ifdef MALLOC_UTRACE 1.1907 +static int 1.1908 +utrace(const void *addr, size_t len) 1.1909 +{ 1.1910 + malloc_utrace_t *ut = (malloc_utrace_t *)addr; 1.1911 + char buf_a[UMAX2S_BUFSIZE]; 1.1912 + char buf_b[UMAX2S_BUFSIZE]; 1.1913 + 1.1914 + assert(len == sizeof(malloc_utrace_t)); 1.1915 + 1.1916 + if (ut->p == NULL && ut->s == 0 && ut->r == NULL) { 1.1917 + _malloc_message( 1.1918 + umax2s(getpid(), 10, buf_a), 1.1919 + " x USER malloc_init()\n", "", ""); 1.1920 + } else if (ut->p == NULL && ut->r != NULL) { 1.1921 + _malloc_message( 1.1922 + umax2s(getpid(), 10, buf_a), 1.1923 + " x USER 0x", 1.1924 + umax2s((uintptr_t)ut->r, 16, buf_b), 1.1925 + " = malloc("); 1.1926 + _malloc_message( 1.1927 + umax2s(ut->s, 10, buf_a), 1.1928 + ")\n", "", ""); 1.1929 + } else if (ut->p != NULL && ut->r != NULL) { 1.1930 + _malloc_message( 1.1931 + umax2s(getpid(), 10, buf_a), 1.1932 + " x USER 0x", 1.1933 + umax2s((uintptr_t)ut->r, 16, buf_b), 1.1934 + " = realloc(0x"); 1.1935 + _malloc_message( 1.1936 + umax2s((uintptr_t)ut->p, 16, buf_a), 1.1937 + ", ", 1.1938 + umax2s(ut->s, 10, buf_b), 1.1939 + ")\n"); 1.1940 + } else { 1.1941 + _malloc_message( 1.1942 + umax2s(getpid(), 10, buf_a), 1.1943 + " x USER free(0x", 1.1944 + umax2s((uintptr_t)ut->p, 16, buf_b), 1.1945 + ")\n"); 1.1946 + } 1.1947 + 1.1948 + return (0); 1.1949 +} 1.1950 +#endif 1.1951 + 1.1952 +static inline const char * 1.1953 +_getprogname(void) 1.1954 +{ 1.1955 + 1.1956 + return ("<jemalloc>"); 1.1957 +} 1.1958 + 1.1959 +#ifdef MALLOC_STATS 1.1960 +/* 1.1961 + * Print to stderr in such a way as to (hopefully) avoid memory allocation. 1.1962 + */ 1.1963 +static void 1.1964 +malloc_printf(const char *format, ...) 1.1965 +{ 1.1966 + char buf[4096]; 1.1967 + va_list ap; 1.1968 + 1.1969 + va_start(ap, format); 1.1970 + vsnprintf(buf, sizeof(buf), format, ap); 1.1971 + va_end(ap); 1.1972 + _malloc_message(buf, "", "", ""); 1.1973 +} 1.1974 +#endif 1.1975 + 1.1976 +/******************************************************************************/ 1.1977 + 1.1978 +static inline void 1.1979 +pages_decommit(void *addr, size_t size) 1.1980 +{ 1.1981 + 1.1982 +#ifdef MOZ_MEMORY_WINDOWS 1.1983 + VirtualFree(addr, size, MEM_DECOMMIT); 1.1984 +#else 1.1985 + if (mmap(addr, size, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 1.1986 + 0) == MAP_FAILED) 1.1987 + abort(); 1.1988 +#endif 1.1989 +} 1.1990 + 1.1991 +static inline void 1.1992 +pages_commit(void *addr, size_t size) 1.1993 +{ 1.1994 + 1.1995 +# ifdef MOZ_MEMORY_WINDOWS 1.1996 + if (!VirtualAlloc(addr, size, MEM_COMMIT, PAGE_READWRITE)) 1.1997 + abort(); 1.1998 +# else 1.1999 + if (mmap(addr, size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | 1.2000 + MAP_ANON, -1, 0) == MAP_FAILED) 1.2001 + abort(); 1.2002 +# endif 1.2003 +} 1.2004 + 1.2005 +static bool 1.2006 +base_pages_alloc_mmap(size_t minsize) 1.2007 +{ 1.2008 + bool ret; 1.2009 + size_t csize; 1.2010 +#if defined(MALLOC_DECOMMIT) || defined(MALLOC_STATS) 1.2011 + size_t pminsize; 1.2012 +#endif 1.2013 + int pfd; 1.2014 + 1.2015 + assert(minsize != 0); 1.2016 + csize = CHUNK_CEILING(minsize); 1.2017 +#ifdef MALLOC_PAGEFILE 1.2018 + if (opt_pagefile) { 1.2019 + pfd = pagefile_init(csize); 1.2020 + if (pfd == -1) 1.2021 + return (true); 1.2022 + } else 1.2023 +#endif 1.2024 + pfd = -1; 1.2025 + base_pages = pages_map(NULL, csize, pfd); 1.2026 + if (base_pages == NULL) { 1.2027 + ret = true; 1.2028 + goto RETURN; 1.2029 + } 1.2030 + base_next_addr = base_pages; 1.2031 + base_past_addr = (void *)((uintptr_t)base_pages + csize); 1.2032 +#if defined(MALLOC_DECOMMIT) || defined(MALLOC_STATS) 1.2033 + /* 1.2034 + * Leave enough pages for minsize committed, since otherwise they would 1.2035 + * have to be immediately recommitted. 1.2036 + */ 1.2037 + pminsize = PAGE_CEILING(minsize); 1.2038 + base_next_decommitted = (void *)((uintptr_t)base_pages + pminsize); 1.2039 +# if defined(MALLOC_DECOMMIT) 1.2040 + if (pminsize < csize) 1.2041 + pages_decommit(base_next_decommitted, csize - pminsize); 1.2042 +# endif 1.2043 +# ifdef MALLOC_STATS 1.2044 + base_mapped += csize; 1.2045 + base_committed += pminsize; 1.2046 +# endif 1.2047 +#endif 1.2048 + 1.2049 + ret = false; 1.2050 +RETURN: 1.2051 +#ifdef MALLOC_PAGEFILE 1.2052 + if (pfd != -1) 1.2053 + pagefile_close(pfd); 1.2054 +#endif 1.2055 + return (false); 1.2056 +} 1.2057 + 1.2058 +static bool 1.2059 +base_pages_alloc(size_t minsize) 1.2060 +{ 1.2061 + 1.2062 + if (base_pages_alloc_mmap(minsize) == false) 1.2063 + return (false); 1.2064 + 1.2065 + return (true); 1.2066 +} 1.2067 + 1.2068 +static void * 1.2069 +base_alloc(size_t size) 1.2070 +{ 1.2071 + void *ret; 1.2072 + size_t csize; 1.2073 + 1.2074 + /* Round size up to nearest multiple of the cacheline size. */ 1.2075 + csize = CACHELINE_CEILING(size); 1.2076 + 1.2077 + malloc_mutex_lock(&base_mtx); 1.2078 + /* Make sure there's enough space for the allocation. */ 1.2079 + if ((uintptr_t)base_next_addr + csize > (uintptr_t)base_past_addr) { 1.2080 + if (base_pages_alloc(csize)) { 1.2081 + malloc_mutex_unlock(&base_mtx); 1.2082 + return (NULL); 1.2083 + } 1.2084 + } 1.2085 + /* Allocate. */ 1.2086 + ret = base_next_addr; 1.2087 + base_next_addr = (void *)((uintptr_t)base_next_addr + csize); 1.2088 +#if defined(MALLOC_DECOMMIT) || defined(MALLOC_STATS) 1.2089 + /* Make sure enough pages are committed for the new allocation. */ 1.2090 + if ((uintptr_t)base_next_addr > (uintptr_t)base_next_decommitted) { 1.2091 + void *pbase_next_addr = 1.2092 + (void *)(PAGE_CEILING((uintptr_t)base_next_addr)); 1.2093 + 1.2094 +# ifdef MALLOC_DECOMMIT 1.2095 + pages_commit(base_next_decommitted, (uintptr_t)pbase_next_addr - 1.2096 + (uintptr_t)base_next_decommitted); 1.2097 +# endif 1.2098 + base_next_decommitted = pbase_next_addr; 1.2099 +# ifdef MALLOC_STATS 1.2100 + base_committed += (uintptr_t)pbase_next_addr - 1.2101 + (uintptr_t)base_next_decommitted; 1.2102 +# endif 1.2103 + } 1.2104 +#endif 1.2105 + malloc_mutex_unlock(&base_mtx); 1.2106 + VALGRIND_MALLOCLIKE_BLOCK(ret, size, 0, false); 1.2107 + 1.2108 + return (ret); 1.2109 +} 1.2110 + 1.2111 +static void * 1.2112 +base_calloc(size_t number, size_t size) 1.2113 +{ 1.2114 + void *ret; 1.2115 + 1.2116 + ret = base_alloc(number * size); 1.2117 +#ifdef MALLOC_VALGRIND 1.2118 + if (ret != NULL) { 1.2119 + VALGRIND_FREELIKE_BLOCK(ret, 0); 1.2120 + VALGRIND_MALLOCLIKE_BLOCK(ret, size, 0, true); 1.2121 + } 1.2122 +#endif 1.2123 + memset(ret, 0, number * size); 1.2124 + 1.2125 + return (ret); 1.2126 +} 1.2127 + 1.2128 +static extent_node_t * 1.2129 +base_node_alloc(void) 1.2130 +{ 1.2131 + extent_node_t *ret; 1.2132 + 1.2133 + malloc_mutex_lock(&base_mtx); 1.2134 + if (base_nodes != NULL) { 1.2135 + ret = base_nodes; 1.2136 + base_nodes = *(extent_node_t **)ret; 1.2137 + VALGRIND_FREELIKE_BLOCK(ret, 0); 1.2138 + VALGRIND_MALLOCLIKE_BLOCK(ret, sizeof(extent_node_t), 0, false); 1.2139 + malloc_mutex_unlock(&base_mtx); 1.2140 + } else { 1.2141 + malloc_mutex_unlock(&base_mtx); 1.2142 + ret = (extent_node_t *)base_alloc(sizeof(extent_node_t)); 1.2143 + } 1.2144 + 1.2145 + return (ret); 1.2146 +} 1.2147 + 1.2148 +static void 1.2149 +base_node_dealloc(extent_node_t *node) 1.2150 +{ 1.2151 + 1.2152 + malloc_mutex_lock(&base_mtx); 1.2153 + VALGRIND_FREELIKE_BLOCK(node, 0); 1.2154 + VALGRIND_MALLOCLIKE_BLOCK(node, sizeof(extent_node_t *), 0, false); 1.2155 + *(extent_node_t **)node = base_nodes; 1.2156 + base_nodes = node; 1.2157 + malloc_mutex_unlock(&base_mtx); 1.2158 +} 1.2159 + 1.2160 +/******************************************************************************/ 1.2161 + 1.2162 +#ifdef MALLOC_STATS 1.2163 +static void 1.2164 +stats_print(arena_t *arena) 1.2165 +{ 1.2166 + unsigned i, gap_start; 1.2167 + 1.2168 +#ifdef MOZ_MEMORY_WINDOWS 1.2169 + malloc_printf("dirty: %Iu page%s dirty, %I64u sweep%s," 1.2170 + " %I64u madvise%s, %I64u page%s purged\n", 1.2171 + arena->ndirty, arena->ndirty == 1 ? "" : "s", 1.2172 + arena->stats.npurge, arena->stats.npurge == 1 ? "" : "s", 1.2173 + arena->stats.nmadvise, arena->stats.nmadvise == 1 ? "" : "s", 1.2174 + arena->stats.purged, arena->stats.purged == 1 ? "" : "s"); 1.2175 +# ifdef MALLOC_DECOMMIT 1.2176 + malloc_printf("decommit: %I64u decommit%s, %I64u commit%s," 1.2177 + " %I64u page%s decommitted\n", 1.2178 + arena->stats.ndecommit, (arena->stats.ndecommit == 1) ? "" : "s", 1.2179 + arena->stats.ncommit, (arena->stats.ncommit == 1) ? "" : "s", 1.2180 + arena->stats.decommitted, 1.2181 + (arena->stats.decommitted == 1) ? "" : "s"); 1.2182 +# endif 1.2183 + 1.2184 + malloc_printf(" allocated nmalloc ndalloc\n"); 1.2185 + malloc_printf("small: %12Iu %12I64u %12I64u\n", 1.2186 + arena->stats.allocated_small, arena->stats.nmalloc_small, 1.2187 + arena->stats.ndalloc_small); 1.2188 + malloc_printf("large: %12Iu %12I64u %12I64u\n", 1.2189 + arena->stats.allocated_large, arena->stats.nmalloc_large, 1.2190 + arena->stats.ndalloc_large); 1.2191 + malloc_printf("total: %12Iu %12I64u %12I64u\n", 1.2192 + arena->stats.allocated_small + arena->stats.allocated_large, 1.2193 + arena->stats.nmalloc_small + arena->stats.nmalloc_large, 1.2194 + arena->stats.ndalloc_small + arena->stats.ndalloc_large); 1.2195 + malloc_printf("mapped: %12Iu\n", arena->stats.mapped); 1.2196 +#else 1.2197 + malloc_printf("dirty: %zu page%s dirty, %llu sweep%s," 1.2198 + " %llu madvise%s, %llu page%s purged\n", 1.2199 + arena->ndirty, arena->ndirty == 1 ? "" : "s", 1.2200 + arena->stats.npurge, arena->stats.npurge == 1 ? "" : "s", 1.2201 + arena->stats.nmadvise, arena->stats.nmadvise == 1 ? "" : "s", 1.2202 + arena->stats.purged, arena->stats.purged == 1 ? "" : "s"); 1.2203 +# ifdef MALLOC_DECOMMIT 1.2204 + malloc_printf("decommit: %llu decommit%s, %llu commit%s," 1.2205 + " %llu page%s decommitted\n", 1.2206 + arena->stats.ndecommit, (arena->stats.ndecommit == 1) ? "" : "s", 1.2207 + arena->stats.ncommit, (arena->stats.ncommit == 1) ? "" : "s", 1.2208 + arena->stats.decommitted, 1.2209 + (arena->stats.decommitted == 1) ? "" : "s"); 1.2210 +# endif 1.2211 + 1.2212 + malloc_printf(" allocated nmalloc ndalloc\n"); 1.2213 + malloc_printf("small: %12zu %12llu %12llu\n", 1.2214 + arena->stats.allocated_small, arena->stats.nmalloc_small, 1.2215 + arena->stats.ndalloc_small); 1.2216 + malloc_printf("large: %12zu %12llu %12llu\n", 1.2217 + arena->stats.allocated_large, arena->stats.nmalloc_large, 1.2218 + arena->stats.ndalloc_large); 1.2219 + malloc_printf("total: %12zu %12llu %12llu\n", 1.2220 + arena->stats.allocated_small + arena->stats.allocated_large, 1.2221 + arena->stats.nmalloc_small + arena->stats.nmalloc_large, 1.2222 + arena->stats.ndalloc_small + arena->stats.ndalloc_large); 1.2223 + malloc_printf("mapped: %12zu\n", arena->stats.mapped); 1.2224 +#endif 1.2225 + malloc_printf("bins: bin size regs pgs requests newruns" 1.2226 + " reruns maxruns curruns\n"); 1.2227 + for (i = 0, gap_start = UINT_MAX; i < ntbins + nqbins + nsbins; i++) { 1.2228 + if (arena->bins[i].stats.nrequests == 0) { 1.2229 + if (gap_start == UINT_MAX) 1.2230 + gap_start = i; 1.2231 + } else { 1.2232 + if (gap_start != UINT_MAX) { 1.2233 + if (i > gap_start + 1) { 1.2234 + /* Gap of more than one size class. */ 1.2235 + malloc_printf("[%u..%u]\n", 1.2236 + gap_start, i - 1); 1.2237 + } else { 1.2238 + /* Gap of one size class. */ 1.2239 + malloc_printf("[%u]\n", gap_start); 1.2240 + } 1.2241 + gap_start = UINT_MAX; 1.2242 + } 1.2243 + malloc_printf( 1.2244 +#if defined(MOZ_MEMORY_WINDOWS) 1.2245 + "%13u %1s %4u %4u %3u %9I64u %9I64u" 1.2246 + " %9I64u %7u %7u\n", 1.2247 +#else 1.2248 + "%13u %1s %4u %4u %3u %9llu %9llu" 1.2249 + " %9llu %7lu %7lu\n", 1.2250 +#endif 1.2251 + i, 1.2252 + i < ntbins ? "T" : i < ntbins + nqbins ? "Q" : "S", 1.2253 + arena->bins[i].reg_size, 1.2254 + arena->bins[i].nregs, 1.2255 + arena->bins[i].run_size >> pagesize_2pow, 1.2256 + arena->bins[i].stats.nrequests, 1.2257 + arena->bins[i].stats.nruns, 1.2258 + arena->bins[i].stats.reruns, 1.2259 + arena->bins[i].stats.highruns, 1.2260 + arena->bins[i].stats.curruns); 1.2261 + } 1.2262 + } 1.2263 + if (gap_start != UINT_MAX) { 1.2264 + if (i > gap_start + 1) { 1.2265 + /* Gap of more than one size class. */ 1.2266 + malloc_printf("[%u..%u]\n", gap_start, i - 1); 1.2267 + } else { 1.2268 + /* Gap of one size class. */ 1.2269 + malloc_printf("[%u]\n", gap_start); 1.2270 + } 1.2271 + } 1.2272 +} 1.2273 +#endif 1.2274 + 1.2275 +/* 1.2276 + * End Utility functions/macros. 1.2277 + */ 1.2278 +/******************************************************************************/ 1.2279 +/* 1.2280 + * Begin extent tree code. 1.2281 + */ 1.2282 + 1.2283 +static inline int 1.2284 +extent_szad_comp(extent_node_t *a, extent_node_t *b) 1.2285 +{ 1.2286 + int ret; 1.2287 + size_t a_size = a->size; 1.2288 + size_t b_size = b->size; 1.2289 + 1.2290 + ret = (a_size > b_size) - (a_size < b_size); 1.2291 + if (ret == 0) { 1.2292 + uintptr_t a_addr = (uintptr_t)a->addr; 1.2293 + uintptr_t b_addr = (uintptr_t)b->addr; 1.2294 + 1.2295 + ret = (a_addr > b_addr) - (a_addr < b_addr); 1.2296 + } 1.2297 + 1.2298 + return (ret); 1.2299 +} 1.2300 + 1.2301 +/* Wrap red-black tree macros in functions. */ 1.2302 +rb_wrap(static, extent_tree_szad_, extent_tree_t, extent_node_t, 1.2303 + link_szad, extent_szad_comp) 1.2304 + 1.2305 +static inline int 1.2306 +extent_ad_comp(extent_node_t *a, extent_node_t *b) 1.2307 +{ 1.2308 + uintptr_t a_addr = (uintptr_t)a->addr; 1.2309 + uintptr_t b_addr = (uintptr_t)b->addr; 1.2310 + 1.2311 + return ((a_addr > b_addr) - (a_addr < b_addr)); 1.2312 +} 1.2313 + 1.2314 +/* Wrap red-black tree macros in functions. */ 1.2315 +rb_wrap(static, extent_tree_ad_, extent_tree_t, extent_node_t, link_ad, 1.2316 + extent_ad_comp) 1.2317 + 1.2318 +/* 1.2319 + * End extent tree code. 1.2320 + */ 1.2321 +/******************************************************************************/ 1.2322 +/* 1.2323 + * Begin chunk management functions. 1.2324 + */ 1.2325 + 1.2326 +#ifdef MOZ_MEMORY_WINDOWS 1.2327 + 1.2328 +static void * 1.2329 +pages_map(void *addr, size_t size, int pfd) 1.2330 +{ 1.2331 + void *ret = NULL; 1.2332 + ret = VirtualAlloc(addr, size, MEM_COMMIT | MEM_RESERVE, 1.2333 + PAGE_READWRITE); 1.2334 + return (ret); 1.2335 +} 1.2336 + 1.2337 +static void 1.2338 +pages_unmap(void *addr, size_t size) 1.2339 +{ 1.2340 + if (VirtualFree(addr, 0, MEM_RELEASE) == 0) { 1.2341 + _malloc_message(_getprogname(), 1.2342 + ": (malloc) Error in VirtualFree()\n", "", ""); 1.2343 + if (opt_abort) 1.2344 + abort(); 1.2345 + } 1.2346 +} 1.2347 +#else 1.2348 +#ifdef JEMALLOC_USES_MAP_ALIGN 1.2349 +static void * 1.2350 +pages_map_align(size_t size, int pfd, size_t alignment) 1.2351 +{ 1.2352 + void *ret; 1.2353 + 1.2354 + /* 1.2355 + * We don't use MAP_FIXED here, because it can cause the *replacement* 1.2356 + * of existing mappings, and we only want to create new mappings. 1.2357 + */ 1.2358 +#ifdef MALLOC_PAGEFILE 1.2359 + if (pfd != -1) { 1.2360 + ret = mmap((void *)alignment, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | 1.2361 + MAP_NOSYNC | MAP_ALIGN, pfd, 0); 1.2362 + } else 1.2363 +#endif 1.2364 + { 1.2365 + ret = mmap((void *)alignment, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | 1.2366 + MAP_NOSYNC | MAP_ALIGN | MAP_ANON, -1, 0); 1.2367 + } 1.2368 + assert(ret != NULL); 1.2369 + 1.2370 + if (ret == MAP_FAILED) 1.2371 + ret = NULL; 1.2372 + return (ret); 1.2373 +} 1.2374 +#endif 1.2375 + 1.2376 +static void * 1.2377 +pages_map(void *addr, size_t size, int pfd) 1.2378 +{ 1.2379 + void *ret; 1.2380 +#if defined(__ia64__) 1.2381 + /* 1.2382 + * The JS engine assumes that all allocated pointers have their high 17 bits clear, 1.2383 + * which ia64's mmap doesn't support directly. However, we can emulate it by passing 1.2384 + * mmap an "addr" parameter with those bits clear. The mmap will return that address, 1.2385 + * or the nearest available memory above that address, providing a near-guarantee 1.2386 + * that those bits are clear. If they are not, we return NULL below to indicate 1.2387 + * out-of-memory. 1.2388 + * 1.2389 + * The addr is chosen as 0x0000070000000000, which still allows about 120TB of virtual 1.2390 + * address space. 1.2391 + * 1.2392 + * See Bug 589735 for more information. 1.2393 + */ 1.2394 + bool check_placement = true; 1.2395 + if (addr == NULL) { 1.2396 + addr = (void*)0x0000070000000000; 1.2397 + check_placement = false; 1.2398 + } 1.2399 +#endif 1.2400 + 1.2401 + /* 1.2402 + * We don't use MAP_FIXED here, because it can cause the *replacement* 1.2403 + * of existing mappings, and we only want to create new mappings. 1.2404 + */ 1.2405 +#ifdef MALLOC_PAGEFILE 1.2406 + if (pfd != -1) { 1.2407 + ret = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | 1.2408 + MAP_NOSYNC, pfd, 0); 1.2409 + } else 1.2410 +#endif 1.2411 + { 1.2412 + ret = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | 1.2413 + MAP_ANON, -1, 0); 1.2414 + } 1.2415 + assert(ret != NULL); 1.2416 + 1.2417 + if (ret == MAP_FAILED) { 1.2418 + ret = NULL; 1.2419 + } 1.2420 +#if defined(__ia64__) 1.2421 + /* 1.2422 + * If the allocated memory doesn't have its upper 17 bits clear, consider it 1.2423 + * as out of memory. 1.2424 + */ 1.2425 + else if ((long long)ret & 0xffff800000000000) { 1.2426 + munmap(ret, size); 1.2427 + ret = NULL; 1.2428 + } 1.2429 + /* If the caller requested a specific memory location, verify that's what mmap returned. */ 1.2430 + else if (check_placement && ret != addr) { 1.2431 +#else 1.2432 + else if (addr != NULL && ret != addr) { 1.2433 +#endif 1.2434 + /* 1.2435 + * We succeeded in mapping memory, but not in the right place. 1.2436 + */ 1.2437 + if (munmap(ret, size) == -1) { 1.2438 + char buf[STRERROR_BUF]; 1.2439 + 1.2440 + strerror_r(errno, buf, sizeof(buf)); 1.2441 + _malloc_message(_getprogname(), 1.2442 + ": (malloc) Error in munmap(): ", buf, "\n"); 1.2443 + if (opt_abort) 1.2444 + abort(); 1.2445 + } 1.2446 + ret = NULL; 1.2447 + } 1.2448 + 1.2449 +#if defined(__ia64__) 1.2450 + assert(ret == NULL || (!check_placement && ret != NULL) 1.2451 + || (check_placement && ret == addr)); 1.2452 +#else 1.2453 + assert(ret == NULL || (addr == NULL && ret != addr) 1.2454 + || (addr != NULL && ret == addr)); 1.2455 +#endif 1.2456 + return (ret); 1.2457 +} 1.2458 + 1.2459 +static void 1.2460 +pages_unmap(void *addr, size_t size) 1.2461 +{ 1.2462 + 1.2463 + if (munmap(addr, size) == -1) { 1.2464 + char buf[STRERROR_BUF]; 1.2465 + 1.2466 + strerror_r(errno, buf, sizeof(buf)); 1.2467 + _malloc_message(_getprogname(), 1.2468 + ": (malloc) Error in munmap(): ", buf, "\n"); 1.2469 + if (opt_abort) 1.2470 + abort(); 1.2471 + } 1.2472 +} 1.2473 +#endif 1.2474 + 1.2475 +#ifdef MOZ_MEMORY_DARWIN 1.2476 +#define VM_COPY_MIN (pagesize << 5) 1.2477 +static inline void 1.2478 +pages_copy(void *dest, const void *src, size_t n) 1.2479 +{ 1.2480 + 1.2481 + assert((void *)((uintptr_t)dest & ~pagesize_mask) == dest); 1.2482 + assert(n >= VM_COPY_MIN); 1.2483 + assert((void *)((uintptr_t)src & ~pagesize_mask) == src); 1.2484 + 1.2485 + vm_copy(mach_task_self(), (vm_address_t)src, (vm_size_t)n, 1.2486 + (vm_address_t)dest); 1.2487 +} 1.2488 +#endif 1.2489 + 1.2490 +#ifdef MALLOC_VALIDATE 1.2491 +static inline malloc_rtree_t * 1.2492 +malloc_rtree_new(unsigned bits) 1.2493 +{ 1.2494 + malloc_rtree_t *ret; 1.2495 + unsigned bits_per_level, height, i; 1.2496 + 1.2497 + bits_per_level = ffs(pow2_ceil((MALLOC_RTREE_NODESIZE / 1.2498 + sizeof(void *)))) - 1; 1.2499 + height = bits / bits_per_level; 1.2500 + if (height * bits_per_level != bits) 1.2501 + height++; 1.2502 + RELEASE_ASSERT(height * bits_per_level >= bits); 1.2503 + 1.2504 + ret = (malloc_rtree_t*)base_calloc(1, sizeof(malloc_rtree_t) + 1.2505 + (sizeof(unsigned) * (height - 1))); 1.2506 + if (ret == NULL) 1.2507 + return (NULL); 1.2508 + 1.2509 + malloc_spin_init(&ret->lock); 1.2510 + ret->height = height; 1.2511 + if (bits_per_level * height > bits) 1.2512 + ret->level2bits[0] = bits % bits_per_level; 1.2513 + else 1.2514 + ret->level2bits[0] = bits_per_level; 1.2515 + for (i = 1; i < height; i++) 1.2516 + ret->level2bits[i] = bits_per_level; 1.2517 + 1.2518 + ret->root = (void**)base_calloc(1, sizeof(void *) << ret->level2bits[0]); 1.2519 + if (ret->root == NULL) { 1.2520 + /* 1.2521 + * We leak the rtree here, since there's no generic base 1.2522 + * deallocation. 1.2523 + */ 1.2524 + return (NULL); 1.2525 + } 1.2526 + 1.2527 + return (ret); 1.2528 +} 1.2529 + 1.2530 +#define MALLOC_RTREE_GET_GENERATE(f) \ 1.2531 +/* The least significant bits of the key are ignored. */ \ 1.2532 +static inline void * \ 1.2533 +f(malloc_rtree_t *rtree, uintptr_t key) \ 1.2534 +{ \ 1.2535 + void *ret; \ 1.2536 + uintptr_t subkey; \ 1.2537 + unsigned i, lshift, height, bits; \ 1.2538 + void **node, **child; \ 1.2539 + \ 1.2540 + MALLOC_RTREE_LOCK(&rtree->lock); \ 1.2541 + for (i = lshift = 0, height = rtree->height, node = rtree->root;\ 1.2542 + i < height - 1; \ 1.2543 + i++, lshift += bits, node = child) { \ 1.2544 + bits = rtree->level2bits[i]; \ 1.2545 + subkey = (key << lshift) >> ((SIZEOF_PTR << 3) - bits); \ 1.2546 + child = (void**)node[subkey]; \ 1.2547 + if (child == NULL) { \ 1.2548 + MALLOC_RTREE_UNLOCK(&rtree->lock); \ 1.2549 + return (NULL); \ 1.2550 + } \ 1.2551 + } \ 1.2552 + \ 1.2553 + /* \ 1.2554 + * node is a leaf, so it contains values rather than node \ 1.2555 + * pointers. \ 1.2556 + */ \ 1.2557 + bits = rtree->level2bits[i]; \ 1.2558 + subkey = (key << lshift) >> ((SIZEOF_PTR << 3) - bits); \ 1.2559 + ret = node[subkey]; \ 1.2560 + MALLOC_RTREE_UNLOCK(&rtree->lock); \ 1.2561 + \ 1.2562 + MALLOC_RTREE_GET_VALIDATE \ 1.2563 + return (ret); \ 1.2564 +} 1.2565 + 1.2566 +#ifdef MALLOC_DEBUG 1.2567 +# define MALLOC_RTREE_LOCK(l) malloc_spin_lock(l) 1.2568 +# define MALLOC_RTREE_UNLOCK(l) malloc_spin_unlock(l) 1.2569 +# define MALLOC_RTREE_GET_VALIDATE 1.2570 +MALLOC_RTREE_GET_GENERATE(malloc_rtree_get_locked) 1.2571 +# undef MALLOC_RTREE_LOCK 1.2572 +# undef MALLOC_RTREE_UNLOCK 1.2573 +# undef MALLOC_RTREE_GET_VALIDATE 1.2574 +#endif 1.2575 + 1.2576 +#define MALLOC_RTREE_LOCK(l) 1.2577 +#define MALLOC_RTREE_UNLOCK(l) 1.2578 +#ifdef MALLOC_DEBUG 1.2579 + /* 1.2580 + * Suppose that it were possible for a jemalloc-allocated chunk to be 1.2581 + * munmap()ped, followed by a different allocator in another thread re-using 1.2582 + * overlapping virtual memory, all without invalidating the cached rtree 1.2583 + * value. The result would be a false positive (the rtree would claim that 1.2584 + * jemalloc owns memory that it had actually discarded). I don't think this 1.2585 + * scenario is possible, but the following assertion is a prudent sanity 1.2586 + * check. 1.2587 + */ 1.2588 +# define MALLOC_RTREE_GET_VALIDATE \ 1.2589 + assert(malloc_rtree_get_locked(rtree, key) == ret); 1.2590 +#else 1.2591 +# define MALLOC_RTREE_GET_VALIDATE 1.2592 +#endif 1.2593 +MALLOC_RTREE_GET_GENERATE(malloc_rtree_get) 1.2594 +#undef MALLOC_RTREE_LOCK 1.2595 +#undef MALLOC_RTREE_UNLOCK 1.2596 +#undef MALLOC_RTREE_GET_VALIDATE 1.2597 + 1.2598 +static inline bool 1.2599 +malloc_rtree_set(malloc_rtree_t *rtree, uintptr_t key, void *val) 1.2600 +{ 1.2601 + uintptr_t subkey; 1.2602 + unsigned i, lshift, height, bits; 1.2603 + void **node, **child; 1.2604 + 1.2605 + malloc_spin_lock(&rtree->lock); 1.2606 + for (i = lshift = 0, height = rtree->height, node = rtree->root; 1.2607 + i < height - 1; 1.2608 + i++, lshift += bits, node = child) { 1.2609 + bits = rtree->level2bits[i]; 1.2610 + subkey = (key << lshift) >> ((SIZEOF_PTR << 3) - bits); 1.2611 + child = (void**)node[subkey]; 1.2612 + if (child == NULL) { 1.2613 + child = (void**)base_calloc(1, sizeof(void *) << 1.2614 + rtree->level2bits[i+1]); 1.2615 + if (child == NULL) { 1.2616 + malloc_spin_unlock(&rtree->lock); 1.2617 + return (true); 1.2618 + } 1.2619 + node[subkey] = child; 1.2620 + } 1.2621 + } 1.2622 + 1.2623 + /* node is a leaf, so it contains values rather than node pointers. */ 1.2624 + bits = rtree->level2bits[i]; 1.2625 + subkey = (key << lshift) >> ((SIZEOF_PTR << 3) - bits); 1.2626 + node[subkey] = val; 1.2627 + malloc_spin_unlock(&rtree->lock); 1.2628 + 1.2629 + return (false); 1.2630 +} 1.2631 +#endif 1.2632 + 1.2633 +#if defined(MOZ_MEMORY_WINDOWS) || defined(JEMALLOC_USES_MAP_ALIGN) || defined(MALLOC_PAGEFILE) 1.2634 + 1.2635 +/* Allocate an aligned chunk while maintaining a 1:1 correspondence between 1.2636 + * mmap and unmap calls. This is important on Windows, but not elsewhere. */ 1.2637 +static void * 1.2638 +chunk_alloc_mmap(size_t size, bool pagefile) 1.2639 +{ 1.2640 + void *ret; 1.2641 +#ifndef JEMALLOC_USES_MAP_ALIGN 1.2642 + size_t offset; 1.2643 +#endif 1.2644 + int pfd; 1.2645 + 1.2646 +#ifdef MALLOC_PAGEFILE 1.2647 + if (opt_pagefile && pagefile) { 1.2648 + pfd = pagefile_init(size); 1.2649 + if (pfd == -1) 1.2650 + return (NULL); 1.2651 + } else 1.2652 +#endif 1.2653 + pfd = -1; 1.2654 + 1.2655 +#ifdef JEMALLOC_USES_MAP_ALIGN 1.2656 + ret = pages_map_align(size, pfd, chunksize); 1.2657 +#else 1.2658 + ret = pages_map(NULL, size, pfd); 1.2659 + if (ret == NULL) 1.2660 + goto RETURN; 1.2661 + 1.2662 + offset = CHUNK_ADDR2OFFSET(ret); 1.2663 + if (offset != 0) { 1.2664 + /* Deallocate, then try to allocate at (ret + size - offset). */ 1.2665 + pages_unmap(ret, size); 1.2666 + ret = pages_map((void *)((uintptr_t)ret + size - offset), size, 1.2667 + pfd); 1.2668 + while (ret == NULL) { 1.2669 + /* 1.2670 + * Over-allocate in order to map a memory region that 1.2671 + * is definitely large enough. 1.2672 + */ 1.2673 + ret = pages_map(NULL, size + chunksize, -1); 1.2674 + if (ret == NULL) 1.2675 + goto RETURN; 1.2676 + /* 1.2677 + * Deallocate, then allocate the correct size, within 1.2678 + * the over-sized mapping. 1.2679 + */ 1.2680 + offset = CHUNK_ADDR2OFFSET(ret); 1.2681 + pages_unmap(ret, size + chunksize); 1.2682 + if (offset == 0) 1.2683 + ret = pages_map(ret, size, pfd); 1.2684 + else { 1.2685 + ret = pages_map((void *)((uintptr_t)ret + 1.2686 + chunksize - offset), size, pfd); 1.2687 + } 1.2688 + /* 1.2689 + * Failure here indicates a race with another thread, so 1.2690 + * try again. 1.2691 + */ 1.2692 + } 1.2693 + } 1.2694 +RETURN: 1.2695 +#endif 1.2696 +#ifdef MALLOC_PAGEFILE 1.2697 + if (pfd != -1) 1.2698 + pagefile_close(pfd); 1.2699 +#endif 1.2700 + 1.2701 + return (ret); 1.2702 +} 1.2703 + 1.2704 +#else /* ! (defined(MOZ_MEMORY_WINDOWS) || defined(JEMALLOC_USES_MAP_ALIGN) || defined(MALLOC_PAGEFILE)) */ 1.2705 + 1.2706 +/* pages_trim, chunk_alloc_mmap_slow and chunk_alloc_mmap were cherry-picked 1.2707 + * from upstream jemalloc 3.4.1 to fix Mozilla bug 956501. */ 1.2708 + 1.2709 +/* Return the offset between a and the nearest aligned address at or below a. */ 1.2710 +#define ALIGNMENT_ADDR2OFFSET(a, alignment) \ 1.2711 + ((size_t)((uintptr_t)(a) & (alignment - 1))) 1.2712 + 1.2713 +/* Return the smallest alignment multiple that is >= s. */ 1.2714 +#define ALIGNMENT_CEILING(s, alignment) \ 1.2715 + (((s) + (alignment - 1)) & (-(alignment))) 1.2716 + 1.2717 +static void * 1.2718 +pages_trim(void *addr, size_t alloc_size, size_t leadsize, size_t size) 1.2719 +{ 1.2720 + size_t trailsize; 1.2721 + void *ret = (void *)((uintptr_t)addr + leadsize); 1.2722 + 1.2723 + assert(alloc_size >= leadsize + size); 1.2724 + trailsize = alloc_size - leadsize - size; 1.2725 + 1.2726 + if (leadsize != 0) 1.2727 + pages_unmap(addr, leadsize); 1.2728 + if (trailsize != 0) 1.2729 + pages_unmap((void *)((uintptr_t)ret + size), trailsize); 1.2730 + return (ret); 1.2731 +} 1.2732 + 1.2733 +static void * 1.2734 +chunk_alloc_mmap_slow(size_t size, size_t alignment) 1.2735 +{ 1.2736 + void *ret, *pages; 1.2737 + size_t alloc_size, leadsize; 1.2738 + 1.2739 + alloc_size = size + alignment - pagesize; 1.2740 + /* Beware size_t wrap-around. */ 1.2741 + if (alloc_size < size) 1.2742 + return (NULL); 1.2743 + do { 1.2744 + pages = pages_map(NULL, alloc_size, -1); 1.2745 + if (pages == NULL) 1.2746 + return (NULL); 1.2747 + leadsize = ALIGNMENT_CEILING((uintptr_t)pages, alignment) - 1.2748 + (uintptr_t)pages; 1.2749 + ret = pages_trim(pages, alloc_size, leadsize, size); 1.2750 + } while (ret == NULL); 1.2751 + 1.2752 + assert(ret != NULL); 1.2753 + return (ret); 1.2754 +} 1.2755 + 1.2756 +static void * 1.2757 +chunk_alloc_mmap(size_t size, bool pagefile) 1.2758 +{ 1.2759 + void *ret; 1.2760 + size_t offset; 1.2761 + 1.2762 + /* 1.2763 + * Ideally, there would be a way to specify alignment to mmap() (like 1.2764 + * NetBSD has), but in the absence of such a feature, we have to work 1.2765 + * hard to efficiently create aligned mappings. The reliable, but 1.2766 + * slow method is to create a mapping that is over-sized, then trim the 1.2767 + * excess. However, that always results in one or two calls to 1.2768 + * pages_unmap(). 1.2769 + * 1.2770 + * Optimistically try mapping precisely the right amount before falling 1.2771 + * back to the slow method, with the expectation that the optimistic 1.2772 + * approach works most of the time. 1.2773 + */ 1.2774 + 1.2775 + ret = pages_map(NULL, size, -1); 1.2776 + if (ret == NULL) 1.2777 + return (NULL); 1.2778 + offset = ALIGNMENT_ADDR2OFFSET(ret, chunksize); 1.2779 + if (offset != 0) { 1.2780 + pages_unmap(ret, size); 1.2781 + return (chunk_alloc_mmap_slow(size, chunksize)); 1.2782 + } 1.2783 + 1.2784 + assert(ret != NULL); 1.2785 + return (ret); 1.2786 +} 1.2787 + 1.2788 +#endif /* defined(MOZ_MEMORY_WINDOWS) || defined(JEMALLOC_USES_MAP_ALIGN) || defined(MALLOC_PAGEFILE) */ 1.2789 + 1.2790 +#ifdef MALLOC_PAGEFILE 1.2791 +static int 1.2792 +pagefile_init(size_t size) 1.2793 +{ 1.2794 + int ret; 1.2795 + size_t i; 1.2796 + char pagefile_path[PATH_MAX]; 1.2797 + char zbuf[MALLOC_PAGEFILE_WRITE_SIZE]; 1.2798 + 1.2799 + /* 1.2800 + * Create a temporary file, then immediately unlink it so that it will 1.2801 + * not persist. 1.2802 + */ 1.2803 + strcpy(pagefile_path, pagefile_templ); 1.2804 + ret = mkstemp(pagefile_path); 1.2805 + if (ret == -1) 1.2806 + return (ret); 1.2807 + if (unlink(pagefile_path)) { 1.2808 + char buf[STRERROR_BUF]; 1.2809 + 1.2810 + strerror_r(errno, buf, sizeof(buf)); 1.2811 + _malloc_message(_getprogname(), ": (malloc) Error in unlink(\"", 1.2812 + pagefile_path, "\"):"); 1.2813 + _malloc_message(buf, "\n", "", ""); 1.2814 + if (opt_abort) 1.2815 + abort(); 1.2816 + } 1.2817 + 1.2818 + /* 1.2819 + * Write sequential zeroes to the file in order to assure that disk 1.2820 + * space is committed, with minimal fragmentation. It would be 1.2821 + * sufficient to write one zero per disk block, but that potentially 1.2822 + * results in more system calls, for no real gain. 1.2823 + */ 1.2824 + memset(zbuf, 0, sizeof(zbuf)); 1.2825 + for (i = 0; i < size; i += sizeof(zbuf)) { 1.2826 + if (write(ret, zbuf, sizeof(zbuf)) != sizeof(zbuf)) { 1.2827 + if (errno != ENOSPC) { 1.2828 + char buf[STRERROR_BUF]; 1.2829 + 1.2830 + strerror_r(errno, buf, sizeof(buf)); 1.2831 + _malloc_message(_getprogname(), 1.2832 + ": (malloc) Error in write(): ", buf, "\n"); 1.2833 + if (opt_abort) 1.2834 + abort(); 1.2835 + } 1.2836 + pagefile_close(ret); 1.2837 + return (-1); 1.2838 + } 1.2839 + } 1.2840 + 1.2841 + return (ret); 1.2842 +} 1.2843 + 1.2844 +static void 1.2845 +pagefile_close(int pfd) 1.2846 +{ 1.2847 + 1.2848 + if (close(pfd)) { 1.2849 + char buf[STRERROR_BUF]; 1.2850 + 1.2851 + strerror_r(errno, buf, sizeof(buf)); 1.2852 + _malloc_message(_getprogname(), 1.2853 + ": (malloc) Error in close(): ", buf, "\n"); 1.2854 + if (opt_abort) 1.2855 + abort(); 1.2856 + } 1.2857 +} 1.2858 +#endif 1.2859 + 1.2860 +static void * 1.2861 +chunk_alloc(size_t size, bool zero, bool pagefile) 1.2862 +{ 1.2863 + void *ret; 1.2864 + 1.2865 + assert(size != 0); 1.2866 + assert((size & chunksize_mask) == 0); 1.2867 + 1.2868 + ret = chunk_alloc_mmap(size, pagefile); 1.2869 + if (ret != NULL) { 1.2870 + goto RETURN; 1.2871 + } 1.2872 + 1.2873 + /* All strategies for allocation failed. */ 1.2874 + ret = NULL; 1.2875 +RETURN: 1.2876 + 1.2877 +#ifdef MALLOC_VALIDATE 1.2878 + if (ret != NULL) { 1.2879 + if (malloc_rtree_set(chunk_rtree, (uintptr_t)ret, ret)) { 1.2880 + chunk_dealloc(ret, size); 1.2881 + return (NULL); 1.2882 + } 1.2883 + } 1.2884 +#endif 1.2885 + 1.2886 + assert(CHUNK_ADDR2BASE(ret) == ret); 1.2887 + return (ret); 1.2888 +} 1.2889 + 1.2890 +static void 1.2891 +chunk_dealloc_mmap(void *chunk, size_t size) 1.2892 +{ 1.2893 + 1.2894 + pages_unmap(chunk, size); 1.2895 +} 1.2896 + 1.2897 +static void 1.2898 +chunk_dealloc(void *chunk, size_t size) 1.2899 +{ 1.2900 + 1.2901 + assert(chunk != NULL); 1.2902 + assert(CHUNK_ADDR2BASE(chunk) == chunk); 1.2903 + assert(size != 0); 1.2904 + assert((size & chunksize_mask) == 0); 1.2905 + 1.2906 +#ifdef MALLOC_VALIDATE 1.2907 + malloc_rtree_set(chunk_rtree, (uintptr_t)chunk, NULL); 1.2908 +#endif 1.2909 + 1.2910 + chunk_dealloc_mmap(chunk, size); 1.2911 +} 1.2912 + 1.2913 +/* 1.2914 + * End chunk management functions. 1.2915 + */ 1.2916 +/******************************************************************************/ 1.2917 +/* 1.2918 + * Begin arena. 1.2919 + */ 1.2920 + 1.2921 +/* 1.2922 + * Choose an arena based on a per-thread value (fast-path code, calls slow-path 1.2923 + * code if necessary). 1.2924 + */ 1.2925 +static inline arena_t * 1.2926 +choose_arena(void) 1.2927 +{ 1.2928 + arena_t *ret; 1.2929 + 1.2930 + /* 1.2931 + * We can only use TLS if this is a PIC library, since for the static 1.2932 + * library version, libc's malloc is used by TLS allocation, which 1.2933 + * introduces a bootstrapping issue. 1.2934 + */ 1.2935 +#ifndef NO_TLS 1.2936 + if (isthreaded == false) { 1.2937 + /* Avoid the overhead of TLS for single-threaded operation. */ 1.2938 + return (arenas[0]); 1.2939 + } 1.2940 + 1.2941 +# ifdef MOZ_MEMORY_WINDOWS 1.2942 + ret = (arena_t*)TlsGetValue(tlsIndex); 1.2943 +# else 1.2944 + ret = arenas_map; 1.2945 +# endif 1.2946 + 1.2947 + if (ret == NULL) { 1.2948 + ret = choose_arena_hard(); 1.2949 + RELEASE_ASSERT(ret != NULL); 1.2950 + } 1.2951 +#else 1.2952 + if (isthreaded && narenas > 1) { 1.2953 + unsigned long ind; 1.2954 + 1.2955 + /* 1.2956 + * Hash _pthread_self() to one of the arenas. There is a prime 1.2957 + * number of arenas, so this has a reasonable chance of 1.2958 + * working. Even so, the hashing can be easily thwarted by 1.2959 + * inconvenient _pthread_self() values. Without specific 1.2960 + * knowledge of how _pthread_self() calculates values, we can't 1.2961 + * easily do much better than this. 1.2962 + */ 1.2963 + ind = (unsigned long) _pthread_self() % narenas; 1.2964 + 1.2965 + /* 1.2966 + * Optimistially assume that arenas[ind] has been initialized. 1.2967 + * At worst, we find out that some other thread has already 1.2968 + * done so, after acquiring the lock in preparation. Note that 1.2969 + * this lazy locking also has the effect of lazily forcing 1.2970 + * cache coherency; without the lock acquisition, there's no 1.2971 + * guarantee that modification of arenas[ind] by another thread 1.2972 + * would be seen on this CPU for an arbitrary amount of time. 1.2973 + * 1.2974 + * In general, this approach to modifying a synchronized value 1.2975 + * isn't a good idea, but in this case we only ever modify the 1.2976 + * value once, so things work out well. 1.2977 + */ 1.2978 + ret = arenas[ind]; 1.2979 + if (ret == NULL) { 1.2980 + /* 1.2981 + * Avoid races with another thread that may have already 1.2982 + * initialized arenas[ind]. 1.2983 + */ 1.2984 + malloc_spin_lock(&arenas_lock); 1.2985 + if (arenas[ind] == NULL) 1.2986 + ret = arenas_extend((unsigned)ind); 1.2987 + else 1.2988 + ret = arenas[ind]; 1.2989 + malloc_spin_unlock(&arenas_lock); 1.2990 + } 1.2991 + } else 1.2992 + ret = arenas[0]; 1.2993 +#endif 1.2994 + 1.2995 + RELEASE_ASSERT(ret != NULL); 1.2996 + return (ret); 1.2997 +} 1.2998 + 1.2999 +#ifndef NO_TLS 1.3000 +/* 1.3001 + * Choose an arena based on a per-thread value (slow-path code only, called 1.3002 + * only by choose_arena()). 1.3003 + */ 1.3004 +static arena_t * 1.3005 +choose_arena_hard(void) 1.3006 +{ 1.3007 + arena_t *ret; 1.3008 + 1.3009 + assert(isthreaded); 1.3010 + 1.3011 +#ifdef MALLOC_BALANCE 1.3012 + /* Seed the PRNG used for arena load balancing. */ 1.3013 + SPRN(balance, (uint32_t)(uintptr_t)(_pthread_self())); 1.3014 +#endif 1.3015 + 1.3016 + if (narenas > 1) { 1.3017 +#ifdef MALLOC_BALANCE 1.3018 + unsigned ind; 1.3019 + 1.3020 + ind = PRN(balance, narenas_2pow); 1.3021 + if ((ret = arenas[ind]) == NULL) { 1.3022 + malloc_spin_lock(&arenas_lock); 1.3023 + if ((ret = arenas[ind]) == NULL) 1.3024 + ret = arenas_extend(ind); 1.3025 + malloc_spin_unlock(&arenas_lock); 1.3026 + } 1.3027 +#else 1.3028 + malloc_spin_lock(&arenas_lock); 1.3029 + if ((ret = arenas[next_arena]) == NULL) 1.3030 + ret = arenas_extend(next_arena); 1.3031 + next_arena = (next_arena + 1) % narenas; 1.3032 + malloc_spin_unlock(&arenas_lock); 1.3033 +#endif 1.3034 + } else 1.3035 + ret = arenas[0]; 1.3036 + 1.3037 +#ifdef MOZ_MEMORY_WINDOWS 1.3038 + TlsSetValue(tlsIndex, ret); 1.3039 +#else 1.3040 + arenas_map = ret; 1.3041 +#endif 1.3042 + 1.3043 + return (ret); 1.3044 +} 1.3045 +#endif 1.3046 + 1.3047 +static inline int 1.3048 +arena_chunk_comp(arena_chunk_t *a, arena_chunk_t *b) 1.3049 +{ 1.3050 + uintptr_t a_chunk = (uintptr_t)a; 1.3051 + uintptr_t b_chunk = (uintptr_t)b; 1.3052 + 1.3053 + assert(a != NULL); 1.3054 + assert(b != NULL); 1.3055 + 1.3056 + return ((a_chunk > b_chunk) - (a_chunk < b_chunk)); 1.3057 +} 1.3058 + 1.3059 +/* Wrap red-black tree macros in functions. */ 1.3060 +rb_wrap(static, arena_chunk_tree_dirty_, arena_chunk_tree_t, 1.3061 + arena_chunk_t, link_dirty, arena_chunk_comp) 1.3062 + 1.3063 +static inline int 1.3064 +arena_run_comp(arena_chunk_map_t *a, arena_chunk_map_t *b) 1.3065 +{ 1.3066 + uintptr_t a_mapelm = (uintptr_t)a; 1.3067 + uintptr_t b_mapelm = (uintptr_t)b; 1.3068 + 1.3069 + assert(a != NULL); 1.3070 + assert(b != NULL); 1.3071 + 1.3072 + return ((a_mapelm > b_mapelm) - (a_mapelm < b_mapelm)); 1.3073 +} 1.3074 + 1.3075 +/* Wrap red-black tree macros in functions. */ 1.3076 +rb_wrap(static, arena_run_tree_, arena_run_tree_t, arena_chunk_map_t, link, 1.3077 + arena_run_comp) 1.3078 + 1.3079 +static inline int 1.3080 +arena_avail_comp(arena_chunk_map_t *a, arena_chunk_map_t *b) 1.3081 +{ 1.3082 + int ret; 1.3083 + size_t a_size = a->bits & ~pagesize_mask; 1.3084 + size_t b_size = b->bits & ~pagesize_mask; 1.3085 + 1.3086 + ret = (a_size > b_size) - (a_size < b_size); 1.3087 + if (ret == 0) { 1.3088 + uintptr_t a_mapelm, b_mapelm; 1.3089 + 1.3090 + if ((a->bits & CHUNK_MAP_KEY) == 0) 1.3091 + a_mapelm = (uintptr_t)a; 1.3092 + else { 1.3093 + /* 1.3094 + * Treat keys as though they are lower than anything 1.3095 + * else. 1.3096 + */ 1.3097 + a_mapelm = 0; 1.3098 + } 1.3099 + b_mapelm = (uintptr_t)b; 1.3100 + 1.3101 + ret = (a_mapelm > b_mapelm) - (a_mapelm < b_mapelm); 1.3102 + } 1.3103 + 1.3104 + return (ret); 1.3105 +} 1.3106 + 1.3107 +/* Wrap red-black tree macros in functions. */ 1.3108 +rb_wrap(static, arena_avail_tree_, arena_avail_tree_t, arena_chunk_map_t, link, 1.3109 + arena_avail_comp) 1.3110 + 1.3111 +static inline void * 1.3112 +arena_run_reg_alloc(arena_run_t *run, arena_bin_t *bin) 1.3113 +{ 1.3114 + void *ret; 1.3115 + unsigned i, mask, bit, regind; 1.3116 + 1.3117 + assert(run->magic == ARENA_RUN_MAGIC); 1.3118 + assert(run->regs_minelm < bin->regs_mask_nelms); 1.3119 + 1.3120 + /* 1.3121 + * Move the first check outside the loop, so that run->regs_minelm can 1.3122 + * be updated unconditionally, without the possibility of updating it 1.3123 + * multiple times. 1.3124 + */ 1.3125 + i = run->regs_minelm; 1.3126 + mask = run->regs_mask[i]; 1.3127 + if (mask != 0) { 1.3128 + /* Usable allocation found. */ 1.3129 + bit = ffs((int)mask) - 1; 1.3130 + 1.3131 + regind = ((i << (SIZEOF_INT_2POW + 3)) + bit); 1.3132 + assert(regind < bin->nregs); 1.3133 + ret = (void *)(((uintptr_t)run) + bin->reg0_offset 1.3134 + + (bin->reg_size * regind)); 1.3135 + 1.3136 + /* Clear bit. */ 1.3137 + mask ^= (1U << bit); 1.3138 + run->regs_mask[i] = mask; 1.3139 + 1.3140 + return (ret); 1.3141 + } 1.3142 + 1.3143 + for (i++; i < bin->regs_mask_nelms; i++) { 1.3144 + mask = run->regs_mask[i]; 1.3145 + if (mask != 0) { 1.3146 + /* Usable allocation found. */ 1.3147 + bit = ffs((int)mask) - 1; 1.3148 + 1.3149 + regind = ((i << (SIZEOF_INT_2POW + 3)) + bit); 1.3150 + assert(regind < bin->nregs); 1.3151 + ret = (void *)(((uintptr_t)run) + bin->reg0_offset 1.3152 + + (bin->reg_size * regind)); 1.3153 + 1.3154 + /* Clear bit. */ 1.3155 + mask ^= (1U << bit); 1.3156 + run->regs_mask[i] = mask; 1.3157 + 1.3158 + /* 1.3159 + * Make a note that nothing before this element 1.3160 + * contains a free region. 1.3161 + */ 1.3162 + run->regs_minelm = i; /* Low payoff: + (mask == 0); */ 1.3163 + 1.3164 + return (ret); 1.3165 + } 1.3166 + } 1.3167 + /* Not reached. */ 1.3168 + RELEASE_ASSERT(0); 1.3169 + return (NULL); 1.3170 +} 1.3171 + 1.3172 +static inline void 1.3173 +arena_run_reg_dalloc(arena_run_t *run, arena_bin_t *bin, void *ptr, size_t size) 1.3174 +{ 1.3175 + /* 1.3176 + * To divide by a number D that is not a power of two we multiply 1.3177 + * by (2^21 / D) and then right shift by 21 positions. 1.3178 + * 1.3179 + * X / D 1.3180 + * 1.3181 + * becomes 1.3182 + * 1.3183 + * (X * size_invs[(D >> QUANTUM_2POW_MIN) - 3]) >> SIZE_INV_SHIFT 1.3184 + */ 1.3185 +#define SIZE_INV_SHIFT 21 1.3186 +#define SIZE_INV(s) (((1U << SIZE_INV_SHIFT) / (s << QUANTUM_2POW_MIN)) + 1) 1.3187 + static const unsigned size_invs[] = { 1.3188 + SIZE_INV(3), 1.3189 + SIZE_INV(4), SIZE_INV(5), SIZE_INV(6), SIZE_INV(7), 1.3190 + SIZE_INV(8), SIZE_INV(9), SIZE_INV(10), SIZE_INV(11), 1.3191 + SIZE_INV(12),SIZE_INV(13), SIZE_INV(14), SIZE_INV(15), 1.3192 + SIZE_INV(16),SIZE_INV(17), SIZE_INV(18), SIZE_INV(19), 1.3193 + SIZE_INV(20),SIZE_INV(21), SIZE_INV(22), SIZE_INV(23), 1.3194 + SIZE_INV(24),SIZE_INV(25), SIZE_INV(26), SIZE_INV(27), 1.3195 + SIZE_INV(28),SIZE_INV(29), SIZE_INV(30), SIZE_INV(31) 1.3196 +#if (QUANTUM_2POW_MIN < 4) 1.3197 + , 1.3198 + SIZE_INV(32), SIZE_INV(33), SIZE_INV(34), SIZE_INV(35), 1.3199 + SIZE_INV(36), SIZE_INV(37), SIZE_INV(38), SIZE_INV(39), 1.3200 + SIZE_INV(40), SIZE_INV(41), SIZE_INV(42), SIZE_INV(43), 1.3201 + SIZE_INV(44), SIZE_INV(45), SIZE_INV(46), SIZE_INV(47), 1.3202 + SIZE_INV(48), SIZE_INV(49), SIZE_INV(50), SIZE_INV(51), 1.3203 + SIZE_INV(52), SIZE_INV(53), SIZE_INV(54), SIZE_INV(55), 1.3204 + SIZE_INV(56), SIZE_INV(57), SIZE_INV(58), SIZE_INV(59), 1.3205 + SIZE_INV(60), SIZE_INV(61), SIZE_INV(62), SIZE_INV(63) 1.3206 +#endif 1.3207 + }; 1.3208 + unsigned diff, regind, elm, bit; 1.3209 + 1.3210 + assert(run->magic == ARENA_RUN_MAGIC); 1.3211 + assert(((sizeof(size_invs)) / sizeof(unsigned)) + 3 1.3212 + >= (SMALL_MAX_DEFAULT >> QUANTUM_2POW_MIN)); 1.3213 + 1.3214 + /* 1.3215 + * Avoid doing division with a variable divisor if possible. Using 1.3216 + * actual division here can reduce allocator throughput by over 20%! 1.3217 + */ 1.3218 + diff = (unsigned)((uintptr_t)ptr - (uintptr_t)run - bin->reg0_offset); 1.3219 + if ((size & (size - 1)) == 0) { 1.3220 + /* 1.3221 + * log2_table allows fast division of a power of two in the 1.3222 + * [1..128] range. 1.3223 + * 1.3224 + * (x / divisor) becomes (x >> log2_table[divisor - 1]). 1.3225 + */ 1.3226 + static const unsigned char log2_table[] = { 1.3227 + 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 1.3228 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 1.3229 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.3230 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 1.3231 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.3232 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.3233 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.3234 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7 1.3235 + }; 1.3236 + 1.3237 + if (size <= 128) 1.3238 + regind = (diff >> log2_table[size - 1]); 1.3239 + else if (size <= 32768) 1.3240 + regind = diff >> (8 + log2_table[(size >> 8) - 1]); 1.3241 + else { 1.3242 + /* 1.3243 + * The run size is too large for us to use the lookup 1.3244 + * table. Use real division. 1.3245 + */ 1.3246 + regind = diff / size; 1.3247 + } 1.3248 + } else if (size <= ((sizeof(size_invs) / sizeof(unsigned)) 1.3249 + << QUANTUM_2POW_MIN) + 2) { 1.3250 + regind = size_invs[(size >> QUANTUM_2POW_MIN) - 3] * diff; 1.3251 + regind >>= SIZE_INV_SHIFT; 1.3252 + } else { 1.3253 + /* 1.3254 + * size_invs isn't large enough to handle this size class, so 1.3255 + * calculate regind using actual division. This only happens 1.3256 + * if the user increases small_max via the 'S' runtime 1.3257 + * configuration option. 1.3258 + */ 1.3259 + regind = diff / size; 1.3260 + }; 1.3261 + RELEASE_ASSERT(diff == regind * size); 1.3262 + RELEASE_ASSERT(regind < bin->nregs); 1.3263 + 1.3264 + elm = regind >> (SIZEOF_INT_2POW + 3); 1.3265 + if (elm < run->regs_minelm) 1.3266 + run->regs_minelm = elm; 1.3267 + bit = regind - (elm << (SIZEOF_INT_2POW + 3)); 1.3268 + RELEASE_ASSERT((run->regs_mask[elm] & (1U << bit)) == 0); 1.3269 + run->regs_mask[elm] |= (1U << bit); 1.3270 +#undef SIZE_INV 1.3271 +#undef SIZE_INV_SHIFT 1.3272 +} 1.3273 + 1.3274 +static void 1.3275 +arena_run_split(arena_t *arena, arena_run_t *run, size_t size, bool large, 1.3276 + bool zero) 1.3277 +{ 1.3278 + arena_chunk_t *chunk; 1.3279 + size_t old_ndirty, run_ind, total_pages, need_pages, rem_pages, i; 1.3280 + 1.3281 + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); 1.3282 + old_ndirty = chunk->ndirty; 1.3283 + run_ind = (unsigned)(((uintptr_t)run - (uintptr_t)chunk) 1.3284 + >> pagesize_2pow); 1.3285 + total_pages = (chunk->map[run_ind].bits & ~pagesize_mask) >> 1.3286 + pagesize_2pow; 1.3287 + need_pages = (size >> pagesize_2pow); 1.3288 + assert(need_pages > 0); 1.3289 + assert(need_pages <= total_pages); 1.3290 + rem_pages = total_pages - need_pages; 1.3291 + 1.3292 + arena_avail_tree_remove(&arena->runs_avail, &chunk->map[run_ind]); 1.3293 + 1.3294 + /* Keep track of trailing unused pages for later use. */ 1.3295 + if (rem_pages > 0) { 1.3296 + chunk->map[run_ind+need_pages].bits = (rem_pages << 1.3297 + pagesize_2pow) | (chunk->map[run_ind+need_pages].bits & 1.3298 + pagesize_mask); 1.3299 + chunk->map[run_ind+total_pages-1].bits = (rem_pages << 1.3300 + pagesize_2pow) | (chunk->map[run_ind+total_pages-1].bits & 1.3301 + pagesize_mask); 1.3302 + arena_avail_tree_insert(&arena->runs_avail, 1.3303 + &chunk->map[run_ind+need_pages]); 1.3304 + } 1.3305 + 1.3306 + for (i = 0; i < need_pages; i++) { 1.3307 +#if defined(MALLOC_DECOMMIT) || defined(MALLOC_STATS) || defined(MALLOC_DOUBLE_PURGE) 1.3308 + /* 1.3309 + * Commit decommitted pages if necessary. If a decommitted 1.3310 + * page is encountered, commit all needed adjacent decommitted 1.3311 + * pages in one operation, in order to reduce system call 1.3312 + * overhead. 1.3313 + */ 1.3314 + if (chunk->map[run_ind + i].bits & CHUNK_MAP_MADVISED_OR_DECOMMITTED) { 1.3315 + size_t j; 1.3316 + 1.3317 + /* 1.3318 + * Advance i+j to just past the index of the last page 1.3319 + * to commit. Clear CHUNK_MAP_DECOMMITTED and 1.3320 + * CHUNK_MAP_MADVISED along the way. 1.3321 + */ 1.3322 + for (j = 0; i + j < need_pages && (chunk->map[run_ind + 1.3323 + i + j].bits & CHUNK_MAP_MADVISED_OR_DECOMMITTED); j++) { 1.3324 + /* DECOMMITTED and MADVISED are mutually exclusive. */ 1.3325 + assert(!(chunk->map[run_ind + i + j].bits & CHUNK_MAP_DECOMMITTED && 1.3326 + chunk->map[run_ind + i + j].bits & CHUNK_MAP_MADVISED)); 1.3327 + 1.3328 + chunk->map[run_ind + i + j].bits &= 1.3329 + ~CHUNK_MAP_MADVISED_OR_DECOMMITTED; 1.3330 + } 1.3331 + 1.3332 +# ifdef MALLOC_DECOMMIT 1.3333 + pages_commit((void *)((uintptr_t)chunk + ((run_ind + i) 1.3334 + << pagesize_2pow)), (j << pagesize_2pow)); 1.3335 +# ifdef MALLOC_STATS 1.3336 + arena->stats.ncommit++; 1.3337 +# endif 1.3338 +# endif 1.3339 + 1.3340 +# ifdef MALLOC_STATS 1.3341 + arena->stats.committed += j; 1.3342 +# endif 1.3343 + 1.3344 +# ifndef MALLOC_DECOMMIT 1.3345 + } 1.3346 +# else 1.3347 + } else /* No need to zero since commit zeros. */ 1.3348 +# endif 1.3349 + 1.3350 +#endif 1.3351 + 1.3352 + /* Zero if necessary. */ 1.3353 + if (zero) { 1.3354 + if ((chunk->map[run_ind + i].bits & CHUNK_MAP_ZEROED) 1.3355 + == 0) { 1.3356 + VALGRIND_MALLOCLIKE_BLOCK((void *)((uintptr_t) 1.3357 + chunk + ((run_ind + i) << pagesize_2pow)), 1.3358 + pagesize, 0, false); 1.3359 + memset((void *)((uintptr_t)chunk + ((run_ind 1.3360 + + i) << pagesize_2pow)), 0, pagesize); 1.3361 + VALGRIND_FREELIKE_BLOCK((void *)((uintptr_t) 1.3362 + chunk + ((run_ind + i) << pagesize_2pow)), 1.3363 + 0); 1.3364 + /* CHUNK_MAP_ZEROED is cleared below. */ 1.3365 + } 1.3366 + } 1.3367 + 1.3368 + /* Update dirty page accounting. */ 1.3369 + if (chunk->map[run_ind + i].bits & CHUNK_MAP_DIRTY) { 1.3370 + chunk->ndirty--; 1.3371 + arena->ndirty--; 1.3372 + /* CHUNK_MAP_DIRTY is cleared below. */ 1.3373 + } 1.3374 + 1.3375 + /* Initialize the chunk map. */ 1.3376 + if (large) { 1.3377 + chunk->map[run_ind + i].bits = CHUNK_MAP_LARGE 1.3378 + | CHUNK_MAP_ALLOCATED; 1.3379 + } else { 1.3380 + chunk->map[run_ind + i].bits = (size_t)run 1.3381 + | CHUNK_MAP_ALLOCATED; 1.3382 + } 1.3383 + } 1.3384 + 1.3385 + /* 1.3386 + * Set the run size only in the first element for large runs. This is 1.3387 + * primarily a debugging aid, since the lack of size info for trailing 1.3388 + * pages only matters if the application tries to operate on an 1.3389 + * interior pointer. 1.3390 + */ 1.3391 + if (large) 1.3392 + chunk->map[run_ind].bits |= size; 1.3393 + 1.3394 + if (chunk->ndirty == 0 && old_ndirty > 0) 1.3395 + arena_chunk_tree_dirty_remove(&arena->chunks_dirty, chunk); 1.3396 +} 1.3397 + 1.3398 +static void 1.3399 +arena_chunk_init(arena_t *arena, arena_chunk_t *chunk) 1.3400 +{ 1.3401 + arena_run_t *run; 1.3402 + size_t i; 1.3403 + 1.3404 + VALGRIND_MALLOCLIKE_BLOCK(chunk, (arena_chunk_header_npages << 1.3405 + pagesize_2pow), 0, false); 1.3406 +#ifdef MALLOC_STATS 1.3407 + arena->stats.mapped += chunksize; 1.3408 +#endif 1.3409 + 1.3410 + chunk->arena = arena; 1.3411 + 1.3412 + /* 1.3413 + * Claim that no pages are in use, since the header is merely overhead. 1.3414 + */ 1.3415 + chunk->ndirty = 0; 1.3416 + 1.3417 + /* Initialize the map to contain one maximal free untouched run. */ 1.3418 + run = (arena_run_t *)((uintptr_t)chunk + (arena_chunk_header_npages << 1.3419 + pagesize_2pow)); 1.3420 + for (i = 0; i < arena_chunk_header_npages; i++) 1.3421 + chunk->map[i].bits = 0; 1.3422 + chunk->map[i].bits = arena_maxclass | CHUNK_MAP_DECOMMITTED | CHUNK_MAP_ZEROED; 1.3423 + for (i++; i < chunk_npages-1; i++) { 1.3424 + chunk->map[i].bits = CHUNK_MAP_DECOMMITTED | CHUNK_MAP_ZEROED; 1.3425 + } 1.3426 + chunk->map[chunk_npages-1].bits = arena_maxclass | CHUNK_MAP_DECOMMITTED | CHUNK_MAP_ZEROED; 1.3427 + 1.3428 +#ifdef MALLOC_DECOMMIT 1.3429 + /* 1.3430 + * Start out decommitted, in order to force a closer correspondence 1.3431 + * between dirty pages and committed untouched pages. 1.3432 + */ 1.3433 + pages_decommit(run, arena_maxclass); 1.3434 +# ifdef MALLOC_STATS 1.3435 + arena->stats.ndecommit++; 1.3436 + arena->stats.decommitted += (chunk_npages - arena_chunk_header_npages); 1.3437 +# endif 1.3438 +#endif 1.3439 +#ifdef MALLOC_STATS 1.3440 + arena->stats.committed += arena_chunk_header_npages; 1.3441 +#endif 1.3442 + 1.3443 + /* Insert the run into the runs_avail tree. */ 1.3444 + arena_avail_tree_insert(&arena->runs_avail, 1.3445 + &chunk->map[arena_chunk_header_npages]); 1.3446 + 1.3447 +#ifdef MALLOC_DOUBLE_PURGE 1.3448 + LinkedList_Init(&chunk->chunks_madvised_elem); 1.3449 +#endif 1.3450 +} 1.3451 + 1.3452 +static void 1.3453 +arena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk) 1.3454 +{ 1.3455 + 1.3456 + if (arena->spare != NULL) { 1.3457 + if (arena->spare->ndirty > 0) { 1.3458 + arena_chunk_tree_dirty_remove( 1.3459 + &chunk->arena->chunks_dirty, arena->spare); 1.3460 + arena->ndirty -= arena->spare->ndirty; 1.3461 +#ifdef MALLOC_STATS 1.3462 + arena->stats.committed -= arena->spare->ndirty; 1.3463 +#endif 1.3464 + } 1.3465 + 1.3466 +#ifdef MALLOC_DOUBLE_PURGE 1.3467 + /* This is safe to do even if arena->spare is not in the list. */ 1.3468 + LinkedList_Remove(&arena->spare->chunks_madvised_elem); 1.3469 +#endif 1.3470 + 1.3471 + VALGRIND_FREELIKE_BLOCK(arena->spare, 0); 1.3472 + chunk_dealloc((void *)arena->spare, chunksize); 1.3473 +#ifdef MALLOC_STATS 1.3474 + arena->stats.mapped -= chunksize; 1.3475 + arena->stats.committed -= arena_chunk_header_npages; 1.3476 +#endif 1.3477 + } 1.3478 + 1.3479 + /* 1.3480 + * Remove run from runs_avail, so that the arena does not use it. 1.3481 + * Dirty page flushing only uses the chunks_dirty tree, so leaving this 1.3482 + * chunk in the chunks_* trees is sufficient for that purpose. 1.3483 + */ 1.3484 + arena_avail_tree_remove(&arena->runs_avail, 1.3485 + &chunk->map[arena_chunk_header_npages]); 1.3486 + 1.3487 + arena->spare = chunk; 1.3488 +} 1.3489 + 1.3490 +static arena_run_t * 1.3491 +arena_run_alloc(arena_t *arena, arena_bin_t *bin, size_t size, bool large, 1.3492 + bool zero) 1.3493 +{ 1.3494 + arena_run_t *run; 1.3495 + arena_chunk_map_t *mapelm, key; 1.3496 + 1.3497 + assert(size <= arena_maxclass); 1.3498 + assert((size & pagesize_mask) == 0); 1.3499 + 1.3500 + /* Search the arena's chunks for the lowest best fit. */ 1.3501 + key.bits = size | CHUNK_MAP_KEY; 1.3502 + mapelm = arena_avail_tree_nsearch(&arena->runs_avail, &key); 1.3503 + if (mapelm != NULL) { 1.3504 + arena_chunk_t *chunk = 1.3505 + (arena_chunk_t*)CHUNK_ADDR2BASE(mapelm); 1.3506 + size_t pageind = ((uintptr_t)mapelm - 1.3507 + (uintptr_t)chunk->map) / 1.3508 + sizeof(arena_chunk_map_t); 1.3509 + 1.3510 + run = (arena_run_t *)((uintptr_t)chunk + (pageind 1.3511 + << pagesize_2pow)); 1.3512 + arena_run_split(arena, run, size, large, zero); 1.3513 + return (run); 1.3514 + } 1.3515 + 1.3516 + if (arena->spare != NULL) { 1.3517 + /* Use the spare. */ 1.3518 + arena_chunk_t *chunk = arena->spare; 1.3519 + arena->spare = NULL; 1.3520 + run = (arena_run_t *)((uintptr_t)chunk + 1.3521 + (arena_chunk_header_npages << pagesize_2pow)); 1.3522 + /* Insert the run into the runs_avail tree. */ 1.3523 + arena_avail_tree_insert(&arena->runs_avail, 1.3524 + &chunk->map[arena_chunk_header_npages]); 1.3525 + arena_run_split(arena, run, size, large, zero); 1.3526 + return (run); 1.3527 + } 1.3528 + 1.3529 + /* 1.3530 + * No usable runs. Create a new chunk from which to allocate 1.3531 + * the run. 1.3532 + */ 1.3533 + { 1.3534 + arena_chunk_t *chunk = (arena_chunk_t *) 1.3535 + chunk_alloc(chunksize, true, true); 1.3536 + if (chunk == NULL) 1.3537 + return (NULL); 1.3538 + 1.3539 + arena_chunk_init(arena, chunk); 1.3540 + run = (arena_run_t *)((uintptr_t)chunk + 1.3541 + (arena_chunk_header_npages << pagesize_2pow)); 1.3542 + } 1.3543 + /* Update page map. */ 1.3544 + arena_run_split(arena, run, size, large, zero); 1.3545 + return (run); 1.3546 +} 1.3547 + 1.3548 +static void 1.3549 +arena_purge(arena_t *arena, bool all) 1.3550 +{ 1.3551 + arena_chunk_t *chunk; 1.3552 + size_t i, npages; 1.3553 + /* If all is set purge all dirty pages. */ 1.3554 + size_t dirty_max = all ? 1 : opt_dirty_max; 1.3555 +#ifdef MALLOC_DEBUG 1.3556 + size_t ndirty = 0; 1.3557 + rb_foreach_begin(arena_chunk_t, link_dirty, &arena->chunks_dirty, 1.3558 + chunk) { 1.3559 + ndirty += chunk->ndirty; 1.3560 + } rb_foreach_end(arena_chunk_t, link_dirty, &arena->chunks_dirty, chunk) 1.3561 + assert(ndirty == arena->ndirty); 1.3562 +#endif 1.3563 + RELEASE_ASSERT(all || (arena->ndirty > opt_dirty_max)); 1.3564 + 1.3565 +#ifdef MALLOC_STATS 1.3566 + arena->stats.npurge++; 1.3567 +#endif 1.3568 + 1.3569 + /* 1.3570 + * Iterate downward through chunks until enough dirty memory has been 1.3571 + * purged. Terminate as soon as possible in order to minimize the 1.3572 + * number of system calls, even if a chunk has only been partially 1.3573 + * purged. 1.3574 + */ 1.3575 + while (arena->ndirty > (dirty_max >> 1)) { 1.3576 +#ifdef MALLOC_DOUBLE_PURGE 1.3577 + bool madvised = false; 1.3578 +#endif 1.3579 + chunk = arena_chunk_tree_dirty_last(&arena->chunks_dirty); 1.3580 + RELEASE_ASSERT(chunk != NULL); 1.3581 + 1.3582 + for (i = chunk_npages - 1; chunk->ndirty > 0; i--) { 1.3583 + RELEASE_ASSERT(i >= arena_chunk_header_npages); 1.3584 + 1.3585 + if (chunk->map[i].bits & CHUNK_MAP_DIRTY) { 1.3586 +#ifdef MALLOC_DECOMMIT 1.3587 + const size_t free_operation = CHUNK_MAP_DECOMMITTED; 1.3588 +#else 1.3589 + const size_t free_operation = CHUNK_MAP_MADVISED; 1.3590 +#endif 1.3591 + assert((chunk->map[i].bits & 1.3592 + CHUNK_MAP_MADVISED_OR_DECOMMITTED) == 0); 1.3593 + chunk->map[i].bits ^= free_operation | CHUNK_MAP_DIRTY; 1.3594 + /* Find adjacent dirty run(s). */ 1.3595 + for (npages = 1; 1.3596 + i > arena_chunk_header_npages && 1.3597 + (chunk->map[i - 1].bits & CHUNK_MAP_DIRTY); 1.3598 + npages++) { 1.3599 + i--; 1.3600 + assert((chunk->map[i].bits & 1.3601 + CHUNK_MAP_MADVISED_OR_DECOMMITTED) == 0); 1.3602 + chunk->map[i].bits ^= free_operation | CHUNK_MAP_DIRTY; 1.3603 + } 1.3604 + chunk->ndirty -= npages; 1.3605 + arena->ndirty -= npages; 1.3606 + 1.3607 +#ifdef MALLOC_DECOMMIT 1.3608 + pages_decommit((void *)((uintptr_t) 1.3609 + chunk + (i << pagesize_2pow)), 1.3610 + (npages << pagesize_2pow)); 1.3611 +# ifdef MALLOC_STATS 1.3612 + arena->stats.ndecommit++; 1.3613 + arena->stats.decommitted += npages; 1.3614 +# endif 1.3615 +#endif 1.3616 +#ifdef MALLOC_STATS 1.3617 + arena->stats.committed -= npages; 1.3618 +#endif 1.3619 + 1.3620 +#ifndef MALLOC_DECOMMIT 1.3621 + madvise((void *)((uintptr_t)chunk + (i << 1.3622 + pagesize_2pow)), (npages << pagesize_2pow), 1.3623 + MADV_FREE); 1.3624 +# ifdef MALLOC_DOUBLE_PURGE 1.3625 + madvised = true; 1.3626 +# endif 1.3627 +#endif 1.3628 +#ifdef MALLOC_STATS 1.3629 + arena->stats.nmadvise++; 1.3630 + arena->stats.purged += npages; 1.3631 +#endif 1.3632 + if (arena->ndirty <= (dirty_max >> 1)) 1.3633 + break; 1.3634 + } 1.3635 + } 1.3636 + 1.3637 + if (chunk->ndirty == 0) { 1.3638 + arena_chunk_tree_dirty_remove(&arena->chunks_dirty, 1.3639 + chunk); 1.3640 + } 1.3641 +#ifdef MALLOC_DOUBLE_PURGE 1.3642 + if (madvised) { 1.3643 + /* The chunk might already be in the list, but this 1.3644 + * makes sure it's at the front. */ 1.3645 + LinkedList_Remove(&chunk->chunks_madvised_elem); 1.3646 + LinkedList_InsertHead(&arena->chunks_madvised, &chunk->chunks_madvised_elem); 1.3647 + } 1.3648 +#endif 1.3649 + } 1.3650 +} 1.3651 + 1.3652 +static void 1.3653 +arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty) 1.3654 +{ 1.3655 + arena_chunk_t *chunk; 1.3656 + size_t size, run_ind, run_pages; 1.3657 + 1.3658 + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); 1.3659 + run_ind = (size_t)(((uintptr_t)run - (uintptr_t)chunk) 1.3660 + >> pagesize_2pow); 1.3661 + RELEASE_ASSERT(run_ind >= arena_chunk_header_npages); 1.3662 + RELEASE_ASSERT(run_ind < chunk_npages); 1.3663 + if ((chunk->map[run_ind].bits & CHUNK_MAP_LARGE) != 0) 1.3664 + size = chunk->map[run_ind].bits & ~pagesize_mask; 1.3665 + else 1.3666 + size = run->bin->run_size; 1.3667 + run_pages = (size >> pagesize_2pow); 1.3668 + 1.3669 + /* Mark pages as unallocated in the chunk map. */ 1.3670 + if (dirty) { 1.3671 + size_t i; 1.3672 + 1.3673 + for (i = 0; i < run_pages; i++) { 1.3674 + RELEASE_ASSERT((chunk->map[run_ind + i].bits & CHUNK_MAP_DIRTY) 1.3675 + == 0); 1.3676 + chunk->map[run_ind + i].bits = CHUNK_MAP_DIRTY; 1.3677 + } 1.3678 + 1.3679 + if (chunk->ndirty == 0) { 1.3680 + arena_chunk_tree_dirty_insert(&arena->chunks_dirty, 1.3681 + chunk); 1.3682 + } 1.3683 + chunk->ndirty += run_pages; 1.3684 + arena->ndirty += run_pages; 1.3685 + } else { 1.3686 + size_t i; 1.3687 + 1.3688 + for (i = 0; i < run_pages; i++) { 1.3689 + chunk->map[run_ind + i].bits &= ~(CHUNK_MAP_LARGE | 1.3690 + CHUNK_MAP_ALLOCATED); 1.3691 + } 1.3692 + } 1.3693 + chunk->map[run_ind].bits = size | (chunk->map[run_ind].bits & 1.3694 + pagesize_mask); 1.3695 + chunk->map[run_ind+run_pages-1].bits = size | 1.3696 + (chunk->map[run_ind+run_pages-1].bits & pagesize_mask); 1.3697 + 1.3698 + /* Try to coalesce forward. */ 1.3699 + if (run_ind + run_pages < chunk_npages && 1.3700 + (chunk->map[run_ind+run_pages].bits & CHUNK_MAP_ALLOCATED) == 0) { 1.3701 + size_t nrun_size = chunk->map[run_ind+run_pages].bits & 1.3702 + ~pagesize_mask; 1.3703 + 1.3704 + /* 1.3705 + * Remove successor from runs_avail; the coalesced run is 1.3706 + * inserted later. 1.3707 + */ 1.3708 + arena_avail_tree_remove(&arena->runs_avail, 1.3709 + &chunk->map[run_ind+run_pages]); 1.3710 + 1.3711 + size += nrun_size; 1.3712 + run_pages = size >> pagesize_2pow; 1.3713 + 1.3714 + RELEASE_ASSERT((chunk->map[run_ind+run_pages-1].bits & ~pagesize_mask) 1.3715 + == nrun_size); 1.3716 + chunk->map[run_ind].bits = size | (chunk->map[run_ind].bits & 1.3717 + pagesize_mask); 1.3718 + chunk->map[run_ind+run_pages-1].bits = size | 1.3719 + (chunk->map[run_ind+run_pages-1].bits & pagesize_mask); 1.3720 + } 1.3721 + 1.3722 + /* Try to coalesce backward. */ 1.3723 + if (run_ind > arena_chunk_header_npages && (chunk->map[run_ind-1].bits & 1.3724 + CHUNK_MAP_ALLOCATED) == 0) { 1.3725 + size_t prun_size = chunk->map[run_ind-1].bits & ~pagesize_mask; 1.3726 + 1.3727 + run_ind -= prun_size >> pagesize_2pow; 1.3728 + 1.3729 + /* 1.3730 + * Remove predecessor from runs_avail; the coalesced run is 1.3731 + * inserted later. 1.3732 + */ 1.3733 + arena_avail_tree_remove(&arena->runs_avail, 1.3734 + &chunk->map[run_ind]); 1.3735 + 1.3736 + size += prun_size; 1.3737 + run_pages = size >> pagesize_2pow; 1.3738 + 1.3739 + RELEASE_ASSERT((chunk->map[run_ind].bits & ~pagesize_mask) == 1.3740 + prun_size); 1.3741 + chunk->map[run_ind].bits = size | (chunk->map[run_ind].bits & 1.3742 + pagesize_mask); 1.3743 + chunk->map[run_ind+run_pages-1].bits = size | 1.3744 + (chunk->map[run_ind+run_pages-1].bits & pagesize_mask); 1.3745 + } 1.3746 + 1.3747 + /* Insert into runs_avail, now that coalescing is complete. */ 1.3748 + arena_avail_tree_insert(&arena->runs_avail, &chunk->map[run_ind]); 1.3749 + 1.3750 + /* Deallocate chunk if it is now completely unused. */ 1.3751 + if ((chunk->map[arena_chunk_header_npages].bits & (~pagesize_mask | 1.3752 + CHUNK_MAP_ALLOCATED)) == arena_maxclass) 1.3753 + arena_chunk_dealloc(arena, chunk); 1.3754 + 1.3755 + /* Enforce opt_dirty_max. */ 1.3756 + if (arena->ndirty > opt_dirty_max) 1.3757 + arena_purge(arena, false); 1.3758 +} 1.3759 + 1.3760 +static void 1.3761 +arena_run_trim_head(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, 1.3762 + size_t oldsize, size_t newsize) 1.3763 +{ 1.3764 + size_t pageind = ((uintptr_t)run - (uintptr_t)chunk) >> pagesize_2pow; 1.3765 + size_t head_npages = (oldsize - newsize) >> pagesize_2pow; 1.3766 + 1.3767 + assert(oldsize > newsize); 1.3768 + 1.3769 + /* 1.3770 + * Update the chunk map so that arena_run_dalloc() can treat the 1.3771 + * leading run as separately allocated. 1.3772 + */ 1.3773 + chunk->map[pageind].bits = (oldsize - newsize) | CHUNK_MAP_LARGE | 1.3774 + CHUNK_MAP_ALLOCATED; 1.3775 + chunk->map[pageind+head_npages].bits = newsize | CHUNK_MAP_LARGE | 1.3776 + CHUNK_MAP_ALLOCATED; 1.3777 + 1.3778 + arena_run_dalloc(arena, run, false); 1.3779 +} 1.3780 + 1.3781 +static void 1.3782 +arena_run_trim_tail(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, 1.3783 + size_t oldsize, size_t newsize, bool dirty) 1.3784 +{ 1.3785 + size_t pageind = ((uintptr_t)run - (uintptr_t)chunk) >> pagesize_2pow; 1.3786 + size_t npages = newsize >> pagesize_2pow; 1.3787 + 1.3788 + assert(oldsize > newsize); 1.3789 + 1.3790 + /* 1.3791 + * Update the chunk map so that arena_run_dalloc() can treat the 1.3792 + * trailing run as separately allocated. 1.3793 + */ 1.3794 + chunk->map[pageind].bits = newsize | CHUNK_MAP_LARGE | 1.3795 + CHUNK_MAP_ALLOCATED; 1.3796 + chunk->map[pageind+npages].bits = (oldsize - newsize) | CHUNK_MAP_LARGE 1.3797 + | CHUNK_MAP_ALLOCATED; 1.3798 + 1.3799 + arena_run_dalloc(arena, (arena_run_t *)((uintptr_t)run + newsize), 1.3800 + dirty); 1.3801 +} 1.3802 + 1.3803 +static arena_run_t * 1.3804 +arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin) 1.3805 +{ 1.3806 + arena_chunk_map_t *mapelm; 1.3807 + arena_run_t *run; 1.3808 + unsigned i, remainder; 1.3809 + 1.3810 + /* Look for a usable run. */ 1.3811 + mapelm = arena_run_tree_first(&bin->runs); 1.3812 + if (mapelm != NULL) { 1.3813 + /* run is guaranteed to have available space. */ 1.3814 + arena_run_tree_remove(&bin->runs, mapelm); 1.3815 + run = (arena_run_t *)(mapelm->bits & ~pagesize_mask); 1.3816 +#ifdef MALLOC_STATS 1.3817 + bin->stats.reruns++; 1.3818 +#endif 1.3819 + return (run); 1.3820 + } 1.3821 + /* No existing runs have any space available. */ 1.3822 + 1.3823 + /* Allocate a new run. */ 1.3824 + run = arena_run_alloc(arena, bin, bin->run_size, false, false); 1.3825 + if (run == NULL) 1.3826 + return (NULL); 1.3827 + /* 1.3828 + * Don't initialize if a race in arena_run_alloc() allowed an existing 1.3829 + * run to become usable. 1.3830 + */ 1.3831 + if (run == bin->runcur) 1.3832 + return (run); 1.3833 + 1.3834 + VALGRIND_MALLOCLIKE_BLOCK(run, sizeof(arena_run_t) + (sizeof(unsigned) * 1.3835 + (bin->regs_mask_nelms - 1)), 0, false); 1.3836 + 1.3837 + /* Initialize run internals. */ 1.3838 + run->bin = bin; 1.3839 + 1.3840 + for (i = 0; i < bin->regs_mask_nelms - 1; i++) 1.3841 + run->regs_mask[i] = UINT_MAX; 1.3842 + remainder = bin->nregs & ((1U << (SIZEOF_INT_2POW + 3)) - 1); 1.3843 + if (remainder == 0) 1.3844 + run->regs_mask[i] = UINT_MAX; 1.3845 + else { 1.3846 + /* The last element has spare bits that need to be unset. */ 1.3847 + run->regs_mask[i] = (UINT_MAX >> ((1U << (SIZEOF_INT_2POW + 3)) 1.3848 + - remainder)); 1.3849 + } 1.3850 + 1.3851 + run->regs_minelm = 0; 1.3852 + 1.3853 + run->nfree = bin->nregs; 1.3854 +#if defined(MALLOC_DEBUG) || defined(MOZ_JEMALLOC_HARD_ASSERTS) 1.3855 + run->magic = ARENA_RUN_MAGIC; 1.3856 +#endif 1.3857 + 1.3858 +#ifdef MALLOC_STATS 1.3859 + bin->stats.nruns++; 1.3860 + bin->stats.curruns++; 1.3861 + if (bin->stats.curruns > bin->stats.highruns) 1.3862 + bin->stats.highruns = bin->stats.curruns; 1.3863 +#endif 1.3864 + return (run); 1.3865 +} 1.3866 + 1.3867 +/* bin->runcur must have space available before this function is called. */ 1.3868 +static inline void * 1.3869 +arena_bin_malloc_easy(arena_t *arena, arena_bin_t *bin, arena_run_t *run) 1.3870 +{ 1.3871 + void *ret; 1.3872 + 1.3873 + RELEASE_ASSERT(run->magic == ARENA_RUN_MAGIC); 1.3874 + RELEASE_ASSERT(run->nfree > 0); 1.3875 + 1.3876 + ret = arena_run_reg_alloc(run, bin); 1.3877 + RELEASE_ASSERT(ret != NULL); 1.3878 + run->nfree--; 1.3879 + 1.3880 + return (ret); 1.3881 +} 1.3882 + 1.3883 +/* Re-fill bin->runcur, then call arena_bin_malloc_easy(). */ 1.3884 +static void * 1.3885 +arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin) 1.3886 +{ 1.3887 + 1.3888 + bin->runcur = arena_bin_nonfull_run_get(arena, bin); 1.3889 + if (bin->runcur == NULL) 1.3890 + return (NULL); 1.3891 + RELEASE_ASSERT(bin->runcur->magic == ARENA_RUN_MAGIC); 1.3892 + RELEASE_ASSERT(bin->runcur->nfree > 0); 1.3893 + 1.3894 + return (arena_bin_malloc_easy(arena, bin, bin->runcur)); 1.3895 +} 1.3896 + 1.3897 +/* 1.3898 + * Calculate bin->run_size such that it meets the following constraints: 1.3899 + * 1.3900 + * *) bin->run_size >= min_run_size 1.3901 + * *) bin->run_size <= arena_maxclass 1.3902 + * *) bin->run_size <= RUN_MAX_SMALL 1.3903 + * *) run header overhead <= RUN_MAX_OVRHD (or header overhead relaxed). 1.3904 + * 1.3905 + * bin->nregs, bin->regs_mask_nelms, and bin->reg0_offset are 1.3906 + * also calculated here, since these settings are all interdependent. 1.3907 + */ 1.3908 +static size_t 1.3909 +arena_bin_run_size_calc(arena_bin_t *bin, size_t min_run_size) 1.3910 +{ 1.3911 + size_t try_run_size, good_run_size; 1.3912 + unsigned good_nregs, good_mask_nelms, good_reg0_offset; 1.3913 + unsigned try_nregs, try_mask_nelms, try_reg0_offset; 1.3914 + 1.3915 + assert(min_run_size >= pagesize); 1.3916 + assert(min_run_size <= arena_maxclass); 1.3917 + assert(min_run_size <= RUN_MAX_SMALL); 1.3918 + 1.3919 + /* 1.3920 + * Calculate known-valid settings before entering the run_size 1.3921 + * expansion loop, so that the first part of the loop always copies 1.3922 + * valid settings. 1.3923 + * 1.3924 + * The do..while loop iteratively reduces the number of regions until 1.3925 + * the run header and the regions no longer overlap. A closed formula 1.3926 + * would be quite messy, since there is an interdependency between the 1.3927 + * header's mask length and the number of regions. 1.3928 + */ 1.3929 + try_run_size = min_run_size; 1.3930 + try_nregs = ((try_run_size - sizeof(arena_run_t)) / bin->reg_size) 1.3931 + + 1; /* Counter-act try_nregs-- in loop. */ 1.3932 + do { 1.3933 + try_nregs--; 1.3934 + try_mask_nelms = (try_nregs >> (SIZEOF_INT_2POW + 3)) + 1.3935 + ((try_nregs & ((1U << (SIZEOF_INT_2POW + 3)) - 1)) ? 1 : 0); 1.3936 + try_reg0_offset = try_run_size - (try_nregs * bin->reg_size); 1.3937 + } while (sizeof(arena_run_t) + (sizeof(unsigned) * (try_mask_nelms - 1)) 1.3938 + > try_reg0_offset); 1.3939 + 1.3940 + /* run_size expansion loop. */ 1.3941 + do { 1.3942 + /* 1.3943 + * Copy valid settings before trying more aggressive settings. 1.3944 + */ 1.3945 + good_run_size = try_run_size; 1.3946 + good_nregs = try_nregs; 1.3947 + good_mask_nelms = try_mask_nelms; 1.3948 + good_reg0_offset = try_reg0_offset; 1.3949 + 1.3950 + /* Try more aggressive settings. */ 1.3951 + try_run_size += pagesize; 1.3952 + try_nregs = ((try_run_size - sizeof(arena_run_t)) / 1.3953 + bin->reg_size) + 1; /* Counter-act try_nregs-- in loop. */ 1.3954 + do { 1.3955 + try_nregs--; 1.3956 + try_mask_nelms = (try_nregs >> (SIZEOF_INT_2POW + 3)) + 1.3957 + ((try_nregs & ((1U << (SIZEOF_INT_2POW + 3)) - 1)) ? 1.3958 + 1 : 0); 1.3959 + try_reg0_offset = try_run_size - (try_nregs * 1.3960 + bin->reg_size); 1.3961 + } while (sizeof(arena_run_t) + (sizeof(unsigned) * 1.3962 + (try_mask_nelms - 1)) > try_reg0_offset); 1.3963 + } while (try_run_size <= arena_maxclass && try_run_size <= RUN_MAX_SMALL 1.3964 + && RUN_MAX_OVRHD * (bin->reg_size << 3) > RUN_MAX_OVRHD_RELAX 1.3965 + && (try_reg0_offset << RUN_BFP) > RUN_MAX_OVRHD * try_run_size); 1.3966 + 1.3967 + assert(sizeof(arena_run_t) + (sizeof(unsigned) * (good_mask_nelms - 1)) 1.3968 + <= good_reg0_offset); 1.3969 + assert((good_mask_nelms << (SIZEOF_INT_2POW + 3)) >= good_nregs); 1.3970 + 1.3971 + /* Copy final settings. */ 1.3972 + bin->run_size = good_run_size; 1.3973 + bin->nregs = good_nregs; 1.3974 + bin->regs_mask_nelms = good_mask_nelms; 1.3975 + bin->reg0_offset = good_reg0_offset; 1.3976 + 1.3977 + return (good_run_size); 1.3978 +} 1.3979 + 1.3980 +#ifdef MALLOC_BALANCE 1.3981 +static inline void 1.3982 +arena_lock_balance(arena_t *arena) 1.3983 +{ 1.3984 + unsigned contention; 1.3985 + 1.3986 + contention = malloc_spin_lock(&arena->lock); 1.3987 + if (narenas > 1) { 1.3988 + /* 1.3989 + * Calculate the exponentially averaged contention for this 1.3990 + * arena. Due to integer math always rounding down, this value 1.3991 + * decays somewhat faster then normal. 1.3992 + */ 1.3993 + arena->contention = (((uint64_t)arena->contention 1.3994 + * (uint64_t)((1U << BALANCE_ALPHA_INV_2POW)-1)) 1.3995 + + (uint64_t)contention) >> BALANCE_ALPHA_INV_2POW; 1.3996 + if (arena->contention >= opt_balance_threshold) 1.3997 + arena_lock_balance_hard(arena); 1.3998 + } 1.3999 +} 1.4000 + 1.4001 +static void 1.4002 +arena_lock_balance_hard(arena_t *arena) 1.4003 +{ 1.4004 + uint32_t ind; 1.4005 + 1.4006 + arena->contention = 0; 1.4007 +#ifdef MALLOC_STATS 1.4008 + arena->stats.nbalance++; 1.4009 +#endif 1.4010 + ind = PRN(balance, narenas_2pow); 1.4011 + if (arenas[ind] != NULL) { 1.4012 +#ifdef MOZ_MEMORY_WINDOWS 1.4013 + TlsSetValue(tlsIndex, arenas[ind]); 1.4014 +#else 1.4015 + arenas_map = arenas[ind]; 1.4016 +#endif 1.4017 + } else { 1.4018 + malloc_spin_lock(&arenas_lock); 1.4019 + if (arenas[ind] != NULL) { 1.4020 +#ifdef MOZ_MEMORY_WINDOWS 1.4021 + TlsSetValue(tlsIndex, arenas[ind]); 1.4022 +#else 1.4023 + arenas_map = arenas[ind]; 1.4024 +#endif 1.4025 + } else { 1.4026 +#ifdef MOZ_MEMORY_WINDOWS 1.4027 + TlsSetValue(tlsIndex, arenas_extend(ind)); 1.4028 +#else 1.4029 + arenas_map = arenas_extend(ind); 1.4030 +#endif 1.4031 + } 1.4032 + malloc_spin_unlock(&arenas_lock); 1.4033 + } 1.4034 +} 1.4035 +#endif 1.4036 + 1.4037 +static inline void * 1.4038 +arena_malloc_small(arena_t *arena, size_t size, bool zero) 1.4039 +{ 1.4040 + void *ret; 1.4041 + arena_bin_t *bin; 1.4042 + arena_run_t *run; 1.4043 + 1.4044 + if (size < small_min) { 1.4045 + /* Tiny. */ 1.4046 + size = pow2_ceil(size); 1.4047 + bin = &arena->bins[ffs((int)(size >> (TINY_MIN_2POW + 1.4048 + 1)))]; 1.4049 +#if (!defined(NDEBUG) || defined(MALLOC_STATS)) 1.4050 + /* 1.4051 + * Bin calculation is always correct, but we may need 1.4052 + * to fix size for the purposes of assertions and/or 1.4053 + * stats accuracy. 1.4054 + */ 1.4055 + if (size < (1U << TINY_MIN_2POW)) 1.4056 + size = (1U << TINY_MIN_2POW); 1.4057 +#endif 1.4058 + } else if (size <= small_max) { 1.4059 + /* Quantum-spaced. */ 1.4060 + size = QUANTUM_CEILING(size); 1.4061 + bin = &arena->bins[ntbins + (size >> opt_quantum_2pow) 1.4062 + - 1]; 1.4063 + } else { 1.4064 + /* Sub-page. */ 1.4065 + size = pow2_ceil(size); 1.4066 + bin = &arena->bins[ntbins + nqbins 1.4067 + + (ffs((int)(size >> opt_small_max_2pow)) - 2)]; 1.4068 + } 1.4069 + RELEASE_ASSERT(size == bin->reg_size); 1.4070 + 1.4071 +#ifdef MALLOC_BALANCE 1.4072 + arena_lock_balance(arena); 1.4073 +#else 1.4074 + malloc_spin_lock(&arena->lock); 1.4075 +#endif 1.4076 + if ((run = bin->runcur) != NULL && run->nfree > 0) 1.4077 + ret = arena_bin_malloc_easy(arena, bin, run); 1.4078 + else 1.4079 + ret = arena_bin_malloc_hard(arena, bin); 1.4080 + 1.4081 + if (ret == NULL) { 1.4082 + malloc_spin_unlock(&arena->lock); 1.4083 + return (NULL); 1.4084 + } 1.4085 + 1.4086 +#ifdef MALLOC_STATS 1.4087 + bin->stats.nrequests++; 1.4088 + arena->stats.nmalloc_small++; 1.4089 + arena->stats.allocated_small += size; 1.4090 +#endif 1.4091 + malloc_spin_unlock(&arena->lock); 1.4092 + 1.4093 + VALGRIND_MALLOCLIKE_BLOCK(ret, size, 0, zero); 1.4094 + if (zero == false) { 1.4095 +#ifdef MALLOC_FILL 1.4096 + if (opt_junk) 1.4097 + memset(ret, 0xa5, size); 1.4098 + else if (opt_zero) 1.4099 + memset(ret, 0, size); 1.4100 +#endif 1.4101 + } else 1.4102 + memset(ret, 0, size); 1.4103 + 1.4104 + return (ret); 1.4105 +} 1.4106 + 1.4107 +static void * 1.4108 +arena_malloc_large(arena_t *arena, size_t size, bool zero) 1.4109 +{ 1.4110 + void *ret; 1.4111 + 1.4112 + /* Large allocation. */ 1.4113 + size = PAGE_CEILING(size); 1.4114 +#ifdef MALLOC_BALANCE 1.4115 + arena_lock_balance(arena); 1.4116 +#else 1.4117 + malloc_spin_lock(&arena->lock); 1.4118 +#endif 1.4119 + ret = (void *)arena_run_alloc(arena, NULL, size, true, zero); 1.4120 + if (ret == NULL) { 1.4121 + malloc_spin_unlock(&arena->lock); 1.4122 + return (NULL); 1.4123 + } 1.4124 +#ifdef MALLOC_STATS 1.4125 + arena->stats.nmalloc_large++; 1.4126 + arena->stats.allocated_large += size; 1.4127 +#endif 1.4128 + malloc_spin_unlock(&arena->lock); 1.4129 + 1.4130 + VALGRIND_MALLOCLIKE_BLOCK(ret, size, 0, zero); 1.4131 + if (zero == false) { 1.4132 +#ifdef MALLOC_FILL 1.4133 + if (opt_junk) 1.4134 + memset(ret, 0xa5, size); 1.4135 + else if (opt_zero) 1.4136 + memset(ret, 0, size); 1.4137 +#endif 1.4138 + } 1.4139 + 1.4140 + return (ret); 1.4141 +} 1.4142 + 1.4143 +static inline void * 1.4144 +arena_malloc(arena_t *arena, size_t size, bool zero) 1.4145 +{ 1.4146 + 1.4147 + assert(arena != NULL); 1.4148 + RELEASE_ASSERT(arena->magic == ARENA_MAGIC); 1.4149 + assert(size != 0); 1.4150 + assert(QUANTUM_CEILING(size) <= arena_maxclass); 1.4151 + 1.4152 + if (size <= bin_maxclass) { 1.4153 + return (arena_malloc_small(arena, size, zero)); 1.4154 + } else 1.4155 + return (arena_malloc_large(arena, size, zero)); 1.4156 +} 1.4157 + 1.4158 +static inline void * 1.4159 +imalloc(size_t size) 1.4160 +{ 1.4161 + 1.4162 + assert(size != 0); 1.4163 + 1.4164 + if (size <= arena_maxclass) 1.4165 + return (arena_malloc(choose_arena(), size, false)); 1.4166 + else 1.4167 + return (huge_malloc(size, false)); 1.4168 +} 1.4169 + 1.4170 +static inline void * 1.4171 +icalloc(size_t size) 1.4172 +{ 1.4173 + 1.4174 + if (size <= arena_maxclass) 1.4175 + return (arena_malloc(choose_arena(), size, true)); 1.4176 + else 1.4177 + return (huge_malloc(size, true)); 1.4178 +} 1.4179 + 1.4180 +/* Only handles large allocations that require more than page alignment. */ 1.4181 +static void * 1.4182 +arena_palloc(arena_t *arena, size_t alignment, size_t size, size_t alloc_size) 1.4183 +{ 1.4184 + void *ret; 1.4185 + size_t offset; 1.4186 + arena_chunk_t *chunk; 1.4187 + 1.4188 + assert((size & pagesize_mask) == 0); 1.4189 + assert((alignment & pagesize_mask) == 0); 1.4190 + 1.4191 +#ifdef MALLOC_BALANCE 1.4192 + arena_lock_balance(arena); 1.4193 +#else 1.4194 + malloc_spin_lock(&arena->lock); 1.4195 +#endif 1.4196 + ret = (void *)arena_run_alloc(arena, NULL, alloc_size, true, false); 1.4197 + if (ret == NULL) { 1.4198 + malloc_spin_unlock(&arena->lock); 1.4199 + return (NULL); 1.4200 + } 1.4201 + 1.4202 + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ret); 1.4203 + 1.4204 + offset = (uintptr_t)ret & (alignment - 1); 1.4205 + assert((offset & pagesize_mask) == 0); 1.4206 + assert(offset < alloc_size); 1.4207 + if (offset == 0) 1.4208 + arena_run_trim_tail(arena, chunk, (arena_run_t*)ret, alloc_size, size, false); 1.4209 + else { 1.4210 + size_t leadsize, trailsize; 1.4211 + 1.4212 + leadsize = alignment - offset; 1.4213 + if (leadsize > 0) { 1.4214 + arena_run_trim_head(arena, chunk, (arena_run_t*)ret, alloc_size, 1.4215 + alloc_size - leadsize); 1.4216 + ret = (void *)((uintptr_t)ret + leadsize); 1.4217 + } 1.4218 + 1.4219 + trailsize = alloc_size - leadsize - size; 1.4220 + if (trailsize != 0) { 1.4221 + /* Trim trailing space. */ 1.4222 + assert(trailsize < alloc_size); 1.4223 + arena_run_trim_tail(arena, chunk, (arena_run_t*)ret, size + trailsize, 1.4224 + size, false); 1.4225 + } 1.4226 + } 1.4227 + 1.4228 +#ifdef MALLOC_STATS 1.4229 + arena->stats.nmalloc_large++; 1.4230 + arena->stats.allocated_large += size; 1.4231 +#endif 1.4232 + malloc_spin_unlock(&arena->lock); 1.4233 + 1.4234 + VALGRIND_MALLOCLIKE_BLOCK(ret, size, 0, false); 1.4235 +#ifdef MALLOC_FILL 1.4236 + if (opt_junk) 1.4237 + memset(ret, 0xa5, size); 1.4238 + else if (opt_zero) 1.4239 + memset(ret, 0, size); 1.4240 +#endif 1.4241 + return (ret); 1.4242 +} 1.4243 + 1.4244 +static inline void * 1.4245 +ipalloc(size_t alignment, size_t size) 1.4246 +{ 1.4247 + void *ret; 1.4248 + size_t ceil_size; 1.4249 + 1.4250 + /* 1.4251 + * Round size up to the nearest multiple of alignment. 1.4252 + * 1.4253 + * This done, we can take advantage of the fact that for each small 1.4254 + * size class, every object is aligned at the smallest power of two 1.4255 + * that is non-zero in the base two representation of the size. For 1.4256 + * example: 1.4257 + * 1.4258 + * Size | Base 2 | Minimum alignment 1.4259 + * -----+----------+------------------ 1.4260 + * 96 | 1100000 | 32 1.4261 + * 144 | 10100000 | 32 1.4262 + * 192 | 11000000 | 64 1.4263 + * 1.4264 + * Depending on runtime settings, it is possible that arena_malloc() 1.4265 + * will further round up to a power of two, but that never causes 1.4266 + * correctness issues. 1.4267 + */ 1.4268 + ceil_size = (size + (alignment - 1)) & (-alignment); 1.4269 + /* 1.4270 + * (ceil_size < size) protects against the combination of maximal 1.4271 + * alignment and size greater than maximal alignment. 1.4272 + */ 1.4273 + if (ceil_size < size) { 1.4274 + /* size_t overflow. */ 1.4275 + return (NULL); 1.4276 + } 1.4277 + 1.4278 + if (ceil_size <= pagesize || (alignment <= pagesize 1.4279 + && ceil_size <= arena_maxclass)) 1.4280 + ret = arena_malloc(choose_arena(), ceil_size, false); 1.4281 + else { 1.4282 + size_t run_size; 1.4283 + 1.4284 + /* 1.4285 + * We can't achieve sub-page alignment, so round up alignment 1.4286 + * permanently; it makes later calculations simpler. 1.4287 + */ 1.4288 + alignment = PAGE_CEILING(alignment); 1.4289 + ceil_size = PAGE_CEILING(size); 1.4290 + /* 1.4291 + * (ceil_size < size) protects against very large sizes within 1.4292 + * pagesize of SIZE_T_MAX. 1.4293 + * 1.4294 + * (ceil_size + alignment < ceil_size) protects against the 1.4295 + * combination of maximal alignment and ceil_size large enough 1.4296 + * to cause overflow. This is similar to the first overflow 1.4297 + * check above, but it needs to be repeated due to the new 1.4298 + * ceil_size value, which may now be *equal* to maximal 1.4299 + * alignment, whereas before we only detected overflow if the 1.4300 + * original size was *greater* than maximal alignment. 1.4301 + */ 1.4302 + if (ceil_size < size || ceil_size + alignment < ceil_size) { 1.4303 + /* size_t overflow. */ 1.4304 + return (NULL); 1.4305 + } 1.4306 + 1.4307 + /* 1.4308 + * Calculate the size of the over-size run that arena_palloc() 1.4309 + * would need to allocate in order to guarantee the alignment. 1.4310 + */ 1.4311 + if (ceil_size >= alignment) 1.4312 + run_size = ceil_size + alignment - pagesize; 1.4313 + else { 1.4314 + /* 1.4315 + * It is possible that (alignment << 1) will cause 1.4316 + * overflow, but it doesn't matter because we also 1.4317 + * subtract pagesize, which in the case of overflow 1.4318 + * leaves us with a very large run_size. That causes 1.4319 + * the first conditional below to fail, which means 1.4320 + * that the bogus run_size value never gets used for 1.4321 + * anything important. 1.4322 + */ 1.4323 + run_size = (alignment << 1) - pagesize; 1.4324 + } 1.4325 + 1.4326 + if (run_size <= arena_maxclass) { 1.4327 + ret = arena_palloc(choose_arena(), alignment, ceil_size, 1.4328 + run_size); 1.4329 + } else if (alignment <= chunksize) 1.4330 + ret = huge_malloc(ceil_size, false); 1.4331 + else 1.4332 + ret = huge_palloc(alignment, ceil_size); 1.4333 + } 1.4334 + 1.4335 + assert(((uintptr_t)ret & (alignment - 1)) == 0); 1.4336 + return (ret); 1.4337 +} 1.4338 + 1.4339 +/* Return the size of the allocation pointed to by ptr. */ 1.4340 +static size_t 1.4341 +arena_salloc(const void *ptr) 1.4342 +{ 1.4343 + size_t ret; 1.4344 + arena_chunk_t *chunk; 1.4345 + size_t pageind, mapbits; 1.4346 + 1.4347 + assert(ptr != NULL); 1.4348 + assert(CHUNK_ADDR2BASE(ptr) != ptr); 1.4349 + 1.4350 + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); 1.4351 + pageind = (((uintptr_t)ptr - (uintptr_t)chunk) >> pagesize_2pow); 1.4352 + mapbits = chunk->map[pageind].bits; 1.4353 + RELEASE_ASSERT((mapbits & CHUNK_MAP_ALLOCATED) != 0); 1.4354 + if ((mapbits & CHUNK_MAP_LARGE) == 0) { 1.4355 + arena_run_t *run = (arena_run_t *)(mapbits & ~pagesize_mask); 1.4356 + RELEASE_ASSERT(run->magic == ARENA_RUN_MAGIC); 1.4357 + ret = run->bin->reg_size; 1.4358 + } else { 1.4359 + ret = mapbits & ~pagesize_mask; 1.4360 + RELEASE_ASSERT(ret != 0); 1.4361 + } 1.4362 + 1.4363 + return (ret); 1.4364 +} 1.4365 + 1.4366 +#if (defined(MALLOC_VALIDATE) || defined(MOZ_MEMORY_DARWIN)) 1.4367 +/* 1.4368 + * Validate ptr before assuming that it points to an allocation. Currently, 1.4369 + * the following validation is performed: 1.4370 + * 1.4371 + * + Check that ptr is not NULL. 1.4372 + * 1.4373 + * + Check that ptr lies within a mapped chunk. 1.4374 + */ 1.4375 +static inline size_t 1.4376 +isalloc_validate(const void *ptr) 1.4377 +{ 1.4378 + arena_chunk_t *chunk; 1.4379 + 1.4380 + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); 1.4381 + if (chunk == NULL) 1.4382 + return (0); 1.4383 + 1.4384 + if (malloc_rtree_get(chunk_rtree, (uintptr_t)chunk) == NULL) 1.4385 + return (0); 1.4386 + 1.4387 + if (chunk != ptr) { 1.4388 + RELEASE_ASSERT(chunk->arena->magic == ARENA_MAGIC); 1.4389 + return (arena_salloc(ptr)); 1.4390 + } else { 1.4391 + size_t ret; 1.4392 + extent_node_t *node; 1.4393 + extent_node_t key; 1.4394 + 1.4395 + /* Chunk. */ 1.4396 + key.addr = (void *)chunk; 1.4397 + malloc_mutex_lock(&huge_mtx); 1.4398 + node = extent_tree_ad_search(&huge, &key); 1.4399 + if (node != NULL) 1.4400 + ret = node->size; 1.4401 + else 1.4402 + ret = 0; 1.4403 + malloc_mutex_unlock(&huge_mtx); 1.4404 + return (ret); 1.4405 + } 1.4406 +} 1.4407 +#endif 1.4408 + 1.4409 +static inline size_t 1.4410 +isalloc(const void *ptr) 1.4411 +{ 1.4412 + size_t ret; 1.4413 + arena_chunk_t *chunk; 1.4414 + 1.4415 + assert(ptr != NULL); 1.4416 + 1.4417 + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); 1.4418 + if (chunk != ptr) { 1.4419 + /* Region. */ 1.4420 + assert(chunk->arena->magic == ARENA_MAGIC); 1.4421 + 1.4422 + ret = arena_salloc(ptr); 1.4423 + } else { 1.4424 + extent_node_t *node, key; 1.4425 + 1.4426 + /* Chunk (huge allocation). */ 1.4427 + 1.4428 + malloc_mutex_lock(&huge_mtx); 1.4429 + 1.4430 + /* Extract from tree of huge allocations. */ 1.4431 + key.addr = __DECONST(void *, ptr); 1.4432 + node = extent_tree_ad_search(&huge, &key); 1.4433 + RELEASE_ASSERT(node != NULL); 1.4434 + 1.4435 + ret = node->size; 1.4436 + 1.4437 + malloc_mutex_unlock(&huge_mtx); 1.4438 + } 1.4439 + 1.4440 + return (ret); 1.4441 +} 1.4442 + 1.4443 +static inline void 1.4444 +arena_dalloc_small(arena_t *arena, arena_chunk_t *chunk, void *ptr, 1.4445 + arena_chunk_map_t *mapelm) 1.4446 +{ 1.4447 + arena_run_t *run; 1.4448 + arena_bin_t *bin; 1.4449 + size_t size; 1.4450 + 1.4451 + run = (arena_run_t *)(mapelm->bits & ~pagesize_mask); 1.4452 + RELEASE_ASSERT(run->magic == ARENA_RUN_MAGIC); 1.4453 + bin = run->bin; 1.4454 + size = bin->reg_size; 1.4455 + 1.4456 +#ifdef MALLOC_FILL 1.4457 + if (opt_poison) 1.4458 + memset(ptr, 0x5a, size); 1.4459 +#endif 1.4460 + 1.4461 + arena_run_reg_dalloc(run, bin, ptr, size); 1.4462 + run->nfree++; 1.4463 + 1.4464 + if (run->nfree == bin->nregs) { 1.4465 + /* Deallocate run. */ 1.4466 + if (run == bin->runcur) 1.4467 + bin->runcur = NULL; 1.4468 + else if (bin->nregs != 1) { 1.4469 + size_t run_pageind = (((uintptr_t)run - 1.4470 + (uintptr_t)chunk)) >> pagesize_2pow; 1.4471 + arena_chunk_map_t *run_mapelm = 1.4472 + &chunk->map[run_pageind]; 1.4473 + /* 1.4474 + * This block's conditional is necessary because if the 1.4475 + * run only contains one region, then it never gets 1.4476 + * inserted into the non-full runs tree. 1.4477 + */ 1.4478 + RELEASE_ASSERT(arena_run_tree_search(&bin->runs, run_mapelm) == 1.4479 + run_mapelm); 1.4480 + arena_run_tree_remove(&bin->runs, run_mapelm); 1.4481 + } 1.4482 +#if defined(MALLOC_DEBUG) || defined(MOZ_JEMALLOC_HARD_ASSERTS) 1.4483 + run->magic = 0; 1.4484 +#endif 1.4485 + VALGRIND_FREELIKE_BLOCK(run, 0); 1.4486 + arena_run_dalloc(arena, run, true); 1.4487 +#ifdef MALLOC_STATS 1.4488 + bin->stats.curruns--; 1.4489 +#endif 1.4490 + } else if (run->nfree == 1 && run != bin->runcur) { 1.4491 + /* 1.4492 + * Make sure that bin->runcur always refers to the lowest 1.4493 + * non-full run, if one exists. 1.4494 + */ 1.4495 + if (bin->runcur == NULL) 1.4496 + bin->runcur = run; 1.4497 + else if ((uintptr_t)run < (uintptr_t)bin->runcur) { 1.4498 + /* Switch runcur. */ 1.4499 + if (bin->runcur->nfree > 0) { 1.4500 + arena_chunk_t *runcur_chunk = 1.4501 + (arena_chunk_t*)CHUNK_ADDR2BASE(bin->runcur); 1.4502 + size_t runcur_pageind = 1.4503 + (((uintptr_t)bin->runcur - 1.4504 + (uintptr_t)runcur_chunk)) >> pagesize_2pow; 1.4505 + arena_chunk_map_t *runcur_mapelm = 1.4506 + &runcur_chunk->map[runcur_pageind]; 1.4507 + 1.4508 + /* Insert runcur. */ 1.4509 + RELEASE_ASSERT(arena_run_tree_search(&bin->runs, 1.4510 + runcur_mapelm) == NULL); 1.4511 + arena_run_tree_insert(&bin->runs, 1.4512 + runcur_mapelm); 1.4513 + } 1.4514 + bin->runcur = run; 1.4515 + } else { 1.4516 + size_t run_pageind = (((uintptr_t)run - 1.4517 + (uintptr_t)chunk)) >> pagesize_2pow; 1.4518 + arena_chunk_map_t *run_mapelm = 1.4519 + &chunk->map[run_pageind]; 1.4520 + 1.4521 + RELEASE_ASSERT(arena_run_tree_search(&bin->runs, run_mapelm) == 1.4522 + NULL); 1.4523 + arena_run_tree_insert(&bin->runs, run_mapelm); 1.4524 + } 1.4525 + } 1.4526 +#ifdef MALLOC_STATS 1.4527 + arena->stats.allocated_small -= size; 1.4528 + arena->stats.ndalloc_small++; 1.4529 +#endif 1.4530 +} 1.4531 + 1.4532 +static void 1.4533 +arena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, void *ptr) 1.4534 +{ 1.4535 + /* Large allocation. */ 1.4536 + malloc_spin_lock(&arena->lock); 1.4537 + 1.4538 +#ifdef MALLOC_FILL 1.4539 +#ifndef MALLOC_STATS 1.4540 + if (opt_poison) 1.4541 +#endif 1.4542 +#endif 1.4543 + { 1.4544 + size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> 1.4545 + pagesize_2pow; 1.4546 + size_t size = chunk->map[pageind].bits & ~pagesize_mask; 1.4547 + 1.4548 +#ifdef MALLOC_FILL 1.4549 +#ifdef MALLOC_STATS 1.4550 + if (opt_poison) 1.4551 +#endif 1.4552 + memset(ptr, 0x5a, size); 1.4553 +#endif 1.4554 +#ifdef MALLOC_STATS 1.4555 + arena->stats.allocated_large -= size; 1.4556 +#endif 1.4557 + } 1.4558 +#ifdef MALLOC_STATS 1.4559 + arena->stats.ndalloc_large++; 1.4560 +#endif 1.4561 + 1.4562 + arena_run_dalloc(arena, (arena_run_t *)ptr, true); 1.4563 + malloc_spin_unlock(&arena->lock); 1.4564 +} 1.4565 + 1.4566 +static inline void 1.4567 +arena_dalloc(void *ptr, size_t offset) 1.4568 +{ 1.4569 + arena_chunk_t *chunk; 1.4570 + arena_t *arena; 1.4571 + size_t pageind; 1.4572 + arena_chunk_map_t *mapelm; 1.4573 + 1.4574 + assert(ptr != NULL); 1.4575 + assert(offset != 0); 1.4576 + assert(CHUNK_ADDR2OFFSET(ptr) == offset); 1.4577 + 1.4578 + chunk = (arena_chunk_t *) ((uintptr_t)ptr - offset); 1.4579 + arena = chunk->arena; 1.4580 + assert(arena != NULL); 1.4581 + RELEASE_ASSERT(arena->magic == ARENA_MAGIC); 1.4582 + 1.4583 + pageind = offset >> pagesize_2pow; 1.4584 + mapelm = &chunk->map[pageind]; 1.4585 + RELEASE_ASSERT((mapelm->bits & CHUNK_MAP_ALLOCATED) != 0); 1.4586 + if ((mapelm->bits & CHUNK_MAP_LARGE) == 0) { 1.4587 + /* Small allocation. */ 1.4588 + malloc_spin_lock(&arena->lock); 1.4589 + arena_dalloc_small(arena, chunk, ptr, mapelm); 1.4590 + malloc_spin_unlock(&arena->lock); 1.4591 + } else 1.4592 + arena_dalloc_large(arena, chunk, ptr); 1.4593 + VALGRIND_FREELIKE_BLOCK(ptr, 0); 1.4594 +} 1.4595 + 1.4596 +static inline void 1.4597 +idalloc(void *ptr) 1.4598 +{ 1.4599 + size_t offset; 1.4600 + 1.4601 + assert(ptr != NULL); 1.4602 + 1.4603 + offset = CHUNK_ADDR2OFFSET(ptr); 1.4604 + if (offset != 0) 1.4605 + arena_dalloc(ptr, offset); 1.4606 + else 1.4607 + huge_dalloc(ptr); 1.4608 +} 1.4609 + 1.4610 +static void 1.4611 +arena_ralloc_large_shrink(arena_t *arena, arena_chunk_t *chunk, void *ptr, 1.4612 + size_t size, size_t oldsize) 1.4613 +{ 1.4614 + 1.4615 + assert(size < oldsize); 1.4616 + 1.4617 + /* 1.4618 + * Shrink the run, and make trailing pages available for other 1.4619 + * allocations. 1.4620 + */ 1.4621 +#ifdef MALLOC_BALANCE 1.4622 + arena_lock_balance(arena); 1.4623 +#else 1.4624 + malloc_spin_lock(&arena->lock); 1.4625 +#endif 1.4626 + arena_run_trim_tail(arena, chunk, (arena_run_t *)ptr, oldsize, size, 1.4627 + true); 1.4628 +#ifdef MALLOC_STATS 1.4629 + arena->stats.allocated_large -= oldsize - size; 1.4630 +#endif 1.4631 + malloc_spin_unlock(&arena->lock); 1.4632 +} 1.4633 + 1.4634 +static bool 1.4635 +arena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk, void *ptr, 1.4636 + size_t size, size_t oldsize) 1.4637 +{ 1.4638 + size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> pagesize_2pow; 1.4639 + size_t npages = oldsize >> pagesize_2pow; 1.4640 + 1.4641 + RELEASE_ASSERT(oldsize == (chunk->map[pageind].bits & ~pagesize_mask)); 1.4642 + 1.4643 + /* Try to extend the run. */ 1.4644 + assert(size > oldsize); 1.4645 +#ifdef MALLOC_BALANCE 1.4646 + arena_lock_balance(arena); 1.4647 +#else 1.4648 + malloc_spin_lock(&arena->lock); 1.4649 +#endif 1.4650 + if (pageind + npages < chunk_npages && (chunk->map[pageind+npages].bits 1.4651 + & CHUNK_MAP_ALLOCATED) == 0 && (chunk->map[pageind+npages].bits & 1.4652 + ~pagesize_mask) >= size - oldsize) { 1.4653 + /* 1.4654 + * The next run is available and sufficiently large. Split the 1.4655 + * following run, then merge the first part with the existing 1.4656 + * allocation. 1.4657 + */ 1.4658 + arena_run_split(arena, (arena_run_t *)((uintptr_t)chunk + 1.4659 + ((pageind+npages) << pagesize_2pow)), size - oldsize, true, 1.4660 + false); 1.4661 + 1.4662 + chunk->map[pageind].bits = size | CHUNK_MAP_LARGE | 1.4663 + CHUNK_MAP_ALLOCATED; 1.4664 + chunk->map[pageind+npages].bits = CHUNK_MAP_LARGE | 1.4665 + CHUNK_MAP_ALLOCATED; 1.4666 + 1.4667 +#ifdef MALLOC_STATS 1.4668 + arena->stats.allocated_large += size - oldsize; 1.4669 +#endif 1.4670 + malloc_spin_unlock(&arena->lock); 1.4671 + return (false); 1.4672 + } 1.4673 + malloc_spin_unlock(&arena->lock); 1.4674 + 1.4675 + return (true); 1.4676 +} 1.4677 + 1.4678 +/* 1.4679 + * Try to resize a large allocation, in order to avoid copying. This will 1.4680 + * always fail if growing an object, and the following run is already in use. 1.4681 + */ 1.4682 +static bool 1.4683 +arena_ralloc_large(void *ptr, size_t size, size_t oldsize) 1.4684 +{ 1.4685 + size_t psize; 1.4686 + 1.4687 + psize = PAGE_CEILING(size); 1.4688 + if (psize == oldsize) { 1.4689 + /* Same size class. */ 1.4690 +#ifdef MALLOC_FILL 1.4691 + if (opt_poison && size < oldsize) { 1.4692 + memset((void *)((uintptr_t)ptr + size), 0x5a, oldsize - 1.4693 + size); 1.4694 + } 1.4695 +#endif 1.4696 + return (false); 1.4697 + } else { 1.4698 + arena_chunk_t *chunk; 1.4699 + arena_t *arena; 1.4700 + 1.4701 + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); 1.4702 + arena = chunk->arena; 1.4703 + RELEASE_ASSERT(arena->magic == ARENA_MAGIC); 1.4704 + 1.4705 + if (psize < oldsize) { 1.4706 +#ifdef MALLOC_FILL 1.4707 + /* Fill before shrinking in order avoid a race. */ 1.4708 + if (opt_poison) { 1.4709 + memset((void *)((uintptr_t)ptr + size), 0x5a, 1.4710 + oldsize - size); 1.4711 + } 1.4712 +#endif 1.4713 + arena_ralloc_large_shrink(arena, chunk, ptr, psize, 1.4714 + oldsize); 1.4715 + return (false); 1.4716 + } else { 1.4717 + bool ret = arena_ralloc_large_grow(arena, chunk, ptr, 1.4718 + psize, oldsize); 1.4719 +#ifdef MALLOC_FILL 1.4720 + if (ret == false && opt_zero) { 1.4721 + memset((void *)((uintptr_t)ptr + oldsize), 0, 1.4722 + size - oldsize); 1.4723 + } 1.4724 +#endif 1.4725 + return (ret); 1.4726 + } 1.4727 + } 1.4728 +} 1.4729 + 1.4730 +static void * 1.4731 +arena_ralloc(void *ptr, size_t size, size_t oldsize) 1.4732 +{ 1.4733 + void *ret; 1.4734 + size_t copysize; 1.4735 + 1.4736 + /* Try to avoid moving the allocation. */ 1.4737 + if (size < small_min) { 1.4738 + if (oldsize < small_min && 1.4739 + ffs((int)(pow2_ceil(size) >> (TINY_MIN_2POW + 1))) 1.4740 + == ffs((int)(pow2_ceil(oldsize) >> (TINY_MIN_2POW + 1)))) 1.4741 + goto IN_PLACE; /* Same size class. */ 1.4742 + } else if (size <= small_max) { 1.4743 + if (oldsize >= small_min && oldsize <= small_max && 1.4744 + (QUANTUM_CEILING(size) >> opt_quantum_2pow) 1.4745 + == (QUANTUM_CEILING(oldsize) >> opt_quantum_2pow)) 1.4746 + goto IN_PLACE; /* Same size class. */ 1.4747 + } else if (size <= bin_maxclass) { 1.4748 + if (oldsize > small_max && oldsize <= bin_maxclass && 1.4749 + pow2_ceil(size) == pow2_ceil(oldsize)) 1.4750 + goto IN_PLACE; /* Same size class. */ 1.4751 + } else if (oldsize > bin_maxclass && oldsize <= arena_maxclass) { 1.4752 + assert(size > bin_maxclass); 1.4753 + if (arena_ralloc_large(ptr, size, oldsize) == false) 1.4754 + return (ptr); 1.4755 + } 1.4756 + 1.4757 + /* 1.4758 + * If we get here, then size and oldsize are different enough that we 1.4759 + * need to move the object. In that case, fall back to allocating new 1.4760 + * space and copying. 1.4761 + */ 1.4762 + ret = arena_malloc(choose_arena(), size, false); 1.4763 + if (ret == NULL) 1.4764 + return (NULL); 1.4765 + 1.4766 + /* Junk/zero-filling were already done by arena_malloc(). */ 1.4767 + copysize = (size < oldsize) ? size : oldsize; 1.4768 +#ifdef VM_COPY_MIN 1.4769 + if (copysize >= VM_COPY_MIN) 1.4770 + pages_copy(ret, ptr, copysize); 1.4771 + else 1.4772 +#endif 1.4773 + memcpy(ret, ptr, copysize); 1.4774 + idalloc(ptr); 1.4775 + return (ret); 1.4776 +IN_PLACE: 1.4777 +#ifdef MALLOC_FILL 1.4778 + if (opt_poison && size < oldsize) 1.4779 + memset((void *)((uintptr_t)ptr + size), 0x5a, oldsize - size); 1.4780 + else if (opt_zero && size > oldsize) 1.4781 + memset((void *)((uintptr_t)ptr + oldsize), 0, size - oldsize); 1.4782 +#endif 1.4783 + return (ptr); 1.4784 +} 1.4785 + 1.4786 +static inline void * 1.4787 +iralloc(void *ptr, size_t size) 1.4788 +{ 1.4789 + size_t oldsize; 1.4790 + 1.4791 + assert(ptr != NULL); 1.4792 + assert(size != 0); 1.4793 + 1.4794 + oldsize = isalloc(ptr); 1.4795 + 1.4796 +#ifndef MALLOC_VALGRIND 1.4797 + if (size <= arena_maxclass) 1.4798 + return (arena_ralloc(ptr, size, oldsize)); 1.4799 + else 1.4800 + return (huge_ralloc(ptr, size, oldsize)); 1.4801 +#else 1.4802 + /* 1.4803 + * Valgrind does not provide a public interface for modifying an 1.4804 + * existing allocation, so use malloc/memcpy/free instead. 1.4805 + */ 1.4806 + { 1.4807 + void *ret = imalloc(size); 1.4808 + if (ret != NULL) { 1.4809 + if (oldsize < size) 1.4810 + memcpy(ret, ptr, oldsize); 1.4811 + else 1.4812 + memcpy(ret, ptr, size); 1.4813 + idalloc(ptr); 1.4814 + } 1.4815 + return (ret); 1.4816 + } 1.4817 +#endif 1.4818 +} 1.4819 + 1.4820 +static bool 1.4821 +arena_new(arena_t *arena) 1.4822 +{ 1.4823 + unsigned i; 1.4824 + arena_bin_t *bin; 1.4825 + size_t pow2_size, prev_run_size; 1.4826 + 1.4827 + if (malloc_spin_init(&arena->lock)) 1.4828 + return (true); 1.4829 + 1.4830 +#ifdef MALLOC_STATS 1.4831 + memset(&arena->stats, 0, sizeof(arena_stats_t)); 1.4832 +#endif 1.4833 + 1.4834 + /* Initialize chunks. */ 1.4835 + arena_chunk_tree_dirty_new(&arena->chunks_dirty); 1.4836 +#ifdef MALLOC_DOUBLE_PURGE 1.4837 + LinkedList_Init(&arena->chunks_madvised); 1.4838 +#endif 1.4839 + arena->spare = NULL; 1.4840 + 1.4841 + arena->ndirty = 0; 1.4842 + 1.4843 + arena_avail_tree_new(&arena->runs_avail); 1.4844 + 1.4845 +#ifdef MALLOC_BALANCE 1.4846 + arena->contention = 0; 1.4847 +#endif 1.4848 + 1.4849 + /* Initialize bins. */ 1.4850 + prev_run_size = pagesize; 1.4851 + 1.4852 + /* (2^n)-spaced tiny bins. */ 1.4853 + for (i = 0; i < ntbins; i++) { 1.4854 + bin = &arena->bins[i]; 1.4855 + bin->runcur = NULL; 1.4856 + arena_run_tree_new(&bin->runs); 1.4857 + 1.4858 + bin->reg_size = (1U << (TINY_MIN_2POW + i)); 1.4859 + 1.4860 + prev_run_size = arena_bin_run_size_calc(bin, prev_run_size); 1.4861 + 1.4862 +#ifdef MALLOC_STATS 1.4863 + memset(&bin->stats, 0, sizeof(malloc_bin_stats_t)); 1.4864 +#endif 1.4865 + } 1.4866 + 1.4867 + /* Quantum-spaced bins. */ 1.4868 + for (; i < ntbins + nqbins; i++) { 1.4869 + bin = &arena->bins[i]; 1.4870 + bin->runcur = NULL; 1.4871 + arena_run_tree_new(&bin->runs); 1.4872 + 1.4873 + bin->reg_size = quantum * (i - ntbins + 1); 1.4874 + 1.4875 + pow2_size = pow2_ceil(quantum * (i - ntbins + 1)); 1.4876 + prev_run_size = arena_bin_run_size_calc(bin, prev_run_size); 1.4877 + 1.4878 +#ifdef MALLOC_STATS 1.4879 + memset(&bin->stats, 0, sizeof(malloc_bin_stats_t)); 1.4880 +#endif 1.4881 + } 1.4882 + 1.4883 + /* (2^n)-spaced sub-page bins. */ 1.4884 + for (; i < ntbins + nqbins + nsbins; i++) { 1.4885 + bin = &arena->bins[i]; 1.4886 + bin->runcur = NULL; 1.4887 + arena_run_tree_new(&bin->runs); 1.4888 + 1.4889 + bin->reg_size = (small_max << (i - (ntbins + nqbins) + 1)); 1.4890 + 1.4891 + prev_run_size = arena_bin_run_size_calc(bin, prev_run_size); 1.4892 + 1.4893 +#ifdef MALLOC_STATS 1.4894 + memset(&bin->stats, 0, sizeof(malloc_bin_stats_t)); 1.4895 +#endif 1.4896 + } 1.4897 + 1.4898 +#if defined(MALLOC_DEBUG) || defined(MOZ_JEMALLOC_HARD_ASSERTS) 1.4899 + arena->magic = ARENA_MAGIC; 1.4900 +#endif 1.4901 + 1.4902 + return (false); 1.4903 +} 1.4904 + 1.4905 +/* Create a new arena and insert it into the arenas array at index ind. */ 1.4906 +static arena_t * 1.4907 +arenas_extend(unsigned ind) 1.4908 +{ 1.4909 + arena_t *ret; 1.4910 + 1.4911 + /* Allocate enough space for trailing bins. */ 1.4912 + ret = (arena_t *)base_alloc(sizeof(arena_t) 1.4913 + + (sizeof(arena_bin_t) * (ntbins + nqbins + nsbins - 1))); 1.4914 + if (ret != NULL && arena_new(ret) == false) { 1.4915 + arenas[ind] = ret; 1.4916 + return (ret); 1.4917 + } 1.4918 + /* Only reached if there is an OOM error. */ 1.4919 + 1.4920 + /* 1.4921 + * OOM here is quite inconvenient to propagate, since dealing with it 1.4922 + * would require a check for failure in the fast path. Instead, punt 1.4923 + * by using arenas[0]. In practice, this is an extremely unlikely 1.4924 + * failure. 1.4925 + */ 1.4926 + _malloc_message(_getprogname(), 1.4927 + ": (malloc) Error initializing arena\n", "", ""); 1.4928 + if (opt_abort) 1.4929 + abort(); 1.4930 + 1.4931 + return (arenas[0]); 1.4932 +} 1.4933 + 1.4934 +/* 1.4935 + * End arena. 1.4936 + */ 1.4937 +/******************************************************************************/ 1.4938 +/* 1.4939 + * Begin general internal functions. 1.4940 + */ 1.4941 + 1.4942 +static void * 1.4943 +huge_malloc(size_t size, bool zero) 1.4944 +{ 1.4945 + void *ret; 1.4946 + size_t csize; 1.4947 + size_t psize; 1.4948 + extent_node_t *node; 1.4949 + 1.4950 + /* Allocate one or more contiguous chunks for this request. */ 1.4951 + 1.4952 + csize = CHUNK_CEILING(size); 1.4953 + if (csize == 0) { 1.4954 + /* size is large enough to cause size_t wrap-around. */ 1.4955 + return (NULL); 1.4956 + } 1.4957 + 1.4958 + /* Allocate an extent node with which to track the chunk. */ 1.4959 + node = base_node_alloc(); 1.4960 + if (node == NULL) 1.4961 + return (NULL); 1.4962 + 1.4963 + ret = chunk_alloc(csize, zero, true); 1.4964 + if (ret == NULL) { 1.4965 + base_node_dealloc(node); 1.4966 + return (NULL); 1.4967 + } 1.4968 + 1.4969 + /* Insert node into huge. */ 1.4970 + node->addr = ret; 1.4971 + psize = PAGE_CEILING(size); 1.4972 + node->size = psize; 1.4973 + 1.4974 + malloc_mutex_lock(&huge_mtx); 1.4975 + extent_tree_ad_insert(&huge, node); 1.4976 +#ifdef MALLOC_STATS 1.4977 + huge_nmalloc++; 1.4978 + 1.4979 + /* Although we allocated space for csize bytes, we indicate that we've 1.4980 + * allocated only psize bytes. 1.4981 + * 1.4982 + * If DECOMMIT is defined, this is a reasonable thing to do, since 1.4983 + * we'll explicitly decommit the bytes in excess of psize. 1.4984 + * 1.4985 + * If DECOMMIT is not defined, then we're relying on the OS to be lazy 1.4986 + * about how it allocates physical pages to mappings. If we never 1.4987 + * touch the pages in excess of psize, the OS won't allocate a physical 1.4988 + * page, and we won't use more than psize bytes of physical memory. 1.4989 + * 1.4990 + * A correct program will only touch memory in excess of how much it 1.4991 + * requested if it first calls malloc_usable_size and finds out how 1.4992 + * much space it has to play with. But because we set node->size = 1.4993 + * psize above, malloc_usable_size will return psize, not csize, and 1.4994 + * the program will (hopefully) never touch bytes in excess of psize. 1.4995 + * Thus those bytes won't take up space in physical memory, and we can 1.4996 + * reasonably claim we never "allocated" them in the first place. */ 1.4997 + huge_allocated += psize; 1.4998 + huge_mapped += csize; 1.4999 +#endif 1.5000 + malloc_mutex_unlock(&huge_mtx); 1.5001 + 1.5002 +#ifdef MALLOC_DECOMMIT 1.5003 + if (csize - psize > 0) 1.5004 + pages_decommit((void *)((uintptr_t)ret + psize), csize - psize); 1.5005 +#endif 1.5006 + 1.5007 +#ifdef MALLOC_DECOMMIT 1.5008 + VALGRIND_MALLOCLIKE_BLOCK(ret, psize, 0, zero); 1.5009 +#else 1.5010 + VALGRIND_MALLOCLIKE_BLOCK(ret, csize, 0, zero); 1.5011 +#endif 1.5012 + 1.5013 +#ifdef MALLOC_FILL 1.5014 + if (zero == false) { 1.5015 + if (opt_junk) 1.5016 +# ifdef MALLOC_DECOMMIT 1.5017 + memset(ret, 0xa5, psize); 1.5018 +# else 1.5019 + memset(ret, 0xa5, csize); 1.5020 +# endif 1.5021 + else if (opt_zero) 1.5022 +# ifdef MALLOC_DECOMMIT 1.5023 + memset(ret, 0, psize); 1.5024 +# else 1.5025 + memset(ret, 0, csize); 1.5026 +# endif 1.5027 + } 1.5028 +#endif 1.5029 + 1.5030 + return (ret); 1.5031 +} 1.5032 + 1.5033 +/* Only handles large allocations that require more than chunk alignment. */ 1.5034 +static void * 1.5035 +huge_palloc(size_t alignment, size_t size) 1.5036 +{ 1.5037 + void *ret; 1.5038 + size_t alloc_size, chunk_size, offset; 1.5039 + size_t psize; 1.5040 + extent_node_t *node; 1.5041 + int pfd; 1.5042 + 1.5043 + /* 1.5044 + * This allocation requires alignment that is even larger than chunk 1.5045 + * alignment. This means that huge_malloc() isn't good enough. 1.5046 + * 1.5047 + * Allocate almost twice as many chunks as are demanded by the size or 1.5048 + * alignment, in order to assure the alignment can be achieved, then 1.5049 + * unmap leading and trailing chunks. 1.5050 + */ 1.5051 + assert(alignment >= chunksize); 1.5052 + 1.5053 + chunk_size = CHUNK_CEILING(size); 1.5054 + 1.5055 + if (size >= alignment) 1.5056 + alloc_size = chunk_size + alignment - chunksize; 1.5057 + else 1.5058 + alloc_size = (alignment << 1) - chunksize; 1.5059 + 1.5060 + /* Allocate an extent node with which to track the chunk. */ 1.5061 + node = base_node_alloc(); 1.5062 + if (node == NULL) 1.5063 + return (NULL); 1.5064 + 1.5065 + /* 1.5066 + * Windows requires that there be a 1:1 mapping between VM 1.5067 + * allocation/deallocation operations. Therefore, take care here to 1.5068 + * acquire the final result via one mapping operation. 1.5069 + * 1.5070 + * The MALLOC_PAGEFILE code also benefits from this mapping algorithm, 1.5071 + * since it reduces the number of page files. 1.5072 + */ 1.5073 +#ifdef MALLOC_PAGEFILE 1.5074 + if (opt_pagefile) { 1.5075 + pfd = pagefile_init(size); 1.5076 + if (pfd == -1) 1.5077 + return (NULL); 1.5078 + } else 1.5079 +#endif 1.5080 + pfd = -1; 1.5081 +#ifdef JEMALLOC_USES_MAP_ALIGN 1.5082 + ret = pages_map_align(chunk_size, pfd, alignment); 1.5083 +#else 1.5084 + do { 1.5085 + void *over; 1.5086 + 1.5087 + over = chunk_alloc(alloc_size, false, false); 1.5088 + if (over == NULL) { 1.5089 + base_node_dealloc(node); 1.5090 + ret = NULL; 1.5091 + goto RETURN; 1.5092 + } 1.5093 + 1.5094 + offset = (uintptr_t)over & (alignment - 1); 1.5095 + assert((offset & chunksize_mask) == 0); 1.5096 + assert(offset < alloc_size); 1.5097 + ret = (void *)((uintptr_t)over + offset); 1.5098 + chunk_dealloc(over, alloc_size); 1.5099 + ret = pages_map(ret, chunk_size, pfd); 1.5100 + /* 1.5101 + * Failure here indicates a race with another thread, so try 1.5102 + * again. 1.5103 + */ 1.5104 + } while (ret == NULL); 1.5105 +#endif 1.5106 + /* Insert node into huge. */ 1.5107 + node->addr = ret; 1.5108 + psize = PAGE_CEILING(size); 1.5109 + node->size = psize; 1.5110 + 1.5111 + malloc_mutex_lock(&huge_mtx); 1.5112 + extent_tree_ad_insert(&huge, node); 1.5113 +#ifdef MALLOC_STATS 1.5114 + huge_nmalloc++; 1.5115 + /* See note in huge_alloc() for why huge_allocated += psize is correct 1.5116 + * here even when DECOMMIT is not defined. */ 1.5117 + huge_allocated += psize; 1.5118 + huge_mapped += chunk_size; 1.5119 +#endif 1.5120 + malloc_mutex_unlock(&huge_mtx); 1.5121 + 1.5122 +#ifdef MALLOC_DECOMMIT 1.5123 + if (chunk_size - psize > 0) { 1.5124 + pages_decommit((void *)((uintptr_t)ret + psize), 1.5125 + chunk_size - psize); 1.5126 + } 1.5127 +#endif 1.5128 + 1.5129 +#ifdef MALLOC_DECOMMIT 1.5130 + VALGRIND_MALLOCLIKE_BLOCK(ret, psize, 0, false); 1.5131 +#else 1.5132 + VALGRIND_MALLOCLIKE_BLOCK(ret, chunk_size, 0, false); 1.5133 +#endif 1.5134 + 1.5135 +#ifdef MALLOC_FILL 1.5136 + if (opt_junk) 1.5137 +# ifdef MALLOC_DECOMMIT 1.5138 + memset(ret, 0xa5, psize); 1.5139 +# else 1.5140 + memset(ret, 0xa5, chunk_size); 1.5141 +# endif 1.5142 + else if (opt_zero) 1.5143 +# ifdef MALLOC_DECOMMIT 1.5144 + memset(ret, 0, psize); 1.5145 +# else 1.5146 + memset(ret, 0, chunk_size); 1.5147 +# endif 1.5148 +#endif 1.5149 + 1.5150 +RETURN: 1.5151 +#ifdef MALLOC_PAGEFILE 1.5152 + if (pfd != -1) 1.5153 + pagefile_close(pfd); 1.5154 +#endif 1.5155 + return (ret); 1.5156 +} 1.5157 + 1.5158 +static void * 1.5159 +huge_ralloc(void *ptr, size_t size, size_t oldsize) 1.5160 +{ 1.5161 + void *ret; 1.5162 + size_t copysize; 1.5163 + 1.5164 + /* Avoid moving the allocation if the size class would not change. */ 1.5165 + 1.5166 + if (oldsize > arena_maxclass && 1.5167 + CHUNK_CEILING(size) == CHUNK_CEILING(oldsize)) { 1.5168 + size_t psize = PAGE_CEILING(size); 1.5169 +#ifdef MALLOC_FILL 1.5170 + if (opt_poison && size < oldsize) { 1.5171 + memset((void *)((uintptr_t)ptr + size), 0x5a, oldsize 1.5172 + - size); 1.5173 + } 1.5174 +#endif 1.5175 +#ifdef MALLOC_DECOMMIT 1.5176 + if (psize < oldsize) { 1.5177 + extent_node_t *node, key; 1.5178 + 1.5179 + pages_decommit((void *)((uintptr_t)ptr + psize), 1.5180 + oldsize - psize); 1.5181 + 1.5182 + /* Update recorded size. */ 1.5183 + malloc_mutex_lock(&huge_mtx); 1.5184 + key.addr = __DECONST(void *, ptr); 1.5185 + node = extent_tree_ad_search(&huge, &key); 1.5186 + assert(node != NULL); 1.5187 + assert(node->size == oldsize); 1.5188 +# ifdef MALLOC_STATS 1.5189 + huge_allocated -= oldsize - psize; 1.5190 + /* No need to change huge_mapped, because we didn't 1.5191 + * (un)map anything. */ 1.5192 +# endif 1.5193 + node->size = psize; 1.5194 + malloc_mutex_unlock(&huge_mtx); 1.5195 + } else if (psize > oldsize) { 1.5196 + pages_commit((void *)((uintptr_t)ptr + oldsize), 1.5197 + psize - oldsize); 1.5198 + } 1.5199 +#endif 1.5200 + 1.5201 + /* Although we don't have to commit or decommit anything if 1.5202 + * DECOMMIT is not defined and the size class didn't change, we 1.5203 + * do need to update the recorded size if the size increased, 1.5204 + * so malloc_usable_size doesn't return a value smaller than 1.5205 + * what was requested via realloc(). */ 1.5206 + 1.5207 + if (psize > oldsize) { 1.5208 + /* Update recorded size. */ 1.5209 + extent_node_t *node, key; 1.5210 + malloc_mutex_lock(&huge_mtx); 1.5211 + key.addr = __DECONST(void *, ptr); 1.5212 + node = extent_tree_ad_search(&huge, &key); 1.5213 + assert(node != NULL); 1.5214 + assert(node->size == oldsize); 1.5215 +# ifdef MALLOC_STATS 1.5216 + huge_allocated += psize - oldsize; 1.5217 + /* No need to change huge_mapped, because we didn't 1.5218 + * (un)map anything. */ 1.5219 +# endif 1.5220 + node->size = psize; 1.5221 + malloc_mutex_unlock(&huge_mtx); 1.5222 + } 1.5223 + 1.5224 +#ifdef MALLOC_FILL 1.5225 + if (opt_zero && size > oldsize) { 1.5226 + memset((void *)((uintptr_t)ptr + oldsize), 0, size 1.5227 + - oldsize); 1.5228 + } 1.5229 +#endif 1.5230 + return (ptr); 1.5231 + } 1.5232 + 1.5233 + /* 1.5234 + * If we get here, then size and oldsize are different enough that we 1.5235 + * need to use a different size class. In that case, fall back to 1.5236 + * allocating new space and copying. 1.5237 + */ 1.5238 + ret = huge_malloc(size, false); 1.5239 + if (ret == NULL) 1.5240 + return (NULL); 1.5241 + 1.5242 + copysize = (size < oldsize) ? size : oldsize; 1.5243 +#ifdef VM_COPY_MIN 1.5244 + if (copysize >= VM_COPY_MIN) 1.5245 + pages_copy(ret, ptr, copysize); 1.5246 + else 1.5247 +#endif 1.5248 + memcpy(ret, ptr, copysize); 1.5249 + idalloc(ptr); 1.5250 + return (ret); 1.5251 +} 1.5252 + 1.5253 +static void 1.5254 +huge_dalloc(void *ptr) 1.5255 +{ 1.5256 + extent_node_t *node, key; 1.5257 + 1.5258 + malloc_mutex_lock(&huge_mtx); 1.5259 + 1.5260 + /* Extract from tree of huge allocations. */ 1.5261 + key.addr = ptr; 1.5262 + node = extent_tree_ad_search(&huge, &key); 1.5263 + assert(node != NULL); 1.5264 + assert(node->addr == ptr); 1.5265 + extent_tree_ad_remove(&huge, node); 1.5266 + 1.5267 +#ifdef MALLOC_STATS 1.5268 + huge_ndalloc++; 1.5269 + huge_allocated -= node->size; 1.5270 + huge_mapped -= CHUNK_CEILING(node->size); 1.5271 +#endif 1.5272 + 1.5273 + malloc_mutex_unlock(&huge_mtx); 1.5274 + 1.5275 + /* Unmap chunk. */ 1.5276 +#ifdef MALLOC_FILL 1.5277 + if (opt_junk) 1.5278 + memset(node->addr, 0x5a, node->size); 1.5279 +#endif 1.5280 + chunk_dealloc(node->addr, CHUNK_CEILING(node->size)); 1.5281 + VALGRIND_FREELIKE_BLOCK(node->addr, 0); 1.5282 + 1.5283 + base_node_dealloc(node); 1.5284 +} 1.5285 + 1.5286 +#ifndef MOZ_MEMORY_NARENAS_DEFAULT_ONE 1.5287 +#ifdef MOZ_MEMORY_BSD 1.5288 +static inline unsigned 1.5289 +malloc_ncpus(void) 1.5290 +{ 1.5291 + unsigned ret; 1.5292 + int mib[2]; 1.5293 + size_t len; 1.5294 + 1.5295 + mib[0] = CTL_HW; 1.5296 + mib[1] = HW_NCPU; 1.5297 + len = sizeof(ret); 1.5298 + if (sysctl(mib, 2, &ret, &len, (void *) 0, 0) == -1) { 1.5299 + /* Error. */ 1.5300 + return (1); 1.5301 + } 1.5302 + 1.5303 + return (ret); 1.5304 +} 1.5305 +#elif (defined(MOZ_MEMORY_LINUX)) 1.5306 +#include <fcntl.h> 1.5307 + 1.5308 +static inline unsigned 1.5309 +malloc_ncpus(void) 1.5310 +{ 1.5311 + unsigned ret; 1.5312 + int fd, nread, column; 1.5313 + char buf[1024]; 1.5314 + static const char matchstr[] = "processor\t:"; 1.5315 + int i; 1.5316 + 1.5317 + /* 1.5318 + * sysconf(3) would be the preferred method for determining the number 1.5319 + * of CPUs, but it uses malloc internally, which causes untennable 1.5320 + * recursion during malloc initialization. 1.5321 + */ 1.5322 + fd = open("/proc/cpuinfo", O_RDONLY); 1.5323 + if (fd == -1) 1.5324 + return (1); /* Error. */ 1.5325 + /* 1.5326 + * Count the number of occurrences of matchstr at the beginnings of 1.5327 + * lines. This treats hyperthreaded CPUs as multiple processors. 1.5328 + */ 1.5329 + column = 0; 1.5330 + ret = 0; 1.5331 + while (true) { 1.5332 + nread = read(fd, &buf, sizeof(buf)); 1.5333 + if (nread <= 0) 1.5334 + break; /* EOF or error. */ 1.5335 + for (i = 0;i < nread;i++) { 1.5336 + char c = buf[i]; 1.5337 + if (c == '\n') 1.5338 + column = 0; 1.5339 + else if (column != -1) { 1.5340 + if (c == matchstr[column]) { 1.5341 + column++; 1.5342 + if (column == sizeof(matchstr) - 1) { 1.5343 + column = -1; 1.5344 + ret++; 1.5345 + } 1.5346 + } else 1.5347 + column = -1; 1.5348 + } 1.5349 + } 1.5350 + } 1.5351 + 1.5352 + if (ret == 0) 1.5353 + ret = 1; /* Something went wrong in the parser. */ 1.5354 + close(fd); 1.5355 + 1.5356 + return (ret); 1.5357 +} 1.5358 +#elif (defined(MOZ_MEMORY_DARWIN)) 1.5359 +#include <mach/mach_init.h> 1.5360 +#include <mach/mach_host.h> 1.5361 + 1.5362 +static inline unsigned 1.5363 +malloc_ncpus(void) 1.5364 +{ 1.5365 + kern_return_t error; 1.5366 + natural_t n; 1.5367 + processor_info_array_t pinfo; 1.5368 + mach_msg_type_number_t pinfocnt; 1.5369 + 1.5370 + error = host_processor_info(mach_host_self(), PROCESSOR_BASIC_INFO, 1.5371 + &n, &pinfo, &pinfocnt); 1.5372 + if (error != KERN_SUCCESS) 1.5373 + return (1); /* Error. */ 1.5374 + else 1.5375 + return (n); 1.5376 +} 1.5377 +#elif (defined(MOZ_MEMORY_SOLARIS)) 1.5378 + 1.5379 +static inline unsigned 1.5380 +malloc_ncpus(void) 1.5381 +{ 1.5382 + return sysconf(_SC_NPROCESSORS_ONLN); 1.5383 +} 1.5384 +#else 1.5385 +static inline unsigned 1.5386 +malloc_ncpus(void) 1.5387 +{ 1.5388 + 1.5389 + /* 1.5390 + * We lack a way to determine the number of CPUs on this platform, so 1.5391 + * assume 1 CPU. 1.5392 + */ 1.5393 + return (1); 1.5394 +} 1.5395 +#endif 1.5396 +#endif 1.5397 + 1.5398 +static void 1.5399 +malloc_print_stats(void) 1.5400 +{ 1.5401 + 1.5402 + if (opt_print_stats) { 1.5403 + char s[UMAX2S_BUFSIZE]; 1.5404 + _malloc_message("___ Begin malloc statistics ___\n", "", "", 1.5405 + ""); 1.5406 + _malloc_message("Assertions ", 1.5407 +#ifdef NDEBUG 1.5408 + "disabled", 1.5409 +#else 1.5410 + "enabled", 1.5411 +#endif 1.5412 + "\n", ""); 1.5413 + _malloc_message("Boolean MALLOC_OPTIONS: ", 1.5414 + opt_abort ? "A" : "a", "", ""); 1.5415 +#ifdef MALLOC_FILL 1.5416 + _malloc_message(opt_poison ? "C" : "c", "", "", ""); 1.5417 + _malloc_message(opt_junk ? "J" : "j", "", "", ""); 1.5418 +#endif 1.5419 +#ifdef MALLOC_PAGEFILE 1.5420 + _malloc_message(opt_pagefile ? "o" : "O", "", "", ""); 1.5421 +#endif 1.5422 + _malloc_message("P", "", "", ""); 1.5423 +#ifdef MALLOC_UTRACE 1.5424 + _malloc_message(opt_utrace ? "U" : "u", "", "", ""); 1.5425 +#endif 1.5426 +#ifdef MALLOC_SYSV 1.5427 + _malloc_message(opt_sysv ? "V" : "v", "", "", ""); 1.5428 +#endif 1.5429 +#ifdef MALLOC_XMALLOC 1.5430 + _malloc_message(opt_xmalloc ? "X" : "x", "", "", ""); 1.5431 +#endif 1.5432 +#ifdef MALLOC_FILL 1.5433 + _malloc_message(opt_zero ? "Z" : "z", "", "", ""); 1.5434 +#endif 1.5435 + _malloc_message("\n", "", "", ""); 1.5436 + 1.5437 +#ifndef MOZ_MEMORY_NARENAS_DEFAULT_ONE 1.5438 + _malloc_message("CPUs: ", umax2s(ncpus, 10, s), "\n", ""); 1.5439 +#endif 1.5440 + _malloc_message("Max arenas: ", umax2s(narenas, 10, s), "\n", 1.5441 + ""); 1.5442 +#ifdef MALLOC_BALANCE 1.5443 + _malloc_message("Arena balance threshold: ", 1.5444 + umax2s(opt_balance_threshold, 10, s), "\n", ""); 1.5445 +#endif 1.5446 + _malloc_message("Pointer size: ", umax2s(sizeof(void *), 10, s), 1.5447 + "\n", ""); 1.5448 + _malloc_message("Quantum size: ", umax2s(quantum, 10, s), "\n", 1.5449 + ""); 1.5450 + _malloc_message("Max small size: ", umax2s(small_max, 10, s), 1.5451 + "\n", ""); 1.5452 + _malloc_message("Max dirty pages per arena: ", 1.5453 + umax2s(opt_dirty_max, 10, s), "\n", ""); 1.5454 + 1.5455 + _malloc_message("Chunk size: ", umax2s(chunksize, 10, s), "", 1.5456 + ""); 1.5457 + _malloc_message(" (2^", umax2s(opt_chunk_2pow, 10, s), ")\n", 1.5458 + ""); 1.5459 + 1.5460 +#ifdef MALLOC_STATS 1.5461 + { 1.5462 + size_t allocated, mapped = 0; 1.5463 +#ifdef MALLOC_BALANCE 1.5464 + uint64_t nbalance = 0; 1.5465 +#endif 1.5466 + unsigned i; 1.5467 + arena_t *arena; 1.5468 + 1.5469 + /* Calculate and print allocated/mapped stats. */ 1.5470 + 1.5471 + /* arenas. */ 1.5472 + for (i = 0, allocated = 0; i < narenas; i++) { 1.5473 + if (arenas[i] != NULL) { 1.5474 + malloc_spin_lock(&arenas[i]->lock); 1.5475 + allocated += 1.5476 + arenas[i]->stats.allocated_small; 1.5477 + allocated += 1.5478 + arenas[i]->stats.allocated_large; 1.5479 + mapped += arenas[i]->stats.mapped; 1.5480 +#ifdef MALLOC_BALANCE 1.5481 + nbalance += arenas[i]->stats.nbalance; 1.5482 +#endif 1.5483 + malloc_spin_unlock(&arenas[i]->lock); 1.5484 + } 1.5485 + } 1.5486 + 1.5487 + /* huge/base. */ 1.5488 + malloc_mutex_lock(&huge_mtx); 1.5489 + allocated += huge_allocated; 1.5490 + mapped += huge_mapped; 1.5491 + malloc_mutex_unlock(&huge_mtx); 1.5492 + 1.5493 + malloc_mutex_lock(&base_mtx); 1.5494 + mapped += base_mapped; 1.5495 + malloc_mutex_unlock(&base_mtx); 1.5496 + 1.5497 +#ifdef MOZ_MEMORY_WINDOWS 1.5498 + malloc_printf("Allocated: %lu, mapped: %lu\n", 1.5499 + allocated, mapped); 1.5500 +#else 1.5501 + malloc_printf("Allocated: %zu, mapped: %zu\n", 1.5502 + allocated, mapped); 1.5503 +#endif 1.5504 + 1.5505 +#ifdef MALLOC_BALANCE 1.5506 + malloc_printf("Arena balance reassignments: %llu\n", 1.5507 + nbalance); 1.5508 +#endif 1.5509 + 1.5510 + /* Print chunk stats. */ 1.5511 + malloc_printf( 1.5512 + "huge: nmalloc ndalloc allocated\n"); 1.5513 +#ifdef MOZ_MEMORY_WINDOWS 1.5514 + malloc_printf(" %12llu %12llu %12lu\n", 1.5515 + huge_nmalloc, huge_ndalloc, huge_allocated); 1.5516 +#else 1.5517 + malloc_printf(" %12llu %12llu %12zu\n", 1.5518 + huge_nmalloc, huge_ndalloc, huge_allocated); 1.5519 +#endif 1.5520 + /* Print stats for each arena. */ 1.5521 + for (i = 0; i < narenas; i++) { 1.5522 + arena = arenas[i]; 1.5523 + if (arena != NULL) { 1.5524 + malloc_printf( 1.5525 + "\narenas[%u]:\n", i); 1.5526 + malloc_spin_lock(&arena->lock); 1.5527 + stats_print(arena); 1.5528 + malloc_spin_unlock(&arena->lock); 1.5529 + } 1.5530 + } 1.5531 + } 1.5532 +#endif /* #ifdef MALLOC_STATS */ 1.5533 + _malloc_message("--- End malloc statistics ---\n", "", "", ""); 1.5534 + } 1.5535 +} 1.5536 + 1.5537 +/* 1.5538 + * FreeBSD's pthreads implementation calls malloc(3), so the malloc 1.5539 + * implementation has to take pains to avoid infinite recursion during 1.5540 + * initialization. 1.5541 + */ 1.5542 +#if (defined(MOZ_MEMORY_WINDOWS) || defined(MOZ_MEMORY_DARWIN)) 1.5543 +#define malloc_init() false 1.5544 +#else 1.5545 +static inline bool 1.5546 +malloc_init(void) 1.5547 +{ 1.5548 + 1.5549 + if (malloc_initialized == false) 1.5550 + return (malloc_init_hard()); 1.5551 + 1.5552 + return (false); 1.5553 +} 1.5554 +#endif 1.5555 + 1.5556 +#if !defined(MOZ_MEMORY_WINDOWS) 1.5557 +static 1.5558 +#endif 1.5559 +bool 1.5560 +malloc_init_hard(void) 1.5561 +{ 1.5562 + unsigned i; 1.5563 + char buf[PATH_MAX + 1]; 1.5564 + const char *opts; 1.5565 + long result; 1.5566 +#ifndef MOZ_MEMORY_WINDOWS 1.5567 + int linklen; 1.5568 +#endif 1.5569 +#ifdef MOZ_MEMORY_DARWIN 1.5570 + malloc_zone_t* default_zone; 1.5571 +#endif 1.5572 + 1.5573 +#ifndef MOZ_MEMORY_WINDOWS 1.5574 + malloc_mutex_lock(&init_lock); 1.5575 +#endif 1.5576 + 1.5577 + if (malloc_initialized) { 1.5578 + /* 1.5579 + * Another thread initialized the allocator before this one 1.5580 + * acquired init_lock. 1.5581 + */ 1.5582 +#ifndef MOZ_MEMORY_WINDOWS 1.5583 + malloc_mutex_unlock(&init_lock); 1.5584 +#endif 1.5585 + return (false); 1.5586 + } 1.5587 + 1.5588 +#ifdef MOZ_MEMORY_WINDOWS 1.5589 + /* get a thread local storage index */ 1.5590 + tlsIndex = TlsAlloc(); 1.5591 +#endif 1.5592 + 1.5593 + /* Get page size and number of CPUs */ 1.5594 +#ifdef MOZ_MEMORY_WINDOWS 1.5595 + { 1.5596 + SYSTEM_INFO info; 1.5597 + 1.5598 + GetSystemInfo(&info); 1.5599 + result = info.dwPageSize; 1.5600 + 1.5601 +#ifndef MOZ_MEMORY_NARENAS_DEFAULT_ONE 1.5602 + ncpus = info.dwNumberOfProcessors; 1.5603 +#endif 1.5604 + } 1.5605 +#else 1.5606 +#ifndef MOZ_MEMORY_NARENAS_DEFAULT_ONE 1.5607 + ncpus = malloc_ncpus(); 1.5608 +#endif 1.5609 + 1.5610 + result = sysconf(_SC_PAGESIZE); 1.5611 + assert(result != -1); 1.5612 +#endif 1.5613 + 1.5614 + /* We assume that the page size is a power of 2. */ 1.5615 + assert(((result - 1) & result) == 0); 1.5616 +#ifdef MALLOC_STATIC_SIZES 1.5617 + if (pagesize % (size_t) result) { 1.5618 + _malloc_message(_getprogname(), 1.5619 + "Compile-time page size does not divide the runtime one.\n", 1.5620 + "", ""); 1.5621 + abort(); 1.5622 + } 1.5623 +#else 1.5624 + pagesize = (size_t) result; 1.5625 + pagesize_mask = (size_t) result - 1; 1.5626 + pagesize_2pow = ffs((int)result) - 1; 1.5627 +#endif 1.5628 + 1.5629 +#ifdef MALLOC_PAGEFILE 1.5630 + /* 1.5631 + * Determine where to create page files. It is insufficient to 1.5632 + * unconditionally use P_tmpdir (typically "/tmp"), since for some 1.5633 + * operating systems /tmp is a separate filesystem that is rather small. 1.5634 + * Therefore prefer, in order, the following locations: 1.5635 + * 1.5636 + * 1) MALLOC_TMPDIR 1.5637 + * 2) TMPDIR 1.5638 + * 3) P_tmpdir 1.5639 + */ 1.5640 + { 1.5641 + char *s; 1.5642 + size_t slen; 1.5643 + static const char suffix[] = "/jemalloc.XXXXXX"; 1.5644 + 1.5645 + if ((s = getenv("MALLOC_TMPDIR")) == NULL && (s = 1.5646 + getenv("TMPDIR")) == NULL) 1.5647 + s = P_tmpdir; 1.5648 + slen = strlen(s); 1.5649 + if (slen + sizeof(suffix) > sizeof(pagefile_templ)) { 1.5650 + _malloc_message(_getprogname(), 1.5651 + ": (malloc) Page file path too long\n", 1.5652 + "", ""); 1.5653 + abort(); 1.5654 + } 1.5655 + memcpy(pagefile_templ, s, slen); 1.5656 + memcpy(&pagefile_templ[slen], suffix, sizeof(suffix)); 1.5657 + } 1.5658 +#endif 1.5659 + 1.5660 + for (i = 0; i < 3; i++) { 1.5661 + unsigned j; 1.5662 + 1.5663 + /* Get runtime configuration. */ 1.5664 + switch (i) { 1.5665 + case 0: 1.5666 +#ifndef MOZ_MEMORY_WINDOWS 1.5667 + if ((linklen = readlink("/etc/malloc.conf", buf, 1.5668 + sizeof(buf) - 1)) != -1) { 1.5669 + /* 1.5670 + * Use the contents of the "/etc/malloc.conf" 1.5671 + * symbolic link's name. 1.5672 + */ 1.5673 + buf[linklen] = '\0'; 1.5674 + opts = buf; 1.5675 + } else 1.5676 +#endif 1.5677 + { 1.5678 + /* No configuration specified. */ 1.5679 + buf[0] = '\0'; 1.5680 + opts = buf; 1.5681 + } 1.5682 + break; 1.5683 + case 1: 1.5684 + if (issetugid() == 0 && (opts = 1.5685 + getenv("MALLOC_OPTIONS")) != NULL) { 1.5686 + /* 1.5687 + * Do nothing; opts is already initialized to 1.5688 + * the value of the MALLOC_OPTIONS environment 1.5689 + * variable. 1.5690 + */ 1.5691 + } else { 1.5692 + /* No configuration specified. */ 1.5693 + buf[0] = '\0'; 1.5694 + opts = buf; 1.5695 + } 1.5696 + break; 1.5697 + case 2: 1.5698 + if (_malloc_options != NULL) { 1.5699 + /* 1.5700 + * Use options that were compiled into the 1.5701 + * program. 1.5702 + */ 1.5703 + opts = _malloc_options; 1.5704 + } else { 1.5705 + /* No configuration specified. */ 1.5706 + buf[0] = '\0'; 1.5707 + opts = buf; 1.5708 + } 1.5709 + break; 1.5710 + default: 1.5711 + /* NOTREACHED */ 1.5712 + buf[0] = '\0'; 1.5713 + opts = buf; 1.5714 + assert(false); 1.5715 + } 1.5716 + 1.5717 + for (j = 0; opts[j] != '\0'; j++) { 1.5718 + unsigned k, nreps; 1.5719 + bool nseen; 1.5720 + 1.5721 + /* Parse repetition count, if any. */ 1.5722 + for (nreps = 0, nseen = false;; j++, nseen = true) { 1.5723 + switch (opts[j]) { 1.5724 + case '0': case '1': case '2': case '3': 1.5725 + case '4': case '5': case '6': case '7': 1.5726 + case '8': case '9': 1.5727 + nreps *= 10; 1.5728 + nreps += opts[j] - '0'; 1.5729 + break; 1.5730 + default: 1.5731 + goto MALLOC_OUT; 1.5732 + } 1.5733 + } 1.5734 +MALLOC_OUT: 1.5735 + if (nseen == false) 1.5736 + nreps = 1; 1.5737 + 1.5738 + for (k = 0; k < nreps; k++) { 1.5739 + switch (opts[j]) { 1.5740 + case 'a': 1.5741 + opt_abort = false; 1.5742 + break; 1.5743 + case 'A': 1.5744 + opt_abort = true; 1.5745 + break; 1.5746 + case 'b': 1.5747 +#ifdef MALLOC_BALANCE 1.5748 + opt_balance_threshold >>= 1; 1.5749 +#endif 1.5750 + break; 1.5751 + case 'B': 1.5752 +#ifdef MALLOC_BALANCE 1.5753 + if (opt_balance_threshold == 0) 1.5754 + opt_balance_threshold = 1; 1.5755 + else if ((opt_balance_threshold << 1) 1.5756 + > opt_balance_threshold) 1.5757 + opt_balance_threshold <<= 1; 1.5758 +#endif 1.5759 + break; 1.5760 +#ifdef MALLOC_FILL 1.5761 +#ifndef MALLOC_PRODUCTION 1.5762 + case 'c': 1.5763 + opt_poison = false; 1.5764 + break; 1.5765 + case 'C': 1.5766 + opt_poison = true; 1.5767 + break; 1.5768 +#endif 1.5769 +#endif 1.5770 + case 'f': 1.5771 + opt_dirty_max >>= 1; 1.5772 + break; 1.5773 + case 'F': 1.5774 + if (opt_dirty_max == 0) 1.5775 + opt_dirty_max = 1; 1.5776 + else if ((opt_dirty_max << 1) != 0) 1.5777 + opt_dirty_max <<= 1; 1.5778 + break; 1.5779 +#ifdef MALLOC_FILL 1.5780 +#ifndef MALLOC_PRODUCTION 1.5781 + case 'j': 1.5782 + opt_junk = false; 1.5783 + break; 1.5784 + case 'J': 1.5785 + opt_junk = true; 1.5786 + break; 1.5787 +#endif 1.5788 +#endif 1.5789 +#ifndef MALLOC_STATIC_SIZES 1.5790 + case 'k': 1.5791 + /* 1.5792 + * Chunks always require at least one 1.5793 + * header page, so chunks can never be 1.5794 + * smaller than two pages. 1.5795 + */ 1.5796 + if (opt_chunk_2pow > pagesize_2pow + 1) 1.5797 + opt_chunk_2pow--; 1.5798 + break; 1.5799 + case 'K': 1.5800 + if (opt_chunk_2pow + 1 < 1.5801 + (sizeof(size_t) << 3)) 1.5802 + opt_chunk_2pow++; 1.5803 + break; 1.5804 +#endif 1.5805 + case 'n': 1.5806 + opt_narenas_lshift--; 1.5807 + break; 1.5808 + case 'N': 1.5809 + opt_narenas_lshift++; 1.5810 + break; 1.5811 +#ifdef MALLOC_PAGEFILE 1.5812 + case 'o': 1.5813 + /* Do not over-commit. */ 1.5814 + opt_pagefile = true; 1.5815 + break; 1.5816 + case 'O': 1.5817 + /* Allow over-commit. */ 1.5818 + opt_pagefile = false; 1.5819 + break; 1.5820 +#endif 1.5821 + case 'p': 1.5822 + opt_print_stats = false; 1.5823 + break; 1.5824 + case 'P': 1.5825 + opt_print_stats = true; 1.5826 + break; 1.5827 +#ifndef MALLOC_STATIC_SIZES 1.5828 + case 'q': 1.5829 + if (opt_quantum_2pow > QUANTUM_2POW_MIN) 1.5830 + opt_quantum_2pow--; 1.5831 + break; 1.5832 + case 'Q': 1.5833 + if (opt_quantum_2pow < pagesize_2pow - 1.5834 + 1) 1.5835 + opt_quantum_2pow++; 1.5836 + break; 1.5837 + case 's': 1.5838 + if (opt_small_max_2pow > 1.5839 + QUANTUM_2POW_MIN) 1.5840 + opt_small_max_2pow--; 1.5841 + break; 1.5842 + case 'S': 1.5843 + if (opt_small_max_2pow < pagesize_2pow 1.5844 + - 1) 1.5845 + opt_small_max_2pow++; 1.5846 + break; 1.5847 +#endif 1.5848 +#ifdef MALLOC_UTRACE 1.5849 + case 'u': 1.5850 + opt_utrace = false; 1.5851 + break; 1.5852 + case 'U': 1.5853 + opt_utrace = true; 1.5854 + break; 1.5855 +#endif 1.5856 +#ifdef MALLOC_SYSV 1.5857 + case 'v': 1.5858 + opt_sysv = false; 1.5859 + break; 1.5860 + case 'V': 1.5861 + opt_sysv = true; 1.5862 + break; 1.5863 +#endif 1.5864 +#ifdef MALLOC_XMALLOC 1.5865 + case 'x': 1.5866 + opt_xmalloc = false; 1.5867 + break; 1.5868 + case 'X': 1.5869 + opt_xmalloc = true; 1.5870 + break; 1.5871 +#endif 1.5872 +#ifdef MALLOC_FILL 1.5873 +#ifndef MALLOC_PRODUCTION 1.5874 + case 'z': 1.5875 + opt_zero = false; 1.5876 + break; 1.5877 + case 'Z': 1.5878 + opt_zero = true; 1.5879 + break; 1.5880 +#endif 1.5881 +#endif 1.5882 + default: { 1.5883 + char cbuf[2]; 1.5884 + 1.5885 + cbuf[0] = opts[j]; 1.5886 + cbuf[1] = '\0'; 1.5887 + _malloc_message(_getprogname(), 1.5888 + ": (malloc) Unsupported character " 1.5889 + "in malloc options: '", cbuf, 1.5890 + "'\n"); 1.5891 + } 1.5892 + } 1.5893 + } 1.5894 + } 1.5895 + } 1.5896 + 1.5897 + /* Take care to call atexit() only once. */ 1.5898 + if (opt_print_stats) { 1.5899 +#ifndef MOZ_MEMORY_WINDOWS 1.5900 + /* Print statistics at exit. */ 1.5901 + atexit(malloc_print_stats); 1.5902 +#endif 1.5903 + } 1.5904 + 1.5905 +#if !defined(MOZ_MEMORY_WINDOWS) && !defined(MOZ_MEMORY_DARWIN) 1.5906 + /* Prevent potential deadlock on malloc locks after fork. */ 1.5907 + pthread_atfork(_malloc_prefork, _malloc_postfork, _malloc_postfork); 1.5908 +#endif 1.5909 + 1.5910 +#ifndef MALLOC_STATIC_SIZES 1.5911 + /* Set variables according to the value of opt_small_max_2pow. */ 1.5912 + if (opt_small_max_2pow < opt_quantum_2pow) 1.5913 + opt_small_max_2pow = opt_quantum_2pow; 1.5914 + small_max = (1U << opt_small_max_2pow); 1.5915 + 1.5916 + /* Set bin-related variables. */ 1.5917 + bin_maxclass = (pagesize >> 1); 1.5918 + assert(opt_quantum_2pow >= TINY_MIN_2POW); 1.5919 + ntbins = opt_quantum_2pow - TINY_MIN_2POW; 1.5920 + assert(ntbins <= opt_quantum_2pow); 1.5921 + nqbins = (small_max >> opt_quantum_2pow); 1.5922 + nsbins = pagesize_2pow - opt_small_max_2pow - 1; 1.5923 + 1.5924 + /* Set variables according to the value of opt_quantum_2pow. */ 1.5925 + quantum = (1U << opt_quantum_2pow); 1.5926 + quantum_mask = quantum - 1; 1.5927 + if (ntbins > 0) 1.5928 + small_min = (quantum >> 1) + 1; 1.5929 + else 1.5930 + small_min = 1; 1.5931 + assert(small_min <= quantum); 1.5932 + 1.5933 + /* Set variables according to the value of opt_chunk_2pow. */ 1.5934 + chunksize = (1LU << opt_chunk_2pow); 1.5935 + chunksize_mask = chunksize - 1; 1.5936 + chunk_npages = (chunksize >> pagesize_2pow); 1.5937 + 1.5938 + arena_chunk_header_npages = calculate_arena_header_pages(); 1.5939 + arena_maxclass = calculate_arena_maxclass(); 1.5940 +#endif 1.5941 + 1.5942 +#ifdef JEMALLOC_USES_MAP_ALIGN 1.5943 + /* 1.5944 + * When using MAP_ALIGN, the alignment parameter must be a power of two 1.5945 + * multiple of the system pagesize, or mmap will fail. 1.5946 + */ 1.5947 + assert((chunksize % pagesize) == 0); 1.5948 + assert((1 << (ffs(chunksize / pagesize) - 1)) == (chunksize/pagesize)); 1.5949 +#endif 1.5950 + 1.5951 + UTRACE(0, 0, 0); 1.5952 + 1.5953 + /* Various sanity checks that regard configuration. */ 1.5954 + assert(quantum >= sizeof(void *)); 1.5955 + assert(quantum <= pagesize); 1.5956 + assert(chunksize >= pagesize); 1.5957 + assert(quantum * 4 <= chunksize); 1.5958 + 1.5959 + /* Initialize chunks data. */ 1.5960 + malloc_mutex_init(&huge_mtx); 1.5961 + extent_tree_ad_new(&huge); 1.5962 +#ifdef MALLOC_STATS 1.5963 + huge_nmalloc = 0; 1.5964 + huge_ndalloc = 0; 1.5965 + huge_allocated = 0; 1.5966 + huge_mapped = 0; 1.5967 +#endif 1.5968 + 1.5969 + /* Initialize base allocation data structures. */ 1.5970 +#ifdef MALLOC_STATS 1.5971 + base_mapped = 0; 1.5972 + base_committed = 0; 1.5973 +#endif 1.5974 + base_nodes = NULL; 1.5975 + malloc_mutex_init(&base_mtx); 1.5976 + 1.5977 +#ifdef MOZ_MEMORY_NARENAS_DEFAULT_ONE 1.5978 + narenas = 1; 1.5979 +#else 1.5980 + if (ncpus > 1) { 1.5981 + /* 1.5982 + * For SMP systems, create four times as many arenas as there 1.5983 + * are CPUs by default. 1.5984 + */ 1.5985 + opt_narenas_lshift += 2; 1.5986 + } 1.5987 + 1.5988 + /* Determine how many arenas to use. */ 1.5989 + narenas = ncpus; 1.5990 +#endif 1.5991 + if (opt_narenas_lshift > 0) { 1.5992 + if ((narenas << opt_narenas_lshift) > narenas) 1.5993 + narenas <<= opt_narenas_lshift; 1.5994 + /* 1.5995 + * Make sure not to exceed the limits of what base_alloc() can 1.5996 + * handle. 1.5997 + */ 1.5998 + if (narenas * sizeof(arena_t *) > chunksize) 1.5999 + narenas = chunksize / sizeof(arena_t *); 1.6000 + } else if (opt_narenas_lshift < 0) { 1.6001 + if ((narenas >> -opt_narenas_lshift) < narenas) 1.6002 + narenas >>= -opt_narenas_lshift; 1.6003 + /* Make sure there is at least one arena. */ 1.6004 + if (narenas == 0) 1.6005 + narenas = 1; 1.6006 + } 1.6007 +#ifdef MALLOC_BALANCE 1.6008 + assert(narenas != 0); 1.6009 + for (narenas_2pow = 0; 1.6010 + (narenas >> (narenas_2pow + 1)) != 0; 1.6011 + narenas_2pow++); 1.6012 +#endif 1.6013 + 1.6014 +#ifdef NO_TLS 1.6015 + if (narenas > 1) { 1.6016 + static const unsigned primes[] = {1, 3, 5, 7, 11, 13, 17, 19, 1.6017 + 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 1.6018 + 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 1.6019 + 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 1.6020 + 223, 227, 229, 233, 239, 241, 251, 257, 263}; 1.6021 + unsigned nprimes, parenas; 1.6022 + 1.6023 + /* 1.6024 + * Pick a prime number of hash arenas that is more than narenas 1.6025 + * so that direct hashing of pthread_self() pointers tends to 1.6026 + * spread allocations evenly among the arenas. 1.6027 + */ 1.6028 + assert((narenas & 1) == 0); /* narenas must be even. */ 1.6029 + nprimes = (sizeof(primes) >> SIZEOF_INT_2POW); 1.6030 + parenas = primes[nprimes - 1]; /* In case not enough primes. */ 1.6031 + for (i = 1; i < nprimes; i++) { 1.6032 + if (primes[i] > narenas) { 1.6033 + parenas = primes[i]; 1.6034 + break; 1.6035 + } 1.6036 + } 1.6037 + narenas = parenas; 1.6038 + } 1.6039 +#endif 1.6040 + 1.6041 +#ifndef NO_TLS 1.6042 +# ifndef MALLOC_BALANCE 1.6043 + next_arena = 0; 1.6044 +# endif 1.6045 +#endif 1.6046 + 1.6047 + /* Allocate and initialize arenas. */ 1.6048 + arenas = (arena_t **)base_alloc(sizeof(arena_t *) * narenas); 1.6049 + if (arenas == NULL) { 1.6050 +#ifndef MOZ_MEMORY_WINDOWS 1.6051 + malloc_mutex_unlock(&init_lock); 1.6052 +#endif 1.6053 + return (true); 1.6054 + } 1.6055 + /* 1.6056 + * Zero the array. In practice, this should always be pre-zeroed, 1.6057 + * since it was just mmap()ed, but let's be sure. 1.6058 + */ 1.6059 + memset(arenas, 0, sizeof(arena_t *) * narenas); 1.6060 + 1.6061 + /* 1.6062 + * Initialize one arena here. The rest are lazily created in 1.6063 + * choose_arena_hard(). 1.6064 + */ 1.6065 + arenas_extend(0); 1.6066 + if (arenas[0] == NULL) { 1.6067 +#ifndef MOZ_MEMORY_WINDOWS 1.6068 + malloc_mutex_unlock(&init_lock); 1.6069 +#endif 1.6070 + return (true); 1.6071 + } 1.6072 +#ifndef NO_TLS 1.6073 + /* 1.6074 + * Assign the initial arena to the initial thread, in order to avoid 1.6075 + * spurious creation of an extra arena if the application switches to 1.6076 + * threaded mode. 1.6077 + */ 1.6078 +#ifdef MOZ_MEMORY_WINDOWS 1.6079 + TlsSetValue(tlsIndex, arenas[0]); 1.6080 +#else 1.6081 + arenas_map = arenas[0]; 1.6082 +#endif 1.6083 +#endif 1.6084 + 1.6085 + /* 1.6086 + * Seed here for the initial thread, since choose_arena_hard() is only 1.6087 + * called for other threads. The seed value doesn't really matter. 1.6088 + */ 1.6089 +#ifdef MALLOC_BALANCE 1.6090 + SPRN(balance, 42); 1.6091 +#endif 1.6092 + 1.6093 + malloc_spin_init(&arenas_lock); 1.6094 + 1.6095 +#ifdef MALLOC_VALIDATE 1.6096 + chunk_rtree = malloc_rtree_new((SIZEOF_PTR << 3) - opt_chunk_2pow); 1.6097 + if (chunk_rtree == NULL) 1.6098 + return (true); 1.6099 +#endif 1.6100 + 1.6101 + malloc_initialized = true; 1.6102 + 1.6103 +#if defined(NEEDS_PTHREAD_MMAP_UNALIGNED_TSD) 1.6104 + if (pthread_key_create(&mmap_unaligned_tsd, NULL) != 0) { 1.6105 + malloc_printf("<jemalloc>: Error in pthread_key_create()\n"); 1.6106 + } 1.6107 +#endif 1.6108 + 1.6109 +#if defined(MOZ_MEMORY_DARWIN) && !defined(MOZ_REPLACE_MALLOC) 1.6110 + /* 1.6111 + * Overwrite the default memory allocator to use jemalloc everywhere. 1.6112 + */ 1.6113 + default_zone = malloc_default_zone(); 1.6114 + 1.6115 + /* 1.6116 + * We only use jemalloc with MacOS 10.6 and 10.7. jemalloc is disabled 1.6117 + * on 32-bit builds (10.5 and 32-bit 10.6) due to bug 702250, an 1.6118 + * apparent MacOS bug. In fact, this code isn't even compiled on 1.6119 + * 32-bit builds. 1.6120 + * 1.6121 + * We'll have to update our code to work with newer versions, because 1.6122 + * the malloc zone layout is likely to change. 1.6123 + */ 1.6124 + 1.6125 + osx_use_jemalloc = (default_zone->version == SNOW_LEOPARD_MALLOC_ZONE_T_VERSION || 1.6126 + default_zone->version == LION_MALLOC_ZONE_T_VERSION); 1.6127 + 1.6128 + /* Allow us dynamically turn off jemalloc for testing. */ 1.6129 + if (getenv("NO_MAC_JEMALLOC")) { 1.6130 + osx_use_jemalloc = false; 1.6131 +#ifdef __i386__ 1.6132 + malloc_printf("Warning: NO_MAC_JEMALLOC has no effect on " 1.6133 + "i386 machines (such as this one).\n"); 1.6134 +#endif 1.6135 + } 1.6136 + 1.6137 + if (osx_use_jemalloc) { 1.6138 + /* 1.6139 + * Convert the default szone to an "overlay zone" that is capable 1.6140 + * of deallocating szone-allocated objects, but allocating new 1.6141 + * objects from jemalloc. 1.6142 + */ 1.6143 + size_t size = zone_version_size(default_zone->version); 1.6144 + szone2ozone(default_zone, size); 1.6145 + } 1.6146 + else { 1.6147 + szone = default_zone; 1.6148 + } 1.6149 +#endif 1.6150 + 1.6151 +#ifndef MOZ_MEMORY_WINDOWS 1.6152 + malloc_mutex_unlock(&init_lock); 1.6153 +#endif 1.6154 + return (false); 1.6155 +} 1.6156 + 1.6157 +/* XXX Why not just expose malloc_print_stats()? */ 1.6158 +#ifdef MOZ_MEMORY_WINDOWS 1.6159 +void 1.6160 +malloc_shutdown() 1.6161 +{ 1.6162 + 1.6163 + malloc_print_stats(); 1.6164 +} 1.6165 +#endif 1.6166 + 1.6167 +/* 1.6168 + * End general internal functions. 1.6169 + */ 1.6170 +/******************************************************************************/ 1.6171 +/* 1.6172 + * Begin malloc(3)-compatible functions. 1.6173 + */ 1.6174 + 1.6175 +/* 1.6176 + * Even though we compile with MOZ_MEMORY, we may have to dynamically decide 1.6177 + * not to use jemalloc, as discussed above. However, we call jemalloc 1.6178 + * functions directly from mozalloc. Since it's pretty dangerous to mix the 1.6179 + * allocators, we need to call the OSX allocators from the functions below, 1.6180 + * when osx_use_jemalloc is not (dynamically) set. 1.6181 + * 1.6182 + * Note that we assume jemalloc is enabled on i386. This is safe because the 1.6183 + * only i386 versions of MacOS are 10.5 and 10.6, which we support. We have to 1.6184 + * do this because madvise isn't in the malloc zone struct for 10.5. 1.6185 + * 1.6186 + * This means that NO_MAC_JEMALLOC doesn't work on i386. 1.6187 + */ 1.6188 +#if defined(MOZ_MEMORY_DARWIN) && !defined(__i386__) && !defined(MOZ_REPLACE_MALLOC) 1.6189 +#define DARWIN_ONLY(A) if (!osx_use_jemalloc) { A; } 1.6190 +#else 1.6191 +#define DARWIN_ONLY(A) 1.6192 +#endif 1.6193 + 1.6194 +MOZ_MEMORY_API void * 1.6195 +malloc_impl(size_t size) 1.6196 +{ 1.6197 + void *ret; 1.6198 + 1.6199 + DARWIN_ONLY(return (szone->malloc)(szone, size)); 1.6200 + 1.6201 + if (malloc_init()) { 1.6202 + ret = NULL; 1.6203 + goto RETURN; 1.6204 + } 1.6205 + 1.6206 + if (size == 0) { 1.6207 +#ifdef MALLOC_SYSV 1.6208 + if (opt_sysv == false) 1.6209 +#endif 1.6210 + size = 1; 1.6211 +#ifdef MALLOC_SYSV 1.6212 + else { 1.6213 + ret = NULL; 1.6214 + goto RETURN; 1.6215 + } 1.6216 +#endif 1.6217 + } 1.6218 + 1.6219 + ret = imalloc(size); 1.6220 + 1.6221 +RETURN: 1.6222 + if (ret == NULL) { 1.6223 +#ifdef MALLOC_XMALLOC 1.6224 + if (opt_xmalloc) { 1.6225 + _malloc_message(_getprogname(), 1.6226 + ": (malloc) Error in malloc(): out of memory\n", "", 1.6227 + ""); 1.6228 + abort(); 1.6229 + } 1.6230 +#endif 1.6231 + errno = ENOMEM; 1.6232 + } 1.6233 + 1.6234 + UTRACE(0, size, ret); 1.6235 + return (ret); 1.6236 +} 1.6237 + 1.6238 +/* 1.6239 + * In ELF systems the default visibility allows symbols to be preempted at 1.6240 + * runtime. This in turn prevents the uses of memalign in this file from being 1.6241 + * optimized. What we do in here is define two aliasing symbols (they point to 1.6242 + * the same code): memalign and memalign_internal. The internal version has 1.6243 + * hidden visibility and is used in every reference from this file. 1.6244 + * 1.6245 + * For more information on this technique, see section 2.2.7 (Avoid Using 1.6246 + * Exported Symbols) in http://www.akkadia.org/drepper/dsohowto.pdf. 1.6247 + */ 1.6248 + 1.6249 +#ifndef MOZ_REPLACE_MALLOC 1.6250 +#if defined(__GNUC__) && !defined(MOZ_MEMORY_DARWIN) 1.6251 +#define MOZ_MEMORY_ELF 1.6252 +#endif 1.6253 + 1.6254 +#ifdef MOZ_MEMORY_SOLARIS 1.6255 +# ifdef __SUNPRO_C 1.6256 +void * 1.6257 +memalign_impl(size_t alignment, size_t size); 1.6258 +#pragma no_inline(memalign_impl) 1.6259 +# elif (defined(__GNUC__)) 1.6260 +__attribute__((noinline)) 1.6261 +# endif 1.6262 +#else 1.6263 +#if (defined(MOZ_MEMORY_ELF)) 1.6264 +__attribute__((visibility ("hidden"))) 1.6265 +#endif 1.6266 +#endif 1.6267 +#endif /* MOZ_REPLACE_MALLOC */ 1.6268 + 1.6269 +#ifdef MOZ_MEMORY_ELF 1.6270 +#define MEMALIGN memalign_internal 1.6271 +#else 1.6272 +#define MEMALIGN memalign_impl 1.6273 +#endif 1.6274 + 1.6275 +#ifndef MOZ_MEMORY_ELF 1.6276 +MOZ_MEMORY_API 1.6277 +#endif 1.6278 +void * 1.6279 +MEMALIGN(size_t alignment, size_t size) 1.6280 +{ 1.6281 + void *ret; 1.6282 + 1.6283 + DARWIN_ONLY(return (szone->memalign)(szone, alignment, size)); 1.6284 + 1.6285 + assert(((alignment - 1) & alignment) == 0); 1.6286 + 1.6287 + if (malloc_init()) { 1.6288 + ret = NULL; 1.6289 + goto RETURN; 1.6290 + } 1.6291 + 1.6292 + if (size == 0) { 1.6293 +#ifdef MALLOC_SYSV 1.6294 + if (opt_sysv == false) 1.6295 +#endif 1.6296 + size = 1; 1.6297 +#ifdef MALLOC_SYSV 1.6298 + else { 1.6299 + ret = NULL; 1.6300 + goto RETURN; 1.6301 + } 1.6302 +#endif 1.6303 + } 1.6304 + 1.6305 + alignment = alignment < sizeof(void*) ? sizeof(void*) : alignment; 1.6306 + ret = ipalloc(alignment, size); 1.6307 + 1.6308 +RETURN: 1.6309 +#ifdef MALLOC_XMALLOC 1.6310 + if (opt_xmalloc && ret == NULL) { 1.6311 + _malloc_message(_getprogname(), 1.6312 + ": (malloc) Error in memalign(): out of memory\n", "", ""); 1.6313 + abort(); 1.6314 + } 1.6315 +#endif 1.6316 + UTRACE(0, size, ret); 1.6317 + return (ret); 1.6318 +} 1.6319 + 1.6320 +#ifdef MOZ_MEMORY_ELF 1.6321 +extern void * 1.6322 +memalign_impl(size_t alignment, size_t size) __attribute__((alias ("memalign_internal"), visibility ("default"))); 1.6323 +#endif 1.6324 + 1.6325 +MOZ_MEMORY_API int 1.6326 +posix_memalign_impl(void **memptr, size_t alignment, size_t size) 1.6327 +{ 1.6328 + void *result; 1.6329 + 1.6330 + /* Make sure that alignment is a large enough power of 2. */ 1.6331 + if (((alignment - 1) & alignment) != 0 || alignment < sizeof(void *)) { 1.6332 +#ifdef MALLOC_XMALLOC 1.6333 + if (opt_xmalloc) { 1.6334 + _malloc_message(_getprogname(), 1.6335 + ": (malloc) Error in posix_memalign(): " 1.6336 + "invalid alignment\n", "", ""); 1.6337 + abort(); 1.6338 + } 1.6339 +#endif 1.6340 + return (EINVAL); 1.6341 + } 1.6342 + 1.6343 + /* The 0-->1 size promotion is done in the memalign() call below */ 1.6344 + 1.6345 + result = MEMALIGN(alignment, size); 1.6346 + 1.6347 + if (result == NULL) 1.6348 + return (ENOMEM); 1.6349 + 1.6350 + *memptr = result; 1.6351 + return (0); 1.6352 +} 1.6353 + 1.6354 +MOZ_MEMORY_API void * 1.6355 +aligned_alloc_impl(size_t alignment, size_t size) 1.6356 +{ 1.6357 + if (size % alignment) { 1.6358 +#ifdef MALLOC_XMALLOC 1.6359 + if (opt_xmalloc) { 1.6360 + _malloc_message(_getprogname(), 1.6361 + ": (malloc) Error in aligned_alloc(): " 1.6362 + "size is not multiple of alignment\n", "", ""); 1.6363 + abort(); 1.6364 + } 1.6365 +#endif 1.6366 + return (NULL); 1.6367 + } 1.6368 + return MEMALIGN(alignment, size); 1.6369 +} 1.6370 + 1.6371 +MOZ_MEMORY_API void * 1.6372 +valloc_impl(size_t size) 1.6373 +{ 1.6374 + return (MEMALIGN(pagesize, size)); 1.6375 +} 1.6376 + 1.6377 +MOZ_MEMORY_API void * 1.6378 +calloc_impl(size_t num, size_t size) 1.6379 +{ 1.6380 + void *ret; 1.6381 + size_t num_size; 1.6382 + 1.6383 + DARWIN_ONLY(return (szone->calloc)(szone, num, size)); 1.6384 + 1.6385 + if (malloc_init()) { 1.6386 + num_size = 0; 1.6387 + ret = NULL; 1.6388 + goto RETURN; 1.6389 + } 1.6390 + 1.6391 + num_size = num * size; 1.6392 + if (num_size == 0) { 1.6393 +#ifdef MALLOC_SYSV 1.6394 + if ((opt_sysv == false) && ((num == 0) || (size == 0))) 1.6395 +#endif 1.6396 + num_size = 1; 1.6397 +#ifdef MALLOC_SYSV 1.6398 + else { 1.6399 + ret = NULL; 1.6400 + goto RETURN; 1.6401 + } 1.6402 +#endif 1.6403 + /* 1.6404 + * Try to avoid division here. We know that it isn't possible to 1.6405 + * overflow during multiplication if neither operand uses any of the 1.6406 + * most significant half of the bits in a size_t. 1.6407 + */ 1.6408 + } else if (((num | size) & (SIZE_T_MAX << (sizeof(size_t) << 2))) 1.6409 + && (num_size / size != num)) { 1.6410 + /* size_t overflow. */ 1.6411 + ret = NULL; 1.6412 + goto RETURN; 1.6413 + } 1.6414 + 1.6415 + ret = icalloc(num_size); 1.6416 + 1.6417 +RETURN: 1.6418 + if (ret == NULL) { 1.6419 +#ifdef MALLOC_XMALLOC 1.6420 + if (opt_xmalloc) { 1.6421 + _malloc_message(_getprogname(), 1.6422 + ": (malloc) Error in calloc(): out of memory\n", "", 1.6423 + ""); 1.6424 + abort(); 1.6425 + } 1.6426 +#endif 1.6427 + errno = ENOMEM; 1.6428 + } 1.6429 + 1.6430 + UTRACE(0, num_size, ret); 1.6431 + return (ret); 1.6432 +} 1.6433 + 1.6434 +MOZ_MEMORY_API void * 1.6435 +realloc_impl(void *ptr, size_t size) 1.6436 +{ 1.6437 + void *ret; 1.6438 + 1.6439 + DARWIN_ONLY(return (szone->realloc)(szone, ptr, size)); 1.6440 + 1.6441 + if (size == 0) { 1.6442 +#ifdef MALLOC_SYSV 1.6443 + if (opt_sysv == false) 1.6444 +#endif 1.6445 + size = 1; 1.6446 +#ifdef MALLOC_SYSV 1.6447 + else { 1.6448 + if (ptr != NULL) 1.6449 + idalloc(ptr); 1.6450 + ret = NULL; 1.6451 + goto RETURN; 1.6452 + } 1.6453 +#endif 1.6454 + } 1.6455 + 1.6456 + if (ptr != NULL) { 1.6457 + assert(malloc_initialized); 1.6458 + 1.6459 + ret = iralloc(ptr, size); 1.6460 + 1.6461 + if (ret == NULL) { 1.6462 +#ifdef MALLOC_XMALLOC 1.6463 + if (opt_xmalloc) { 1.6464 + _malloc_message(_getprogname(), 1.6465 + ": (malloc) Error in realloc(): out of " 1.6466 + "memory\n", "", ""); 1.6467 + abort(); 1.6468 + } 1.6469 +#endif 1.6470 + errno = ENOMEM; 1.6471 + } 1.6472 + } else { 1.6473 + if (malloc_init()) 1.6474 + ret = NULL; 1.6475 + else 1.6476 + ret = imalloc(size); 1.6477 + 1.6478 + if (ret == NULL) { 1.6479 +#ifdef MALLOC_XMALLOC 1.6480 + if (opt_xmalloc) { 1.6481 + _malloc_message(_getprogname(), 1.6482 + ": (malloc) Error in realloc(): out of " 1.6483 + "memory\n", "", ""); 1.6484 + abort(); 1.6485 + } 1.6486 +#endif 1.6487 + errno = ENOMEM; 1.6488 + } 1.6489 + } 1.6490 + 1.6491 +#ifdef MALLOC_SYSV 1.6492 +RETURN: 1.6493 +#endif 1.6494 + UTRACE(ptr, size, ret); 1.6495 + return (ret); 1.6496 +} 1.6497 + 1.6498 +MOZ_MEMORY_API void 1.6499 +free_impl(void *ptr) 1.6500 +{ 1.6501 + size_t offset; 1.6502 + 1.6503 + DARWIN_ONLY((szone->free)(szone, ptr); return); 1.6504 + 1.6505 + UTRACE(ptr, 0, 0); 1.6506 + 1.6507 + /* 1.6508 + * A version of idalloc that checks for NULL pointer but only for 1.6509 + * huge allocations assuming that CHUNK_ADDR2OFFSET(NULL) == 0. 1.6510 + */ 1.6511 + assert(CHUNK_ADDR2OFFSET(NULL) == 0); 1.6512 + offset = CHUNK_ADDR2OFFSET(ptr); 1.6513 + if (offset != 0) 1.6514 + arena_dalloc(ptr, offset); 1.6515 + else if (ptr != NULL) 1.6516 + huge_dalloc(ptr); 1.6517 +} 1.6518 + 1.6519 +/* 1.6520 + * End malloc(3)-compatible functions. 1.6521 + */ 1.6522 +/******************************************************************************/ 1.6523 +/* 1.6524 + * Begin non-standard functions. 1.6525 + */ 1.6526 + 1.6527 +/* This was added by Mozilla for use by SQLite. */ 1.6528 +#if defined(MOZ_MEMORY_DARWIN) && !defined(MOZ_REPLACE_MALLOC) 1.6529 +static 1.6530 +#else 1.6531 +MOZ_MEMORY_API 1.6532 +#endif 1.6533 +size_t 1.6534 +malloc_good_size_impl(size_t size) 1.6535 +{ 1.6536 + /* 1.6537 + * This duplicates the logic in imalloc(), arena_malloc() and 1.6538 + * arena_malloc_small(). 1.6539 + */ 1.6540 + if (size < small_min) { 1.6541 + /* Small (tiny). */ 1.6542 + size = pow2_ceil(size); 1.6543 + /* 1.6544 + * We omit the #ifdefs from arena_malloc_small() -- 1.6545 + * it can be inaccurate with its size in some cases, but this 1.6546 + * function must be accurate. 1.6547 + */ 1.6548 + if (size < (1U << TINY_MIN_2POW)) 1.6549 + size = (1U << TINY_MIN_2POW); 1.6550 + } else if (size <= small_max) { 1.6551 + /* Small (quantum-spaced). */ 1.6552 + size = QUANTUM_CEILING(size); 1.6553 + } else if (size <= bin_maxclass) { 1.6554 + /* Small (sub-page). */ 1.6555 + size = pow2_ceil(size); 1.6556 + } else if (size <= arena_maxclass) { 1.6557 + /* Large. */ 1.6558 + size = PAGE_CEILING(size); 1.6559 + } else { 1.6560 + /* 1.6561 + * Huge. We use PAGE_CEILING to get psize, instead of using 1.6562 + * CHUNK_CEILING to get csize. This ensures that this 1.6563 + * malloc_usable_size(malloc(n)) always matches 1.6564 + * malloc_good_size(n). 1.6565 + */ 1.6566 + size = PAGE_CEILING(size); 1.6567 + } 1.6568 + return size; 1.6569 +} 1.6570 + 1.6571 + 1.6572 +#if defined(MOZ_MEMORY_ANDROID) && (ANDROID_VERSION < 19) 1.6573 +MOZ_MEMORY_API size_t 1.6574 +malloc_usable_size_impl(void *ptr) 1.6575 +#else 1.6576 +MOZ_MEMORY_API size_t 1.6577 +malloc_usable_size_impl(const void *ptr) 1.6578 +#endif 1.6579 +{ 1.6580 + DARWIN_ONLY(return (szone->size)(szone, ptr)); 1.6581 + 1.6582 +#ifdef MALLOC_VALIDATE 1.6583 + return (isalloc_validate(ptr)); 1.6584 +#else 1.6585 + assert(ptr != NULL); 1.6586 + 1.6587 + return (isalloc(ptr)); 1.6588 +#endif 1.6589 +} 1.6590 + 1.6591 +MOZ_JEMALLOC_API void 1.6592 +jemalloc_stats_impl(jemalloc_stats_t *stats) 1.6593 +{ 1.6594 + size_t i; 1.6595 + 1.6596 + assert(stats != NULL); 1.6597 + 1.6598 + /* 1.6599 + * Gather runtime settings. 1.6600 + */ 1.6601 + stats->opt_abort = opt_abort; 1.6602 + stats->opt_junk = 1.6603 +#ifdef MALLOC_FILL 1.6604 + opt_junk ? true : 1.6605 +#endif 1.6606 + false; 1.6607 + stats->opt_poison = 1.6608 +#ifdef MALLOC_FILL 1.6609 + opt_poison ? true : 1.6610 +#endif 1.6611 + false; 1.6612 + stats->opt_utrace = 1.6613 +#ifdef MALLOC_UTRACE 1.6614 + opt_utrace ? true : 1.6615 +#endif 1.6616 + false; 1.6617 + stats->opt_sysv = 1.6618 +#ifdef MALLOC_SYSV 1.6619 + opt_sysv ? true : 1.6620 +#endif 1.6621 + false; 1.6622 + stats->opt_xmalloc = 1.6623 +#ifdef MALLOC_XMALLOC 1.6624 + opt_xmalloc ? true : 1.6625 +#endif 1.6626 + false; 1.6627 + stats->opt_zero = 1.6628 +#ifdef MALLOC_FILL 1.6629 + opt_zero ? true : 1.6630 +#endif 1.6631 + false; 1.6632 + stats->narenas = narenas; 1.6633 + stats->balance_threshold = 1.6634 +#ifdef MALLOC_BALANCE 1.6635 + opt_balance_threshold 1.6636 +#else 1.6637 + SIZE_T_MAX 1.6638 +#endif 1.6639 + ; 1.6640 + stats->quantum = quantum; 1.6641 + stats->small_max = small_max; 1.6642 + stats->large_max = arena_maxclass; 1.6643 + stats->chunksize = chunksize; 1.6644 + stats->dirty_max = opt_dirty_max; 1.6645 + 1.6646 + /* 1.6647 + * Gather current memory usage statistics. 1.6648 + */ 1.6649 + stats->mapped = 0; 1.6650 + stats->allocated = 0; 1.6651 + stats->waste = 0; 1.6652 + stats->page_cache = 0; 1.6653 + stats->bookkeeping = 0; 1.6654 + 1.6655 + /* Get huge mapped/allocated. */ 1.6656 + malloc_mutex_lock(&huge_mtx); 1.6657 + stats->mapped += huge_mapped; 1.6658 + stats->allocated += huge_allocated; 1.6659 + assert(huge_mapped >= huge_allocated); 1.6660 + malloc_mutex_unlock(&huge_mtx); 1.6661 + 1.6662 + /* Get base mapped/allocated. */ 1.6663 + malloc_mutex_lock(&base_mtx); 1.6664 + stats->mapped += base_mapped; 1.6665 + stats->bookkeeping += base_committed; 1.6666 + assert(base_mapped >= base_committed); 1.6667 + malloc_mutex_unlock(&base_mtx); 1.6668 + 1.6669 + /* Iterate over arenas. */ 1.6670 + for (i = 0; i < narenas; i++) { 1.6671 + arena_t *arena = arenas[i]; 1.6672 + size_t arena_mapped, arena_allocated, arena_committed, arena_dirty; 1.6673 + 1.6674 + if (arena == NULL) { 1.6675 + continue; 1.6676 + } 1.6677 + 1.6678 + malloc_spin_lock(&arena->lock); 1.6679 + 1.6680 + arena_mapped = arena->stats.mapped; 1.6681 + 1.6682 + /* "committed" counts dirty and allocated memory. */ 1.6683 + arena_committed = arena->stats.committed << pagesize_2pow; 1.6684 + 1.6685 + arena_allocated = arena->stats.allocated_small + 1.6686 + arena->stats.allocated_large; 1.6687 + 1.6688 + arena_dirty = arena->ndirty << pagesize_2pow; 1.6689 + 1.6690 + malloc_spin_unlock(&arena->lock); 1.6691 + 1.6692 + assert(arena_mapped >= arena_committed); 1.6693 + assert(arena_committed >= arena_allocated + arena_dirty); 1.6694 + 1.6695 + /* "waste" is committed memory that is neither dirty nor 1.6696 + * allocated. */ 1.6697 + stats->mapped += arena_mapped; 1.6698 + stats->allocated += arena_allocated; 1.6699 + stats->page_cache += arena_dirty; 1.6700 + stats->waste += arena_committed - arena_allocated - arena_dirty; 1.6701 + } 1.6702 + 1.6703 + assert(stats->mapped >= stats->allocated + stats->waste + 1.6704 + stats->page_cache + stats->bookkeeping); 1.6705 +} 1.6706 + 1.6707 +#ifdef MALLOC_DOUBLE_PURGE 1.6708 + 1.6709 +/* Explicitly remove all of this chunk's MADV_FREE'd pages from memory. */ 1.6710 +static void 1.6711 +hard_purge_chunk(arena_chunk_t *chunk) 1.6712 +{ 1.6713 + /* See similar logic in arena_purge(). */ 1.6714 + 1.6715 + size_t i; 1.6716 + for (i = arena_chunk_header_npages; i < chunk_npages; i++) { 1.6717 + /* Find all adjacent pages with CHUNK_MAP_MADVISED set. */ 1.6718 + size_t npages; 1.6719 + for (npages = 0; 1.6720 + chunk->map[i + npages].bits & CHUNK_MAP_MADVISED && i + npages < chunk_npages; 1.6721 + npages++) { 1.6722 + /* Turn off the chunk's MADV_FREED bit and turn on its 1.6723 + * DECOMMITTED bit. */ 1.6724 + RELEASE_ASSERT(!(chunk->map[i + npages].bits & CHUNK_MAP_DECOMMITTED)); 1.6725 + chunk->map[i + npages].bits ^= CHUNK_MAP_MADVISED_OR_DECOMMITTED; 1.6726 + } 1.6727 + 1.6728 + /* We could use mincore to find out which pages are actually 1.6729 + * present, but it's not clear that's better. */ 1.6730 + if (npages > 0) { 1.6731 + pages_decommit(((char*)chunk) + (i << pagesize_2pow), npages << pagesize_2pow); 1.6732 + pages_commit(((char*)chunk) + (i << pagesize_2pow), npages << pagesize_2pow); 1.6733 + } 1.6734 + i += npages; 1.6735 + } 1.6736 +} 1.6737 + 1.6738 +/* Explicitly remove all of this arena's MADV_FREE'd pages from memory. */ 1.6739 +static void 1.6740 +hard_purge_arena(arena_t *arena) 1.6741 +{ 1.6742 + malloc_spin_lock(&arena->lock); 1.6743 + 1.6744 + while (!LinkedList_IsEmpty(&arena->chunks_madvised)) { 1.6745 + LinkedList* next = arena->chunks_madvised.next; 1.6746 + arena_chunk_t *chunk = 1.6747 + LinkedList_Get(arena->chunks_madvised.next, 1.6748 + arena_chunk_t, chunks_madvised_elem); 1.6749 + hard_purge_chunk(chunk); 1.6750 + LinkedList_Remove(&chunk->chunks_madvised_elem); 1.6751 + } 1.6752 + 1.6753 + malloc_spin_unlock(&arena->lock); 1.6754 +} 1.6755 + 1.6756 +MOZ_JEMALLOC_API void 1.6757 +jemalloc_purge_freed_pages_impl() 1.6758 +{ 1.6759 + size_t i; 1.6760 + for (i = 0; i < narenas; i++) { 1.6761 + arena_t *arena = arenas[i]; 1.6762 + if (arena != NULL) 1.6763 + hard_purge_arena(arena); 1.6764 + } 1.6765 +} 1.6766 + 1.6767 +#else /* !defined MALLOC_DOUBLE_PURGE */ 1.6768 + 1.6769 +MOZ_JEMALLOC_API void 1.6770 +jemalloc_purge_freed_pages_impl() 1.6771 +{ 1.6772 + /* Do nothing. */ 1.6773 +} 1.6774 + 1.6775 +#endif /* defined MALLOC_DOUBLE_PURGE */ 1.6776 + 1.6777 + 1.6778 + 1.6779 +#ifdef MOZ_MEMORY_WINDOWS 1.6780 +void* 1.6781 +_recalloc(void *ptr, size_t count, size_t size) 1.6782 +{ 1.6783 + size_t oldsize = (ptr != NULL) ? isalloc(ptr) : 0; 1.6784 + size_t newsize = count * size; 1.6785 + 1.6786 + /* 1.6787 + * In order for all trailing bytes to be zeroed, the caller needs to 1.6788 + * use calloc(), followed by recalloc(). However, the current calloc() 1.6789 + * implementation only zeros the bytes requested, so if recalloc() is 1.6790 + * to work 100% correctly, calloc() will need to change to zero 1.6791 + * trailing bytes. 1.6792 + */ 1.6793 + 1.6794 + ptr = realloc(ptr, newsize); 1.6795 + if (ptr != NULL && oldsize < newsize) { 1.6796 + memset((void *)((uintptr_t)ptr + oldsize), 0, newsize - 1.6797 + oldsize); 1.6798 + } 1.6799 + 1.6800 + return ptr; 1.6801 +} 1.6802 + 1.6803 +/* 1.6804 + * This impl of _expand doesn't ever actually expand or shrink blocks: it 1.6805 + * simply replies that you may continue using a shrunk block. 1.6806 + */ 1.6807 +void* 1.6808 +_expand(void *ptr, size_t newsize) 1.6809 +{ 1.6810 + if (isalloc(ptr) >= newsize) 1.6811 + return ptr; 1.6812 + 1.6813 + return NULL; 1.6814 +} 1.6815 + 1.6816 +size_t 1.6817 +_msize(const void *ptr) 1.6818 +{ 1.6819 + 1.6820 + return malloc_usable_size_impl(ptr); 1.6821 +} 1.6822 +#endif 1.6823 + 1.6824 +MOZ_JEMALLOC_API void 1.6825 +jemalloc_free_dirty_pages_impl(void) 1.6826 +{ 1.6827 + size_t i; 1.6828 + for (i = 0; i < narenas; i++) { 1.6829 + arena_t *arena = arenas[i]; 1.6830 + 1.6831 + if (arena != NULL) { 1.6832 + malloc_spin_lock(&arena->lock); 1.6833 + arena_purge(arena, true); 1.6834 + malloc_spin_unlock(&arena->lock); 1.6835 + } 1.6836 + } 1.6837 +} 1.6838 + 1.6839 +/* 1.6840 + * End non-standard functions. 1.6841 + */ 1.6842 +/******************************************************************************/ 1.6843 +/* 1.6844 + * Begin library-private functions, used by threading libraries for protection 1.6845 + * of malloc during fork(). These functions are only called if the program is 1.6846 + * running in threaded mode, so there is no need to check whether the program 1.6847 + * is threaded here. 1.6848 + */ 1.6849 + 1.6850 +static void 1.6851 +_malloc_prefork(void) 1.6852 +{ 1.6853 + unsigned i; 1.6854 + 1.6855 + /* Acquire all mutexes in a safe order. */ 1.6856 + 1.6857 + malloc_spin_lock(&arenas_lock); 1.6858 + for (i = 0; i < narenas; i++) { 1.6859 + if (arenas[i] != NULL) 1.6860 + malloc_spin_lock(&arenas[i]->lock); 1.6861 + } 1.6862 + 1.6863 + malloc_mutex_lock(&base_mtx); 1.6864 + 1.6865 + malloc_mutex_lock(&huge_mtx); 1.6866 +} 1.6867 + 1.6868 +static void 1.6869 +_malloc_postfork(void) 1.6870 +{ 1.6871 + unsigned i; 1.6872 + 1.6873 + /* Release all mutexes, now that fork() has completed. */ 1.6874 + 1.6875 + malloc_mutex_unlock(&huge_mtx); 1.6876 + 1.6877 + malloc_mutex_unlock(&base_mtx); 1.6878 + 1.6879 + for (i = 0; i < narenas; i++) { 1.6880 + if (arenas[i] != NULL) 1.6881 + malloc_spin_unlock(&arenas[i]->lock); 1.6882 + } 1.6883 + malloc_spin_unlock(&arenas_lock); 1.6884 +} 1.6885 + 1.6886 +/* 1.6887 + * End library-private functions. 1.6888 + */ 1.6889 +/******************************************************************************/ 1.6890 + 1.6891 +#ifdef HAVE_DLOPEN 1.6892 +# include <dlfcn.h> 1.6893 +#endif 1.6894 + 1.6895 +#if defined(MOZ_MEMORY_DARWIN) 1.6896 + 1.6897 +#if !defined(MOZ_REPLACE_MALLOC) 1.6898 +static void * 1.6899 +zone_malloc(malloc_zone_t *zone, size_t size) 1.6900 +{ 1.6901 + 1.6902 + return (malloc_impl(size)); 1.6903 +} 1.6904 + 1.6905 +static void * 1.6906 +zone_calloc(malloc_zone_t *zone, size_t num, size_t size) 1.6907 +{ 1.6908 + 1.6909 + return (calloc_impl(num, size)); 1.6910 +} 1.6911 + 1.6912 +static void * 1.6913 +zone_valloc(malloc_zone_t *zone, size_t size) 1.6914 +{ 1.6915 + void *ret = NULL; /* Assignment avoids useless compiler warning. */ 1.6916 + 1.6917 + posix_memalign_impl(&ret, pagesize, size); 1.6918 + 1.6919 + return (ret); 1.6920 +} 1.6921 + 1.6922 +static void * 1.6923 +zone_memalign(malloc_zone_t *zone, size_t alignment, size_t size) 1.6924 +{ 1.6925 + return (memalign_impl(alignment, size)); 1.6926 +} 1.6927 + 1.6928 +static void * 1.6929 +zone_destroy(malloc_zone_t *zone) 1.6930 +{ 1.6931 + 1.6932 + /* This function should never be called. */ 1.6933 + assert(false); 1.6934 + return (NULL); 1.6935 +} 1.6936 + 1.6937 +static size_t 1.6938 +zone_good_size(malloc_zone_t *zone, size_t size) 1.6939 +{ 1.6940 + return malloc_good_size_impl(size); 1.6941 +} 1.6942 + 1.6943 +static size_t 1.6944 +ozone_size(malloc_zone_t *zone, void *ptr) 1.6945 +{ 1.6946 + size_t ret = isalloc_validate(ptr); 1.6947 + if (ret == 0) 1.6948 + ret = szone->size(zone, ptr); 1.6949 + 1.6950 + return ret; 1.6951 +} 1.6952 + 1.6953 +static void 1.6954 +ozone_free(malloc_zone_t *zone, void *ptr) 1.6955 +{ 1.6956 + if (isalloc_validate(ptr) != 0) 1.6957 + free_impl(ptr); 1.6958 + else { 1.6959 + size_t size = szone->size(zone, ptr); 1.6960 + if (size != 0) 1.6961 + (szone->free)(zone, ptr); 1.6962 + /* Otherwise we leak. */ 1.6963 + } 1.6964 +} 1.6965 + 1.6966 +static void * 1.6967 +ozone_realloc(malloc_zone_t *zone, void *ptr, size_t size) 1.6968 +{ 1.6969 + size_t oldsize; 1.6970 + if (ptr == NULL) 1.6971 + return (malloc_impl(size)); 1.6972 + 1.6973 + oldsize = isalloc_validate(ptr); 1.6974 + if (oldsize != 0) 1.6975 + return (realloc_impl(ptr, size)); 1.6976 + else { 1.6977 + oldsize = szone->size(zone, ptr); 1.6978 + if (oldsize == 0) 1.6979 + return (malloc_impl(size)); 1.6980 + else { 1.6981 + void *ret = malloc_impl(size); 1.6982 + if (ret != NULL) { 1.6983 + memcpy(ret, ptr, (oldsize < size) ? oldsize : 1.6984 + size); 1.6985 + (szone->free)(zone, ptr); 1.6986 + } 1.6987 + return (ret); 1.6988 + } 1.6989 + } 1.6990 +} 1.6991 + 1.6992 +static unsigned 1.6993 +ozone_batch_malloc(malloc_zone_t *zone, size_t size, void **results, 1.6994 + unsigned num_requested) 1.6995 +{ 1.6996 + /* Don't bother implementing this interface, since it isn't required. */ 1.6997 + return 0; 1.6998 +} 1.6999 + 1.7000 +static void 1.7001 +ozone_batch_free(malloc_zone_t *zone, void **to_be_freed, unsigned num) 1.7002 +{ 1.7003 + unsigned i; 1.7004 + 1.7005 + for (i = 0; i < num; i++) 1.7006 + ozone_free(zone, to_be_freed[i]); 1.7007 +} 1.7008 + 1.7009 +static void 1.7010 +ozone_free_definite_size(malloc_zone_t *zone, void *ptr, size_t size) 1.7011 +{ 1.7012 + if (isalloc_validate(ptr) != 0) { 1.7013 + assert(isalloc_validate(ptr) == size); 1.7014 + free_impl(ptr); 1.7015 + } else { 1.7016 + assert(size == szone->size(zone, ptr)); 1.7017 + l_szone.m16(zone, ptr, size); 1.7018 + } 1.7019 +} 1.7020 + 1.7021 +static void 1.7022 +ozone_force_lock(malloc_zone_t *zone) 1.7023 +{ 1.7024 + _malloc_prefork(); 1.7025 + szone->introspect->force_lock(zone); 1.7026 +} 1.7027 + 1.7028 +static void 1.7029 +ozone_force_unlock(malloc_zone_t *zone) 1.7030 +{ 1.7031 + szone->introspect->force_unlock(zone); 1.7032 + _malloc_postfork(); 1.7033 +} 1.7034 + 1.7035 +static size_t 1.7036 +zone_version_size(int version) 1.7037 +{ 1.7038 + switch (version) 1.7039 + { 1.7040 + case SNOW_LEOPARD_MALLOC_ZONE_T_VERSION: 1.7041 + return sizeof(snow_leopard_malloc_zone); 1.7042 + case LEOPARD_MALLOC_ZONE_T_VERSION: 1.7043 + return sizeof(leopard_malloc_zone); 1.7044 + default: 1.7045 + case LION_MALLOC_ZONE_T_VERSION: 1.7046 + return sizeof(lion_malloc_zone); 1.7047 + } 1.7048 +} 1.7049 + 1.7050 +/* 1.7051 + * Overlay the default scalable zone (szone) such that existing allocations are 1.7052 + * drained, and further allocations come from jemalloc. This is necessary 1.7053 + * because Core Foundation directly accesses and uses the szone before the 1.7054 + * jemalloc library is even loaded. 1.7055 + */ 1.7056 +static void 1.7057 +szone2ozone(malloc_zone_t *default_zone, size_t size) 1.7058 +{ 1.7059 + lion_malloc_zone *l_zone; 1.7060 + assert(malloc_initialized); 1.7061 + 1.7062 + /* 1.7063 + * Stash a copy of the original szone so that we can call its 1.7064 + * functions as needed. Note that internally, the szone stores its 1.7065 + * bookkeeping data structures immediately following the malloc_zone_t 1.7066 + * header, so when calling szone functions, we need to pass a pointer to 1.7067 + * the original zone structure. 1.7068 + */ 1.7069 + memcpy(szone, default_zone, size); 1.7070 + 1.7071 + /* OSX 10.7 allocates the default zone in protected memory. */ 1.7072 + if (default_zone->version >= LION_MALLOC_ZONE_T_VERSION) { 1.7073 + void* start_of_page = (void*)((size_t)(default_zone) & ~pagesize_mask); 1.7074 + mprotect (start_of_page, size, PROT_READ | PROT_WRITE); 1.7075 + } 1.7076 + 1.7077 + default_zone->size = (void *)ozone_size; 1.7078 + default_zone->malloc = (void *)zone_malloc; 1.7079 + default_zone->calloc = (void *)zone_calloc; 1.7080 + default_zone->valloc = (void *)zone_valloc; 1.7081 + default_zone->free = (void *)ozone_free; 1.7082 + default_zone->realloc = (void *)ozone_realloc; 1.7083 + default_zone->destroy = (void *)zone_destroy; 1.7084 + default_zone->batch_malloc = NULL; 1.7085 + default_zone->batch_free = ozone_batch_free; 1.7086 + default_zone->introspect = ozone_introspect; 1.7087 + 1.7088 + /* Don't modify default_zone->zone_name; Mac libc may rely on the name 1.7089 + * being unchanged. See Mozilla bug 694896. */ 1.7090 + 1.7091 + ozone_introspect->enumerator = NULL; 1.7092 + ozone_introspect->good_size = (void *)zone_good_size; 1.7093 + ozone_introspect->check = NULL; 1.7094 + ozone_introspect->print = NULL; 1.7095 + ozone_introspect->log = NULL; 1.7096 + ozone_introspect->force_lock = (void *)ozone_force_lock; 1.7097 + ozone_introspect->force_unlock = (void *)ozone_force_unlock; 1.7098 + ozone_introspect->statistics = NULL; 1.7099 + 1.7100 + /* Platform-dependent structs */ 1.7101 + l_zone = (lion_malloc_zone*)(default_zone); 1.7102 + 1.7103 + if (default_zone->version >= SNOW_LEOPARD_MALLOC_ZONE_T_VERSION) { 1.7104 + l_zone->m15 = (void (*)())zone_memalign; 1.7105 + l_zone->m16 = (void (*)())ozone_free_definite_size; 1.7106 + l_ozone_introspect.m9 = NULL; 1.7107 + } 1.7108 + 1.7109 + if (default_zone->version >= LION_MALLOC_ZONE_T_VERSION) { 1.7110 + l_zone->m17 = NULL; 1.7111 + l_ozone_introspect.m10 = NULL; 1.7112 + l_ozone_introspect.m11 = NULL; 1.7113 + l_ozone_introspect.m12 = NULL; 1.7114 + l_ozone_introspect.m13 = NULL; 1.7115 + } 1.7116 +} 1.7117 +#endif 1.7118 + 1.7119 +__attribute__((constructor)) 1.7120 +void 1.7121 +jemalloc_darwin_init(void) 1.7122 +{ 1.7123 + if (malloc_init_hard()) 1.7124 + abort(); 1.7125 +} 1.7126 + 1.7127 +#endif 1.7128 + 1.7129 +/* 1.7130 + * is_malloc(malloc_impl) is some macro magic to detect if malloc_impl is 1.7131 + * defined as "malloc" in mozmemory_wrap.h 1.7132 + */ 1.7133 +#define malloc_is_malloc 1 1.7134 +#define is_malloc_(a) malloc_is_ ## a 1.7135 +#define is_malloc(a) is_malloc_(a) 1.7136 + 1.7137 +#if !defined(MOZ_MEMORY_DARWIN) && (is_malloc(malloc_impl) == 1) 1.7138 +# if defined(__GLIBC__) && !defined(__UCLIBC__) 1.7139 +/* 1.7140 + * glibc provides the RTLD_DEEPBIND flag for dlopen which can make it possible 1.7141 + * to inconsistently reference libc's malloc(3)-compatible functions 1.7142 + * (bug 493541). 1.7143 + * 1.7144 + * These definitions interpose hooks in glibc. The functions are actually 1.7145 + * passed an extra argument for the caller return address, which will be 1.7146 + * ignored. 1.7147 + */ 1.7148 +MOZ_MEMORY_API void (*__free_hook)(void *ptr) = free_impl; 1.7149 +MOZ_MEMORY_API void *(*__malloc_hook)(size_t size) = malloc_impl; 1.7150 +MOZ_MEMORY_API void *(*__realloc_hook)(void *ptr, size_t size) = realloc_impl; 1.7151 +MOZ_MEMORY_API void *(*__memalign_hook)(size_t alignment, size_t size) = MEMALIGN; 1.7152 + 1.7153 +# elif defined(RTLD_DEEPBIND) 1.7154 +/* 1.7155 + * XXX On systems that support RTLD_GROUP or DF_1_GROUP, do their 1.7156 + * implementations permit similar inconsistencies? Should STV_SINGLETON 1.7157 + * visibility be used for interposition where available? 1.7158 + */ 1.7159 +# error "Interposing malloc is unsafe on this system without libc malloc hooks." 1.7160 +# endif 1.7161 +#endif 1.7162 + 1.7163 +#ifdef MOZ_MEMORY_WINDOWS 1.7164 +/* 1.7165 + * In the new style jemalloc integration jemalloc is built as a separate 1.7166 + * shared library. Since we're no longer hooking into the CRT binary, 1.7167 + * we need to initialize the heap at the first opportunity we get. 1.7168 + * DLL_PROCESS_ATTACH in DllMain is that opportunity. 1.7169 + */ 1.7170 +BOOL APIENTRY DllMain(HINSTANCE hModule, 1.7171 + DWORD reason, 1.7172 + LPVOID lpReserved) 1.7173 +{ 1.7174 + switch (reason) { 1.7175 + case DLL_PROCESS_ATTACH: 1.7176 + /* Don't force the system to page DllMain back in every time 1.7177 + * we create/destroy a thread */ 1.7178 + DisableThreadLibraryCalls(hModule); 1.7179 + /* Initialize the heap */ 1.7180 + malloc_init_hard(); 1.7181 + break; 1.7182 + 1.7183 + case DLL_PROCESS_DETACH: 1.7184 + break; 1.7185 + 1.7186 + } 1.7187 + 1.7188 + return TRUE; 1.7189 +} 1.7190 +#endif