memory/mozjemalloc/jemalloc.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

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

mercurial