xref: /freebsd/contrib/jemalloc/src/prof.c (revision c43cad87172039ccf38172129c79755ea79e6102)
1 #include "jemalloc/internal/jemalloc_preamble.h"
2 #include "jemalloc/internal/jemalloc_internal_includes.h"
3 
4 #include "jemalloc/internal/ctl.h"
5 #include "jemalloc/internal/assert.h"
6 #include "jemalloc/internal/mutex.h"
7 #include "jemalloc/internal/counter.h"
8 #include "jemalloc/internal/prof_data.h"
9 #include "jemalloc/internal/prof_log.h"
10 #include "jemalloc/internal/prof_recent.h"
11 #include "jemalloc/internal/prof_stats.h"
12 #include "jemalloc/internal/prof_sys.h"
13 #include "jemalloc/internal/prof_hook.h"
14 #include "jemalloc/internal/thread_event.h"
15 
16 /*
17  * This file implements the profiling "APIs" needed by other parts of jemalloc,
18  * and also manages the relevant "operational" data, mainly options and mutexes;
19  * the core profiling data structures are encapsulated in prof_data.c.
20  */
21 
22 /******************************************************************************/
23 
24 /* Data. */
25 
26 bool opt_prof = false;
27 bool opt_prof_active = true;
28 bool opt_prof_thread_active_init = true;
29 size_t opt_lg_prof_sample = LG_PROF_SAMPLE_DEFAULT;
30 ssize_t opt_lg_prof_interval = LG_PROF_INTERVAL_DEFAULT;
31 bool opt_prof_gdump = false;
32 bool opt_prof_final = false;
33 bool opt_prof_leak = false;
34 bool opt_prof_leak_error = false;
35 bool opt_prof_accum = false;
36 char opt_prof_prefix[PROF_DUMP_FILENAME_LEN];
37 bool opt_prof_sys_thread_name = false;
38 bool opt_prof_unbias = true;
39 
40 /* Accessed via prof_sample_event_handler(). */
41 static counter_accum_t prof_idump_accumulated;
42 
43 /*
44  * Initialized as opt_prof_active, and accessed via
45  * prof_active_[gs]et{_unlocked,}().
46  */
47 bool prof_active_state;
48 static malloc_mutex_t prof_active_mtx;
49 
50 /*
51  * Initialized as opt_prof_thread_active_init, and accessed via
52  * prof_thread_active_init_[gs]et().
53  */
54 static bool prof_thread_active_init;
55 static malloc_mutex_t prof_thread_active_init_mtx;
56 
57 /*
58  * Initialized as opt_prof_gdump, and accessed via
59  * prof_gdump_[gs]et{_unlocked,}().
60  */
61 bool prof_gdump_val;
62 static malloc_mutex_t prof_gdump_mtx;
63 
64 uint64_t prof_interval = 0;
65 
66 size_t lg_prof_sample;
67 
68 static uint64_t next_thr_uid;
69 static malloc_mutex_t next_thr_uid_mtx;
70 
71 /* Do not dump any profiles until bootstrapping is complete. */
72 bool prof_booted = false;
73 
74 /* Logically a prof_backtrace_hook_t. */
75 atomic_p_t prof_backtrace_hook;
76 
77 /* Logically a prof_dump_hook_t. */
78 atomic_p_t prof_dump_hook;
79 
80 /******************************************************************************/
81 
82 void
83 prof_alloc_rollback(tsd_t *tsd, prof_tctx_t *tctx) {
84 	cassert(config_prof);
85 
86 	if (tsd_reentrancy_level_get(tsd) > 0) {
87 		assert((uintptr_t)tctx == (uintptr_t)1U);
88 		return;
89 	}
90 
91 	if ((uintptr_t)tctx > (uintptr_t)1U) {
92 		malloc_mutex_lock(tsd_tsdn(tsd), tctx->tdata->lock);
93 		tctx->prepared = false;
94 		prof_tctx_try_destroy(tsd, tctx);
95 	}
96 }
97 
98 void
99 prof_malloc_sample_object(tsd_t *tsd, const void *ptr, size_t size,
100     size_t usize, prof_tctx_t *tctx) {
101 	cassert(config_prof);
102 
103 	if (opt_prof_sys_thread_name) {
104 		prof_sys_thread_name_fetch(tsd);
105 	}
106 
107 	edata_t *edata = emap_edata_lookup(tsd_tsdn(tsd), &arena_emap_global,
108 	    ptr);
109 	prof_info_set(tsd, edata, tctx, size);
110 
111 	szind_t szind = sz_size2index(usize);
112 
113 	malloc_mutex_lock(tsd_tsdn(tsd), tctx->tdata->lock);
114 	/*
115 	 * We need to do these map lookups while holding the lock, to avoid the
116 	 * possibility of races with prof_reset calls, which update the map and
117 	 * then acquire the lock.  This actually still leaves a data race on the
118 	 * contents of the unbias map, but we have not yet gone through and
119 	 * atomic-ified the prof module, and compilers are not yet causing us
120 	 * issues.  The key thing is to make sure that, if we read garbage data,
121 	 * the prof_reset call is about to mark our tctx as expired before any
122 	 * dumping of our corrupted output is attempted.
123 	 */
124 	size_t shifted_unbiased_cnt = prof_shifted_unbiased_cnt[szind];
125 	size_t unbiased_bytes = prof_unbiased_sz[szind];
126 	tctx->cnts.curobjs++;
127 	tctx->cnts.curobjs_shifted_unbiased += shifted_unbiased_cnt;
128 	tctx->cnts.curbytes += usize;
129 	tctx->cnts.curbytes_unbiased += unbiased_bytes;
130 	if (opt_prof_accum) {
131 		tctx->cnts.accumobjs++;
132 		tctx->cnts.accumobjs_shifted_unbiased += shifted_unbiased_cnt;
133 		tctx->cnts.accumbytes += usize;
134 		tctx->cnts.accumbytes_unbiased += unbiased_bytes;
135 	}
136 	bool record_recent = prof_recent_alloc_prepare(tsd, tctx);
137 	tctx->prepared = false;
138 	malloc_mutex_unlock(tsd_tsdn(tsd), tctx->tdata->lock);
139 	if (record_recent) {
140 		assert(tctx == edata_prof_tctx_get(edata));
141 		prof_recent_alloc(tsd, edata, size, usize);
142 	}
143 
144 	if (opt_prof_stats) {
145 		prof_stats_inc(tsd, szind, size);
146 	}
147 }
148 
149 void
150 prof_free_sampled_object(tsd_t *tsd, size_t usize, prof_info_t *prof_info) {
151 	cassert(config_prof);
152 
153 	assert(prof_info != NULL);
154 	prof_tctx_t *tctx = prof_info->alloc_tctx;
155 	assert((uintptr_t)tctx > (uintptr_t)1U);
156 
157 	szind_t szind = sz_size2index(usize);
158 	malloc_mutex_lock(tsd_tsdn(tsd), tctx->tdata->lock);
159 
160 	assert(tctx->cnts.curobjs > 0);
161 	assert(tctx->cnts.curbytes >= usize);
162 	/*
163 	 * It's not correct to do equivalent asserts for unbiased bytes, because
164 	 * of the potential for races with prof.reset calls.  The map contents
165 	 * should really be atomic, but we have not atomic-ified the prof module
166 	 * yet.
167 	 */
168 	tctx->cnts.curobjs--;
169 	tctx->cnts.curobjs_shifted_unbiased -= prof_shifted_unbiased_cnt[szind];
170 	tctx->cnts.curbytes -= usize;
171 	tctx->cnts.curbytes_unbiased -= prof_unbiased_sz[szind];
172 
173 	prof_try_log(tsd, usize, prof_info);
174 
175 	prof_tctx_try_destroy(tsd, tctx);
176 
177 	if (opt_prof_stats) {
178 		prof_stats_dec(tsd, szind, prof_info->alloc_size);
179 	}
180 }
181 
182 prof_tctx_t *
183 prof_tctx_create(tsd_t *tsd) {
184 	if (!tsd_nominal(tsd) || tsd_reentrancy_level_get(tsd) > 0) {
185 		return NULL;
186 	}
187 
188 	prof_tdata_t *tdata = prof_tdata_get(tsd, true);
189 	if (tdata == NULL) {
190 		return NULL;
191 	}
192 
193 	prof_bt_t bt;
194 	bt_init(&bt, tdata->vec);
195 	prof_backtrace(tsd, &bt);
196 	return prof_lookup(tsd, &bt);
197 }
198 
199 /*
200  * The bodies of this function and prof_leakcheck() are compiled out unless heap
201  * profiling is enabled, so that it is possible to compile jemalloc with
202  * floating point support completely disabled.  Avoiding floating point code is
203  * important on memory-constrained systems, but it also enables a workaround for
204  * versions of glibc that don't properly save/restore floating point registers
205  * during dynamic lazy symbol loading (which internally calls into whatever
206  * malloc implementation happens to be integrated into the application).  Note
207  * that some compilers (e.g.  gcc 4.8) may use floating point registers for fast
208  * memory moves, so jemalloc must be compiled with such optimizations disabled
209  * (e.g.
210  * -mno-sse) in order for the workaround to be complete.
211  */
212 uint64_t
213 prof_sample_new_event_wait(tsd_t *tsd) {
214 #ifdef JEMALLOC_PROF
215 	if (lg_prof_sample == 0) {
216 		return TE_MIN_START_WAIT;
217 	}
218 
219 	/*
220 	 * Compute sample interval as a geometrically distributed random
221 	 * variable with mean (2^lg_prof_sample).
222 	 *
223 	 *                      __        __
224 	 *                      |  log(u)  |                     1
225 	 * bytes_until_sample = | -------- |, where p = ---------------
226 	 *                      | log(1-p) |             lg_prof_sample
227 	 *                                              2
228 	 *
229 	 * For more information on the math, see:
230 	 *
231 	 *   Non-Uniform Random Variate Generation
232 	 *   Luc Devroye
233 	 *   Springer-Verlag, New York, 1986
234 	 *   pp 500
235 	 *   (http://luc.devroye.org/rnbookindex.html)
236 	 *
237 	 * In the actual computation, there's a non-zero probability that our
238 	 * pseudo random number generator generates an exact 0, and to avoid
239 	 * log(0), we set u to 1.0 in case r is 0.  Therefore u effectively is
240 	 * uniformly distributed in (0, 1] instead of [0, 1).  Further, rather
241 	 * than taking the ceiling, we take the floor and then add 1, since
242 	 * otherwise bytes_until_sample would be 0 if u is exactly 1.0.
243 	 */
244 	uint64_t r = prng_lg_range_u64(tsd_prng_statep_get(tsd), 53);
245 	double u = (r == 0U) ? 1.0 : (double)r * (1.0/9007199254740992.0L);
246 	return (uint64_t)(log(u) /
247 	    log(1.0 - (1.0 / (double)((uint64_t)1U << lg_prof_sample))))
248 	    + (uint64_t)1U;
249 #else
250 	not_reached();
251 	return TE_MAX_START_WAIT;
252 #endif
253 }
254 
255 uint64_t
256 prof_sample_postponed_event_wait(tsd_t *tsd) {
257 	/*
258 	 * The postponed wait time for prof sample event is computed as if we
259 	 * want a new wait time (i.e. as if the event were triggered).  If we
260 	 * instead postpone to the immediate next allocation, like how we're
261 	 * handling the other events, then we can have sampling bias, if e.g.
262 	 * the allocation immediately following a reentrancy always comes from
263 	 * the same stack trace.
264 	 */
265 	return prof_sample_new_event_wait(tsd);
266 }
267 
268 void
269 prof_sample_event_handler(tsd_t *tsd, uint64_t elapsed) {
270 	cassert(config_prof);
271 	assert(elapsed > 0 && elapsed != TE_INVALID_ELAPSED);
272 	if (prof_interval == 0 || !prof_active_get_unlocked()) {
273 		return;
274 	}
275 	if (counter_accum(tsd_tsdn(tsd), &prof_idump_accumulated, elapsed)) {
276 		prof_idump(tsd_tsdn(tsd));
277 	}
278 }
279 
280 static void
281 prof_fdump(void) {
282 	tsd_t *tsd;
283 
284 	cassert(config_prof);
285 	assert(opt_prof_final);
286 
287 	if (!prof_booted) {
288 		return;
289 	}
290 	tsd = tsd_fetch();
291 	assert(tsd_reentrancy_level_get(tsd) == 0);
292 
293 	prof_fdump_impl(tsd);
294 }
295 
296 static bool
297 prof_idump_accum_init(void) {
298 	cassert(config_prof);
299 
300 	return counter_accum_init(&prof_idump_accumulated, prof_interval);
301 }
302 
303 void
304 prof_idump(tsdn_t *tsdn) {
305 	tsd_t *tsd;
306 	prof_tdata_t *tdata;
307 
308 	cassert(config_prof);
309 
310 	if (!prof_booted || tsdn_null(tsdn) || !prof_active_get_unlocked()) {
311 		return;
312 	}
313 	tsd = tsdn_tsd(tsdn);
314 	if (tsd_reentrancy_level_get(tsd) > 0) {
315 		return;
316 	}
317 
318 	tdata = prof_tdata_get(tsd, true);
319 	if (tdata == NULL) {
320 		return;
321 	}
322 	if (tdata->enq) {
323 		tdata->enq_idump = true;
324 		return;
325 	}
326 
327 	prof_idump_impl(tsd);
328 }
329 
330 bool
331 prof_mdump(tsd_t *tsd, const char *filename) {
332 	cassert(config_prof);
333 	assert(tsd_reentrancy_level_get(tsd) == 0);
334 
335 	if (!opt_prof || !prof_booted) {
336 		return true;
337 	}
338 
339 	return prof_mdump_impl(tsd, filename);
340 }
341 
342 void
343 prof_gdump(tsdn_t *tsdn) {
344 	tsd_t *tsd;
345 	prof_tdata_t *tdata;
346 
347 	cassert(config_prof);
348 
349 	if (!prof_booted || tsdn_null(tsdn) || !prof_active_get_unlocked()) {
350 		return;
351 	}
352 	tsd = tsdn_tsd(tsdn);
353 	if (tsd_reentrancy_level_get(tsd) > 0) {
354 		return;
355 	}
356 
357 	tdata = prof_tdata_get(tsd, false);
358 	if (tdata == NULL) {
359 		return;
360 	}
361 	if (tdata->enq) {
362 		tdata->enq_gdump = true;
363 		return;
364 	}
365 
366 	prof_gdump_impl(tsd);
367 }
368 
369 static uint64_t
370 prof_thr_uid_alloc(tsdn_t *tsdn) {
371 	uint64_t thr_uid;
372 
373 	malloc_mutex_lock(tsdn, &next_thr_uid_mtx);
374 	thr_uid = next_thr_uid;
375 	next_thr_uid++;
376 	malloc_mutex_unlock(tsdn, &next_thr_uid_mtx);
377 
378 	return thr_uid;
379 }
380 
381 prof_tdata_t *
382 prof_tdata_init(tsd_t *tsd) {
383 	return prof_tdata_init_impl(tsd, prof_thr_uid_alloc(tsd_tsdn(tsd)), 0,
384 	    NULL, prof_thread_active_init_get(tsd_tsdn(tsd)));
385 }
386 
387 prof_tdata_t *
388 prof_tdata_reinit(tsd_t *tsd, prof_tdata_t *tdata) {
389 	uint64_t thr_uid = tdata->thr_uid;
390 	uint64_t thr_discrim = tdata->thr_discrim + 1;
391 	char *thread_name = (tdata->thread_name != NULL) ?
392 	    prof_thread_name_alloc(tsd, tdata->thread_name) : NULL;
393 	bool active = tdata->active;
394 
395 	prof_tdata_detach(tsd, tdata);
396 	return prof_tdata_init_impl(tsd, thr_uid, thr_discrim, thread_name,
397 	    active);
398 }
399 
400 void
401 prof_tdata_cleanup(tsd_t *tsd) {
402 	prof_tdata_t *tdata;
403 
404 	if (!config_prof) {
405 		return;
406 	}
407 
408 	tdata = tsd_prof_tdata_get(tsd);
409 	if (tdata != NULL) {
410 		prof_tdata_detach(tsd, tdata);
411 	}
412 }
413 
414 bool
415 prof_active_get(tsdn_t *tsdn) {
416 	bool prof_active_current;
417 
418 	prof_active_assert();
419 	malloc_mutex_lock(tsdn, &prof_active_mtx);
420 	prof_active_current = prof_active_state;
421 	malloc_mutex_unlock(tsdn, &prof_active_mtx);
422 	return prof_active_current;
423 }
424 
425 bool
426 prof_active_set(tsdn_t *tsdn, bool active) {
427 	bool prof_active_old;
428 
429 	prof_active_assert();
430 	malloc_mutex_lock(tsdn, &prof_active_mtx);
431 	prof_active_old = prof_active_state;
432 	prof_active_state = active;
433 	malloc_mutex_unlock(tsdn, &prof_active_mtx);
434 	prof_active_assert();
435 	return prof_active_old;
436 }
437 
438 const char *
439 prof_thread_name_get(tsd_t *tsd) {
440 	assert(tsd_reentrancy_level_get(tsd) == 0);
441 
442 	prof_tdata_t *tdata;
443 
444 	tdata = prof_tdata_get(tsd, true);
445 	if (tdata == NULL) {
446 		return "";
447 	}
448 	return (tdata->thread_name != NULL ? tdata->thread_name : "");
449 }
450 
451 int
452 prof_thread_name_set(tsd_t *tsd, const char *thread_name) {
453 	if (opt_prof_sys_thread_name) {
454 		return ENOENT;
455 	} else {
456 		return prof_thread_name_set_impl(tsd, thread_name);
457 	}
458 }
459 
460 bool
461 prof_thread_active_get(tsd_t *tsd) {
462 	assert(tsd_reentrancy_level_get(tsd) == 0);
463 
464 	prof_tdata_t *tdata;
465 
466 	tdata = prof_tdata_get(tsd, true);
467 	if (tdata == NULL) {
468 		return false;
469 	}
470 	return tdata->active;
471 }
472 
473 bool
474 prof_thread_active_set(tsd_t *tsd, bool active) {
475 	assert(tsd_reentrancy_level_get(tsd) == 0);
476 
477 	prof_tdata_t *tdata;
478 
479 	tdata = prof_tdata_get(tsd, true);
480 	if (tdata == NULL) {
481 		return true;
482 	}
483 	tdata->active = active;
484 	return false;
485 }
486 
487 bool
488 prof_thread_active_init_get(tsdn_t *tsdn) {
489 	bool active_init;
490 
491 	malloc_mutex_lock(tsdn, &prof_thread_active_init_mtx);
492 	active_init = prof_thread_active_init;
493 	malloc_mutex_unlock(tsdn, &prof_thread_active_init_mtx);
494 	return active_init;
495 }
496 
497 bool
498 prof_thread_active_init_set(tsdn_t *tsdn, bool active_init) {
499 	bool active_init_old;
500 
501 	malloc_mutex_lock(tsdn, &prof_thread_active_init_mtx);
502 	active_init_old = prof_thread_active_init;
503 	prof_thread_active_init = active_init;
504 	malloc_mutex_unlock(tsdn, &prof_thread_active_init_mtx);
505 	return active_init_old;
506 }
507 
508 bool
509 prof_gdump_get(tsdn_t *tsdn) {
510 	bool prof_gdump_current;
511 
512 	malloc_mutex_lock(tsdn, &prof_gdump_mtx);
513 	prof_gdump_current = prof_gdump_val;
514 	malloc_mutex_unlock(tsdn, &prof_gdump_mtx);
515 	return prof_gdump_current;
516 }
517 
518 bool
519 prof_gdump_set(tsdn_t *tsdn, bool gdump) {
520 	bool prof_gdump_old;
521 
522 	malloc_mutex_lock(tsdn, &prof_gdump_mtx);
523 	prof_gdump_old = prof_gdump_val;
524 	prof_gdump_val = gdump;
525 	malloc_mutex_unlock(tsdn, &prof_gdump_mtx);
526 	return prof_gdump_old;
527 }
528 
529 void
530 prof_backtrace_hook_set(prof_backtrace_hook_t hook) {
531 	atomic_store_p(&prof_backtrace_hook, hook, ATOMIC_RELEASE);
532 }
533 
534 prof_backtrace_hook_t
535 prof_backtrace_hook_get() {
536 	return (prof_backtrace_hook_t)atomic_load_p(&prof_backtrace_hook,
537 	    ATOMIC_ACQUIRE);
538 }
539 
540 void
541 prof_dump_hook_set(prof_dump_hook_t hook) {
542 	atomic_store_p(&prof_dump_hook, hook, ATOMIC_RELEASE);
543 }
544 
545 prof_dump_hook_t
546 prof_dump_hook_get() {
547 	return (prof_dump_hook_t)atomic_load_p(&prof_dump_hook,
548 	    ATOMIC_ACQUIRE);
549 }
550 
551 void
552 prof_boot0(void) {
553 	cassert(config_prof);
554 
555 	memcpy(opt_prof_prefix, PROF_PREFIX_DEFAULT,
556 	    sizeof(PROF_PREFIX_DEFAULT));
557 }
558 
559 void
560 prof_boot1(void) {
561 	cassert(config_prof);
562 
563 	/*
564 	 * opt_prof must be in its final state before any arenas are
565 	 * initialized, so this function must be executed early.
566 	 */
567 	if (opt_prof_leak_error && !opt_prof_leak) {
568 		opt_prof_leak = true;
569 	}
570 
571 	if (opt_prof_leak && !opt_prof) {
572 		/*
573 		 * Enable opt_prof, but in such a way that profiles are never
574 		 * automatically dumped.
575 		 */
576 		opt_prof = true;
577 		opt_prof_gdump = false;
578 	} else if (opt_prof) {
579 		if (opt_lg_prof_interval >= 0) {
580 			prof_interval = (((uint64_t)1U) <<
581 			    opt_lg_prof_interval);
582 		}
583 	}
584 }
585 
586 bool
587 prof_boot2(tsd_t *tsd, base_t *base) {
588 	cassert(config_prof);
589 
590 	/*
591 	 * Initialize the global mutexes unconditionally to maintain correct
592 	 * stats when opt_prof is false.
593 	 */
594 	if (malloc_mutex_init(&prof_active_mtx, "prof_active",
595 	    WITNESS_RANK_PROF_ACTIVE, malloc_mutex_rank_exclusive)) {
596 		return true;
597 	}
598 	if (malloc_mutex_init(&prof_gdump_mtx, "prof_gdump",
599 	    WITNESS_RANK_PROF_GDUMP, malloc_mutex_rank_exclusive)) {
600 		return true;
601 	}
602 	if (malloc_mutex_init(&prof_thread_active_init_mtx,
603 	    "prof_thread_active_init", WITNESS_RANK_PROF_THREAD_ACTIVE_INIT,
604 	    malloc_mutex_rank_exclusive)) {
605 		return true;
606 	}
607 	if (malloc_mutex_init(&bt2gctx_mtx, "prof_bt2gctx",
608 	    WITNESS_RANK_PROF_BT2GCTX, malloc_mutex_rank_exclusive)) {
609 		return true;
610 	}
611 	if (malloc_mutex_init(&tdatas_mtx, "prof_tdatas",
612 	    WITNESS_RANK_PROF_TDATAS, malloc_mutex_rank_exclusive)) {
613 		return true;
614 	}
615 	if (malloc_mutex_init(&next_thr_uid_mtx, "prof_next_thr_uid",
616 	    WITNESS_RANK_PROF_NEXT_THR_UID, malloc_mutex_rank_exclusive)) {
617 		return true;
618 	}
619 	if (malloc_mutex_init(&prof_stats_mtx, "prof_stats",
620 	    WITNESS_RANK_PROF_STATS, malloc_mutex_rank_exclusive)) {
621 		return true;
622 	}
623 	if (malloc_mutex_init(&prof_dump_filename_mtx,
624 	    "prof_dump_filename", WITNESS_RANK_PROF_DUMP_FILENAME,
625 	    malloc_mutex_rank_exclusive)) {
626 		return true;
627 	}
628 	if (malloc_mutex_init(&prof_dump_mtx, "prof_dump",
629 	    WITNESS_RANK_PROF_DUMP, malloc_mutex_rank_exclusive)) {
630 		return true;
631 	}
632 
633 	if (opt_prof) {
634 		lg_prof_sample = opt_lg_prof_sample;
635 		prof_unbias_map_init();
636 		prof_active_state = opt_prof_active;
637 		prof_gdump_val = opt_prof_gdump;
638 		prof_thread_active_init = opt_prof_thread_active_init;
639 
640 		if (prof_data_init(tsd)) {
641 			return true;
642 		}
643 
644 		next_thr_uid = 0;
645 		if (prof_idump_accum_init()) {
646 			return true;
647 		}
648 
649 		if (opt_prof_final && opt_prof_prefix[0] != '\0' &&
650 		    atexit(prof_fdump) != 0) {
651 			malloc_write("<jemalloc>: Error in atexit()\n");
652 			if (opt_abort) {
653 				abort();
654 			}
655 		}
656 
657 		if (prof_log_init(tsd)) {
658 			return true;
659 		}
660 
661 		if (prof_recent_init()) {
662 			return true;
663 		}
664 
665 		prof_base = base;
666 
667 		gctx_locks = (malloc_mutex_t *)base_alloc(tsd_tsdn(tsd), base,
668 		    PROF_NCTX_LOCKS * sizeof(malloc_mutex_t), CACHELINE);
669 		if (gctx_locks == NULL) {
670 			return true;
671 		}
672 		for (unsigned i = 0; i < PROF_NCTX_LOCKS; i++) {
673 			if (malloc_mutex_init(&gctx_locks[i], "prof_gctx",
674 			    WITNESS_RANK_PROF_GCTX,
675 			    malloc_mutex_rank_exclusive)) {
676 				return true;
677 			}
678 		}
679 
680 		tdata_locks = (malloc_mutex_t *)base_alloc(tsd_tsdn(tsd), base,
681 		    PROF_NTDATA_LOCKS * sizeof(malloc_mutex_t), CACHELINE);
682 		if (tdata_locks == NULL) {
683 			return true;
684 		}
685 		for (unsigned i = 0; i < PROF_NTDATA_LOCKS; i++) {
686 			if (malloc_mutex_init(&tdata_locks[i], "prof_tdata",
687 			    WITNESS_RANK_PROF_TDATA,
688 			    malloc_mutex_rank_exclusive)) {
689 				return true;
690 			}
691 		}
692 
693 		prof_unwind_init();
694 		prof_hooks_init();
695 	}
696 	prof_booted = true;
697 
698 	return false;
699 }
700 
701 void
702 prof_prefork0(tsdn_t *tsdn) {
703 	if (config_prof && opt_prof) {
704 		unsigned i;
705 
706 		malloc_mutex_prefork(tsdn, &prof_dump_mtx);
707 		malloc_mutex_prefork(tsdn, &bt2gctx_mtx);
708 		malloc_mutex_prefork(tsdn, &tdatas_mtx);
709 		for (i = 0; i < PROF_NTDATA_LOCKS; i++) {
710 			malloc_mutex_prefork(tsdn, &tdata_locks[i]);
711 		}
712 		malloc_mutex_prefork(tsdn, &log_mtx);
713 		for (i = 0; i < PROF_NCTX_LOCKS; i++) {
714 			malloc_mutex_prefork(tsdn, &gctx_locks[i]);
715 		}
716 		malloc_mutex_prefork(tsdn, &prof_recent_dump_mtx);
717 	}
718 }
719 
720 void
721 prof_prefork1(tsdn_t *tsdn) {
722 	if (config_prof && opt_prof) {
723 		counter_prefork(tsdn, &prof_idump_accumulated);
724 		malloc_mutex_prefork(tsdn, &prof_active_mtx);
725 		malloc_mutex_prefork(tsdn, &prof_dump_filename_mtx);
726 		malloc_mutex_prefork(tsdn, &prof_gdump_mtx);
727 		malloc_mutex_prefork(tsdn, &prof_recent_alloc_mtx);
728 		malloc_mutex_prefork(tsdn, &prof_stats_mtx);
729 		malloc_mutex_prefork(tsdn, &next_thr_uid_mtx);
730 		malloc_mutex_prefork(tsdn, &prof_thread_active_init_mtx);
731 	}
732 }
733 
734 void
735 prof_postfork_parent(tsdn_t *tsdn) {
736 	if (config_prof && opt_prof) {
737 		unsigned i;
738 
739 		malloc_mutex_postfork_parent(tsdn,
740 		    &prof_thread_active_init_mtx);
741 		malloc_mutex_postfork_parent(tsdn, &next_thr_uid_mtx);
742 		malloc_mutex_postfork_parent(tsdn, &prof_stats_mtx);
743 		malloc_mutex_postfork_parent(tsdn, &prof_recent_alloc_mtx);
744 		malloc_mutex_postfork_parent(tsdn, &prof_gdump_mtx);
745 		malloc_mutex_postfork_parent(tsdn, &prof_dump_filename_mtx);
746 		malloc_mutex_postfork_parent(tsdn, &prof_active_mtx);
747 		counter_postfork_parent(tsdn, &prof_idump_accumulated);
748 		malloc_mutex_postfork_parent(tsdn, &prof_recent_dump_mtx);
749 		for (i = 0; i < PROF_NCTX_LOCKS; i++) {
750 			malloc_mutex_postfork_parent(tsdn, &gctx_locks[i]);
751 		}
752 		malloc_mutex_postfork_parent(tsdn, &log_mtx);
753 		for (i = 0; i < PROF_NTDATA_LOCKS; i++) {
754 			malloc_mutex_postfork_parent(tsdn, &tdata_locks[i]);
755 		}
756 		malloc_mutex_postfork_parent(tsdn, &tdatas_mtx);
757 		malloc_mutex_postfork_parent(tsdn, &bt2gctx_mtx);
758 		malloc_mutex_postfork_parent(tsdn, &prof_dump_mtx);
759 	}
760 }
761 
762 void
763 prof_postfork_child(tsdn_t *tsdn) {
764 	if (config_prof && opt_prof) {
765 		unsigned i;
766 
767 		malloc_mutex_postfork_child(tsdn, &prof_thread_active_init_mtx);
768 		malloc_mutex_postfork_child(tsdn, &next_thr_uid_mtx);
769 		malloc_mutex_postfork_child(tsdn, &prof_stats_mtx);
770 		malloc_mutex_postfork_child(tsdn, &prof_recent_alloc_mtx);
771 		malloc_mutex_postfork_child(tsdn, &prof_gdump_mtx);
772 		malloc_mutex_postfork_child(tsdn, &prof_dump_filename_mtx);
773 		malloc_mutex_postfork_child(tsdn, &prof_active_mtx);
774 		counter_postfork_child(tsdn, &prof_idump_accumulated);
775 		malloc_mutex_postfork_child(tsdn, &prof_recent_dump_mtx);
776 		for (i = 0; i < PROF_NCTX_LOCKS; i++) {
777 			malloc_mutex_postfork_child(tsdn, &gctx_locks[i]);
778 		}
779 		malloc_mutex_postfork_child(tsdn, &log_mtx);
780 		for (i = 0; i < PROF_NTDATA_LOCKS; i++) {
781 			malloc_mutex_postfork_child(tsdn, &tdata_locks[i]);
782 		}
783 		malloc_mutex_postfork_child(tsdn, &tdatas_mtx);
784 		malloc_mutex_postfork_child(tsdn, &bt2gctx_mtx);
785 		malloc_mutex_postfork_child(tsdn, &prof_dump_mtx);
786 	}
787 }
788 
789 /******************************************************************************/
790