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