Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/jemalloc/src/jemalloc.c
39536 views
1
#define JEMALLOC_C_
2
#include "jemalloc/internal/jemalloc_preamble.h"
3
#include "jemalloc/internal/jemalloc_internal_includes.h"
4
5
#include "jemalloc/internal/assert.h"
6
#include "jemalloc/internal/atomic.h"
7
#include "jemalloc/internal/buf_writer.h"
8
#include "jemalloc/internal/ctl.h"
9
#include "jemalloc/internal/emap.h"
10
#include "jemalloc/internal/extent_dss.h"
11
#include "jemalloc/internal/extent_mmap.h"
12
#include "jemalloc/internal/fxp.h"
13
#include "jemalloc/internal/san.h"
14
#include "jemalloc/internal/hook.h"
15
#include "jemalloc/internal/jemalloc_internal_types.h"
16
#include "jemalloc/internal/log.h"
17
#include "jemalloc/internal/malloc_io.h"
18
#include "jemalloc/internal/mutex.h"
19
#include "jemalloc/internal/nstime.h"
20
#include "jemalloc/internal/rtree.h"
21
#include "jemalloc/internal/safety_check.h"
22
#include "jemalloc/internal/sc.h"
23
#include "jemalloc/internal/spin.h"
24
#include "jemalloc/internal/sz.h"
25
#include "jemalloc/internal/ticker.h"
26
#include "jemalloc/internal/thread_event.h"
27
#include "jemalloc/internal/util.h"
28
29
/******************************************************************************/
30
/* Data. */
31
32
/* Work around <http://llvm.org/bugs/show_bug.cgi?id=12623>: */
33
const char *__malloc_options_1_0 = NULL;
34
__sym_compat(_malloc_options, __malloc_options_1_0, FBSD_1.0);
35
36
/* Runtime configuration options. */
37
const char *je_malloc_conf
38
#ifndef _WIN32
39
JEMALLOC_ATTR(weak)
40
#endif
41
;
42
/*
43
* The usual rule is that the closer to runtime you are, the higher priority
44
* your configuration settings are (so the jemalloc config options get lower
45
* priority than the per-binary setting, which gets lower priority than the /etc
46
* setting, which gets lower priority than the environment settings).
47
*
48
* But it's a fairly common use case in some testing environments for a user to
49
* be able to control the binary, but nothing else (e.g. a performancy canary
50
* uses the production OS and environment variables, but can run any binary in
51
* those circumstances). For these use cases, it's handy to have an in-binary
52
* mechanism for overriding environment variable settings, with the idea that if
53
* the results are positive they get promoted to the official settings, and
54
* moved from the binary to the environment variable.
55
*
56
* We don't actually want this to be widespread, so we'll give it a silly name
57
* and not mention it in headers or documentation.
58
*/
59
const char *je_malloc_conf_2_conf_harder
60
#ifndef _WIN32
61
JEMALLOC_ATTR(weak)
62
#endif
63
;
64
65
bool opt_abort =
66
#ifdef JEMALLOC_DEBUG
67
true
68
#else
69
false
70
#endif
71
;
72
bool opt_abort_conf =
73
#ifdef JEMALLOC_DEBUG
74
true
75
#else
76
false
77
#endif
78
;
79
/* Intentionally default off, even with debug builds. */
80
bool opt_confirm_conf = false;
81
const char *opt_junk =
82
#if (defined(JEMALLOC_DEBUG) && defined(JEMALLOC_FILL))
83
"true"
84
#else
85
"false"
86
#endif
87
;
88
bool opt_junk_alloc =
89
#if (defined(JEMALLOC_DEBUG) && defined(JEMALLOC_FILL))
90
true
91
#else
92
false
93
#endif
94
;
95
bool opt_junk_free =
96
#if (defined(JEMALLOC_DEBUG) && defined(JEMALLOC_FILL))
97
true
98
#else
99
false
100
#endif
101
;
102
bool opt_trust_madvise =
103
#ifdef JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS
104
false
105
#else
106
true
107
#endif
108
;
109
110
bool opt_cache_oblivious =
111
#ifdef JEMALLOC_CACHE_OBLIVIOUS
112
true
113
#else
114
false
115
#endif
116
;
117
118
zero_realloc_action_t opt_zero_realloc_action =
119
#ifdef JEMALLOC_ZERO_REALLOC_DEFAULT_FREE
120
zero_realloc_action_free
121
#else
122
zero_realloc_action_alloc
123
#endif
124
;
125
126
atomic_zu_t zero_realloc_count = ATOMIC_INIT(0);
127
128
const char *zero_realloc_mode_names[] = {
129
"alloc",
130
"free",
131
"abort",
132
};
133
134
/*
135
* These are the documented values for junk fill debugging facilities -- see the
136
* man page.
137
*/
138
static const uint8_t junk_alloc_byte = 0xa5;
139
static const uint8_t junk_free_byte = 0x5a;
140
141
static void default_junk_alloc(void *ptr, size_t usize) {
142
memset(ptr, junk_alloc_byte, usize);
143
}
144
145
static void default_junk_free(void *ptr, size_t usize) {
146
memset(ptr, junk_free_byte, usize);
147
}
148
149
void (*junk_alloc_callback)(void *ptr, size_t size) = &default_junk_alloc;
150
void (*junk_free_callback)(void *ptr, size_t size) = &default_junk_free;
151
152
bool opt_utrace = false;
153
bool opt_xmalloc = false;
154
bool opt_experimental_infallible_new = false;
155
bool opt_zero = false;
156
unsigned opt_narenas = 0;
157
fxp_t opt_narenas_ratio = FXP_INIT_INT(4);
158
159
unsigned ncpus;
160
161
/* Protects arenas initialization. */
162
malloc_mutex_t arenas_lock;
163
164
/* The global hpa, and whether it's on. */
165
bool opt_hpa = false;
166
hpa_shard_opts_t opt_hpa_opts = HPA_SHARD_OPTS_DEFAULT;
167
sec_opts_t opt_hpa_sec_opts = SEC_OPTS_DEFAULT;
168
169
/*
170
* Arenas that are used to service external requests. Not all elements of the
171
* arenas array are necessarily used; arenas are created lazily as needed.
172
*
173
* arenas[0..narenas_auto) are used for automatic multiplexing of threads and
174
* arenas. arenas[narenas_auto..narenas_total) are only used if the application
175
* takes some action to create them and allocate from them.
176
*
177
* Points to an arena_t.
178
*/
179
JEMALLOC_ALIGNED(CACHELINE)
180
atomic_p_t arenas[MALLOCX_ARENA_LIMIT];
181
static atomic_u_t narenas_total; /* Use narenas_total_*(). */
182
/* Below three are read-only after initialization. */
183
static arena_t *a0; /* arenas[0]. */
184
unsigned narenas_auto;
185
unsigned manual_arena_base;
186
187
malloc_init_t malloc_init_state = malloc_init_uninitialized;
188
189
/* False should be the common case. Set to true to trigger initialization. */
190
bool malloc_slow = true;
191
192
/* When malloc_slow is true, set the corresponding bits for sanity check. */
193
enum {
194
flag_opt_junk_alloc = (1U),
195
flag_opt_junk_free = (1U << 1),
196
flag_opt_zero = (1U << 2),
197
flag_opt_utrace = (1U << 3),
198
flag_opt_xmalloc = (1U << 4)
199
};
200
static uint8_t malloc_slow_flags;
201
202
#ifdef JEMALLOC_THREADED_INIT
203
/* Used to let the initializing thread recursively allocate. */
204
# define NO_INITIALIZER ((unsigned long)0)
205
# define INITIALIZER pthread_self()
206
# define IS_INITIALIZER (malloc_initializer == pthread_self())
207
static pthread_t malloc_initializer = NO_INITIALIZER;
208
#else
209
# define NO_INITIALIZER false
210
# define INITIALIZER true
211
# define IS_INITIALIZER malloc_initializer
212
static bool malloc_initializer = NO_INITIALIZER;
213
#endif
214
215
/* Used to avoid initialization races. */
216
#ifdef _WIN32
217
#if _WIN32_WINNT >= 0x0600
218
static malloc_mutex_t init_lock = SRWLOCK_INIT;
219
#else
220
static malloc_mutex_t init_lock;
221
static bool init_lock_initialized = false;
222
223
JEMALLOC_ATTR(constructor)
224
static void WINAPI
225
_init_init_lock(void) {
226
/*
227
* If another constructor in the same binary is using mallctl to e.g.
228
* set up extent hooks, it may end up running before this one, and
229
* malloc_init_hard will crash trying to lock the uninitialized lock. So
230
* we force an initialization of the lock in malloc_init_hard as well.
231
* We don't try to care about atomicity of the accessed to the
232
* init_lock_initialized boolean, since it really only matters early in
233
* the process creation, before any separate thread normally starts
234
* doing anything.
235
*/
236
if (!init_lock_initialized) {
237
malloc_mutex_init(&init_lock, "init", WITNESS_RANK_INIT,
238
malloc_mutex_rank_exclusive);
239
}
240
init_lock_initialized = true;
241
}
242
243
#ifdef _MSC_VER
244
# pragma section(".CRT$XCU", read)
245
JEMALLOC_SECTION(".CRT$XCU") JEMALLOC_ATTR(used)
246
static const void (WINAPI *init_init_lock)(void) = _init_init_lock;
247
#endif
248
#endif
249
#else
250
static malloc_mutex_t init_lock = MALLOC_MUTEX_INITIALIZER;
251
#endif
252
253
typedef struct {
254
void *p; /* Input pointer (as in realloc(p, s)). */
255
size_t s; /* Request size. */
256
void *r; /* Result pointer. */
257
} malloc_utrace_t;
258
259
#ifdef JEMALLOC_UTRACE
260
# define UTRACE(a, b, c) do { \
261
if (unlikely(opt_utrace)) { \
262
int utrace_serrno = errno; \
263
malloc_utrace_t ut; \
264
ut.p = (a); \
265
ut.s = (b); \
266
ut.r = (c); \
267
UTRACE_CALL(&ut, sizeof(ut)); \
268
errno = utrace_serrno; \
269
} \
270
} while (0)
271
#else
272
# define UTRACE(a, b, c)
273
#endif
274
275
/* Whether encountered any invalid config options. */
276
static bool had_conf_error = false;
277
278
/******************************************************************************/
279
/*
280
* Function prototypes for static functions that are referenced prior to
281
* definition.
282
*/
283
284
static bool malloc_init_hard_a0(void);
285
static bool malloc_init_hard(void);
286
287
/******************************************************************************/
288
/*
289
* Begin miscellaneous support functions.
290
*/
291
292
JEMALLOC_ALWAYS_INLINE bool
293
malloc_init_a0(void) {
294
if (unlikely(malloc_init_state == malloc_init_uninitialized)) {
295
return malloc_init_hard_a0();
296
}
297
return false;
298
}
299
300
JEMALLOC_ALWAYS_INLINE bool
301
malloc_init(void) {
302
if (unlikely(!malloc_initialized()) && malloc_init_hard()) {
303
return true;
304
}
305
return false;
306
}
307
308
/*
309
* The a0*() functions are used instead of i{d,}alloc() in situations that
310
* cannot tolerate TLS variable access.
311
*/
312
313
static void *
314
a0ialloc(size_t size, bool zero, bool is_internal) {
315
if (unlikely(malloc_init_a0())) {
316
return NULL;
317
}
318
319
return iallocztm(TSDN_NULL, size, sz_size2index(size), zero, NULL,
320
is_internal, arena_get(TSDN_NULL, 0, true), true);
321
}
322
323
static void
324
a0idalloc(void *ptr, bool is_internal) {
325
idalloctm(TSDN_NULL, ptr, NULL, NULL, is_internal, true);
326
}
327
328
void *
329
a0malloc(size_t size) {
330
return a0ialloc(size, false, true);
331
}
332
333
void
334
a0dalloc(void *ptr) {
335
a0idalloc(ptr, true);
336
}
337
338
/*
339
* FreeBSD's libc uses the bootstrap_*() functions in bootstrap-sensitive
340
* situations that cannot tolerate TLS variable access (TLS allocation and very
341
* early internal data structure initialization).
342
*/
343
344
void *
345
bootstrap_malloc(size_t size) {
346
if (unlikely(size == 0)) {
347
size = 1;
348
}
349
350
return a0ialloc(size, false, false);
351
}
352
353
void *
354
bootstrap_calloc(size_t num, size_t size) {
355
size_t num_size;
356
357
num_size = num * size;
358
if (unlikely(num_size == 0)) {
359
assert(num == 0 || size == 0);
360
num_size = 1;
361
}
362
363
return a0ialloc(num_size, true, false);
364
}
365
366
void
367
bootstrap_free(void *ptr) {
368
if (unlikely(ptr == NULL)) {
369
return;
370
}
371
372
a0idalloc(ptr, false);
373
}
374
375
void
376
arena_set(unsigned ind, arena_t *arena) {
377
atomic_store_p(&arenas[ind], arena, ATOMIC_RELEASE);
378
}
379
380
static void
381
narenas_total_set(unsigned narenas) {
382
atomic_store_u(&narenas_total, narenas, ATOMIC_RELEASE);
383
}
384
385
static void
386
narenas_total_inc(void) {
387
atomic_fetch_add_u(&narenas_total, 1, ATOMIC_RELEASE);
388
}
389
390
unsigned
391
narenas_total_get(void) {
392
return atomic_load_u(&narenas_total, ATOMIC_ACQUIRE);
393
}
394
395
/* Create a new arena and insert it into the arenas array at index ind. */
396
static arena_t *
397
arena_init_locked(tsdn_t *tsdn, unsigned ind, const arena_config_t *config) {
398
arena_t *arena;
399
400
assert(ind <= narenas_total_get());
401
if (ind >= MALLOCX_ARENA_LIMIT) {
402
return NULL;
403
}
404
if (ind == narenas_total_get()) {
405
narenas_total_inc();
406
}
407
408
/*
409
* Another thread may have already initialized arenas[ind] if it's an
410
* auto arena.
411
*/
412
arena = arena_get(tsdn, ind, false);
413
if (arena != NULL) {
414
assert(arena_is_auto(arena));
415
return arena;
416
}
417
418
/* Actually initialize the arena. */
419
arena = arena_new(tsdn, ind, config);
420
421
return arena;
422
}
423
424
static void
425
arena_new_create_background_thread(tsdn_t *tsdn, unsigned ind) {
426
if (ind == 0) {
427
return;
428
}
429
/*
430
* Avoid creating a new background thread just for the huge arena, which
431
* purges eagerly by default.
432
*/
433
if (have_background_thread && !arena_is_huge(ind)) {
434
if (background_thread_create(tsdn_tsd(tsdn), ind)) {
435
malloc_printf("<jemalloc>: error in background thread "
436
"creation for arena %u. Abort.\n", ind);
437
abort();
438
}
439
}
440
}
441
442
arena_t *
443
arena_init(tsdn_t *tsdn, unsigned ind, const arena_config_t *config) {
444
arena_t *arena;
445
446
malloc_mutex_lock(tsdn, &arenas_lock);
447
arena = arena_init_locked(tsdn, ind, config);
448
malloc_mutex_unlock(tsdn, &arenas_lock);
449
450
arena_new_create_background_thread(tsdn, ind);
451
452
return arena;
453
}
454
455
static void
456
arena_bind(tsd_t *tsd, unsigned ind, bool internal) {
457
arena_t *arena = arena_get(tsd_tsdn(tsd), ind, false);
458
arena_nthreads_inc(arena, internal);
459
460
if (internal) {
461
tsd_iarena_set(tsd, arena);
462
} else {
463
tsd_arena_set(tsd, arena);
464
unsigned shard = atomic_fetch_add_u(&arena->binshard_next, 1,
465
ATOMIC_RELAXED);
466
tsd_binshards_t *bins = tsd_binshardsp_get(tsd);
467
for (unsigned i = 0; i < SC_NBINS; i++) {
468
assert(bin_infos[i].n_shards > 0 &&
469
bin_infos[i].n_shards <= BIN_SHARDS_MAX);
470
bins->binshard[i] = shard % bin_infos[i].n_shards;
471
}
472
}
473
}
474
475
void
476
arena_migrate(tsd_t *tsd, arena_t *oldarena, arena_t *newarena) {
477
assert(oldarena != NULL);
478
assert(newarena != NULL);
479
480
arena_nthreads_dec(oldarena, false);
481
arena_nthreads_inc(newarena, false);
482
tsd_arena_set(tsd, newarena);
483
484
if (arena_nthreads_get(oldarena, false) == 0) {
485
/* Purge if the old arena has no associated threads anymore. */
486
arena_decay(tsd_tsdn(tsd), oldarena,
487
/* is_background_thread */ false, /* all */ true);
488
}
489
}
490
491
static void
492
arena_unbind(tsd_t *tsd, unsigned ind, bool internal) {
493
arena_t *arena;
494
495
arena = arena_get(tsd_tsdn(tsd), ind, false);
496
arena_nthreads_dec(arena, internal);
497
498
if (internal) {
499
tsd_iarena_set(tsd, NULL);
500
} else {
501
tsd_arena_set(tsd, NULL);
502
}
503
}
504
505
/* Slow path, called only by arena_choose(). */
506
arena_t *
507
arena_choose_hard(tsd_t *tsd, bool internal) {
508
arena_t *ret JEMALLOC_CC_SILENCE_INIT(NULL);
509
510
if (have_percpu_arena && PERCPU_ARENA_ENABLED(opt_percpu_arena)) {
511
unsigned choose = percpu_arena_choose();
512
ret = arena_get(tsd_tsdn(tsd), choose, true);
513
assert(ret != NULL);
514
arena_bind(tsd, arena_ind_get(ret), false);
515
arena_bind(tsd, arena_ind_get(ret), true);
516
517
return ret;
518
}
519
520
if (narenas_auto > 1) {
521
unsigned i, j, choose[2], first_null;
522
bool is_new_arena[2];
523
524
/*
525
* Determine binding for both non-internal and internal
526
* allocation.
527
*
528
* choose[0]: For application allocation.
529
* choose[1]: For internal metadata allocation.
530
*/
531
532
for (j = 0; j < 2; j++) {
533
choose[j] = 0;
534
is_new_arena[j] = false;
535
}
536
537
first_null = narenas_auto;
538
malloc_mutex_lock(tsd_tsdn(tsd), &arenas_lock);
539
assert(arena_get(tsd_tsdn(tsd), 0, false) != NULL);
540
for (i = 1; i < narenas_auto; i++) {
541
if (arena_get(tsd_tsdn(tsd), i, false) != NULL) {
542
/*
543
* Choose the first arena that has the lowest
544
* number of threads assigned to it.
545
*/
546
for (j = 0; j < 2; j++) {
547
if (arena_nthreads_get(arena_get(
548
tsd_tsdn(tsd), i, false), !!j) <
549
arena_nthreads_get(arena_get(
550
tsd_tsdn(tsd), choose[j], false),
551
!!j)) {
552
choose[j] = i;
553
}
554
}
555
} else if (first_null == narenas_auto) {
556
/*
557
* Record the index of the first uninitialized
558
* arena, in case all extant arenas are in use.
559
*
560
* NB: It is possible for there to be
561
* discontinuities in terms of initialized
562
* versus uninitialized arenas, due to the
563
* "thread.arena" mallctl.
564
*/
565
first_null = i;
566
}
567
}
568
569
for (j = 0; j < 2; j++) {
570
if (arena_nthreads_get(arena_get(tsd_tsdn(tsd),
571
choose[j], false), !!j) == 0 || first_null ==
572
narenas_auto) {
573
/*
574
* Use an unloaded arena, or the least loaded
575
* arena if all arenas are already initialized.
576
*/
577
if (!!j == internal) {
578
ret = arena_get(tsd_tsdn(tsd),
579
choose[j], false);
580
}
581
} else {
582
arena_t *arena;
583
584
/* Initialize a new arena. */
585
choose[j] = first_null;
586
arena = arena_init_locked(tsd_tsdn(tsd),
587
choose[j], &arena_config_default);
588
if (arena == NULL) {
589
malloc_mutex_unlock(tsd_tsdn(tsd),
590
&arenas_lock);
591
return NULL;
592
}
593
is_new_arena[j] = true;
594
if (!!j == internal) {
595
ret = arena;
596
}
597
}
598
arena_bind(tsd, choose[j], !!j);
599
}
600
malloc_mutex_unlock(tsd_tsdn(tsd), &arenas_lock);
601
602
for (j = 0; j < 2; j++) {
603
if (is_new_arena[j]) {
604
assert(choose[j] > 0);
605
arena_new_create_background_thread(
606
tsd_tsdn(tsd), choose[j]);
607
}
608
}
609
610
} else {
611
ret = arena_get(tsd_tsdn(tsd), 0, false);
612
arena_bind(tsd, 0, false);
613
arena_bind(tsd, 0, true);
614
}
615
616
return ret;
617
}
618
619
void
620
iarena_cleanup(tsd_t *tsd) {
621
arena_t *iarena;
622
623
iarena = tsd_iarena_get(tsd);
624
if (iarena != NULL) {
625
arena_unbind(tsd, arena_ind_get(iarena), true);
626
}
627
}
628
629
void
630
arena_cleanup(tsd_t *tsd) {
631
arena_t *arena;
632
633
arena = tsd_arena_get(tsd);
634
if (arena != NULL) {
635
arena_unbind(tsd, arena_ind_get(arena), false);
636
}
637
}
638
639
static void
640
stats_print_atexit(void) {
641
if (config_stats) {
642
tsdn_t *tsdn;
643
unsigned narenas, i;
644
645
tsdn = tsdn_fetch();
646
647
/*
648
* Merge stats from extant threads. This is racy, since
649
* individual threads do not lock when recording tcache stats
650
* events. As a consequence, the final stats may be slightly
651
* out of date by the time they are reported, if other threads
652
* continue to allocate.
653
*/
654
for (i = 0, narenas = narenas_total_get(); i < narenas; i++) {
655
arena_t *arena = arena_get(tsdn, i, false);
656
if (arena != NULL) {
657
tcache_slow_t *tcache_slow;
658
659
malloc_mutex_lock(tsdn, &arena->tcache_ql_mtx);
660
ql_foreach(tcache_slow, &arena->tcache_ql,
661
link) {
662
tcache_stats_merge(tsdn,
663
tcache_slow->tcache, arena);
664
}
665
malloc_mutex_unlock(tsdn,
666
&arena->tcache_ql_mtx);
667
}
668
}
669
}
670
je_malloc_stats_print(NULL, NULL, opt_stats_print_opts);
671
}
672
673
/*
674
* Ensure that we don't hold any locks upon entry to or exit from allocator
675
* code (in a "broad" sense that doesn't count a reentrant allocation as an
676
* entrance or exit).
677
*/
678
JEMALLOC_ALWAYS_INLINE void
679
check_entry_exit_locking(tsdn_t *tsdn) {
680
if (!config_debug) {
681
return;
682
}
683
if (tsdn_null(tsdn)) {
684
return;
685
}
686
tsd_t *tsd = tsdn_tsd(tsdn);
687
/*
688
* It's possible we hold locks at entry/exit if we're in a nested
689
* allocation.
690
*/
691
int8_t reentrancy_level = tsd_reentrancy_level_get(tsd);
692
if (reentrancy_level != 0) {
693
return;
694
}
695
witness_assert_lockless(tsdn_witness_tsdp_get(tsdn));
696
}
697
698
/*
699
* End miscellaneous support functions.
700
*/
701
/******************************************************************************/
702
/*
703
* Begin initialization functions.
704
*/
705
706
static char *
707
jemalloc_secure_getenv(const char *name) {
708
#ifdef JEMALLOC_HAVE_SECURE_GETENV
709
return secure_getenv(name);
710
#else
711
# ifdef JEMALLOC_HAVE_ISSETUGID
712
if (issetugid() != 0) {
713
return NULL;
714
}
715
# endif
716
return getenv(name);
717
#endif
718
}
719
720
static unsigned
721
malloc_ncpus(void) {
722
long result;
723
724
#ifdef _WIN32
725
SYSTEM_INFO si;
726
GetSystemInfo(&si);
727
result = si.dwNumberOfProcessors;
728
#elif defined(CPU_COUNT)
729
/*
730
* glibc >= 2.6 has the CPU_COUNT macro.
731
*
732
* glibc's sysconf() uses isspace(). glibc allocates for the first time
733
* *before* setting up the isspace tables. Therefore we need a
734
* different method to get the number of CPUs.
735
*
736
* The getaffinity approach is also preferred when only a subset of CPUs
737
* is available, to avoid using more arenas than necessary.
738
*/
739
{
740
# if defined(__FreeBSD__) || defined(__DragonFly__)
741
cpuset_t set;
742
# else
743
cpu_set_t set;
744
# endif
745
# if defined(JEMALLOC_HAVE_SCHED_SETAFFINITY)
746
sched_getaffinity(0, sizeof(set), &set);
747
# else
748
pthread_getaffinity_np(pthread_self(), sizeof(set), &set);
749
# endif
750
result = CPU_COUNT(&set);
751
}
752
#else
753
result = sysconf(_SC_NPROCESSORS_ONLN);
754
#endif
755
return ((result == -1) ? 1 : (unsigned)result);
756
}
757
758
/*
759
* Ensure that number of CPUs is determistinc, i.e. it is the same based on:
760
* - sched_getaffinity()
761
* - _SC_NPROCESSORS_ONLN
762
* - _SC_NPROCESSORS_CONF
763
* Since otherwise tricky things is possible with percpu arenas in use.
764
*/
765
static bool
766
malloc_cpu_count_is_deterministic()
767
{
768
#ifdef _WIN32
769
return true;
770
#else
771
long cpu_onln = sysconf(_SC_NPROCESSORS_ONLN);
772
long cpu_conf = sysconf(_SC_NPROCESSORS_CONF);
773
if (cpu_onln != cpu_conf) {
774
return false;
775
}
776
# if defined(CPU_COUNT)
777
# if defined(__FreeBSD__) || defined(__DragonFly__)
778
cpuset_t set;
779
# else
780
cpu_set_t set;
781
# endif /* __FreeBSD__ */
782
# if defined(JEMALLOC_HAVE_SCHED_SETAFFINITY)
783
sched_getaffinity(0, sizeof(set), &set);
784
# else /* !JEMALLOC_HAVE_SCHED_SETAFFINITY */
785
pthread_getaffinity_np(pthread_self(), sizeof(set), &set);
786
# endif /* JEMALLOC_HAVE_SCHED_SETAFFINITY */
787
long cpu_affinity = CPU_COUNT(&set);
788
if (cpu_affinity != cpu_conf) {
789
return false;
790
}
791
# endif /* CPU_COUNT */
792
return true;
793
#endif
794
}
795
796
static void
797
init_opt_stats_opts(const char *v, size_t vlen, char *dest) {
798
size_t opts_len = strlen(dest);
799
assert(opts_len <= stats_print_tot_num_options);
800
801
for (size_t i = 0; i < vlen; i++) {
802
switch (v[i]) {
803
#define OPTION(o, v, d, s) case o: break;
804
STATS_PRINT_OPTIONS
805
#undef OPTION
806
default: continue;
807
}
808
809
if (strchr(dest, v[i]) != NULL) {
810
/* Ignore repeated. */
811
continue;
812
}
813
814
dest[opts_len++] = v[i];
815
dest[opts_len] = '\0';
816
assert(opts_len <= stats_print_tot_num_options);
817
}
818
assert(opts_len == strlen(dest));
819
}
820
821
/* Reads the next size pair in a multi-sized option. */
822
static bool
823
malloc_conf_multi_sizes_next(const char **slab_size_segment_cur,
824
size_t *vlen_left, size_t *slab_start, size_t *slab_end, size_t *new_size) {
825
const char *cur = *slab_size_segment_cur;
826
char *end;
827
uintmax_t um;
828
829
set_errno(0);
830
831
/* First number, then '-' */
832
um = malloc_strtoumax(cur, &end, 0);
833
if (get_errno() != 0 || *end != '-') {
834
return true;
835
}
836
*slab_start = (size_t)um;
837
cur = end + 1;
838
839
/* Second number, then ':' */
840
um = malloc_strtoumax(cur, &end, 0);
841
if (get_errno() != 0 || *end != ':') {
842
return true;
843
}
844
*slab_end = (size_t)um;
845
cur = end + 1;
846
847
/* Last number */
848
um = malloc_strtoumax(cur, &end, 0);
849
if (get_errno() != 0) {
850
return true;
851
}
852
*new_size = (size_t)um;
853
854
/* Consume the separator if there is one. */
855
if (*end == '|') {
856
end++;
857
}
858
859
*vlen_left -= end - *slab_size_segment_cur;
860
*slab_size_segment_cur = end;
861
862
return false;
863
}
864
865
static bool
866
malloc_conf_next(char const **opts_p, char const **k_p, size_t *klen_p,
867
char const **v_p, size_t *vlen_p) {
868
bool accept;
869
const char *opts = *opts_p;
870
871
*k_p = opts;
872
873
for (accept = false; !accept;) {
874
switch (*opts) {
875
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
876
case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
877
case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
878
case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
879
case 'Y': case 'Z':
880
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
881
case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
882
case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
883
case 's': case 't': case 'u': case 'v': case 'w': case 'x':
884
case 'y': case 'z':
885
case '0': case '1': case '2': case '3': case '4': case '5':
886
case '6': case '7': case '8': case '9':
887
case '_':
888
opts++;
889
break;
890
case ':':
891
opts++;
892
*klen_p = (uintptr_t)opts - 1 - (uintptr_t)*k_p;
893
*v_p = opts;
894
accept = true;
895
break;
896
case '\0':
897
if (opts != *opts_p) {
898
malloc_write("<jemalloc>: Conf string ends "
899
"with key\n");
900
had_conf_error = true;
901
}
902
return true;
903
default:
904
malloc_write("<jemalloc>: Malformed conf string\n");
905
had_conf_error = true;
906
return true;
907
}
908
}
909
910
for (accept = false; !accept;) {
911
switch (*opts) {
912
case ',':
913
opts++;
914
/*
915
* Look ahead one character here, because the next time
916
* this function is called, it will assume that end of
917
* input has been cleanly reached if no input remains,
918
* but we have optimistically already consumed the
919
* comma if one exists.
920
*/
921
if (*opts == '\0') {
922
malloc_write("<jemalloc>: Conf string ends "
923
"with comma\n");
924
had_conf_error = true;
925
}
926
*vlen_p = (uintptr_t)opts - 1 - (uintptr_t)*v_p;
927
accept = true;
928
break;
929
case '\0':
930
*vlen_p = (uintptr_t)opts - (uintptr_t)*v_p;
931
accept = true;
932
break;
933
default:
934
opts++;
935
break;
936
}
937
}
938
939
*opts_p = opts;
940
return false;
941
}
942
943
static void
944
malloc_abort_invalid_conf(void) {
945
assert(opt_abort_conf);
946
malloc_printf("<jemalloc>: Abort (abort_conf:true) on invalid conf "
947
"value (see above).\n");
948
abort();
949
}
950
951
static void
952
malloc_conf_error(const char *msg, const char *k, size_t klen, const char *v,
953
size_t vlen) {
954
malloc_printf("<jemalloc>: %s: %.*s:%.*s\n", msg, (int)klen, k,
955
(int)vlen, v);
956
/* If abort_conf is set, error out after processing all options. */
957
const char *experimental = "experimental_";
958
if (strncmp(k, experimental, strlen(experimental)) == 0) {
959
/* However, tolerate experimental features. */
960
return;
961
}
962
had_conf_error = true;
963
}
964
965
static void
966
malloc_slow_flag_init(void) {
967
/*
968
* Combine the runtime options into malloc_slow for fast path. Called
969
* after processing all the options.
970
*/
971
malloc_slow_flags |= (opt_junk_alloc ? flag_opt_junk_alloc : 0)
972
| (opt_junk_free ? flag_opt_junk_free : 0)
973
| (opt_zero ? flag_opt_zero : 0)
974
| (opt_utrace ? flag_opt_utrace : 0)
975
| (opt_xmalloc ? flag_opt_xmalloc : 0);
976
977
malloc_slow = (malloc_slow_flags != 0);
978
}
979
980
/* Number of sources for initializing malloc_conf */
981
#define MALLOC_CONF_NSOURCES 5
982
983
static const char *
984
obtain_malloc_conf(unsigned which_source, char buf[PATH_MAX + 1]) {
985
if (config_debug) {
986
static unsigned read_source = 0;
987
/*
988
* Each source should only be read once, to minimize # of
989
* syscalls on init.
990
*/
991
assert(read_source++ == which_source);
992
}
993
assert(which_source < MALLOC_CONF_NSOURCES);
994
995
const char *ret;
996
switch (which_source) {
997
case 0:
998
ret = config_malloc_conf;
999
break;
1000
case 1:
1001
if (je_malloc_conf != NULL) {
1002
/* Use options that were compiled into the program. */
1003
ret = je_malloc_conf;
1004
} else {
1005
/* No configuration specified. */
1006
ret = NULL;
1007
}
1008
break;
1009
case 2: {
1010
ssize_t linklen = 0;
1011
#ifndef _WIN32
1012
int saved_errno = errno;
1013
const char *linkname =
1014
# ifdef JEMALLOC_PREFIX
1015
"/etc/"JEMALLOC_PREFIX"malloc.conf"
1016
# else
1017
"/etc/malloc.conf"
1018
# endif
1019
;
1020
1021
/*
1022
* Try to use the contents of the "/etc/malloc.conf" symbolic
1023
* link's name.
1024
*/
1025
#ifndef JEMALLOC_READLINKAT
1026
linklen = readlink(linkname, buf, PATH_MAX);
1027
#else
1028
linklen = readlinkat(AT_FDCWD, linkname, buf, PATH_MAX);
1029
#endif
1030
if (linklen == -1) {
1031
/* No configuration specified. */
1032
linklen = 0;
1033
/* Restore errno. */
1034
set_errno(saved_errno);
1035
}
1036
#endif
1037
buf[linklen] = '\0';
1038
ret = buf;
1039
break;
1040
} case 3: {
1041
const char *envname =
1042
#ifdef JEMALLOC_PREFIX
1043
JEMALLOC_CPREFIX"MALLOC_CONF"
1044
#else
1045
"MALLOC_CONF"
1046
#endif
1047
;
1048
1049
if ((ret = jemalloc_secure_getenv(envname)) != NULL) {
1050
/*
1051
* Do nothing; opts is already initialized to the value
1052
* of the MALLOC_CONF environment variable.
1053
*/
1054
} else {
1055
/* No configuration specified. */
1056
ret = NULL;
1057
}
1058
break;
1059
} case 4: {
1060
ret = je_malloc_conf_2_conf_harder;
1061
break;
1062
} default:
1063
not_reached();
1064
ret = NULL;
1065
}
1066
return ret;
1067
}
1068
1069
static void
1070
malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
1071
bool initial_call, const char *opts_cache[MALLOC_CONF_NSOURCES],
1072
char buf[PATH_MAX + 1]) {
1073
static const char *opts_explain[MALLOC_CONF_NSOURCES] = {
1074
"string specified via --with-malloc-conf",
1075
"string pointed to by the global variable malloc_conf",
1076
"\"name\" of the file referenced by the symbolic link named "
1077
"/etc/malloc.conf",
1078
"value of the environment variable MALLOC_CONF",
1079
"string pointed to by the global variable "
1080
"malloc_conf_2_conf_harder",
1081
};
1082
unsigned i;
1083
const char *opts, *k, *v;
1084
size_t klen, vlen;
1085
1086
for (i = 0; i < MALLOC_CONF_NSOURCES; i++) {
1087
/* Get runtime configuration. */
1088
if (initial_call) {
1089
opts_cache[i] = obtain_malloc_conf(i, buf);
1090
}
1091
opts = opts_cache[i];
1092
if (!initial_call && opt_confirm_conf) {
1093
malloc_printf(
1094
"<jemalloc>: malloc_conf #%u (%s): \"%s\"\n",
1095
i + 1, opts_explain[i], opts != NULL ? opts : "");
1096
}
1097
if (opts == NULL) {
1098
continue;
1099
}
1100
1101
while (*opts != '\0' && !malloc_conf_next(&opts, &k, &klen, &v,
1102
&vlen)) {
1103
1104
#define CONF_ERROR(msg, k, klen, v, vlen) \
1105
if (!initial_call) { \
1106
malloc_conf_error( \
1107
msg, k, klen, v, vlen); \
1108
cur_opt_valid = false; \
1109
}
1110
#define CONF_CONTINUE { \
1111
if (!initial_call && opt_confirm_conf \
1112
&& cur_opt_valid) { \
1113
malloc_printf("<jemalloc>: -- " \
1114
"Set conf value: %.*s:%.*s" \
1115
"\n", (int)klen, k, \
1116
(int)vlen, v); \
1117
} \
1118
continue; \
1119
}
1120
#define CONF_MATCH(n) \
1121
(sizeof(n)-1 == klen && strncmp(n, k, klen) == 0)
1122
#define CONF_MATCH_VALUE(n) \
1123
(sizeof(n)-1 == vlen && strncmp(n, v, vlen) == 0)
1124
#define CONF_HANDLE_BOOL(o, n) \
1125
if (CONF_MATCH(n)) { \
1126
if (CONF_MATCH_VALUE("true")) { \
1127
o = true; \
1128
} else if (CONF_MATCH_VALUE("false")) { \
1129
o = false; \
1130
} else { \
1131
CONF_ERROR("Invalid conf value",\
1132
k, klen, v, vlen); \
1133
} \
1134
CONF_CONTINUE; \
1135
}
1136
/*
1137
* One of the CONF_MIN macros below expands, in one of the use points,
1138
* to "unsigned integer < 0", which is always false, triggering the
1139
* GCC -Wtype-limits warning, which we disable here and re-enable below.
1140
*/
1141
JEMALLOC_DIAGNOSTIC_PUSH
1142
JEMALLOC_DIAGNOSTIC_IGNORE_TYPE_LIMITS
1143
1144
#define CONF_DONT_CHECK_MIN(um, min) false
1145
#define CONF_CHECK_MIN(um, min) ((um) < (min))
1146
#define CONF_DONT_CHECK_MAX(um, max) false
1147
#define CONF_CHECK_MAX(um, max) ((um) > (max))
1148
1149
#define CONF_VALUE_READ(max_t, result) \
1150
char *end; \
1151
set_errno(0); \
1152
result = (max_t)malloc_strtoumax(v, &end, 0);
1153
#define CONF_VALUE_READ_FAIL() \
1154
(get_errno() != 0 || (uintptr_t)end - (uintptr_t)v != vlen)
1155
1156
#define CONF_HANDLE_T(t, max_t, o, n, min, max, check_min, check_max, clip) \
1157
if (CONF_MATCH(n)) { \
1158
max_t mv; \
1159
CONF_VALUE_READ(max_t, mv) \
1160
if (CONF_VALUE_READ_FAIL()) { \
1161
CONF_ERROR("Invalid conf value",\
1162
k, klen, v, vlen); \
1163
} else if (clip) { \
1164
if (check_min(mv, (t)(min))) { \
1165
o = (t)(min); \
1166
} else if ( \
1167
check_max(mv, (t)(max))) { \
1168
o = (t)(max); \
1169
} else { \
1170
o = (t)mv; \
1171
} \
1172
} else { \
1173
if (check_min(mv, (t)(min)) || \
1174
check_max(mv, (t)(max))) { \
1175
CONF_ERROR( \
1176
"Out-of-range " \
1177
"conf value", \
1178
k, klen, v, vlen); \
1179
} else { \
1180
o = (t)mv; \
1181
} \
1182
} \
1183
CONF_CONTINUE; \
1184
}
1185
#define CONF_HANDLE_T_U(t, o, n, min, max, check_min, check_max, clip) \
1186
CONF_HANDLE_T(t, uintmax_t, o, n, min, max, check_min, \
1187
check_max, clip)
1188
#define CONF_HANDLE_T_SIGNED(t, o, n, min, max, check_min, check_max, clip)\
1189
CONF_HANDLE_T(t, intmax_t, o, n, min, max, check_min, \
1190
check_max, clip)
1191
1192
#define CONF_HANDLE_UNSIGNED(o, n, min, max, check_min, check_max, \
1193
clip) \
1194
CONF_HANDLE_T_U(unsigned, o, n, min, max, \
1195
check_min, check_max, clip)
1196
#define CONF_HANDLE_SIZE_T(o, n, min, max, check_min, check_max, clip) \
1197
CONF_HANDLE_T_U(size_t, o, n, min, max, \
1198
check_min, check_max, clip)
1199
#define CONF_HANDLE_INT64_T(o, n, min, max, check_min, check_max, clip) \
1200
CONF_HANDLE_T_SIGNED(int64_t, o, n, min, max, \
1201
check_min, check_max, clip)
1202
#define CONF_HANDLE_UINT64_T(o, n, min, max, check_min, check_max, clip)\
1203
CONF_HANDLE_T_U(uint64_t, o, n, min, max, \
1204
check_min, check_max, clip)
1205
#define CONF_HANDLE_SSIZE_T(o, n, min, max) \
1206
CONF_HANDLE_T_SIGNED(ssize_t, o, n, min, max, \
1207
CONF_CHECK_MIN, CONF_CHECK_MAX, false)
1208
#define CONF_HANDLE_CHAR_P(o, n, d) \
1209
if (CONF_MATCH(n)) { \
1210
size_t cpylen = (vlen <= \
1211
sizeof(o)-1) ? vlen : \
1212
sizeof(o)-1; \
1213
strncpy(o, v, cpylen); \
1214
o[cpylen] = '\0'; \
1215
CONF_CONTINUE; \
1216
}
1217
1218
bool cur_opt_valid = true;
1219
1220
CONF_HANDLE_BOOL(opt_confirm_conf, "confirm_conf")
1221
if (initial_call) {
1222
continue;
1223
}
1224
1225
CONF_HANDLE_BOOL(opt_abort, "abort")
1226
CONF_HANDLE_BOOL(opt_abort_conf, "abort_conf")
1227
CONF_HANDLE_BOOL(opt_trust_madvise, "trust_madvise")
1228
if (strncmp("metadata_thp", k, klen) == 0) {
1229
int m;
1230
bool match = false;
1231
for (m = 0; m < metadata_thp_mode_limit; m++) {
1232
if (strncmp(metadata_thp_mode_names[m],
1233
v, vlen) == 0) {
1234
opt_metadata_thp = m;
1235
match = true;
1236
break;
1237
}
1238
}
1239
if (!match) {
1240
CONF_ERROR("Invalid conf value",
1241
k, klen, v, vlen);
1242
}
1243
CONF_CONTINUE;
1244
}
1245
CONF_HANDLE_BOOL(opt_retain, "retain")
1246
if (strncmp("dss", k, klen) == 0) {
1247
int m;
1248
bool match = false;
1249
for (m = 0; m < dss_prec_limit; m++) {
1250
if (strncmp(dss_prec_names[m], v, vlen)
1251
== 0) {
1252
if (extent_dss_prec_set(m)) {
1253
CONF_ERROR(
1254
"Error setting dss",
1255
k, klen, v, vlen);
1256
} else {
1257
opt_dss =
1258
dss_prec_names[m];
1259
match = true;
1260
break;
1261
}
1262
}
1263
}
1264
if (!match) {
1265
CONF_ERROR("Invalid conf value",
1266
k, klen, v, vlen);
1267
}
1268
CONF_CONTINUE;
1269
}
1270
if (CONF_MATCH("narenas")) {
1271
if (CONF_MATCH_VALUE("default")) {
1272
opt_narenas = 0;
1273
CONF_CONTINUE;
1274
} else {
1275
CONF_HANDLE_UNSIGNED(opt_narenas,
1276
"narenas", 1, UINT_MAX,
1277
CONF_CHECK_MIN, CONF_DONT_CHECK_MAX,
1278
/* clip */ false)
1279
}
1280
}
1281
if (CONF_MATCH("narenas_ratio")) {
1282
char *end;
1283
bool err = fxp_parse(&opt_narenas_ratio, v,
1284
&end);
1285
if (err || (size_t)(end - v) != vlen) {
1286
CONF_ERROR("Invalid conf value",
1287
k, klen, v, vlen);
1288
}
1289
CONF_CONTINUE;
1290
}
1291
if (CONF_MATCH("bin_shards")) {
1292
const char *bin_shards_segment_cur = v;
1293
size_t vlen_left = vlen;
1294
do {
1295
size_t size_start;
1296
size_t size_end;
1297
size_t nshards;
1298
bool err = malloc_conf_multi_sizes_next(
1299
&bin_shards_segment_cur, &vlen_left,
1300
&size_start, &size_end, &nshards);
1301
if (err || bin_update_shard_size(
1302
bin_shard_sizes, size_start,
1303
size_end, nshards)) {
1304
CONF_ERROR(
1305
"Invalid settings for "
1306
"bin_shards", k, klen, v,
1307
vlen);
1308
break;
1309
}
1310
} while (vlen_left > 0);
1311
CONF_CONTINUE;
1312
}
1313
CONF_HANDLE_INT64_T(opt_mutex_max_spin,
1314
"mutex_max_spin", -1, INT64_MAX, CONF_CHECK_MIN,
1315
CONF_DONT_CHECK_MAX, false);
1316
CONF_HANDLE_SSIZE_T(opt_dirty_decay_ms,
1317
"dirty_decay_ms", -1, NSTIME_SEC_MAX * KQU(1000) <
1318
QU(SSIZE_MAX) ? NSTIME_SEC_MAX * KQU(1000) :
1319
SSIZE_MAX);
1320
CONF_HANDLE_SSIZE_T(opt_muzzy_decay_ms,
1321
"muzzy_decay_ms", -1, NSTIME_SEC_MAX * KQU(1000) <
1322
QU(SSIZE_MAX) ? NSTIME_SEC_MAX * KQU(1000) :
1323
SSIZE_MAX);
1324
CONF_HANDLE_BOOL(opt_stats_print, "stats_print")
1325
if (CONF_MATCH("stats_print_opts")) {
1326
init_opt_stats_opts(v, vlen,
1327
opt_stats_print_opts);
1328
CONF_CONTINUE;
1329
}
1330
CONF_HANDLE_INT64_T(opt_stats_interval,
1331
"stats_interval", -1, INT64_MAX,
1332
CONF_CHECK_MIN, CONF_DONT_CHECK_MAX, false)
1333
if (CONF_MATCH("stats_interval_opts")) {
1334
init_opt_stats_opts(v, vlen,
1335
opt_stats_interval_opts);
1336
CONF_CONTINUE;
1337
}
1338
if (config_fill) {
1339
if (CONF_MATCH("junk")) {
1340
if (CONF_MATCH_VALUE("true")) {
1341
opt_junk = "true";
1342
opt_junk_alloc = opt_junk_free =
1343
true;
1344
} else if (CONF_MATCH_VALUE("false")) {
1345
opt_junk = "false";
1346
opt_junk_alloc = opt_junk_free =
1347
false;
1348
} else if (CONF_MATCH_VALUE("alloc")) {
1349
opt_junk = "alloc";
1350
opt_junk_alloc = true;
1351
opt_junk_free = false;
1352
} else if (CONF_MATCH_VALUE("free")) {
1353
opt_junk = "free";
1354
opt_junk_alloc = false;
1355
opt_junk_free = true;
1356
} else {
1357
CONF_ERROR(
1358
"Invalid conf value",
1359
k, klen, v, vlen);
1360
}
1361
CONF_CONTINUE;
1362
}
1363
CONF_HANDLE_BOOL(opt_zero, "zero")
1364
}
1365
if (config_utrace) {
1366
CONF_HANDLE_BOOL(opt_utrace, "utrace")
1367
}
1368
if (config_xmalloc) {
1369
CONF_HANDLE_BOOL(opt_xmalloc, "xmalloc")
1370
}
1371
if (config_enable_cxx) {
1372
CONF_HANDLE_BOOL(
1373
opt_experimental_infallible_new,
1374
"experimental_infallible_new")
1375
}
1376
1377
CONF_HANDLE_BOOL(opt_tcache, "tcache")
1378
CONF_HANDLE_SIZE_T(opt_tcache_max, "tcache_max",
1379
0, TCACHE_MAXCLASS_LIMIT, CONF_DONT_CHECK_MIN,
1380
CONF_CHECK_MAX, /* clip */ true)
1381
if (CONF_MATCH("lg_tcache_max")) {
1382
size_t m;
1383
CONF_VALUE_READ(size_t, m)
1384
if (CONF_VALUE_READ_FAIL()) {
1385
CONF_ERROR("Invalid conf value",
1386
k, klen, v, vlen);
1387
} else {
1388
/* clip if necessary */
1389
if (m > TCACHE_LG_MAXCLASS_LIMIT) {
1390
m = TCACHE_LG_MAXCLASS_LIMIT;
1391
}
1392
opt_tcache_max = (size_t)1 << m;
1393
}
1394
CONF_CONTINUE;
1395
}
1396
/*
1397
* Anyone trying to set a value outside -16 to 16 is
1398
* deeply confused.
1399
*/
1400
CONF_HANDLE_SSIZE_T(opt_lg_tcache_nslots_mul,
1401
"lg_tcache_nslots_mul", -16, 16)
1402
/* Ditto with values past 2048. */
1403
CONF_HANDLE_UNSIGNED(opt_tcache_nslots_small_min,
1404
"tcache_nslots_small_min", 1, 2048,
1405
CONF_CHECK_MIN, CONF_CHECK_MAX, /* clip */ true)
1406
CONF_HANDLE_UNSIGNED(opt_tcache_nslots_small_max,
1407
"tcache_nslots_small_max", 1, 2048,
1408
CONF_CHECK_MIN, CONF_CHECK_MAX, /* clip */ true)
1409
CONF_HANDLE_UNSIGNED(opt_tcache_nslots_large,
1410
"tcache_nslots_large", 1, 2048,
1411
CONF_CHECK_MIN, CONF_CHECK_MAX, /* clip */ true)
1412
CONF_HANDLE_SIZE_T(opt_tcache_gc_incr_bytes,
1413
"tcache_gc_incr_bytes", 1024, SIZE_T_MAX,
1414
CONF_CHECK_MIN, CONF_DONT_CHECK_MAX,
1415
/* clip */ true)
1416
CONF_HANDLE_SIZE_T(opt_tcache_gc_delay_bytes,
1417
"tcache_gc_delay_bytes", 0, SIZE_T_MAX,
1418
CONF_DONT_CHECK_MIN, CONF_DONT_CHECK_MAX,
1419
/* clip */ false)
1420
CONF_HANDLE_UNSIGNED(opt_lg_tcache_flush_small_div,
1421
"lg_tcache_flush_small_div", 1, 16,
1422
CONF_CHECK_MIN, CONF_CHECK_MAX, /* clip */ true)
1423
CONF_HANDLE_UNSIGNED(opt_lg_tcache_flush_large_div,
1424
"lg_tcache_flush_large_div", 1, 16,
1425
CONF_CHECK_MIN, CONF_CHECK_MAX, /* clip */ true)
1426
1427
/*
1428
* The runtime option of oversize_threshold remains
1429
* undocumented. It may be tweaked in the next major
1430
* release (6.0). The default value 8M is rather
1431
* conservative / safe. Tuning it further down may
1432
* improve fragmentation a bit more, but may also cause
1433
* contention on the huge arena.
1434
*/
1435
CONF_HANDLE_SIZE_T(opt_oversize_threshold,
1436
"oversize_threshold", 0, SC_LARGE_MAXCLASS,
1437
CONF_DONT_CHECK_MIN, CONF_CHECK_MAX, false)
1438
CONF_HANDLE_SIZE_T(opt_lg_extent_max_active_fit,
1439
"lg_extent_max_active_fit", 0,
1440
(sizeof(size_t) << 3), CONF_DONT_CHECK_MIN,
1441
CONF_CHECK_MAX, false)
1442
1443
if (strncmp("percpu_arena", k, klen) == 0) {
1444
bool match = false;
1445
for (int m = percpu_arena_mode_names_base; m <
1446
percpu_arena_mode_names_limit; m++) {
1447
if (strncmp(percpu_arena_mode_names[m],
1448
v, vlen) == 0) {
1449
if (!have_percpu_arena) {
1450
CONF_ERROR(
1451
"No getcpu support",
1452
k, klen, v, vlen);
1453
}
1454
opt_percpu_arena = m;
1455
match = true;
1456
break;
1457
}
1458
}
1459
if (!match) {
1460
CONF_ERROR("Invalid conf value",
1461
k, klen, v, vlen);
1462
}
1463
CONF_CONTINUE;
1464
}
1465
CONF_HANDLE_BOOL(opt_background_thread,
1466
"background_thread");
1467
CONF_HANDLE_SIZE_T(opt_max_background_threads,
1468
"max_background_threads", 1,
1469
opt_max_background_threads,
1470
CONF_CHECK_MIN, CONF_CHECK_MAX,
1471
true);
1472
CONF_HANDLE_BOOL(opt_hpa, "hpa")
1473
CONF_HANDLE_SIZE_T(opt_hpa_opts.slab_max_alloc,
1474
"hpa_slab_max_alloc", PAGE, HUGEPAGE,
1475
CONF_CHECK_MIN, CONF_CHECK_MAX, true);
1476
1477
/*
1478
* Accept either a ratio-based or an exact hugification
1479
* threshold.
1480
*/
1481
CONF_HANDLE_SIZE_T(opt_hpa_opts.hugification_threshold,
1482
"hpa_hugification_threshold", PAGE, HUGEPAGE,
1483
CONF_CHECK_MIN, CONF_CHECK_MAX, true);
1484
if (CONF_MATCH("hpa_hugification_threshold_ratio")) {
1485
fxp_t ratio;
1486
char *end;
1487
bool err = fxp_parse(&ratio, v,
1488
&end);
1489
if (err || (size_t)(end - v) != vlen
1490
|| ratio > FXP_INIT_INT(1)) {
1491
CONF_ERROR("Invalid conf value",
1492
k, klen, v, vlen);
1493
} else {
1494
opt_hpa_opts.hugification_threshold =
1495
fxp_mul_frac(HUGEPAGE, ratio);
1496
}
1497
CONF_CONTINUE;
1498
}
1499
1500
CONF_HANDLE_UINT64_T(
1501
opt_hpa_opts.hugify_delay_ms, "hpa_hugify_delay_ms",
1502
0, 0, CONF_DONT_CHECK_MIN, CONF_DONT_CHECK_MAX,
1503
false);
1504
1505
CONF_HANDLE_UINT64_T(
1506
opt_hpa_opts.min_purge_interval_ms,
1507
"hpa_min_purge_interval_ms", 0, 0,
1508
CONF_DONT_CHECK_MIN, CONF_DONT_CHECK_MAX, false);
1509
1510
if (CONF_MATCH("hpa_dirty_mult")) {
1511
if (CONF_MATCH_VALUE("-1")) {
1512
opt_hpa_opts.dirty_mult = (fxp_t)-1;
1513
CONF_CONTINUE;
1514
}
1515
fxp_t ratio;
1516
char *end;
1517
bool err = fxp_parse(&ratio, v,
1518
&end);
1519
if (err || (size_t)(end - v) != vlen) {
1520
CONF_ERROR("Invalid conf value",
1521
k, klen, v, vlen);
1522
} else {
1523
opt_hpa_opts.dirty_mult = ratio;
1524
}
1525
CONF_CONTINUE;
1526
}
1527
1528
CONF_HANDLE_SIZE_T(opt_hpa_sec_opts.nshards,
1529
"hpa_sec_nshards", 0, 0, CONF_CHECK_MIN,
1530
CONF_DONT_CHECK_MAX, true);
1531
CONF_HANDLE_SIZE_T(opt_hpa_sec_opts.max_alloc,
1532
"hpa_sec_max_alloc", PAGE, 0, CONF_CHECK_MIN,
1533
CONF_DONT_CHECK_MAX, true);
1534
CONF_HANDLE_SIZE_T(opt_hpa_sec_opts.max_bytes,
1535
"hpa_sec_max_bytes", PAGE, 0, CONF_CHECK_MIN,
1536
CONF_DONT_CHECK_MAX, true);
1537
CONF_HANDLE_SIZE_T(opt_hpa_sec_opts.bytes_after_flush,
1538
"hpa_sec_bytes_after_flush", PAGE, 0,
1539
CONF_CHECK_MIN, CONF_DONT_CHECK_MAX, true);
1540
CONF_HANDLE_SIZE_T(opt_hpa_sec_opts.batch_fill_extra,
1541
"hpa_sec_batch_fill_extra", 0, HUGEPAGE_PAGES,
1542
CONF_CHECK_MIN, CONF_CHECK_MAX, true);
1543
1544
if (CONF_MATCH("slab_sizes")) {
1545
if (CONF_MATCH_VALUE("default")) {
1546
sc_data_init(sc_data);
1547
CONF_CONTINUE;
1548
}
1549
bool err;
1550
const char *slab_size_segment_cur = v;
1551
size_t vlen_left = vlen;
1552
do {
1553
size_t slab_start;
1554
size_t slab_end;
1555
size_t pgs;
1556
err = malloc_conf_multi_sizes_next(
1557
&slab_size_segment_cur,
1558
&vlen_left, &slab_start, &slab_end,
1559
&pgs);
1560
if (!err) {
1561
sc_data_update_slab_size(
1562
sc_data, slab_start,
1563
slab_end, (int)pgs);
1564
} else {
1565
CONF_ERROR("Invalid settings "
1566
"for slab_sizes",
1567
k, klen, v, vlen);
1568
}
1569
} while (!err && vlen_left > 0);
1570
CONF_CONTINUE;
1571
}
1572
if (config_prof) {
1573
CONF_HANDLE_BOOL(opt_prof, "prof")
1574
CONF_HANDLE_CHAR_P(opt_prof_prefix,
1575
"prof_prefix", "jeprof")
1576
CONF_HANDLE_BOOL(opt_prof_active, "prof_active")
1577
CONF_HANDLE_BOOL(opt_prof_thread_active_init,
1578
"prof_thread_active_init")
1579
CONF_HANDLE_SIZE_T(opt_lg_prof_sample,
1580
"lg_prof_sample", 0, (sizeof(uint64_t) << 3)
1581
- 1, CONF_DONT_CHECK_MIN, CONF_CHECK_MAX,
1582
true)
1583
CONF_HANDLE_BOOL(opt_prof_accum, "prof_accum")
1584
CONF_HANDLE_SSIZE_T(opt_lg_prof_interval,
1585
"lg_prof_interval", -1,
1586
(sizeof(uint64_t) << 3) - 1)
1587
CONF_HANDLE_BOOL(opt_prof_gdump, "prof_gdump")
1588
CONF_HANDLE_BOOL(opt_prof_final, "prof_final")
1589
CONF_HANDLE_BOOL(opt_prof_leak, "prof_leak")
1590
CONF_HANDLE_BOOL(opt_prof_leak_error,
1591
"prof_leak_error")
1592
CONF_HANDLE_BOOL(opt_prof_log, "prof_log")
1593
CONF_HANDLE_SSIZE_T(opt_prof_recent_alloc_max,
1594
"prof_recent_alloc_max", -1, SSIZE_MAX)
1595
CONF_HANDLE_BOOL(opt_prof_stats, "prof_stats")
1596
CONF_HANDLE_BOOL(opt_prof_sys_thread_name,
1597
"prof_sys_thread_name")
1598
if (CONF_MATCH("prof_time_resolution")) {
1599
if (CONF_MATCH_VALUE("default")) {
1600
opt_prof_time_res =
1601
prof_time_res_default;
1602
} else if (CONF_MATCH_VALUE("high")) {
1603
if (!config_high_res_timer) {
1604
CONF_ERROR(
1605
"No high resolution"
1606
" timer support",
1607
k, klen, v, vlen);
1608
} else {
1609
opt_prof_time_res =
1610
prof_time_res_high;
1611
}
1612
} else {
1613
CONF_ERROR("Invalid conf value",
1614
k, klen, v, vlen);
1615
}
1616
CONF_CONTINUE;
1617
}
1618
/*
1619
* Undocumented. When set to false, don't
1620
* correct for an unbiasing bug in jeprof
1621
* attribution. This can be handy if you want
1622
* to get consistent numbers from your binary
1623
* across different jemalloc versions, even if
1624
* those numbers are incorrect. The default is
1625
* true.
1626
*/
1627
CONF_HANDLE_BOOL(opt_prof_unbias, "prof_unbias")
1628
}
1629
if (config_log) {
1630
if (CONF_MATCH("log")) {
1631
size_t cpylen = (
1632
vlen <= sizeof(log_var_names) ?
1633
vlen : sizeof(log_var_names) - 1);
1634
strncpy(log_var_names, v, cpylen);
1635
log_var_names[cpylen] = '\0';
1636
CONF_CONTINUE;
1637
}
1638
}
1639
if (CONF_MATCH("thp")) {
1640
bool match = false;
1641
for (int m = 0; m < thp_mode_names_limit; m++) {
1642
if (strncmp(thp_mode_names[m],v, vlen)
1643
== 0) {
1644
if (!have_madvise_huge && !have_memcntl) {
1645
CONF_ERROR(
1646
"No THP support",
1647
k, klen, v, vlen);
1648
}
1649
opt_thp = m;
1650
match = true;
1651
break;
1652
}
1653
}
1654
if (!match) {
1655
CONF_ERROR("Invalid conf value",
1656
k, klen, v, vlen);
1657
}
1658
CONF_CONTINUE;
1659
}
1660
if (CONF_MATCH("zero_realloc")) {
1661
if (CONF_MATCH_VALUE("alloc")) {
1662
opt_zero_realloc_action
1663
= zero_realloc_action_alloc;
1664
} else if (CONF_MATCH_VALUE("free")) {
1665
opt_zero_realloc_action
1666
= zero_realloc_action_free;
1667
} else if (CONF_MATCH_VALUE("abort")) {
1668
opt_zero_realloc_action
1669
= zero_realloc_action_abort;
1670
} else {
1671
CONF_ERROR("Invalid conf value",
1672
k, klen, v, vlen);
1673
}
1674
CONF_CONTINUE;
1675
}
1676
if (config_uaf_detection &&
1677
CONF_MATCH("lg_san_uaf_align")) {
1678
ssize_t a;
1679
CONF_VALUE_READ(ssize_t, a)
1680
if (CONF_VALUE_READ_FAIL() || a < -1) {
1681
CONF_ERROR("Invalid conf value",
1682
k, klen, v, vlen);
1683
}
1684
if (a == -1) {
1685
opt_lg_san_uaf_align = -1;
1686
CONF_CONTINUE;
1687
}
1688
1689
/* clip if necessary */
1690
ssize_t max_allowed = (sizeof(size_t) << 3) - 1;
1691
ssize_t min_allowed = LG_PAGE;
1692
if (a > max_allowed) {
1693
a = max_allowed;
1694
} else if (a < min_allowed) {
1695
a = min_allowed;
1696
}
1697
1698
opt_lg_san_uaf_align = a;
1699
CONF_CONTINUE;
1700
}
1701
1702
CONF_HANDLE_SIZE_T(opt_san_guard_small,
1703
"san_guard_small", 0, SIZE_T_MAX,
1704
CONF_DONT_CHECK_MIN, CONF_DONT_CHECK_MAX, false)
1705
CONF_HANDLE_SIZE_T(opt_san_guard_large,
1706
"san_guard_large", 0, SIZE_T_MAX,
1707
CONF_DONT_CHECK_MIN, CONF_DONT_CHECK_MAX, false)
1708
1709
CONF_ERROR("Invalid conf pair", k, klen, v, vlen);
1710
#undef CONF_ERROR
1711
#undef CONF_CONTINUE
1712
#undef CONF_MATCH
1713
#undef CONF_MATCH_VALUE
1714
#undef CONF_HANDLE_BOOL
1715
#undef CONF_DONT_CHECK_MIN
1716
#undef CONF_CHECK_MIN
1717
#undef CONF_DONT_CHECK_MAX
1718
#undef CONF_CHECK_MAX
1719
#undef CONF_HANDLE_T
1720
#undef CONF_HANDLE_T_U
1721
#undef CONF_HANDLE_T_SIGNED
1722
#undef CONF_HANDLE_UNSIGNED
1723
#undef CONF_HANDLE_SIZE_T
1724
#undef CONF_HANDLE_SSIZE_T
1725
#undef CONF_HANDLE_CHAR_P
1726
/* Re-enable diagnostic "-Wtype-limits" */
1727
JEMALLOC_DIAGNOSTIC_POP
1728
}
1729
if (opt_abort_conf && had_conf_error) {
1730
malloc_abort_invalid_conf();
1731
}
1732
}
1733
atomic_store_b(&log_init_done, true, ATOMIC_RELEASE);
1734
}
1735
1736
static bool
1737
malloc_conf_init_check_deps(void) {
1738
if (opt_prof_leak_error && !opt_prof_final) {
1739
malloc_printf("<jemalloc>: prof_leak_error is set w/o "
1740
"prof_final.\n");
1741
return true;
1742
}
1743
1744
return false;
1745
}
1746
1747
static void
1748
malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
1749
const char *opts_cache[MALLOC_CONF_NSOURCES] = {NULL, NULL, NULL, NULL,
1750
NULL};
1751
char buf[PATH_MAX + 1];
1752
1753
/* The first call only set the confirm_conf option and opts_cache */
1754
malloc_conf_init_helper(NULL, NULL, true, opts_cache, buf);
1755
malloc_conf_init_helper(sc_data, bin_shard_sizes, false, opts_cache,
1756
NULL);
1757
if (malloc_conf_init_check_deps()) {
1758
/* check_deps does warning msg only; abort below if needed. */
1759
if (opt_abort_conf) {
1760
malloc_abort_invalid_conf();
1761
}
1762
}
1763
}
1764
1765
#undef MALLOC_CONF_NSOURCES
1766
1767
static bool
1768
malloc_init_hard_needed(void) {
1769
if (malloc_initialized() || (IS_INITIALIZER && malloc_init_state ==
1770
malloc_init_recursible)) {
1771
/*
1772
* Another thread initialized the allocator before this one
1773
* acquired init_lock, or this thread is the initializing
1774
* thread, and it is recursively allocating.
1775
*/
1776
return false;
1777
}
1778
#ifdef JEMALLOC_THREADED_INIT
1779
if (malloc_initializer != NO_INITIALIZER && !IS_INITIALIZER) {
1780
/* Busy-wait until the initializing thread completes. */
1781
spin_t spinner = SPIN_INITIALIZER;
1782
do {
1783
malloc_mutex_unlock(TSDN_NULL, &init_lock);
1784
spin_adaptive(&spinner);
1785
malloc_mutex_lock(TSDN_NULL, &init_lock);
1786
} while (!malloc_initialized());
1787
return false;
1788
}
1789
#endif
1790
return true;
1791
}
1792
1793
static bool
1794
malloc_init_hard_a0_locked() {
1795
malloc_initializer = INITIALIZER;
1796
1797
JEMALLOC_DIAGNOSTIC_PUSH
1798
JEMALLOC_DIAGNOSTIC_IGNORE_MISSING_STRUCT_FIELD_INITIALIZERS
1799
sc_data_t sc_data = {0};
1800
JEMALLOC_DIAGNOSTIC_POP
1801
1802
/*
1803
* Ordering here is somewhat tricky; we need sc_boot() first, since that
1804
* determines what the size classes will be, and then
1805
* malloc_conf_init(), since any slab size tweaking will need to be done
1806
* before sz_boot and bin_info_boot, which assume that the values they
1807
* read out of sc_data_global are final.
1808
*/
1809
sc_boot(&sc_data);
1810
unsigned bin_shard_sizes[SC_NBINS];
1811
bin_shard_sizes_boot(bin_shard_sizes);
1812
/*
1813
* prof_boot0 only initializes opt_prof_prefix. We need to do it before
1814
* we parse malloc_conf options, in case malloc_conf parsing overwrites
1815
* it.
1816
*/
1817
if (config_prof) {
1818
prof_boot0();
1819
}
1820
malloc_conf_init(&sc_data, bin_shard_sizes);
1821
san_init(opt_lg_san_uaf_align);
1822
sz_boot(&sc_data, opt_cache_oblivious);
1823
bin_info_boot(&sc_data, bin_shard_sizes);
1824
1825
if (opt_stats_print) {
1826
/* Print statistics at exit. */
1827
if (atexit(stats_print_atexit) != 0) {
1828
malloc_write("<jemalloc>: Error in atexit()\n");
1829
if (opt_abort) {
1830
abort();
1831
}
1832
}
1833
}
1834
1835
if (stats_boot()) {
1836
return true;
1837
}
1838
if (pages_boot()) {
1839
return true;
1840
}
1841
if (base_boot(TSDN_NULL)) {
1842
return true;
1843
}
1844
/* emap_global is static, hence zeroed. */
1845
if (emap_init(&arena_emap_global, b0get(), /* zeroed */ true)) {
1846
return true;
1847
}
1848
if (extent_boot()) {
1849
return true;
1850
}
1851
if (ctl_boot()) {
1852
return true;
1853
}
1854
if (config_prof) {
1855
prof_boot1();
1856
}
1857
if (opt_hpa && !hpa_supported()) {
1858
malloc_printf("<jemalloc>: HPA not supported in the current "
1859
"configuration; %s.",
1860
opt_abort_conf ? "aborting" : "disabling");
1861
if (opt_abort_conf) {
1862
malloc_abort_invalid_conf();
1863
} else {
1864
opt_hpa = false;
1865
}
1866
}
1867
if (arena_boot(&sc_data, b0get(), opt_hpa)) {
1868
return true;
1869
}
1870
if (tcache_boot(TSDN_NULL, b0get())) {
1871
return true;
1872
}
1873
if (malloc_mutex_init(&arenas_lock, "arenas", WITNESS_RANK_ARENAS,
1874
malloc_mutex_rank_exclusive)) {
1875
return true;
1876
}
1877
hook_boot();
1878
/*
1879
* Create enough scaffolding to allow recursive allocation in
1880
* malloc_ncpus().
1881
*/
1882
narenas_auto = 1;
1883
manual_arena_base = narenas_auto + 1;
1884
memset(arenas, 0, sizeof(arena_t *) * narenas_auto);
1885
/*
1886
* Initialize one arena here. The rest are lazily created in
1887
* arena_choose_hard().
1888
*/
1889
if (arena_init(TSDN_NULL, 0, &arena_config_default) == NULL) {
1890
return true;
1891
}
1892
a0 = arena_get(TSDN_NULL, 0, false);
1893
1894
if (opt_hpa && !hpa_supported()) {
1895
malloc_printf("<jemalloc>: HPA not supported in the current "
1896
"configuration; %s.",
1897
opt_abort_conf ? "aborting" : "disabling");
1898
if (opt_abort_conf) {
1899
malloc_abort_invalid_conf();
1900
} else {
1901
opt_hpa = false;
1902
}
1903
} else if (opt_hpa) {
1904
hpa_shard_opts_t hpa_shard_opts = opt_hpa_opts;
1905
hpa_shard_opts.deferral_allowed = background_thread_enabled();
1906
if (pa_shard_enable_hpa(TSDN_NULL, &a0->pa_shard,
1907
&hpa_shard_opts, &opt_hpa_sec_opts)) {
1908
return true;
1909
}
1910
}
1911
1912
malloc_init_state = malloc_init_a0_initialized;
1913
1914
return false;
1915
}
1916
1917
static bool
1918
malloc_init_hard_a0(void) {
1919
bool ret;
1920
1921
malloc_mutex_lock(TSDN_NULL, &init_lock);
1922
ret = malloc_init_hard_a0_locked();
1923
malloc_mutex_unlock(TSDN_NULL, &init_lock);
1924
return ret;
1925
}
1926
1927
/* Initialize data structures which may trigger recursive allocation. */
1928
static bool
1929
malloc_init_hard_recursible(void) {
1930
malloc_init_state = malloc_init_recursible;
1931
1932
ncpus = malloc_ncpus();
1933
if (opt_percpu_arena != percpu_arena_disabled) {
1934
bool cpu_count_is_deterministic =
1935
malloc_cpu_count_is_deterministic();
1936
if (!cpu_count_is_deterministic) {
1937
/*
1938
* If # of CPU is not deterministic, and narenas not
1939
* specified, disables per cpu arena since it may not
1940
* detect CPU IDs properly.
1941
*/
1942
if (opt_narenas == 0) {
1943
opt_percpu_arena = percpu_arena_disabled;
1944
malloc_write("<jemalloc>: Number of CPUs "
1945
"detected is not deterministic. Per-CPU "
1946
"arena disabled.\n");
1947
if (opt_abort_conf) {
1948
malloc_abort_invalid_conf();
1949
}
1950
if (opt_abort) {
1951
abort();
1952
}
1953
}
1954
}
1955
}
1956
1957
#if (defined(JEMALLOC_HAVE_PTHREAD_ATFORK) && !defined(JEMALLOC_MUTEX_INIT_CB) \
1958
&& !defined(JEMALLOC_ZONE) && !defined(_WIN32) && \
1959
!defined(__native_client__))
1960
/* LinuxThreads' pthread_atfork() allocates. */
1961
if (pthread_atfork(jemalloc_prefork, jemalloc_postfork_parent,
1962
jemalloc_postfork_child) != 0) {
1963
malloc_write("<jemalloc>: Error in pthread_atfork()\n");
1964
if (opt_abort) {
1965
abort();
1966
}
1967
return true;
1968
}
1969
#endif
1970
1971
if (background_thread_boot0()) {
1972
return true;
1973
}
1974
1975
return false;
1976
}
1977
1978
static unsigned
1979
malloc_narenas_default(void) {
1980
assert(ncpus > 0);
1981
/*
1982
* For SMP systems, create more than one arena per CPU by
1983
* default.
1984
*/
1985
if (ncpus > 1) {
1986
fxp_t fxp_ncpus = FXP_INIT_INT(ncpus);
1987
fxp_t goal = fxp_mul(fxp_ncpus, opt_narenas_ratio);
1988
uint32_t int_goal = fxp_round_nearest(goal);
1989
if (int_goal == 0) {
1990
return 1;
1991
}
1992
return int_goal;
1993
} else {
1994
return 1;
1995
}
1996
}
1997
1998
static percpu_arena_mode_t
1999
percpu_arena_as_initialized(percpu_arena_mode_t mode) {
2000
assert(!malloc_initialized());
2001
assert(mode <= percpu_arena_disabled);
2002
2003
if (mode != percpu_arena_disabled) {
2004
mode += percpu_arena_mode_enabled_base;
2005
}
2006
2007
return mode;
2008
}
2009
2010
static bool
2011
malloc_init_narenas(void) {
2012
assert(ncpus > 0);
2013
2014
if (opt_percpu_arena != percpu_arena_disabled) {
2015
if (!have_percpu_arena || malloc_getcpu() < 0) {
2016
opt_percpu_arena = percpu_arena_disabled;
2017
malloc_printf("<jemalloc>: perCPU arena getcpu() not "
2018
"available. Setting narenas to %u.\n", opt_narenas ?
2019
opt_narenas : malloc_narenas_default());
2020
if (opt_abort) {
2021
abort();
2022
}
2023
} else {
2024
if (ncpus >= MALLOCX_ARENA_LIMIT) {
2025
malloc_printf("<jemalloc>: narenas w/ percpu"
2026
"arena beyond limit (%d)\n", ncpus);
2027
if (opt_abort) {
2028
abort();
2029
}
2030
return true;
2031
}
2032
/* NB: opt_percpu_arena isn't fully initialized yet. */
2033
if (percpu_arena_as_initialized(opt_percpu_arena) ==
2034
per_phycpu_arena && ncpus % 2 != 0) {
2035
malloc_printf("<jemalloc>: invalid "
2036
"configuration -- per physical CPU arena "
2037
"with odd number (%u) of CPUs (no hyper "
2038
"threading?).\n", ncpus);
2039
if (opt_abort)
2040
abort();
2041
}
2042
unsigned n = percpu_arena_ind_limit(
2043
percpu_arena_as_initialized(opt_percpu_arena));
2044
if (opt_narenas < n) {
2045
/*
2046
* If narenas is specified with percpu_arena
2047
* enabled, actual narenas is set as the greater
2048
* of the two. percpu_arena_choose will be free
2049
* to use any of the arenas based on CPU
2050
* id. This is conservative (at a small cost)
2051
* but ensures correctness.
2052
*
2053
* If for some reason the ncpus determined at
2054
* boot is not the actual number (e.g. because
2055
* of affinity setting from numactl), reserving
2056
* narenas this way provides a workaround for
2057
* percpu_arena.
2058
*/
2059
opt_narenas = n;
2060
}
2061
}
2062
}
2063
if (opt_narenas == 0) {
2064
opt_narenas = malloc_narenas_default();
2065
}
2066
assert(opt_narenas > 0);
2067
2068
narenas_auto = opt_narenas;
2069
/*
2070
* Limit the number of arenas to the indexing range of MALLOCX_ARENA().
2071
*/
2072
if (narenas_auto >= MALLOCX_ARENA_LIMIT) {
2073
narenas_auto = MALLOCX_ARENA_LIMIT - 1;
2074
malloc_printf("<jemalloc>: Reducing narenas to limit (%d)\n",
2075
narenas_auto);
2076
}
2077
narenas_total_set(narenas_auto);
2078
if (arena_init_huge()) {
2079
narenas_total_inc();
2080
}
2081
manual_arena_base = narenas_total_get();
2082
2083
return false;
2084
}
2085
2086
static void
2087
malloc_init_percpu(void) {
2088
opt_percpu_arena = percpu_arena_as_initialized(opt_percpu_arena);
2089
}
2090
2091
static bool
2092
malloc_init_hard_finish(void) {
2093
if (malloc_mutex_boot()) {
2094
return true;
2095
}
2096
2097
malloc_init_state = malloc_init_initialized;
2098
malloc_slow_flag_init();
2099
2100
return false;
2101
}
2102
2103
static void
2104
malloc_init_hard_cleanup(tsdn_t *tsdn, bool reentrancy_set) {
2105
malloc_mutex_assert_owner(tsdn, &init_lock);
2106
malloc_mutex_unlock(tsdn, &init_lock);
2107
if (reentrancy_set) {
2108
assert(!tsdn_null(tsdn));
2109
tsd_t *tsd = tsdn_tsd(tsdn);
2110
assert(tsd_reentrancy_level_get(tsd) > 0);
2111
post_reentrancy(tsd);
2112
}
2113
}
2114
2115
static bool
2116
malloc_init_hard(void) {
2117
tsd_t *tsd;
2118
2119
#if defined(_WIN32) && _WIN32_WINNT < 0x0600
2120
_init_init_lock();
2121
#endif
2122
malloc_mutex_lock(TSDN_NULL, &init_lock);
2123
2124
#define UNLOCK_RETURN(tsdn, ret, reentrancy) \
2125
malloc_init_hard_cleanup(tsdn, reentrancy); \
2126
return ret;
2127
2128
if (!malloc_init_hard_needed()) {
2129
UNLOCK_RETURN(TSDN_NULL, false, false)
2130
}
2131
2132
if (malloc_init_state != malloc_init_a0_initialized &&
2133
malloc_init_hard_a0_locked()) {
2134
UNLOCK_RETURN(TSDN_NULL, true, false)
2135
}
2136
2137
malloc_mutex_unlock(TSDN_NULL, &init_lock);
2138
/* Recursive allocation relies on functional tsd. */
2139
tsd = malloc_tsd_boot0();
2140
if (tsd == NULL) {
2141
return true;
2142
}
2143
if (malloc_init_hard_recursible()) {
2144
return true;
2145
}
2146
2147
malloc_mutex_lock(tsd_tsdn(tsd), &init_lock);
2148
/* Set reentrancy level to 1 during init. */
2149
pre_reentrancy(tsd, NULL);
2150
/* Initialize narenas before prof_boot2 (for allocation). */
2151
if (malloc_init_narenas()
2152
|| background_thread_boot1(tsd_tsdn(tsd), b0get())) {
2153
UNLOCK_RETURN(tsd_tsdn(tsd), true, true)
2154
}
2155
if (config_prof && prof_boot2(tsd, b0get())) {
2156
UNLOCK_RETURN(tsd_tsdn(tsd), true, true)
2157
}
2158
2159
malloc_init_percpu();
2160
2161
if (malloc_init_hard_finish()) {
2162
UNLOCK_RETURN(tsd_tsdn(tsd), true, true)
2163
}
2164
post_reentrancy(tsd);
2165
malloc_mutex_unlock(tsd_tsdn(tsd), &init_lock);
2166
2167
witness_assert_lockless(witness_tsd_tsdn(
2168
tsd_witness_tsdp_get_unsafe(tsd)));
2169
malloc_tsd_boot1();
2170
/* Update TSD after tsd_boot1. */
2171
tsd = tsd_fetch();
2172
if (opt_background_thread) {
2173
assert(have_background_thread);
2174
/*
2175
* Need to finish init & unlock first before creating background
2176
* threads (pthread_create depends on malloc). ctl_init (which
2177
* sets isthreaded) needs to be called without holding any lock.
2178
*/
2179
background_thread_ctl_init(tsd_tsdn(tsd));
2180
if (background_thread_create(tsd, 0)) {
2181
return true;
2182
}
2183
}
2184
#undef UNLOCK_RETURN
2185
return false;
2186
}
2187
2188
/*
2189
* End initialization functions.
2190
*/
2191
/******************************************************************************/
2192
/*
2193
* Begin allocation-path internal functions and data structures.
2194
*/
2195
2196
/*
2197
* Settings determined by the documented behavior of the allocation functions.
2198
*/
2199
typedef struct static_opts_s static_opts_t;
2200
struct static_opts_s {
2201
/* Whether or not allocation size may overflow. */
2202
bool may_overflow;
2203
2204
/*
2205
* Whether or not allocations (with alignment) of size 0 should be
2206
* treated as size 1.
2207
*/
2208
bool bump_empty_aligned_alloc;
2209
/*
2210
* Whether to assert that allocations are not of size 0 (after any
2211
* bumping).
2212
*/
2213
bool assert_nonempty_alloc;
2214
2215
/*
2216
* Whether or not to modify the 'result' argument to malloc in case of
2217
* error.
2218
*/
2219
bool null_out_result_on_error;
2220
/* Whether to set errno when we encounter an error condition. */
2221
bool set_errno_on_error;
2222
2223
/*
2224
* The minimum valid alignment for functions requesting aligned storage.
2225
*/
2226
size_t min_alignment;
2227
2228
/* The error string to use if we oom. */
2229
const char *oom_string;
2230
/* The error string to use if the passed-in alignment is invalid. */
2231
const char *invalid_alignment_string;
2232
2233
/*
2234
* False if we're configured to skip some time-consuming operations.
2235
*
2236
* This isn't really a malloc "behavior", but it acts as a useful
2237
* summary of several other static (or at least, static after program
2238
* initialization) options.
2239
*/
2240
bool slow;
2241
/*
2242
* Return size.
2243
*/
2244
bool usize;
2245
};
2246
2247
JEMALLOC_ALWAYS_INLINE void
2248
static_opts_init(static_opts_t *static_opts) {
2249
static_opts->may_overflow = false;
2250
static_opts->bump_empty_aligned_alloc = false;
2251
static_opts->assert_nonempty_alloc = false;
2252
static_opts->null_out_result_on_error = false;
2253
static_opts->set_errno_on_error = false;
2254
static_opts->min_alignment = 0;
2255
static_opts->oom_string = "";
2256
static_opts->invalid_alignment_string = "";
2257
static_opts->slow = false;
2258
static_opts->usize = false;
2259
}
2260
2261
/*
2262
* These correspond to the macros in jemalloc/jemalloc_macros.h. Broadly, we
2263
* should have one constant here per magic value there. Note however that the
2264
* representations need not be related.
2265
*/
2266
#define TCACHE_IND_NONE ((unsigned)-1)
2267
#define TCACHE_IND_AUTOMATIC ((unsigned)-2)
2268
#define ARENA_IND_AUTOMATIC ((unsigned)-1)
2269
2270
typedef struct dynamic_opts_s dynamic_opts_t;
2271
struct dynamic_opts_s {
2272
void **result;
2273
size_t usize;
2274
size_t num_items;
2275
size_t item_size;
2276
size_t alignment;
2277
bool zero;
2278
unsigned tcache_ind;
2279
unsigned arena_ind;
2280
};
2281
2282
JEMALLOC_ALWAYS_INLINE void
2283
dynamic_opts_init(dynamic_opts_t *dynamic_opts) {
2284
dynamic_opts->result = NULL;
2285
dynamic_opts->usize = 0;
2286
dynamic_opts->num_items = 0;
2287
dynamic_opts->item_size = 0;
2288
dynamic_opts->alignment = 0;
2289
dynamic_opts->zero = false;
2290
dynamic_opts->tcache_ind = TCACHE_IND_AUTOMATIC;
2291
dynamic_opts->arena_ind = ARENA_IND_AUTOMATIC;
2292
}
2293
2294
/*
2295
* ind parameter is optional and is only checked and filled if alignment == 0;
2296
* return true if result is out of range.
2297
*/
2298
JEMALLOC_ALWAYS_INLINE bool
2299
aligned_usize_get(size_t size, size_t alignment, size_t *usize, szind_t *ind,
2300
bool bump_empty_aligned_alloc) {
2301
assert(usize != NULL);
2302
if (alignment == 0) {
2303
if (ind != NULL) {
2304
*ind = sz_size2index(size);
2305
if (unlikely(*ind >= SC_NSIZES)) {
2306
return true;
2307
}
2308
*usize = sz_index2size(*ind);
2309
assert(*usize > 0 && *usize <= SC_LARGE_MAXCLASS);
2310
return false;
2311
}
2312
*usize = sz_s2u(size);
2313
} else {
2314
if (bump_empty_aligned_alloc && unlikely(size == 0)) {
2315
size = 1;
2316
}
2317
*usize = sz_sa2u(size, alignment);
2318
}
2319
if (unlikely(*usize == 0 || *usize > SC_LARGE_MAXCLASS)) {
2320
return true;
2321
}
2322
return false;
2323
}
2324
2325
JEMALLOC_ALWAYS_INLINE bool
2326
zero_get(bool guarantee, bool slow) {
2327
if (config_fill && slow && unlikely(opt_zero)) {
2328
return true;
2329
} else {
2330
return guarantee;
2331
}
2332
}
2333
2334
JEMALLOC_ALWAYS_INLINE tcache_t *
2335
tcache_get_from_ind(tsd_t *tsd, unsigned tcache_ind, bool slow, bool is_alloc) {
2336
tcache_t *tcache;
2337
if (tcache_ind == TCACHE_IND_AUTOMATIC) {
2338
if (likely(!slow)) {
2339
/* Getting tcache ptr unconditionally. */
2340
tcache = tsd_tcachep_get(tsd);
2341
assert(tcache == tcache_get(tsd));
2342
} else if (is_alloc ||
2343
likely(tsd_reentrancy_level_get(tsd) == 0)) {
2344
tcache = tcache_get(tsd);
2345
} else {
2346
tcache = NULL;
2347
}
2348
} else {
2349
/*
2350
* Should not specify tcache on deallocation path when being
2351
* reentrant.
2352
*/
2353
assert(is_alloc || tsd_reentrancy_level_get(tsd) == 0 ||
2354
tsd_state_nocleanup(tsd));
2355
if (tcache_ind == TCACHE_IND_NONE) {
2356
tcache = NULL;
2357
} else {
2358
tcache = tcaches_get(tsd, tcache_ind);
2359
}
2360
}
2361
return tcache;
2362
}
2363
2364
/* Return true if a manual arena is specified and arena_get() OOMs. */
2365
JEMALLOC_ALWAYS_INLINE bool
2366
arena_get_from_ind(tsd_t *tsd, unsigned arena_ind, arena_t **arena_p) {
2367
if (arena_ind == ARENA_IND_AUTOMATIC) {
2368
/*
2369
* In case of automatic arena management, we defer arena
2370
* computation until as late as we can, hoping to fill the
2371
* allocation out of the tcache.
2372
*/
2373
*arena_p = NULL;
2374
} else {
2375
*arena_p = arena_get(tsd_tsdn(tsd), arena_ind, true);
2376
if (unlikely(*arena_p == NULL) && arena_ind >= narenas_auto) {
2377
return true;
2378
}
2379
}
2380
return false;
2381
}
2382
2383
/* ind is ignored if dopts->alignment > 0. */
2384
JEMALLOC_ALWAYS_INLINE void *
2385
imalloc_no_sample(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd,
2386
size_t size, size_t usize, szind_t ind) {
2387
/* Fill in the tcache. */
2388
tcache_t *tcache = tcache_get_from_ind(tsd, dopts->tcache_ind,
2389
sopts->slow, /* is_alloc */ true);
2390
2391
/* Fill in the arena. */
2392
arena_t *arena;
2393
if (arena_get_from_ind(tsd, dopts->arena_ind, &arena)) {
2394
return NULL;
2395
}
2396
2397
if (unlikely(dopts->alignment != 0)) {
2398
return ipalloct(tsd_tsdn(tsd), usize, dopts->alignment,
2399
dopts->zero, tcache, arena);
2400
}
2401
2402
return iallocztm(tsd_tsdn(tsd), size, ind, dopts->zero, tcache, false,
2403
arena, sopts->slow);
2404
}
2405
2406
JEMALLOC_ALWAYS_INLINE void *
2407
imalloc_sample(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd,
2408
size_t usize, szind_t ind) {
2409
void *ret;
2410
2411
/*
2412
* For small allocations, sampling bumps the usize. If so, we allocate
2413
* from the ind_large bucket.
2414
*/
2415
szind_t ind_large;
2416
size_t bumped_usize = usize;
2417
2418
dopts->alignment = prof_sample_align(dopts->alignment);
2419
if (usize <= SC_SMALL_MAXCLASS) {
2420
assert(((dopts->alignment == 0) ?
2421
sz_s2u(SC_LARGE_MINCLASS) :
2422
sz_sa2u(SC_LARGE_MINCLASS, dopts->alignment))
2423
== SC_LARGE_MINCLASS);
2424
ind_large = sz_size2index(SC_LARGE_MINCLASS);
2425
bumped_usize = sz_s2u(SC_LARGE_MINCLASS);
2426
ret = imalloc_no_sample(sopts, dopts, tsd, bumped_usize,
2427
bumped_usize, ind_large);
2428
if (unlikely(ret == NULL)) {
2429
return NULL;
2430
}
2431
arena_prof_promote(tsd_tsdn(tsd), ret, usize);
2432
} else {
2433
ret = imalloc_no_sample(sopts, dopts, tsd, usize, usize, ind);
2434
}
2435
assert(prof_sample_aligned(ret));
2436
2437
return ret;
2438
}
2439
2440
/*
2441
* Returns true if the allocation will overflow, and false otherwise. Sets
2442
* *size to the product either way.
2443
*/
2444
JEMALLOC_ALWAYS_INLINE bool
2445
compute_size_with_overflow(bool may_overflow, dynamic_opts_t *dopts,
2446
size_t *size) {
2447
/*
2448
* This function is just num_items * item_size, except that we may have
2449
* to check for overflow.
2450
*/
2451
2452
if (!may_overflow) {
2453
assert(dopts->num_items == 1);
2454
*size = dopts->item_size;
2455
return false;
2456
}
2457
2458
/* A size_t with its high-half bits all set to 1. */
2459
static const size_t high_bits = SIZE_T_MAX << (sizeof(size_t) * 8 / 2);
2460
2461
*size = dopts->item_size * dopts->num_items;
2462
2463
if (unlikely(*size == 0)) {
2464
return (dopts->num_items != 0 && dopts->item_size != 0);
2465
}
2466
2467
/*
2468
* We got a non-zero size, but we don't know if we overflowed to get
2469
* there. To avoid having to do a divide, we'll be clever and note that
2470
* if both A and B can be represented in N/2 bits, then their product
2471
* can be represented in N bits (without the possibility of overflow).
2472
*/
2473
if (likely((high_bits & (dopts->num_items | dopts->item_size)) == 0)) {
2474
return false;
2475
}
2476
if (likely(*size / dopts->item_size == dopts->num_items)) {
2477
return false;
2478
}
2479
return true;
2480
}
2481
2482
JEMALLOC_ALWAYS_INLINE int
2483
imalloc_body(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd) {
2484
/* Where the actual allocated memory will live. */
2485
void *allocation = NULL;
2486
/* Filled in by compute_size_with_overflow below. */
2487
size_t size = 0;
2488
/*
2489
* The zero initialization for ind is actually dead store, in that its
2490
* value is reset before any branch on its value is taken. Sometimes
2491
* though, it's convenient to pass it as arguments before this point.
2492
* To avoid undefined behavior then, we initialize it with dummy stores.
2493
*/
2494
szind_t ind = 0;
2495
/* usize will always be properly initialized. */
2496
size_t usize;
2497
2498
/* Reentrancy is only checked on slow path. */
2499
int8_t reentrancy_level;
2500
2501
/* Compute the amount of memory the user wants. */
2502
if (unlikely(compute_size_with_overflow(sopts->may_overflow, dopts,
2503
&size))) {
2504
goto label_oom;
2505
}
2506
2507
if (unlikely(dopts->alignment < sopts->min_alignment
2508
|| (dopts->alignment & (dopts->alignment - 1)) != 0)) {
2509
goto label_invalid_alignment;
2510
}
2511
2512
/* This is the beginning of the "core" algorithm. */
2513
dopts->zero = zero_get(dopts->zero, sopts->slow);
2514
if (aligned_usize_get(size, dopts->alignment, &usize, &ind,
2515
sopts->bump_empty_aligned_alloc)) {
2516
goto label_oom;
2517
}
2518
dopts->usize = usize;
2519
/* Validate the user input. */
2520
if (sopts->assert_nonempty_alloc) {
2521
assert (size != 0);
2522
}
2523
2524
check_entry_exit_locking(tsd_tsdn(tsd));
2525
2526
/*
2527
* If we need to handle reentrancy, we can do it out of a
2528
* known-initialized arena (i.e. arena 0).
2529
*/
2530
reentrancy_level = tsd_reentrancy_level_get(tsd);
2531
if (sopts->slow && unlikely(reentrancy_level > 0)) {
2532
/*
2533
* We should never specify particular arenas or tcaches from
2534
* within our internal allocations.
2535
*/
2536
assert(dopts->tcache_ind == TCACHE_IND_AUTOMATIC ||
2537
dopts->tcache_ind == TCACHE_IND_NONE);
2538
assert(dopts->arena_ind == ARENA_IND_AUTOMATIC);
2539
dopts->tcache_ind = TCACHE_IND_NONE;
2540
/* We know that arena 0 has already been initialized. */
2541
dopts->arena_ind = 0;
2542
}
2543
2544
/*
2545
* If dopts->alignment > 0, then ind is still 0, but usize was computed
2546
* in the previous if statement. Down the positive alignment path,
2547
* imalloc_no_sample and imalloc_sample will ignore ind.
2548
*/
2549
2550
/* If profiling is on, get our profiling context. */
2551
if (config_prof && opt_prof) {
2552
bool prof_active = prof_active_get_unlocked();
2553
bool sample_event = te_prof_sample_event_lookahead(tsd, usize);
2554
prof_tctx_t *tctx = prof_alloc_prep(tsd, prof_active,
2555
sample_event);
2556
2557
emap_alloc_ctx_t alloc_ctx;
2558
if (likely((uintptr_t)tctx == (uintptr_t)1U)) {
2559
alloc_ctx.slab = (usize <= SC_SMALL_MAXCLASS);
2560
allocation = imalloc_no_sample(
2561
sopts, dopts, tsd, usize, usize, ind);
2562
} else if ((uintptr_t)tctx > (uintptr_t)1U) {
2563
allocation = imalloc_sample(
2564
sopts, dopts, tsd, usize, ind);
2565
alloc_ctx.slab = false;
2566
} else {
2567
allocation = NULL;
2568
}
2569
2570
if (unlikely(allocation == NULL)) {
2571
prof_alloc_rollback(tsd, tctx);
2572
goto label_oom;
2573
}
2574
prof_malloc(tsd, allocation, size, usize, &alloc_ctx, tctx);
2575
} else {
2576
assert(!opt_prof);
2577
allocation = imalloc_no_sample(sopts, dopts, tsd, size, usize,
2578
ind);
2579
if (unlikely(allocation == NULL)) {
2580
goto label_oom;
2581
}
2582
}
2583
2584
/*
2585
* Allocation has been done at this point. We still have some
2586
* post-allocation work to do though.
2587
*/
2588
2589
thread_alloc_event(tsd, usize);
2590
2591
assert(dopts->alignment == 0
2592
|| ((uintptr_t)allocation & (dopts->alignment - 1)) == ZU(0));
2593
2594
assert(usize == isalloc(tsd_tsdn(tsd), allocation));
2595
2596
if (config_fill && sopts->slow && !dopts->zero
2597
&& unlikely(opt_junk_alloc)) {
2598
junk_alloc_callback(allocation, usize);
2599
}
2600
2601
if (sopts->slow) {
2602
UTRACE(0, size, allocation);
2603
}
2604
2605
/* Success! */
2606
check_entry_exit_locking(tsd_tsdn(tsd));
2607
*dopts->result = allocation;
2608
return 0;
2609
2610
label_oom:
2611
if (unlikely(sopts->slow) && config_xmalloc && unlikely(opt_xmalloc)) {
2612
malloc_write(sopts->oom_string);
2613
abort();
2614
}
2615
2616
if (sopts->slow) {
2617
UTRACE(NULL, size, NULL);
2618
}
2619
2620
check_entry_exit_locking(tsd_tsdn(tsd));
2621
2622
if (sopts->set_errno_on_error) {
2623
set_errno(ENOMEM);
2624
}
2625
2626
if (sopts->null_out_result_on_error) {
2627
*dopts->result = NULL;
2628
}
2629
2630
return ENOMEM;
2631
2632
/*
2633
* This label is only jumped to by one goto; we move it out of line
2634
* anyways to avoid obscuring the non-error paths, and for symmetry with
2635
* the oom case.
2636
*/
2637
label_invalid_alignment:
2638
if (config_xmalloc && unlikely(opt_xmalloc)) {
2639
malloc_write(sopts->invalid_alignment_string);
2640
abort();
2641
}
2642
2643
if (sopts->set_errno_on_error) {
2644
set_errno(EINVAL);
2645
}
2646
2647
if (sopts->slow) {
2648
UTRACE(NULL, size, NULL);
2649
}
2650
2651
check_entry_exit_locking(tsd_tsdn(tsd));
2652
2653
if (sopts->null_out_result_on_error) {
2654
*dopts->result = NULL;
2655
}
2656
2657
return EINVAL;
2658
}
2659
2660
JEMALLOC_ALWAYS_INLINE bool
2661
imalloc_init_check(static_opts_t *sopts, dynamic_opts_t *dopts) {
2662
if (unlikely(!malloc_initialized()) && unlikely(malloc_init())) {
2663
if (config_xmalloc && unlikely(opt_xmalloc)) {
2664
malloc_write(sopts->oom_string);
2665
abort();
2666
}
2667
UTRACE(NULL, dopts->num_items * dopts->item_size, NULL);
2668
set_errno(ENOMEM);
2669
*dopts->result = NULL;
2670
2671
return false;
2672
}
2673
2674
return true;
2675
}
2676
2677
/* Returns the errno-style error code of the allocation. */
2678
JEMALLOC_ALWAYS_INLINE int
2679
imalloc(static_opts_t *sopts, dynamic_opts_t *dopts) {
2680
if (tsd_get_allocates() && !imalloc_init_check(sopts, dopts)) {
2681
return ENOMEM;
2682
}
2683
2684
/* We always need the tsd. Let's grab it right away. */
2685
tsd_t *tsd = tsd_fetch();
2686
assert(tsd);
2687
if (likely(tsd_fast(tsd))) {
2688
/* Fast and common path. */
2689
tsd_assert_fast(tsd);
2690
sopts->slow = false;
2691
return imalloc_body(sopts, dopts, tsd);
2692
} else {
2693
if (!tsd_get_allocates() && !imalloc_init_check(sopts, dopts)) {
2694
return ENOMEM;
2695
}
2696
2697
sopts->slow = true;
2698
return imalloc_body(sopts, dopts, tsd);
2699
}
2700
}
2701
2702
JEMALLOC_NOINLINE
2703
void *
2704
malloc_default(size_t size) {
2705
void *ret;
2706
static_opts_t sopts;
2707
dynamic_opts_t dopts;
2708
2709
/*
2710
* This variant has logging hook on exit but not on entry. It's callled
2711
* only by je_malloc, below, which emits the entry one for us (and, if
2712
* it calls us, does so only via tail call).
2713
*/
2714
2715
static_opts_init(&sopts);
2716
dynamic_opts_init(&dopts);
2717
2718
sopts.null_out_result_on_error = true;
2719
sopts.set_errno_on_error = true;
2720
sopts.oom_string = "<jemalloc>: Error in malloc(): out of memory\n";
2721
2722
dopts.result = &ret;
2723
dopts.num_items = 1;
2724
dopts.item_size = size;
2725
2726
imalloc(&sopts, &dopts);
2727
/*
2728
* Note that this branch gets optimized away -- it immediately follows
2729
* the check on tsd_fast that sets sopts.slow.
2730
*/
2731
if (sopts.slow) {
2732
uintptr_t args[3] = {size};
2733
hook_invoke_alloc(hook_alloc_malloc, ret, (uintptr_t)ret, args);
2734
}
2735
2736
LOG("core.malloc.exit", "result: %p", ret);
2737
2738
return ret;
2739
}
2740
2741
/******************************************************************************/
2742
/*
2743
* Begin malloc(3)-compatible functions.
2744
*/
2745
2746
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
2747
void JEMALLOC_NOTHROW *
2748
JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1)
2749
je_malloc(size_t size) {
2750
return imalloc_fastpath(size, &malloc_default);
2751
}
2752
2753
JEMALLOC_EXPORT int JEMALLOC_NOTHROW
2754
JEMALLOC_ATTR(nonnull(1))
2755
je_posix_memalign(void **memptr, size_t alignment, size_t size) {
2756
int ret;
2757
static_opts_t sopts;
2758
dynamic_opts_t dopts;
2759
2760
LOG("core.posix_memalign.entry", "mem ptr: %p, alignment: %zu, "
2761
"size: %zu", memptr, alignment, size);
2762
2763
static_opts_init(&sopts);
2764
dynamic_opts_init(&dopts);
2765
2766
sopts.bump_empty_aligned_alloc = true;
2767
sopts.min_alignment = sizeof(void *);
2768
sopts.oom_string =
2769
"<jemalloc>: Error allocating aligned memory: out of memory\n";
2770
sopts.invalid_alignment_string =
2771
"<jemalloc>: Error allocating aligned memory: invalid alignment\n";
2772
2773
dopts.result = memptr;
2774
dopts.num_items = 1;
2775
dopts.item_size = size;
2776
dopts.alignment = alignment;
2777
2778
ret = imalloc(&sopts, &dopts);
2779
if (sopts.slow) {
2780
uintptr_t args[3] = {(uintptr_t)memptr, (uintptr_t)alignment,
2781
(uintptr_t)size};
2782
hook_invoke_alloc(hook_alloc_posix_memalign, *memptr,
2783
(uintptr_t)ret, args);
2784
}
2785
2786
LOG("core.posix_memalign.exit", "result: %d, alloc ptr: %p", ret,
2787
*memptr);
2788
2789
return ret;
2790
}
2791
2792
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
2793
void JEMALLOC_NOTHROW *
2794
JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(2)
2795
je_aligned_alloc(size_t alignment, size_t size) {
2796
void *ret;
2797
2798
static_opts_t sopts;
2799
dynamic_opts_t dopts;
2800
2801
LOG("core.aligned_alloc.entry", "alignment: %zu, size: %zu\n",
2802
alignment, size);
2803
2804
static_opts_init(&sopts);
2805
dynamic_opts_init(&dopts);
2806
2807
sopts.bump_empty_aligned_alloc = true;
2808
sopts.null_out_result_on_error = true;
2809
sopts.set_errno_on_error = true;
2810
sopts.min_alignment = 1;
2811
sopts.oom_string =
2812
"<jemalloc>: Error allocating aligned memory: out of memory\n";
2813
sopts.invalid_alignment_string =
2814
"<jemalloc>: Error allocating aligned memory: invalid alignment\n";
2815
2816
dopts.result = &ret;
2817
dopts.num_items = 1;
2818
dopts.item_size = size;
2819
dopts.alignment = alignment;
2820
2821
imalloc(&sopts, &dopts);
2822
if (sopts.slow) {
2823
uintptr_t args[3] = {(uintptr_t)alignment, (uintptr_t)size};
2824
hook_invoke_alloc(hook_alloc_aligned_alloc, ret,
2825
(uintptr_t)ret, args);
2826
}
2827
2828
LOG("core.aligned_alloc.exit", "result: %p", ret);
2829
2830
return ret;
2831
}
2832
2833
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
2834
void JEMALLOC_NOTHROW *
2835
JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE2(1, 2)
2836
je_calloc(size_t num, size_t size) {
2837
void *ret;
2838
static_opts_t sopts;
2839
dynamic_opts_t dopts;
2840
2841
LOG("core.calloc.entry", "num: %zu, size: %zu\n", num, size);
2842
2843
static_opts_init(&sopts);
2844
dynamic_opts_init(&dopts);
2845
2846
sopts.may_overflow = true;
2847
sopts.null_out_result_on_error = true;
2848
sopts.set_errno_on_error = true;
2849
sopts.oom_string = "<jemalloc>: Error in calloc(): out of memory\n";
2850
2851
dopts.result = &ret;
2852
dopts.num_items = num;
2853
dopts.item_size = size;
2854
dopts.zero = true;
2855
2856
imalloc(&sopts, &dopts);
2857
if (sopts.slow) {
2858
uintptr_t args[3] = {(uintptr_t)num, (uintptr_t)size};
2859
hook_invoke_alloc(hook_alloc_calloc, ret, (uintptr_t)ret, args);
2860
}
2861
2862
LOG("core.calloc.exit", "result: %p", ret);
2863
2864
return ret;
2865
}
2866
2867
JEMALLOC_ALWAYS_INLINE void
2868
ifree(tsd_t *tsd, void *ptr, tcache_t *tcache, bool slow_path) {
2869
if (!slow_path) {
2870
tsd_assert_fast(tsd);
2871
}
2872
check_entry_exit_locking(tsd_tsdn(tsd));
2873
if (tsd_reentrancy_level_get(tsd) != 0) {
2874
assert(slow_path);
2875
}
2876
2877
assert(ptr != NULL);
2878
assert(malloc_initialized() || IS_INITIALIZER);
2879
2880
emap_alloc_ctx_t alloc_ctx;
2881
emap_alloc_ctx_lookup(tsd_tsdn(tsd), &arena_emap_global, ptr,
2882
&alloc_ctx);
2883
assert(alloc_ctx.szind != SC_NSIZES);
2884
2885
size_t usize = sz_index2size(alloc_ctx.szind);
2886
if (config_prof && opt_prof) {
2887
prof_free(tsd, ptr, usize, &alloc_ctx);
2888
}
2889
2890
if (likely(!slow_path)) {
2891
idalloctm(tsd_tsdn(tsd), ptr, tcache, &alloc_ctx, false,
2892
false);
2893
} else {
2894
if (config_fill && slow_path && opt_junk_free) {
2895
junk_free_callback(ptr, usize);
2896
}
2897
idalloctm(tsd_tsdn(tsd), ptr, tcache, &alloc_ctx, false,
2898
true);
2899
}
2900
thread_dalloc_event(tsd, usize);
2901
}
2902
2903
JEMALLOC_ALWAYS_INLINE bool
2904
maybe_check_alloc_ctx(tsd_t *tsd, void *ptr, emap_alloc_ctx_t *alloc_ctx) {
2905
if (config_opt_size_checks) {
2906
emap_alloc_ctx_t dbg_ctx;
2907
emap_alloc_ctx_lookup(tsd_tsdn(tsd), &arena_emap_global, ptr,
2908
&dbg_ctx);
2909
if (alloc_ctx->szind != dbg_ctx.szind) {
2910
safety_check_fail_sized_dealloc(
2911
/* current_dealloc */ true, ptr,
2912
/* true_size */ sz_size2index(dbg_ctx.szind),
2913
/* input_size */ sz_size2index(alloc_ctx->szind));
2914
return true;
2915
}
2916
if (alloc_ctx->slab != dbg_ctx.slab) {
2917
safety_check_fail(
2918
"Internal heap corruption detected: "
2919
"mismatch in slab bit");
2920
return true;
2921
}
2922
}
2923
return false;
2924
}
2925
2926
JEMALLOC_ALWAYS_INLINE void
2927
isfree(tsd_t *tsd, void *ptr, size_t usize, tcache_t *tcache, bool slow_path) {
2928
if (!slow_path) {
2929
tsd_assert_fast(tsd);
2930
}
2931
check_entry_exit_locking(tsd_tsdn(tsd));
2932
if (tsd_reentrancy_level_get(tsd) != 0) {
2933
assert(slow_path);
2934
}
2935
2936
assert(ptr != NULL);
2937
assert(malloc_initialized() || IS_INITIALIZER);
2938
2939
emap_alloc_ctx_t alloc_ctx;
2940
if (!config_prof) {
2941
alloc_ctx.szind = sz_size2index(usize);
2942
alloc_ctx.slab = (alloc_ctx.szind < SC_NBINS);
2943
} else {
2944
if (likely(!prof_sample_aligned(ptr))) {
2945
/*
2946
* When the ptr is not page aligned, it was not sampled.
2947
* usize can be trusted to determine szind and slab.
2948
*/
2949
alloc_ctx.szind = sz_size2index(usize);
2950
alloc_ctx.slab = (alloc_ctx.szind < SC_NBINS);
2951
} else if (opt_prof) {
2952
emap_alloc_ctx_lookup(tsd_tsdn(tsd), &arena_emap_global,
2953
ptr, &alloc_ctx);
2954
2955
if (config_opt_safety_checks) {
2956
/* Small alloc may have !slab (sampled). */
2957
if (unlikely(alloc_ctx.szind !=
2958
sz_size2index(usize))) {
2959
safety_check_fail_sized_dealloc(
2960
/* current_dealloc */ true, ptr,
2961
/* true_size */ sz_index2size(
2962
alloc_ctx.szind),
2963
/* input_size */ usize);
2964
}
2965
}
2966
} else {
2967
alloc_ctx.szind = sz_size2index(usize);
2968
alloc_ctx.slab = (alloc_ctx.szind < SC_NBINS);
2969
}
2970
}
2971
bool fail = maybe_check_alloc_ctx(tsd, ptr, &alloc_ctx);
2972
if (fail) {
2973
/*
2974
* This is a heap corruption bug. In real life we'll crash; for
2975
* the unit test we just want to avoid breaking anything too
2976
* badly to get a test result out. Let's leak instead of trying
2977
* to free.
2978
*/
2979
return;
2980
}
2981
2982
if (config_prof && opt_prof) {
2983
prof_free(tsd, ptr, usize, &alloc_ctx);
2984
}
2985
if (likely(!slow_path)) {
2986
isdalloct(tsd_tsdn(tsd), ptr, usize, tcache, &alloc_ctx,
2987
false);
2988
} else {
2989
if (config_fill && slow_path && opt_junk_free) {
2990
junk_free_callback(ptr, usize);
2991
}
2992
isdalloct(tsd_tsdn(tsd), ptr, usize, tcache, &alloc_ctx,
2993
true);
2994
}
2995
thread_dalloc_event(tsd, usize);
2996
}
2997
2998
JEMALLOC_NOINLINE
2999
void
3000
free_default(void *ptr) {
3001
UTRACE(ptr, 0, 0);
3002
if (likely(ptr != NULL)) {
3003
/*
3004
* We avoid setting up tsd fully (e.g. tcache, arena binding)
3005
* based on only free() calls -- other activities trigger the
3006
* minimal to full transition. This is because free() may
3007
* happen during thread shutdown after tls deallocation: if a
3008
* thread never had any malloc activities until then, a
3009
* fully-setup tsd won't be destructed properly.
3010
*/
3011
tsd_t *tsd = tsd_fetch_min();
3012
check_entry_exit_locking(tsd_tsdn(tsd));
3013
3014
if (likely(tsd_fast(tsd))) {
3015
tcache_t *tcache = tcache_get_from_ind(tsd,
3016
TCACHE_IND_AUTOMATIC, /* slow */ false,
3017
/* is_alloc */ false);
3018
ifree(tsd, ptr, tcache, /* slow */ false);
3019
} else {
3020
tcache_t *tcache = tcache_get_from_ind(tsd,
3021
TCACHE_IND_AUTOMATIC, /* slow */ true,
3022
/* is_alloc */ false);
3023
uintptr_t args_raw[3] = {(uintptr_t)ptr};
3024
hook_invoke_dalloc(hook_dalloc_free, ptr, args_raw);
3025
ifree(tsd, ptr, tcache, /* slow */ true);
3026
}
3027
3028
check_entry_exit_locking(tsd_tsdn(tsd));
3029
}
3030
}
3031
3032
JEMALLOC_ALWAYS_INLINE bool
3033
free_fastpath_nonfast_aligned(void *ptr, bool check_prof) {
3034
/*
3035
* free_fastpath do not handle two uncommon cases: 1) sampled profiled
3036
* objects and 2) sampled junk & stash for use-after-free detection.
3037
* Both have special alignments which are used to escape the fastpath.
3038
*
3039
* prof_sample is page-aligned, which covers the UAF check when both
3040
* are enabled (the assertion below). Avoiding redundant checks since
3041
* this is on the fastpath -- at most one runtime branch from this.
3042
*/
3043
if (config_debug && cache_bin_nonfast_aligned(ptr)) {
3044
assert(prof_sample_aligned(ptr));
3045
}
3046
3047
if (config_prof && check_prof) {
3048
/* When prof is enabled, the prof_sample alignment is enough. */
3049
if (prof_sample_aligned(ptr)) {
3050
return true;
3051
} else {
3052
return false;
3053
}
3054
}
3055
3056
if (config_uaf_detection) {
3057
if (cache_bin_nonfast_aligned(ptr)) {
3058
return true;
3059
} else {
3060
return false;
3061
}
3062
}
3063
3064
return false;
3065
}
3066
3067
/* Returns whether or not the free attempt was successful. */
3068
JEMALLOC_ALWAYS_INLINE
3069
bool free_fastpath(void *ptr, size_t size, bool size_hint) {
3070
tsd_t *tsd = tsd_get(false);
3071
/* The branch gets optimized away unless tsd_get_allocates(). */
3072
if (unlikely(tsd == NULL)) {
3073
return false;
3074
}
3075
/*
3076
* The tsd_fast() / initialized checks are folded into the branch
3077
* testing (deallocated_after >= threshold) later in this function.
3078
* The threshold will be set to 0 when !tsd_fast.
3079
*/
3080
assert(tsd_fast(tsd) ||
3081
*tsd_thread_deallocated_next_event_fastp_get_unsafe(tsd) == 0);
3082
3083
emap_alloc_ctx_t alloc_ctx;
3084
if (!size_hint) {
3085
bool err = emap_alloc_ctx_try_lookup_fast(tsd,
3086
&arena_emap_global, ptr, &alloc_ctx);
3087
3088
/* Note: profiled objects will have alloc_ctx.slab set */
3089
if (unlikely(err || !alloc_ctx.slab ||
3090
free_fastpath_nonfast_aligned(ptr,
3091
/* check_prof */ false))) {
3092
return false;
3093
}
3094
assert(alloc_ctx.szind != SC_NSIZES);
3095
} else {
3096
/*
3097
* Check for both sizes that are too large, and for sampled /
3098
* special aligned objects. The alignment check will also check
3099
* for null ptr.
3100
*/
3101
if (unlikely(size > SC_LOOKUP_MAXCLASS ||
3102
free_fastpath_nonfast_aligned(ptr,
3103
/* check_prof */ true))) {
3104
return false;
3105
}
3106
alloc_ctx.szind = sz_size2index_lookup(size);
3107
/* Max lookup class must be small. */
3108
assert(alloc_ctx.szind < SC_NBINS);
3109
/* This is a dead store, except when opt size checking is on. */
3110
alloc_ctx.slab = true;
3111
}
3112
/*
3113
* Currently the fastpath only handles small sizes. The branch on
3114
* SC_LOOKUP_MAXCLASS makes sure of it. This lets us avoid checking
3115
* tcache szind upper limit (i.e. tcache_maxclass) as well.
3116
*/
3117
assert(alloc_ctx.slab);
3118
3119
uint64_t deallocated, threshold;
3120
te_free_fastpath_ctx(tsd, &deallocated, &threshold);
3121
3122
size_t usize = sz_index2size(alloc_ctx.szind);
3123
uint64_t deallocated_after = deallocated + usize;
3124
/*
3125
* Check for events and tsd non-nominal (fast_threshold will be set to
3126
* 0) in a single branch. Note that this handles the uninitialized case
3127
* as well (TSD init will be triggered on the non-fastpath). Therefore
3128
* anything depends on a functional TSD (e.g. the alloc_ctx sanity check
3129
* below) needs to be after this branch.
3130
*/
3131
if (unlikely(deallocated_after >= threshold)) {
3132
return false;
3133
}
3134
assert(tsd_fast(tsd));
3135
bool fail = maybe_check_alloc_ctx(tsd, ptr, &alloc_ctx);
3136
if (fail) {
3137
/* See the comment in isfree. */
3138
return true;
3139
}
3140
3141
tcache_t *tcache = tcache_get_from_ind(tsd, TCACHE_IND_AUTOMATIC,
3142
/* slow */ false, /* is_alloc */ false);
3143
cache_bin_t *bin = &tcache->bins[alloc_ctx.szind];
3144
3145
/*
3146
* If junking were enabled, this is where we would do it. It's not
3147
* though, since we ensured above that we're on the fast path. Assert
3148
* that to double-check.
3149
*/
3150
assert(!opt_junk_free);
3151
3152
if (!cache_bin_dalloc_easy(bin, ptr)) {
3153
return false;
3154
}
3155
3156
*tsd_thread_deallocatedp_get(tsd) = deallocated_after;
3157
3158
return true;
3159
}
3160
3161
JEMALLOC_EXPORT void JEMALLOC_NOTHROW
3162
je_free(void *ptr) {
3163
LOG("core.free.entry", "ptr: %p", ptr);
3164
3165
if (!free_fastpath(ptr, 0, false)) {
3166
free_default(ptr);
3167
}
3168
3169
LOG("core.free.exit", "");
3170
}
3171
3172
/*
3173
* End malloc(3)-compatible functions.
3174
*/
3175
/******************************************************************************/
3176
/*
3177
* Begin non-standard override functions.
3178
*/
3179
3180
#ifdef JEMALLOC_OVERRIDE_MEMALIGN
3181
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
3182
void JEMALLOC_NOTHROW *
3183
JEMALLOC_ATTR(malloc)
3184
je_memalign(size_t alignment, size_t size) {
3185
void *ret;
3186
static_opts_t sopts;
3187
dynamic_opts_t dopts;
3188
3189
LOG("core.memalign.entry", "alignment: %zu, size: %zu\n", alignment,
3190
size);
3191
3192
static_opts_init(&sopts);
3193
dynamic_opts_init(&dopts);
3194
3195
sopts.min_alignment = 1;
3196
sopts.oom_string =
3197
"<jemalloc>: Error allocating aligned memory: out of memory\n";
3198
sopts.invalid_alignment_string =
3199
"<jemalloc>: Error allocating aligned memory: invalid alignment\n";
3200
sopts.null_out_result_on_error = true;
3201
3202
dopts.result = &ret;
3203
dopts.num_items = 1;
3204
dopts.item_size = size;
3205
dopts.alignment = alignment;
3206
3207
imalloc(&sopts, &dopts);
3208
if (sopts.slow) {
3209
uintptr_t args[3] = {alignment, size};
3210
hook_invoke_alloc(hook_alloc_memalign, ret, (uintptr_t)ret,
3211
args);
3212
}
3213
3214
LOG("core.memalign.exit", "result: %p", ret);
3215
return ret;
3216
}
3217
#endif
3218
3219
#ifdef JEMALLOC_OVERRIDE_VALLOC
3220
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
3221
void JEMALLOC_NOTHROW *
3222
JEMALLOC_ATTR(malloc)
3223
je_valloc(size_t size) {
3224
void *ret;
3225
3226
static_opts_t sopts;
3227
dynamic_opts_t dopts;
3228
3229
LOG("core.valloc.entry", "size: %zu\n", size);
3230
3231
static_opts_init(&sopts);
3232
dynamic_opts_init(&dopts);
3233
3234
sopts.null_out_result_on_error = true;
3235
sopts.min_alignment = PAGE;
3236
sopts.oom_string =
3237
"<jemalloc>: Error allocating aligned memory: out of memory\n";
3238
sopts.invalid_alignment_string =
3239
"<jemalloc>: Error allocating aligned memory: invalid alignment\n";
3240
3241
dopts.result = &ret;
3242
dopts.num_items = 1;
3243
dopts.item_size = size;
3244
dopts.alignment = PAGE;
3245
3246
imalloc(&sopts, &dopts);
3247
if (sopts.slow) {
3248
uintptr_t args[3] = {size};
3249
hook_invoke_alloc(hook_alloc_valloc, ret, (uintptr_t)ret, args);
3250
}
3251
3252
LOG("core.valloc.exit", "result: %p\n", ret);
3253
return ret;
3254
}
3255
#endif
3256
3257
#if defined(JEMALLOC_IS_MALLOC) && defined(JEMALLOC_GLIBC_MALLOC_HOOK)
3258
/*
3259
* glibc provides the RTLD_DEEPBIND flag for dlopen which can make it possible
3260
* to inconsistently reference libc's malloc(3)-compatible functions
3261
* (https://bugzilla.mozilla.org/show_bug.cgi?id=493541).
3262
*
3263
* These definitions interpose hooks in glibc. The functions are actually
3264
* passed an extra argument for the caller return address, which will be
3265
* ignored.
3266
*/
3267
#include <features.h> // defines __GLIBC__ if we are compiling against glibc
3268
3269
JEMALLOC_EXPORT void (*__free_hook)(void *ptr) = je_free;
3270
JEMALLOC_EXPORT void *(*__malloc_hook)(size_t size) = je_malloc;
3271
JEMALLOC_EXPORT void *(*__realloc_hook)(void *ptr, size_t size) = je_realloc;
3272
# ifdef JEMALLOC_GLIBC_MEMALIGN_HOOK
3273
JEMALLOC_EXPORT void *(*__memalign_hook)(size_t alignment, size_t size) =
3274
je_memalign;
3275
# endif
3276
3277
# ifdef __GLIBC__
3278
/*
3279
* To enable static linking with glibc, the libc specific malloc interface must
3280
* be implemented also, so none of glibc's malloc.o functions are added to the
3281
* link.
3282
*/
3283
# define ALIAS(je_fn) __attribute__((alias (#je_fn), used))
3284
/* To force macro expansion of je_ prefix before stringification. */
3285
# define PREALIAS(je_fn) ALIAS(je_fn)
3286
# ifdef JEMALLOC_OVERRIDE___LIBC_CALLOC
3287
void *__libc_calloc(size_t n, size_t size) PREALIAS(je_calloc);
3288
# endif
3289
# ifdef JEMALLOC_OVERRIDE___LIBC_FREE
3290
void __libc_free(void* ptr) PREALIAS(je_free);
3291
# endif
3292
# ifdef JEMALLOC_OVERRIDE___LIBC_MALLOC
3293
void *__libc_malloc(size_t size) PREALIAS(je_malloc);
3294
# endif
3295
# ifdef JEMALLOC_OVERRIDE___LIBC_MEMALIGN
3296
void *__libc_memalign(size_t align, size_t s) PREALIAS(je_memalign);
3297
# endif
3298
# ifdef JEMALLOC_OVERRIDE___LIBC_REALLOC
3299
void *__libc_realloc(void* ptr, size_t size) PREALIAS(je_realloc);
3300
# endif
3301
# ifdef JEMALLOC_OVERRIDE___LIBC_VALLOC
3302
void *__libc_valloc(size_t size) PREALIAS(je_valloc);
3303
# endif
3304
# ifdef JEMALLOC_OVERRIDE___POSIX_MEMALIGN
3305
int __posix_memalign(void** r, size_t a, size_t s) PREALIAS(je_posix_memalign);
3306
# endif
3307
# undef PREALIAS
3308
# undef ALIAS
3309
# endif
3310
#endif
3311
3312
/*
3313
* End non-standard override functions.
3314
*/
3315
/******************************************************************************/
3316
/*
3317
* Begin non-standard functions.
3318
*/
3319
3320
JEMALLOC_ALWAYS_INLINE unsigned
3321
mallocx_tcache_get(int flags) {
3322
if (likely((flags & MALLOCX_TCACHE_MASK) == 0)) {
3323
return TCACHE_IND_AUTOMATIC;
3324
} else if ((flags & MALLOCX_TCACHE_MASK) == MALLOCX_TCACHE_NONE) {
3325
return TCACHE_IND_NONE;
3326
} else {
3327
return MALLOCX_TCACHE_GET(flags);
3328
}
3329
}
3330
3331
JEMALLOC_ALWAYS_INLINE unsigned
3332
mallocx_arena_get(int flags) {
3333
if (unlikely((flags & MALLOCX_ARENA_MASK) != 0)) {
3334
return MALLOCX_ARENA_GET(flags);
3335
} else {
3336
return ARENA_IND_AUTOMATIC;
3337
}
3338
}
3339
3340
#ifdef JEMALLOC_EXPERIMENTAL_SMALLOCX_API
3341
3342
#define JEMALLOC_SMALLOCX_CONCAT_HELPER(x, y) x ## y
3343
#define JEMALLOC_SMALLOCX_CONCAT_HELPER2(x, y) \
3344
JEMALLOC_SMALLOCX_CONCAT_HELPER(x, y)
3345
3346
typedef struct {
3347
void *ptr;
3348
size_t size;
3349
} smallocx_return_t;
3350
3351
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
3352
smallocx_return_t JEMALLOC_NOTHROW
3353
/*
3354
* The attribute JEMALLOC_ATTR(malloc) cannot be used due to:
3355
* - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86488
3356
*/
3357
JEMALLOC_SMALLOCX_CONCAT_HELPER2(je_smallocx_, JEMALLOC_VERSION_GID_IDENT)
3358
(size_t size, int flags) {
3359
/*
3360
* Note: the attribute JEMALLOC_ALLOC_SIZE(1) cannot be
3361
* used here because it makes writing beyond the `size`
3362
* of the `ptr` undefined behavior, but the objective
3363
* of this function is to allow writing beyond `size`
3364
* up to `smallocx_return_t::size`.
3365
*/
3366
smallocx_return_t ret;
3367
static_opts_t sopts;
3368
dynamic_opts_t dopts;
3369
3370
LOG("core.smallocx.entry", "size: %zu, flags: %d", size, flags);
3371
3372
static_opts_init(&sopts);
3373
dynamic_opts_init(&dopts);
3374
3375
sopts.assert_nonempty_alloc = true;
3376
sopts.null_out_result_on_error = true;
3377
sopts.oom_string = "<jemalloc>: Error in mallocx(): out of memory\n";
3378
sopts.usize = true;
3379
3380
dopts.result = &ret.ptr;
3381
dopts.num_items = 1;
3382
dopts.item_size = size;
3383
if (unlikely(flags != 0)) {
3384
dopts.alignment = MALLOCX_ALIGN_GET(flags);
3385
dopts.zero = MALLOCX_ZERO_GET(flags);
3386
dopts.tcache_ind = mallocx_tcache_get(flags);
3387
dopts.arena_ind = mallocx_arena_get(flags);
3388
}
3389
3390
imalloc(&sopts, &dopts);
3391
assert(dopts.usize == je_nallocx(size, flags));
3392
ret.size = dopts.usize;
3393
3394
LOG("core.smallocx.exit", "result: %p, size: %zu", ret.ptr, ret.size);
3395
return ret;
3396
}
3397
#undef JEMALLOC_SMALLOCX_CONCAT_HELPER
3398
#undef JEMALLOC_SMALLOCX_CONCAT_HELPER2
3399
#endif
3400
3401
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
3402
void JEMALLOC_NOTHROW *
3403
JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1)
3404
je_mallocx(size_t size, int flags) {
3405
void *ret;
3406
static_opts_t sopts;
3407
dynamic_opts_t dopts;
3408
3409
LOG("core.mallocx.entry", "size: %zu, flags: %d", size, flags);
3410
3411
static_opts_init(&sopts);
3412
dynamic_opts_init(&dopts);
3413
3414
sopts.assert_nonempty_alloc = true;
3415
sopts.null_out_result_on_error = true;
3416
sopts.oom_string = "<jemalloc>: Error in mallocx(): out of memory\n";
3417
3418
dopts.result = &ret;
3419
dopts.num_items = 1;
3420
dopts.item_size = size;
3421
if (unlikely(flags != 0)) {
3422
dopts.alignment = MALLOCX_ALIGN_GET(flags);
3423
dopts.zero = MALLOCX_ZERO_GET(flags);
3424
dopts.tcache_ind = mallocx_tcache_get(flags);
3425
dopts.arena_ind = mallocx_arena_get(flags);
3426
}
3427
3428
imalloc(&sopts, &dopts);
3429
if (sopts.slow) {
3430
uintptr_t args[3] = {size, flags};
3431
hook_invoke_alloc(hook_alloc_mallocx, ret, (uintptr_t)ret,
3432
args);
3433
}
3434
3435
LOG("core.mallocx.exit", "result: %p", ret);
3436
return ret;
3437
}
3438
3439
static void *
3440
irallocx_prof_sample(tsdn_t *tsdn, void *old_ptr, size_t old_usize,
3441
size_t usize, size_t alignment, bool zero, tcache_t *tcache, arena_t *arena,
3442
prof_tctx_t *tctx, hook_ralloc_args_t *hook_args) {
3443
void *p;
3444
3445
if (tctx == NULL) {
3446
return NULL;
3447
}
3448
3449
alignment = prof_sample_align(alignment);
3450
if (usize <= SC_SMALL_MAXCLASS) {
3451
p = iralloct(tsdn, old_ptr, old_usize,
3452
SC_LARGE_MINCLASS, alignment, zero, tcache,
3453
arena, hook_args);
3454
if (p == NULL) {
3455
return NULL;
3456
}
3457
arena_prof_promote(tsdn, p, usize);
3458
} else {
3459
p = iralloct(tsdn, old_ptr, old_usize, usize, alignment, zero,
3460
tcache, arena, hook_args);
3461
}
3462
assert(prof_sample_aligned(p));
3463
3464
return p;
3465
}
3466
3467
JEMALLOC_ALWAYS_INLINE void *
3468
irallocx_prof(tsd_t *tsd, void *old_ptr, size_t old_usize, size_t size,
3469
size_t alignment, size_t usize, bool zero, tcache_t *tcache,
3470
arena_t *arena, emap_alloc_ctx_t *alloc_ctx,
3471
hook_ralloc_args_t *hook_args) {
3472
prof_info_t old_prof_info;
3473
prof_info_get_and_reset_recent(tsd, old_ptr, alloc_ctx, &old_prof_info);
3474
bool prof_active = prof_active_get_unlocked();
3475
bool sample_event = te_prof_sample_event_lookahead(tsd, usize);
3476
prof_tctx_t *tctx = prof_alloc_prep(tsd, prof_active, sample_event);
3477
void *p;
3478
if (unlikely((uintptr_t)tctx != (uintptr_t)1U)) {
3479
p = irallocx_prof_sample(tsd_tsdn(tsd), old_ptr, old_usize,
3480
usize, alignment, zero, tcache, arena, tctx, hook_args);
3481
} else {
3482
p = iralloct(tsd_tsdn(tsd), old_ptr, old_usize, size, alignment,
3483
zero, tcache, arena, hook_args);
3484
}
3485
if (unlikely(p == NULL)) {
3486
prof_alloc_rollback(tsd, tctx);
3487
return NULL;
3488
}
3489
assert(usize == isalloc(tsd_tsdn(tsd), p));
3490
prof_realloc(tsd, p, size, usize, tctx, prof_active, old_ptr,
3491
old_usize, &old_prof_info, sample_event);
3492
3493
return p;
3494
}
3495
3496
static void *
3497
do_rallocx(void *ptr, size_t size, int flags, bool is_realloc) {
3498
void *p;
3499
tsd_t *tsd;
3500
size_t usize;
3501
size_t old_usize;
3502
size_t alignment = MALLOCX_ALIGN_GET(flags);
3503
arena_t *arena;
3504
3505
assert(ptr != NULL);
3506
assert(size != 0);
3507
assert(malloc_initialized() || IS_INITIALIZER);
3508
tsd = tsd_fetch();
3509
check_entry_exit_locking(tsd_tsdn(tsd));
3510
3511
bool zero = zero_get(MALLOCX_ZERO_GET(flags), /* slow */ true);
3512
3513
unsigned arena_ind = mallocx_arena_get(flags);
3514
if (arena_get_from_ind(tsd, arena_ind, &arena)) {
3515
goto label_oom;
3516
}
3517
3518
unsigned tcache_ind = mallocx_tcache_get(flags);
3519
tcache_t *tcache = tcache_get_from_ind(tsd, tcache_ind,
3520
/* slow */ true, /* is_alloc */ true);
3521
3522
emap_alloc_ctx_t alloc_ctx;
3523
emap_alloc_ctx_lookup(tsd_tsdn(tsd), &arena_emap_global, ptr,
3524
&alloc_ctx);
3525
assert(alloc_ctx.szind != SC_NSIZES);
3526
old_usize = sz_index2size(alloc_ctx.szind);
3527
assert(old_usize == isalloc(tsd_tsdn(tsd), ptr));
3528
if (aligned_usize_get(size, alignment, &usize, NULL, false)) {
3529
goto label_oom;
3530
}
3531
3532
hook_ralloc_args_t hook_args = {is_realloc, {(uintptr_t)ptr, size,
3533
flags, 0}};
3534
if (config_prof && opt_prof) {
3535
p = irallocx_prof(tsd, ptr, old_usize, size, alignment, usize,
3536
zero, tcache, arena, &alloc_ctx, &hook_args);
3537
if (unlikely(p == NULL)) {
3538
goto label_oom;
3539
}
3540
} else {
3541
p = iralloct(tsd_tsdn(tsd), ptr, old_usize, size, alignment,
3542
zero, tcache, arena, &hook_args);
3543
if (unlikely(p == NULL)) {
3544
goto label_oom;
3545
}
3546
assert(usize == isalloc(tsd_tsdn(tsd), p));
3547
}
3548
assert(alignment == 0 || ((uintptr_t)p & (alignment - 1)) == ZU(0));
3549
thread_alloc_event(tsd, usize);
3550
thread_dalloc_event(tsd, old_usize);
3551
3552
UTRACE(ptr, size, p);
3553
check_entry_exit_locking(tsd_tsdn(tsd));
3554
3555
if (config_fill && unlikely(opt_junk_alloc) && usize > old_usize
3556
&& !zero) {
3557
size_t excess_len = usize - old_usize;
3558
void *excess_start = (void *)((uintptr_t)p + old_usize);
3559
junk_alloc_callback(excess_start, excess_len);
3560
}
3561
3562
return p;
3563
label_oom:
3564
if (config_xmalloc && unlikely(opt_xmalloc)) {
3565
malloc_write("<jemalloc>: Error in rallocx(): out of memory\n");
3566
abort();
3567
}
3568
UTRACE(ptr, size, 0);
3569
check_entry_exit_locking(tsd_tsdn(tsd));
3570
3571
return NULL;
3572
}
3573
3574
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
3575
void JEMALLOC_NOTHROW *
3576
JEMALLOC_ALLOC_SIZE(2)
3577
je_rallocx(void *ptr, size_t size, int flags) {
3578
LOG("core.rallocx.entry", "ptr: %p, size: %zu, flags: %d", ptr,
3579
size, flags);
3580
void *ret = do_rallocx(ptr, size, flags, false);
3581
LOG("core.rallocx.exit", "result: %p", ret);
3582
return ret;
3583
}
3584
3585
static void *
3586
do_realloc_nonnull_zero(void *ptr) {
3587
if (config_stats) {
3588
atomic_fetch_add_zu(&zero_realloc_count, 1, ATOMIC_RELAXED);
3589
}
3590
if (opt_zero_realloc_action == zero_realloc_action_alloc) {
3591
/*
3592
* The user might have gotten an alloc setting while expecting a
3593
* free setting. If that's the case, we at least try to
3594
* reduce the harm, and turn off the tcache while allocating, so
3595
* that we'll get a true first fit.
3596
*/
3597
return do_rallocx(ptr, 1, MALLOCX_TCACHE_NONE, true);
3598
} else if (opt_zero_realloc_action == zero_realloc_action_free) {
3599
UTRACE(ptr, 0, 0);
3600
tsd_t *tsd = tsd_fetch();
3601
check_entry_exit_locking(tsd_tsdn(tsd));
3602
3603
tcache_t *tcache = tcache_get_from_ind(tsd,
3604
TCACHE_IND_AUTOMATIC, /* slow */ true,
3605
/* is_alloc */ false);
3606
uintptr_t args[3] = {(uintptr_t)ptr, 0};
3607
hook_invoke_dalloc(hook_dalloc_realloc, ptr, args);
3608
ifree(tsd, ptr, tcache, true);
3609
3610
check_entry_exit_locking(tsd_tsdn(tsd));
3611
return NULL;
3612
} else {
3613
safety_check_fail("Called realloc(non-null-ptr, 0) with "
3614
"zero_realloc:abort set\n");
3615
/* In real code, this will never run; the safety check failure
3616
* will call abort. In the unit test, we just want to bail out
3617
* without corrupting internal state that the test needs to
3618
* finish.
3619
*/
3620
return NULL;
3621
}
3622
}
3623
3624
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
3625
void JEMALLOC_NOTHROW *
3626
JEMALLOC_ALLOC_SIZE(2)
3627
je_realloc(void *ptr, size_t size) {
3628
LOG("core.realloc.entry", "ptr: %p, size: %zu\n", ptr, size);
3629
3630
if (likely(ptr != NULL && size != 0)) {
3631
void *ret = do_rallocx(ptr, size, 0, true);
3632
LOG("core.realloc.exit", "result: %p", ret);
3633
return ret;
3634
} else if (ptr != NULL && size == 0) {
3635
void *ret = do_realloc_nonnull_zero(ptr);
3636
LOG("core.realloc.exit", "result: %p", ret);
3637
return ret;
3638
} else {
3639
/* realloc(NULL, size) is equivalent to malloc(size). */
3640
void *ret;
3641
3642
static_opts_t sopts;
3643
dynamic_opts_t dopts;
3644
3645
static_opts_init(&sopts);
3646
dynamic_opts_init(&dopts);
3647
3648
sopts.null_out_result_on_error = true;
3649
sopts.set_errno_on_error = true;
3650
sopts.oom_string =
3651
"<jemalloc>: Error in realloc(): out of memory\n";
3652
3653
dopts.result = &ret;
3654
dopts.num_items = 1;
3655
dopts.item_size = size;
3656
3657
imalloc(&sopts, &dopts);
3658
if (sopts.slow) {
3659
uintptr_t args[3] = {(uintptr_t)ptr, size};
3660
hook_invoke_alloc(hook_alloc_realloc, ret,
3661
(uintptr_t)ret, args);
3662
}
3663
LOG("core.realloc.exit", "result: %p", ret);
3664
return ret;
3665
}
3666
}
3667
3668
JEMALLOC_ALWAYS_INLINE size_t
3669
ixallocx_helper(tsdn_t *tsdn, void *ptr, size_t old_usize, size_t size,
3670
size_t extra, size_t alignment, bool zero) {
3671
size_t newsize;
3672
3673
if (ixalloc(tsdn, ptr, old_usize, size, extra, alignment, zero,
3674
&newsize)) {
3675
return old_usize;
3676
}
3677
3678
return newsize;
3679
}
3680
3681
static size_t
3682
ixallocx_prof_sample(tsdn_t *tsdn, void *ptr, size_t old_usize, size_t size,
3683
size_t extra, size_t alignment, bool zero, prof_tctx_t *tctx) {
3684
/* Sampled allocation needs to be page aligned. */
3685
if (tctx == NULL || !prof_sample_aligned(ptr)) {
3686
return old_usize;
3687
}
3688
3689
return ixallocx_helper(tsdn, ptr, old_usize, size, extra, alignment,
3690
zero);
3691
}
3692
3693
JEMALLOC_ALWAYS_INLINE size_t
3694
ixallocx_prof(tsd_t *tsd, void *ptr, size_t old_usize, size_t size,
3695
size_t extra, size_t alignment, bool zero, emap_alloc_ctx_t *alloc_ctx) {
3696
/*
3697
* old_prof_info is only used for asserting that the profiling info
3698
* isn't changed by the ixalloc() call.
3699
*/
3700
prof_info_t old_prof_info;
3701
prof_info_get(tsd, ptr, alloc_ctx, &old_prof_info);
3702
3703
/*
3704
* usize isn't knowable before ixalloc() returns when extra is non-zero.
3705
* Therefore, compute its maximum possible value and use that in
3706
* prof_alloc_prep() to decide whether to capture a backtrace.
3707
* prof_realloc() will use the actual usize to decide whether to sample.
3708
*/
3709
size_t usize_max;
3710
if (aligned_usize_get(size + extra, alignment, &usize_max, NULL,
3711
false)) {
3712
/*
3713
* usize_max is out of range, and chances are that allocation
3714
* will fail, but use the maximum possible value and carry on
3715
* with prof_alloc_prep(), just in case allocation succeeds.
3716
*/
3717
usize_max = SC_LARGE_MAXCLASS;
3718
}
3719
bool prof_active = prof_active_get_unlocked();
3720
bool sample_event = te_prof_sample_event_lookahead(tsd, usize_max);
3721
prof_tctx_t *tctx = prof_alloc_prep(tsd, prof_active, sample_event);
3722
3723
size_t usize;
3724
if (unlikely((uintptr_t)tctx != (uintptr_t)1U)) {
3725
usize = ixallocx_prof_sample(tsd_tsdn(tsd), ptr, old_usize,
3726
size, extra, alignment, zero, tctx);
3727
} else {
3728
usize = ixallocx_helper(tsd_tsdn(tsd), ptr, old_usize, size,
3729
extra, alignment, zero);
3730
}
3731
3732
/*
3733
* At this point we can still safely get the original profiling
3734
* information associated with the ptr, because (a) the edata_t object
3735
* associated with the ptr still lives and (b) the profiling info
3736
* fields are not touched. "(a)" is asserted in the outer je_xallocx()
3737
* function, and "(b)" is indirectly verified below by checking that
3738
* the alloc_tctx field is unchanged.
3739
*/
3740
prof_info_t prof_info;
3741
if (usize == old_usize) {
3742
prof_info_get(tsd, ptr, alloc_ctx, &prof_info);
3743
prof_alloc_rollback(tsd, tctx);
3744
} else {
3745
prof_info_get_and_reset_recent(tsd, ptr, alloc_ctx, &prof_info);
3746
assert(usize <= usize_max);
3747
sample_event = te_prof_sample_event_lookahead(tsd, usize);
3748
prof_realloc(tsd, ptr, size, usize, tctx, prof_active, ptr,
3749
old_usize, &prof_info, sample_event);
3750
}
3751
3752
assert(old_prof_info.alloc_tctx == prof_info.alloc_tctx);
3753
return usize;
3754
}
3755
3756
JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW
3757
je_xallocx(void *ptr, size_t size, size_t extra, int flags) {
3758
tsd_t *tsd;
3759
size_t usize, old_usize;
3760
size_t alignment = MALLOCX_ALIGN_GET(flags);
3761
bool zero = zero_get(MALLOCX_ZERO_GET(flags), /* slow */ true);
3762
3763
LOG("core.xallocx.entry", "ptr: %p, size: %zu, extra: %zu, "
3764
"flags: %d", ptr, size, extra, flags);
3765
3766
assert(ptr != NULL);
3767
assert(size != 0);
3768
assert(SIZE_T_MAX - size >= extra);
3769
assert(malloc_initialized() || IS_INITIALIZER);
3770
tsd = tsd_fetch();
3771
check_entry_exit_locking(tsd_tsdn(tsd));
3772
3773
/*
3774
* old_edata is only for verifying that xallocx() keeps the edata_t
3775
* object associated with the ptr (though the content of the edata_t
3776
* object can be changed).
3777
*/
3778
edata_t *old_edata = emap_edata_lookup(tsd_tsdn(tsd),
3779
&arena_emap_global, ptr);
3780
3781
emap_alloc_ctx_t alloc_ctx;
3782
emap_alloc_ctx_lookup(tsd_tsdn(tsd), &arena_emap_global, ptr,
3783
&alloc_ctx);
3784
assert(alloc_ctx.szind != SC_NSIZES);
3785
old_usize = sz_index2size(alloc_ctx.szind);
3786
assert(old_usize == isalloc(tsd_tsdn(tsd), ptr));
3787
/*
3788
* The API explicitly absolves itself of protecting against (size +
3789
* extra) numerical overflow, but we may need to clamp extra to avoid
3790
* exceeding SC_LARGE_MAXCLASS.
3791
*
3792
* Ordinarily, size limit checking is handled deeper down, but here we
3793
* have to check as part of (size + extra) clamping, since we need the
3794
* clamped value in the above helper functions.
3795
*/
3796
if (unlikely(size > SC_LARGE_MAXCLASS)) {
3797
usize = old_usize;
3798
goto label_not_resized;
3799
}
3800
if (unlikely(SC_LARGE_MAXCLASS - size < extra)) {
3801
extra = SC_LARGE_MAXCLASS - size;
3802
}
3803
3804
if (config_prof && opt_prof) {
3805
usize = ixallocx_prof(tsd, ptr, old_usize, size, extra,
3806
alignment, zero, &alloc_ctx);
3807
} else {
3808
usize = ixallocx_helper(tsd_tsdn(tsd), ptr, old_usize, size,
3809
extra, alignment, zero);
3810
}
3811
3812
/*
3813
* xallocx() should keep using the same edata_t object (though its
3814
* content can be changed).
3815
*/
3816
assert(emap_edata_lookup(tsd_tsdn(tsd), &arena_emap_global, ptr)
3817
== old_edata);
3818
3819
if (unlikely(usize == old_usize)) {
3820
goto label_not_resized;
3821
}
3822
thread_alloc_event(tsd, usize);
3823
thread_dalloc_event(tsd, old_usize);
3824
3825
if (config_fill && unlikely(opt_junk_alloc) && usize > old_usize &&
3826
!zero) {
3827
size_t excess_len = usize - old_usize;
3828
void *excess_start = (void *)((uintptr_t)ptr + old_usize);
3829
junk_alloc_callback(excess_start, excess_len);
3830
}
3831
label_not_resized:
3832
if (unlikely(!tsd_fast(tsd))) {
3833
uintptr_t args[4] = {(uintptr_t)ptr, size, extra, flags};
3834
hook_invoke_expand(hook_expand_xallocx, ptr, old_usize,
3835
usize, (uintptr_t)usize, args);
3836
}
3837
3838
UTRACE(ptr, size, ptr);
3839
check_entry_exit_locking(tsd_tsdn(tsd));
3840
3841
LOG("core.xallocx.exit", "result: %zu", usize);
3842
return usize;
3843
}
3844
3845
JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW
3846
JEMALLOC_ATTR(pure)
3847
je_sallocx(const void *ptr, int flags) {
3848
size_t usize;
3849
tsdn_t *tsdn;
3850
3851
LOG("core.sallocx.entry", "ptr: %p, flags: %d", ptr, flags);
3852
3853
assert(malloc_initialized() || IS_INITIALIZER);
3854
assert(ptr != NULL);
3855
3856
tsdn = tsdn_fetch();
3857
check_entry_exit_locking(tsdn);
3858
3859
if (config_debug || force_ivsalloc) {
3860
usize = ivsalloc(tsdn, ptr);
3861
assert(force_ivsalloc || usize != 0);
3862
} else {
3863
usize = isalloc(tsdn, ptr);
3864
}
3865
3866
check_entry_exit_locking(tsdn);
3867
3868
LOG("core.sallocx.exit", "result: %zu", usize);
3869
return usize;
3870
}
3871
3872
JEMALLOC_EXPORT void JEMALLOC_NOTHROW
3873
je_dallocx(void *ptr, int flags) {
3874
LOG("core.dallocx.entry", "ptr: %p, flags: %d", ptr, flags);
3875
3876
assert(ptr != NULL);
3877
assert(malloc_initialized() || IS_INITIALIZER);
3878
3879
tsd_t *tsd = tsd_fetch_min();
3880
bool fast = tsd_fast(tsd);
3881
check_entry_exit_locking(tsd_tsdn(tsd));
3882
3883
unsigned tcache_ind = mallocx_tcache_get(flags);
3884
tcache_t *tcache = tcache_get_from_ind(tsd, tcache_ind, !fast,
3885
/* is_alloc */ false);
3886
3887
UTRACE(ptr, 0, 0);
3888
if (likely(fast)) {
3889
tsd_assert_fast(tsd);
3890
ifree(tsd, ptr, tcache, false);
3891
} else {
3892
uintptr_t args_raw[3] = {(uintptr_t)ptr, flags};
3893
hook_invoke_dalloc(hook_dalloc_dallocx, ptr, args_raw);
3894
ifree(tsd, ptr, tcache, true);
3895
}
3896
check_entry_exit_locking(tsd_tsdn(tsd));
3897
3898
LOG("core.dallocx.exit", "");
3899
}
3900
3901
JEMALLOC_ALWAYS_INLINE size_t
3902
inallocx(tsdn_t *tsdn, size_t size, int flags) {
3903
check_entry_exit_locking(tsdn);
3904
size_t usize;
3905
/* In case of out of range, let the user see it rather than fail. */
3906
aligned_usize_get(size, MALLOCX_ALIGN_GET(flags), &usize, NULL, false);
3907
check_entry_exit_locking(tsdn);
3908
return usize;
3909
}
3910
3911
JEMALLOC_NOINLINE void
3912
sdallocx_default(void *ptr, size_t size, int flags) {
3913
assert(ptr != NULL);
3914
assert(malloc_initialized() || IS_INITIALIZER);
3915
3916
tsd_t *tsd = tsd_fetch_min();
3917
bool fast = tsd_fast(tsd);
3918
size_t usize = inallocx(tsd_tsdn(tsd), size, flags);
3919
check_entry_exit_locking(tsd_tsdn(tsd));
3920
3921
unsigned tcache_ind = mallocx_tcache_get(flags);
3922
tcache_t *tcache = tcache_get_from_ind(tsd, tcache_ind, !fast,
3923
/* is_alloc */ false);
3924
3925
UTRACE(ptr, 0, 0);
3926
if (likely(fast)) {
3927
tsd_assert_fast(tsd);
3928
isfree(tsd, ptr, usize, tcache, false);
3929
} else {
3930
uintptr_t args_raw[3] = {(uintptr_t)ptr, size, flags};
3931
hook_invoke_dalloc(hook_dalloc_sdallocx, ptr, args_raw);
3932
isfree(tsd, ptr, usize, tcache, true);
3933
}
3934
check_entry_exit_locking(tsd_tsdn(tsd));
3935
}
3936
3937
JEMALLOC_EXPORT void JEMALLOC_NOTHROW
3938
je_sdallocx(void *ptr, size_t size, int flags) {
3939
LOG("core.sdallocx.entry", "ptr: %p, size: %zu, flags: %d", ptr,
3940
size, flags);
3941
3942
if (flags != 0 || !free_fastpath(ptr, size, true)) {
3943
sdallocx_default(ptr, size, flags);
3944
}
3945
3946
LOG("core.sdallocx.exit", "");
3947
}
3948
3949
void JEMALLOC_NOTHROW
3950
je_sdallocx_noflags(void *ptr, size_t size) {
3951
LOG("core.sdallocx.entry", "ptr: %p, size: %zu, flags: 0", ptr,
3952
size);
3953
3954
if (!free_fastpath(ptr, size, true)) {
3955
sdallocx_default(ptr, size, 0);
3956
}
3957
3958
LOG("core.sdallocx.exit", "");
3959
}
3960
3961
JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW
3962
JEMALLOC_ATTR(pure)
3963
je_nallocx(size_t size, int flags) {
3964
size_t usize;
3965
tsdn_t *tsdn;
3966
3967
assert(size != 0);
3968
3969
if (unlikely(malloc_init())) {
3970
LOG("core.nallocx.exit", "result: %zu", ZU(0));
3971
return 0;
3972
}
3973
3974
tsdn = tsdn_fetch();
3975
check_entry_exit_locking(tsdn);
3976
3977
usize = inallocx(tsdn, size, flags);
3978
if (unlikely(usize > SC_LARGE_MAXCLASS)) {
3979
LOG("core.nallocx.exit", "result: %zu", ZU(0));
3980
return 0;
3981
}
3982
3983
check_entry_exit_locking(tsdn);
3984
LOG("core.nallocx.exit", "result: %zu", usize);
3985
return usize;
3986
}
3987
3988
JEMALLOC_EXPORT int JEMALLOC_NOTHROW
3989
je_mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp,
3990
size_t newlen) {
3991
int ret;
3992
tsd_t *tsd;
3993
3994
LOG("core.mallctl.entry", "name: %s", name);
3995
3996
if (unlikely(malloc_init())) {
3997
LOG("core.mallctl.exit", "result: %d", EAGAIN);
3998
return EAGAIN;
3999
}
4000
4001
tsd = tsd_fetch();
4002
check_entry_exit_locking(tsd_tsdn(tsd));
4003
ret = ctl_byname(tsd, name, oldp, oldlenp, newp, newlen);
4004
check_entry_exit_locking(tsd_tsdn(tsd));
4005
4006
LOG("core.mallctl.exit", "result: %d", ret);
4007
return ret;
4008
}
4009
4010
JEMALLOC_EXPORT int JEMALLOC_NOTHROW
4011
je_mallctlnametomib(const char *name, size_t *mibp, size_t *miblenp) {
4012
int ret;
4013
4014
LOG("core.mallctlnametomib.entry", "name: %s", name);
4015
4016
if (unlikely(malloc_init())) {
4017
LOG("core.mallctlnametomib.exit", "result: %d", EAGAIN);
4018
return EAGAIN;
4019
}
4020
4021
tsd_t *tsd = tsd_fetch();
4022
check_entry_exit_locking(tsd_tsdn(tsd));
4023
ret = ctl_nametomib(tsd, name, mibp, miblenp);
4024
check_entry_exit_locking(tsd_tsdn(tsd));
4025
4026
LOG("core.mallctlnametomib.exit", "result: %d", ret);
4027
return ret;
4028
}
4029
4030
JEMALLOC_EXPORT int JEMALLOC_NOTHROW
4031
je_mallctlbymib(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
4032
void *newp, size_t newlen) {
4033
int ret;
4034
tsd_t *tsd;
4035
4036
LOG("core.mallctlbymib.entry", "");
4037
4038
if (unlikely(malloc_init())) {
4039
LOG("core.mallctlbymib.exit", "result: %d", EAGAIN);
4040
return EAGAIN;
4041
}
4042
4043
tsd = tsd_fetch();
4044
check_entry_exit_locking(tsd_tsdn(tsd));
4045
ret = ctl_bymib(tsd, mib, miblen, oldp, oldlenp, newp, newlen);
4046
check_entry_exit_locking(tsd_tsdn(tsd));
4047
LOG("core.mallctlbymib.exit", "result: %d", ret);
4048
return ret;
4049
}
4050
4051
#define STATS_PRINT_BUFSIZE 65536
4052
JEMALLOC_EXPORT void JEMALLOC_NOTHROW
4053
je_malloc_stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
4054
const char *opts) {
4055
tsdn_t *tsdn;
4056
4057
LOG("core.malloc_stats_print.entry", "");
4058
4059
tsdn = tsdn_fetch();
4060
check_entry_exit_locking(tsdn);
4061
4062
if (config_debug) {
4063
stats_print(write_cb, cbopaque, opts);
4064
} else {
4065
buf_writer_t buf_writer;
4066
buf_writer_init(tsdn, &buf_writer, write_cb, cbopaque, NULL,
4067
STATS_PRINT_BUFSIZE);
4068
stats_print(buf_writer_cb, &buf_writer, opts);
4069
buf_writer_terminate(tsdn, &buf_writer);
4070
}
4071
4072
check_entry_exit_locking(tsdn);
4073
LOG("core.malloc_stats_print.exit", "");
4074
}
4075
#undef STATS_PRINT_BUFSIZE
4076
4077
JEMALLOC_ALWAYS_INLINE size_t
4078
je_malloc_usable_size_impl(JEMALLOC_USABLE_SIZE_CONST void *ptr) {
4079
assert(malloc_initialized() || IS_INITIALIZER);
4080
4081
tsdn_t *tsdn = tsdn_fetch();
4082
check_entry_exit_locking(tsdn);
4083
4084
size_t ret;
4085
if (unlikely(ptr == NULL)) {
4086
ret = 0;
4087
} else {
4088
if (config_debug || force_ivsalloc) {
4089
ret = ivsalloc(tsdn, ptr);
4090
assert(force_ivsalloc || ret != 0);
4091
} else {
4092
ret = isalloc(tsdn, ptr);
4093
}
4094
}
4095
check_entry_exit_locking(tsdn);
4096
4097
return ret;
4098
}
4099
4100
JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW
4101
je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr) {
4102
LOG("core.malloc_usable_size.entry", "ptr: %p", ptr);
4103
4104
size_t ret = je_malloc_usable_size_impl(ptr);
4105
4106
LOG("core.malloc_usable_size.exit", "result: %zu", ret);
4107
return ret;
4108
}
4109
4110
#ifdef JEMALLOC_HAVE_MALLOC_SIZE
4111
JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW
4112
je_malloc_size(const void *ptr) {
4113
LOG("core.malloc_size.entry", "ptr: %p", ptr);
4114
4115
size_t ret = je_malloc_usable_size_impl(ptr);
4116
4117
LOG("core.malloc_size.exit", "result: %zu", ret);
4118
return ret;
4119
}
4120
#endif
4121
4122
static void
4123
batch_alloc_prof_sample_assert(tsd_t *tsd, size_t batch, size_t usize) {
4124
assert(config_prof && opt_prof);
4125
bool prof_sample_event = te_prof_sample_event_lookahead(tsd,
4126
batch * usize);
4127
assert(!prof_sample_event);
4128
size_t surplus;
4129
prof_sample_event = te_prof_sample_event_lookahead_surplus(tsd,
4130
(batch + 1) * usize, &surplus);
4131
assert(prof_sample_event);
4132
assert(surplus < usize);
4133
}
4134
4135
size_t
4136
batch_alloc(void **ptrs, size_t num, size_t size, int flags) {
4137
LOG("core.batch_alloc.entry",
4138
"ptrs: %p, num: %zu, size: %zu, flags: %d", ptrs, num, size, flags);
4139
4140
tsd_t *tsd = tsd_fetch();
4141
check_entry_exit_locking(tsd_tsdn(tsd));
4142
4143
size_t filled = 0;
4144
4145
if (unlikely(tsd == NULL || tsd_reentrancy_level_get(tsd) > 0)) {
4146
goto label_done;
4147
}
4148
4149
size_t alignment = MALLOCX_ALIGN_GET(flags);
4150
size_t usize;
4151
if (aligned_usize_get(size, alignment, &usize, NULL, false)) {
4152
goto label_done;
4153
}
4154
szind_t ind = sz_size2index(usize);
4155
bool zero = zero_get(MALLOCX_ZERO_GET(flags), /* slow */ true);
4156
4157
/*
4158
* The cache bin and arena will be lazily initialized; it's hard to
4159
* know in advance whether each of them needs to be initialized.
4160
*/
4161
cache_bin_t *bin = NULL;
4162
arena_t *arena = NULL;
4163
4164
size_t nregs = 0;
4165
if (likely(ind < SC_NBINS)) {
4166
nregs = bin_infos[ind].nregs;
4167
assert(nregs > 0);
4168
}
4169
4170
while (filled < num) {
4171
size_t batch = num - filled;
4172
size_t surplus = SIZE_MAX; /* Dead store. */
4173
bool prof_sample_event = config_prof && opt_prof
4174
&& prof_active_get_unlocked()
4175
&& te_prof_sample_event_lookahead_surplus(tsd,
4176
batch * usize, &surplus);
4177
4178
if (prof_sample_event) {
4179
/*
4180
* Adjust so that the batch does not trigger prof
4181
* sampling.
4182
*/
4183
batch -= surplus / usize + 1;
4184
batch_alloc_prof_sample_assert(tsd, batch, usize);
4185
}
4186
4187
size_t progress = 0;
4188
4189
if (likely(ind < SC_NBINS) && batch >= nregs) {
4190
if (arena == NULL) {
4191
unsigned arena_ind = mallocx_arena_get(flags);
4192
if (arena_get_from_ind(tsd, arena_ind,
4193
&arena)) {
4194
goto label_done;
4195
}
4196
if (arena == NULL) {
4197
arena = arena_choose(tsd, NULL);
4198
}
4199
if (unlikely(arena == NULL)) {
4200
goto label_done;
4201
}
4202
}
4203
size_t arena_batch = batch - batch % nregs;
4204
size_t n = arena_fill_small_fresh(tsd_tsdn(tsd), arena,
4205
ind, ptrs + filled, arena_batch, zero);
4206
progress += n;
4207
filled += n;
4208
}
4209
4210
if (likely(ind < nhbins) && progress < batch) {
4211
if (bin == NULL) {
4212
unsigned tcache_ind = mallocx_tcache_get(flags);
4213
tcache_t *tcache = tcache_get_from_ind(tsd,
4214
tcache_ind, /* slow */ true,
4215
/* is_alloc */ true);
4216
if (tcache != NULL) {
4217
bin = &tcache->bins[ind];
4218
}
4219
}
4220
/*
4221
* If we don't have a tcache bin, we don't want to
4222
* immediately give up, because there's the possibility
4223
* that the user explicitly requested to bypass the
4224
* tcache, or that the user explicitly turned off the
4225
* tcache; in such cases, we go through the slow path,
4226
* i.e. the mallocx() call at the end of the while loop.
4227
*/
4228
if (bin != NULL) {
4229
size_t bin_batch = batch - progress;
4230
/*
4231
* n can be less than bin_batch, meaning that
4232
* the cache bin does not have enough memory.
4233
* In such cases, we rely on the slow path,
4234
* i.e. the mallocx() call at the end of the
4235
* while loop, to fill in the cache, and in the
4236
* next iteration of the while loop, the tcache
4237
* will contain a lot of memory, and we can
4238
* harvest them here. Compared to the
4239
* alternative approach where we directly go to
4240
* the arena bins here, the overhead of our
4241
* current approach should usually be minimal,
4242
* since we never try to fetch more memory than
4243
* what a slab contains via the tcache. An
4244
* additional benefit is that the tcache will
4245
* not be empty for the next allocation request.
4246
*/
4247
size_t n = cache_bin_alloc_batch(bin, bin_batch,
4248
ptrs + filled);
4249
if (config_stats) {
4250
bin->tstats.nrequests += n;
4251
}
4252
if (zero) {
4253
for (size_t i = 0; i < n; ++i) {
4254
memset(ptrs[filled + i], 0,
4255
usize);
4256
}
4257
}
4258
if (config_prof && opt_prof
4259
&& unlikely(ind >= SC_NBINS)) {
4260
for (size_t i = 0; i < n; ++i) {
4261
prof_tctx_reset_sampled(tsd,
4262
ptrs[filled + i]);
4263
}
4264
}
4265
progress += n;
4266
filled += n;
4267
}
4268
}
4269
4270
/*
4271
* For thread events other than prof sampling, trigger them as
4272
* if there's a single allocation of size (n * usize). This is
4273
* fine because:
4274
* (a) these events do not alter the allocation itself, and
4275
* (b) it's possible that some event would have been triggered
4276
* multiple times, instead of only once, if the allocations
4277
* were handled individually, but it would do no harm (or
4278
* even be beneficial) to coalesce the triggerings.
4279
*/
4280
thread_alloc_event(tsd, progress * usize);
4281
4282
if (progress < batch || prof_sample_event) {
4283
void *p = je_mallocx(size, flags);
4284
if (p == NULL) { /* OOM */
4285
break;
4286
}
4287
if (progress == batch) {
4288
assert(prof_sampled(tsd, p));
4289
}
4290
ptrs[filled++] = p;
4291
}
4292
}
4293
4294
label_done:
4295
check_entry_exit_locking(tsd_tsdn(tsd));
4296
LOG("core.batch_alloc.exit", "result: %zu", filled);
4297
return filled;
4298
}
4299
4300
/*
4301
* End non-standard functions.
4302
*/
4303
/******************************************************************************/
4304
/*
4305
* Begin compatibility functions.
4306
*/
4307
4308
#define ALLOCM_LG_ALIGN(la) (la)
4309
#define ALLOCM_ALIGN(a) (ffsl(a)-1)
4310
#define ALLOCM_ZERO ((int)0x40)
4311
#define ALLOCM_NO_MOVE ((int)0x80)
4312
4313
#define ALLOCM_SUCCESS 0
4314
#define ALLOCM_ERR_OOM 1
4315
#define ALLOCM_ERR_NOT_MOVED 2
4316
4317
int
4318
je_allocm(void **ptr, size_t *rsize, size_t size, int flags) {
4319
assert(ptr != NULL);
4320
4321
void *p = je_mallocx(size, flags);
4322
if (p == NULL) {
4323
return (ALLOCM_ERR_OOM);
4324
}
4325
if (rsize != NULL) {
4326
*rsize = isalloc(tsdn_fetch(), p);
4327
}
4328
*ptr = p;
4329
return ALLOCM_SUCCESS;
4330
}
4331
4332
int
4333
je_rallocm(void **ptr, size_t *rsize, size_t size, size_t extra, int flags) {
4334
assert(ptr != NULL);
4335
assert(*ptr != NULL);
4336
assert(size != 0);
4337
assert(SIZE_T_MAX - size >= extra);
4338
4339
int ret;
4340
bool no_move = flags & ALLOCM_NO_MOVE;
4341
4342
if (no_move) {
4343
size_t usize = je_xallocx(*ptr, size, extra, flags);
4344
ret = (usize >= size) ? ALLOCM_SUCCESS : ALLOCM_ERR_NOT_MOVED;
4345
if (rsize != NULL) {
4346
*rsize = usize;
4347
}
4348
} else {
4349
void *p = je_rallocx(*ptr, size+extra, flags);
4350
if (p != NULL) {
4351
*ptr = p;
4352
ret = ALLOCM_SUCCESS;
4353
} else {
4354
ret = ALLOCM_ERR_OOM;
4355
}
4356
if (rsize != NULL) {
4357
*rsize = isalloc(tsdn_fetch(), *ptr);
4358
}
4359
}
4360
return ret;
4361
}
4362
4363
int
4364
je_sallocm(const void *ptr, size_t *rsize, int flags) {
4365
assert(rsize != NULL);
4366
*rsize = je_sallocx(ptr, flags);
4367
return ALLOCM_SUCCESS;
4368
}
4369
4370
int
4371
je_dallocm(void *ptr, int flags) {
4372
je_dallocx(ptr, flags);
4373
return ALLOCM_SUCCESS;
4374
}
4375
4376
int
4377
je_nallocm(size_t *rsize, size_t size, int flags) {
4378
size_t usize = je_nallocx(size, flags);
4379
if (usize == 0) {
4380
return ALLOCM_ERR_OOM;
4381
}
4382
if (rsize != NULL) {
4383
*rsize = usize;
4384
}
4385
return ALLOCM_SUCCESS;
4386
}
4387
4388
#undef ALLOCM_LG_ALIGN
4389
#undef ALLOCM_ALIGN
4390
#undef ALLOCM_ZERO
4391
#undef ALLOCM_NO_MOVE
4392
4393
#undef ALLOCM_SUCCESS
4394
#undef ALLOCM_ERR_OOM
4395
#undef ALLOCM_ERR_NOT_MOVED
4396
4397
/*
4398
* End compatibility functions.
4399
*/
4400
/******************************************************************************/
4401
/*
4402
* The following functions are used by threading libraries for protection of
4403
* malloc during fork().
4404
*/
4405
4406
/*
4407
* If an application creates a thread before doing any allocation in the main
4408
* thread, then calls fork(2) in the main thread followed by memory allocation
4409
* in the child process, a race can occur that results in deadlock within the
4410
* child: the main thread may have forked while the created thread had
4411
* partially initialized the allocator. Ordinarily jemalloc prevents
4412
* fork/malloc races via the following functions it registers during
4413
* initialization using pthread_atfork(), but of course that does no good if
4414
* the allocator isn't fully initialized at fork time. The following library
4415
* constructor is a partial solution to this problem. It may still be possible
4416
* to trigger the deadlock described above, but doing so would involve forking
4417
* via a library constructor that runs before jemalloc's runs.
4418
*/
4419
#ifndef JEMALLOC_JET
4420
JEMALLOC_ATTR(constructor)
4421
static void
4422
jemalloc_constructor(void) {
4423
malloc_init();
4424
}
4425
#endif
4426
4427
#ifndef JEMALLOC_MUTEX_INIT_CB
4428
void
4429
jemalloc_prefork(void)
4430
#else
4431
JEMALLOC_EXPORT void
4432
_malloc_prefork(void)
4433
#endif
4434
{
4435
tsd_t *tsd;
4436
unsigned i, j, narenas;
4437
arena_t *arena;
4438
4439
#ifdef JEMALLOC_MUTEX_INIT_CB
4440
if (!malloc_initialized()) {
4441
return;
4442
}
4443
#endif
4444
assert(malloc_initialized());
4445
4446
tsd = tsd_fetch();
4447
4448
narenas = narenas_total_get();
4449
4450
witness_prefork(tsd_witness_tsdp_get(tsd));
4451
/* Acquire all mutexes in a safe order. */
4452
ctl_prefork(tsd_tsdn(tsd));
4453
tcache_prefork(tsd_tsdn(tsd));
4454
malloc_mutex_prefork(tsd_tsdn(tsd), &arenas_lock);
4455
if (have_background_thread) {
4456
background_thread_prefork0(tsd_tsdn(tsd));
4457
}
4458
prof_prefork0(tsd_tsdn(tsd));
4459
if (have_background_thread) {
4460
background_thread_prefork1(tsd_tsdn(tsd));
4461
}
4462
/* Break arena prefork into stages to preserve lock order. */
4463
for (i = 0; i < 9; i++) {
4464
for (j = 0; j < narenas; j++) {
4465
if ((arena = arena_get(tsd_tsdn(tsd), j, false)) !=
4466
NULL) {
4467
switch (i) {
4468
case 0:
4469
arena_prefork0(tsd_tsdn(tsd), arena);
4470
break;
4471
case 1:
4472
arena_prefork1(tsd_tsdn(tsd), arena);
4473
break;
4474
case 2:
4475
arena_prefork2(tsd_tsdn(tsd), arena);
4476
break;
4477
case 3:
4478
arena_prefork3(tsd_tsdn(tsd), arena);
4479
break;
4480
case 4:
4481
arena_prefork4(tsd_tsdn(tsd), arena);
4482
break;
4483
case 5:
4484
arena_prefork5(tsd_tsdn(tsd), arena);
4485
break;
4486
case 6:
4487
arena_prefork6(tsd_tsdn(tsd), arena);
4488
break;
4489
case 7:
4490
arena_prefork7(tsd_tsdn(tsd), arena);
4491
break;
4492
case 8:
4493
arena_prefork8(tsd_tsdn(tsd), arena);
4494
break;
4495
default: not_reached();
4496
}
4497
}
4498
}
4499
4500
}
4501
prof_prefork1(tsd_tsdn(tsd));
4502
stats_prefork(tsd_tsdn(tsd));
4503
tsd_prefork(tsd);
4504
}
4505
4506
#ifndef JEMALLOC_MUTEX_INIT_CB
4507
void
4508
jemalloc_postfork_parent(void)
4509
#else
4510
JEMALLOC_EXPORT void
4511
_malloc_postfork(void)
4512
#endif
4513
{
4514
tsd_t *tsd;
4515
unsigned i, narenas;
4516
4517
#ifdef JEMALLOC_MUTEX_INIT_CB
4518
if (!malloc_initialized()) {
4519
return;
4520
}
4521
#endif
4522
assert(malloc_initialized());
4523
4524
tsd = tsd_fetch();
4525
4526
tsd_postfork_parent(tsd);
4527
4528
witness_postfork_parent(tsd_witness_tsdp_get(tsd));
4529
/* Release all mutexes, now that fork() has completed. */
4530
stats_postfork_parent(tsd_tsdn(tsd));
4531
for (i = 0, narenas = narenas_total_get(); i < narenas; i++) {
4532
arena_t *arena;
4533
4534
if ((arena = arena_get(tsd_tsdn(tsd), i, false)) != NULL) {
4535
arena_postfork_parent(tsd_tsdn(tsd), arena);
4536
}
4537
}
4538
prof_postfork_parent(tsd_tsdn(tsd));
4539
if (have_background_thread) {
4540
background_thread_postfork_parent(tsd_tsdn(tsd));
4541
}
4542
malloc_mutex_postfork_parent(tsd_tsdn(tsd), &arenas_lock);
4543
tcache_postfork_parent(tsd_tsdn(tsd));
4544
ctl_postfork_parent(tsd_tsdn(tsd));
4545
}
4546
4547
void
4548
jemalloc_postfork_child(void) {
4549
tsd_t *tsd;
4550
unsigned i, narenas;
4551
4552
assert(malloc_initialized());
4553
4554
tsd = tsd_fetch();
4555
4556
tsd_postfork_child(tsd);
4557
4558
witness_postfork_child(tsd_witness_tsdp_get(tsd));
4559
/* Release all mutexes, now that fork() has completed. */
4560
stats_postfork_child(tsd_tsdn(tsd));
4561
for (i = 0, narenas = narenas_total_get(); i < narenas; i++) {
4562
arena_t *arena;
4563
4564
if ((arena = arena_get(tsd_tsdn(tsd), i, false)) != NULL) {
4565
arena_postfork_child(tsd_tsdn(tsd), arena);
4566
}
4567
}
4568
prof_postfork_child(tsd_tsdn(tsd));
4569
if (have_background_thread) {
4570
background_thread_postfork_child(tsd_tsdn(tsd));
4571
}
4572
malloc_mutex_postfork_child(tsd_tsdn(tsd), &arenas_lock);
4573
tcache_postfork_child(tsd_tsdn(tsd));
4574
ctl_postfork_child(tsd_tsdn(tsd));
4575
}
4576
4577
void
4578
_malloc_first_thread(void)
4579
{
4580
4581
(void)malloc_mutex_first_thread();
4582
}
4583
4584
/******************************************************************************/
4585
4586