xref: /freebsd/contrib/jemalloc/src/prof.c (revision e17f5b1d)
1 #define JEMALLOC_PROF_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/ckh.h"
7 #include "jemalloc/internal/hash.h"
8 #include "jemalloc/internal/malloc_io.h"
9 #include "jemalloc/internal/mutex.h"
10 #include "jemalloc/internal/emitter.h"
11 
12 /******************************************************************************/
13 
14 #ifdef JEMALLOC_PROF_LIBUNWIND
15 #define UNW_LOCAL_ONLY
16 #include <libunwind.h>
17 #endif
18 
19 #ifdef JEMALLOC_PROF_LIBGCC
20 /*
21  * We have a circular dependency -- jemalloc_internal.h tells us if we should
22  * use libgcc's unwinding functionality, but after we've included that, we've
23  * already hooked _Unwind_Backtrace.  We'll temporarily disable hooking.
24  */
25 #undef _Unwind_Backtrace
26 #include <unwind.h>
27 #define _Unwind_Backtrace JEMALLOC_HOOK(_Unwind_Backtrace, test_hooks_libc_hook)
28 #endif
29 
30 /******************************************************************************/
31 /* Data. */
32 
33 bool		opt_prof = false;
34 bool		opt_prof_active = true;
35 bool		opt_prof_thread_active_init = true;
36 size_t		opt_lg_prof_sample = LG_PROF_SAMPLE_DEFAULT;
37 ssize_t		opt_lg_prof_interval = LG_PROF_INTERVAL_DEFAULT;
38 bool		opt_prof_gdump = false;
39 bool		opt_prof_final = false;
40 bool		opt_prof_leak = false;
41 bool		opt_prof_accum = false;
42 bool		opt_prof_log = false;
43 char		opt_prof_prefix[
44     /* Minimize memory bloat for non-prof builds. */
45 #ifdef JEMALLOC_PROF
46     PATH_MAX +
47 #endif
48     1];
49 
50 /*
51  * Initialized as opt_prof_active, and accessed via
52  * prof_active_[gs]et{_unlocked,}().
53  */
54 bool			prof_active;
55 static malloc_mutex_t	prof_active_mtx;
56 
57 /*
58  * Initialized as opt_prof_thread_active_init, and accessed via
59  * prof_thread_active_init_[gs]et().
60  */
61 static bool		prof_thread_active_init;
62 static malloc_mutex_t	prof_thread_active_init_mtx;
63 
64 /*
65  * Initialized as opt_prof_gdump, and accessed via
66  * prof_gdump_[gs]et{_unlocked,}().
67  */
68 bool			prof_gdump_val;
69 static malloc_mutex_t	prof_gdump_mtx;
70 
71 uint64_t	prof_interval = 0;
72 
73 size_t		lg_prof_sample;
74 
75 typedef enum prof_logging_state_e prof_logging_state_t;
76 enum prof_logging_state_e {
77 	prof_logging_state_stopped,
78 	prof_logging_state_started,
79 	prof_logging_state_dumping
80 };
81 
82 /*
83  * - stopped: log_start never called, or previous log_stop has completed.
84  * - started: log_start called, log_stop not called yet. Allocations are logged.
85  * - dumping: log_stop called but not finished; samples are not logged anymore.
86  */
87 prof_logging_state_t prof_logging_state = prof_logging_state_stopped;
88 
89 #ifdef JEMALLOC_JET
90 static bool prof_log_dummy = false;
91 #endif
92 
93 /* Incremented for every log file that is output. */
94 static uint64_t log_seq = 0;
95 static char log_filename[
96     /* Minimize memory bloat for non-prof builds. */
97 #ifdef JEMALLOC_PROF
98     PATH_MAX +
99 #endif
100     1];
101 
102 /* Timestamp for most recent call to log_start(). */
103 static nstime_t log_start_timestamp = NSTIME_ZERO_INITIALIZER;
104 
105 /* Increment these when adding to the log_bt and log_thr linked lists. */
106 static size_t log_bt_index = 0;
107 static size_t log_thr_index = 0;
108 
109 /* Linked list node definitions. These are only used in prof.c. */
110 typedef struct prof_bt_node_s prof_bt_node_t;
111 
112 struct prof_bt_node_s {
113 	prof_bt_node_t *next;
114 	size_t index;
115 	prof_bt_t bt;
116 	/* Variable size backtrace vector pointed to by bt. */
117 	void *vec[1];
118 };
119 
120 typedef struct prof_thr_node_s prof_thr_node_t;
121 
122 struct prof_thr_node_s {
123 	prof_thr_node_t *next;
124 	size_t index;
125 	uint64_t thr_uid;
126 	/* Variable size based on thr_name_sz. */
127 	char name[1];
128 };
129 
130 typedef struct prof_alloc_node_s prof_alloc_node_t;
131 
132 /* This is output when logging sampled allocations. */
133 struct prof_alloc_node_s {
134 	prof_alloc_node_t *next;
135 	/* Indices into an array of thread data. */
136 	size_t alloc_thr_ind;
137 	size_t free_thr_ind;
138 
139 	/* Indices into an array of backtraces. */
140 	size_t alloc_bt_ind;
141 	size_t free_bt_ind;
142 
143 	uint64_t alloc_time_ns;
144 	uint64_t free_time_ns;
145 
146 	size_t usize;
147 };
148 
149 /*
150  * Created on the first call to prof_log_start and deleted on prof_log_stop.
151  * These are the backtraces and threads that have already been logged by an
152  * allocation.
153  */
154 static bool log_tables_initialized = false;
155 static ckh_t log_bt_node_set;
156 static ckh_t log_thr_node_set;
157 
158 /* Store linked lists for logged data. */
159 static prof_bt_node_t *log_bt_first = NULL;
160 static prof_bt_node_t *log_bt_last = NULL;
161 static prof_thr_node_t *log_thr_first = NULL;
162 static prof_thr_node_t *log_thr_last = NULL;
163 static prof_alloc_node_t *log_alloc_first = NULL;
164 static prof_alloc_node_t *log_alloc_last = NULL;
165 
166 /* Protects the prof_logging_state and any log_{...} variable. */
167 static malloc_mutex_t log_mtx;
168 
169 /*
170  * Table of mutexes that are shared among gctx's.  These are leaf locks, so
171  * there is no problem with using them for more than one gctx at the same time.
172  * The primary motivation for this sharing though is that gctx's are ephemeral,
173  * and destroying mutexes causes complications for systems that allocate when
174  * creating/destroying mutexes.
175  */
176 static malloc_mutex_t	*gctx_locks;
177 static atomic_u_t	cum_gctxs; /* Atomic counter. */
178 
179 /*
180  * Table of mutexes that are shared among tdata's.  No operations require
181  * holding multiple tdata locks, so there is no problem with using them for more
182  * than one tdata at the same time, even though a gctx lock may be acquired
183  * while holding a tdata lock.
184  */
185 static malloc_mutex_t	*tdata_locks;
186 
187 /*
188  * Global hash of (prof_bt_t *)-->(prof_gctx_t *).  This is the master data
189  * structure that knows about all backtraces currently captured.
190  */
191 static ckh_t		bt2gctx;
192 /* Non static to enable profiling. */
193 malloc_mutex_t		bt2gctx_mtx;
194 
195 /*
196  * Tree of all extant prof_tdata_t structures, regardless of state,
197  * {attached,detached,expired}.
198  */
199 static prof_tdata_tree_t	tdatas;
200 static malloc_mutex_t	tdatas_mtx;
201 
202 static uint64_t		next_thr_uid;
203 static malloc_mutex_t	next_thr_uid_mtx;
204 
205 static malloc_mutex_t	prof_dump_seq_mtx;
206 static uint64_t		prof_dump_seq;
207 static uint64_t		prof_dump_iseq;
208 static uint64_t		prof_dump_mseq;
209 static uint64_t		prof_dump_useq;
210 
211 /*
212  * This buffer is rather large for stack allocation, so use a single buffer for
213  * all profile dumps.
214  */
215 static malloc_mutex_t	prof_dump_mtx;
216 static char		prof_dump_buf[
217     /* Minimize memory bloat for non-prof builds. */
218 #ifdef JEMALLOC_PROF
219     PROF_DUMP_BUFSIZE
220 #else
221     1
222 #endif
223 ];
224 static size_t		prof_dump_buf_end;
225 static int		prof_dump_fd;
226 
227 /* Do not dump any profiles until bootstrapping is complete. */
228 static bool		prof_booted = false;
229 
230 /******************************************************************************/
231 /*
232  * Function prototypes for static functions that are referenced prior to
233  * definition.
234  */
235 
236 static bool	prof_tctx_should_destroy(tsdn_t *tsdn, prof_tctx_t *tctx);
237 static void	prof_tctx_destroy(tsd_t *tsd, prof_tctx_t *tctx);
238 static bool	prof_tdata_should_destroy(tsdn_t *tsdn, prof_tdata_t *tdata,
239     bool even_if_attached);
240 static void	prof_tdata_destroy(tsd_t *tsd, prof_tdata_t *tdata,
241     bool even_if_attached);
242 static char	*prof_thread_name_alloc(tsdn_t *tsdn, const char *thread_name);
243 
244 /* Hashtable functions for log_bt_node_set and log_thr_node_set. */
245 static void prof_thr_node_hash(const void *key, size_t r_hash[2]);
246 static bool prof_thr_node_keycomp(const void *k1, const void *k2);
247 static void prof_bt_node_hash(const void *key, size_t r_hash[2]);
248 static bool prof_bt_node_keycomp(const void *k1, const void *k2);
249 
250 /******************************************************************************/
251 /* Red-black trees. */
252 
253 static int
254 prof_tctx_comp(const prof_tctx_t *a, const prof_tctx_t *b) {
255 	uint64_t a_thr_uid = a->thr_uid;
256 	uint64_t b_thr_uid = b->thr_uid;
257 	int ret = (a_thr_uid > b_thr_uid) - (a_thr_uid < b_thr_uid);
258 	if (ret == 0) {
259 		uint64_t a_thr_discrim = a->thr_discrim;
260 		uint64_t b_thr_discrim = b->thr_discrim;
261 		ret = (a_thr_discrim > b_thr_discrim) - (a_thr_discrim <
262 		    b_thr_discrim);
263 		if (ret == 0) {
264 			uint64_t a_tctx_uid = a->tctx_uid;
265 			uint64_t b_tctx_uid = b->tctx_uid;
266 			ret = (a_tctx_uid > b_tctx_uid) - (a_tctx_uid <
267 			    b_tctx_uid);
268 		}
269 	}
270 	return ret;
271 }
272 
273 rb_gen(static UNUSED, tctx_tree_, prof_tctx_tree_t, prof_tctx_t,
274     tctx_link, prof_tctx_comp)
275 
276 static int
277 prof_gctx_comp(const prof_gctx_t *a, const prof_gctx_t *b) {
278 	unsigned a_len = a->bt.len;
279 	unsigned b_len = b->bt.len;
280 	unsigned comp_len = (a_len < b_len) ? a_len : b_len;
281 	int ret = memcmp(a->bt.vec, b->bt.vec, comp_len * sizeof(void *));
282 	if (ret == 0) {
283 		ret = (a_len > b_len) - (a_len < b_len);
284 	}
285 	return ret;
286 }
287 
288 rb_gen(static UNUSED, gctx_tree_, prof_gctx_tree_t, prof_gctx_t, dump_link,
289     prof_gctx_comp)
290 
291 static int
292 prof_tdata_comp(const prof_tdata_t *a, const prof_tdata_t *b) {
293 	int ret;
294 	uint64_t a_uid = a->thr_uid;
295 	uint64_t b_uid = b->thr_uid;
296 
297 	ret = ((a_uid > b_uid) - (a_uid < b_uid));
298 	if (ret == 0) {
299 		uint64_t a_discrim = a->thr_discrim;
300 		uint64_t b_discrim = b->thr_discrim;
301 
302 		ret = ((a_discrim > b_discrim) - (a_discrim < b_discrim));
303 	}
304 	return ret;
305 }
306 
307 rb_gen(static UNUSED, tdata_tree_, prof_tdata_tree_t, prof_tdata_t, tdata_link,
308     prof_tdata_comp)
309 
310 /******************************************************************************/
311 
312 void
313 prof_alloc_rollback(tsd_t *tsd, prof_tctx_t *tctx, bool updated) {
314 	prof_tdata_t *tdata;
315 
316 	cassert(config_prof);
317 
318 	if (updated) {
319 		/*
320 		 * Compute a new sample threshold.  This isn't very important in
321 		 * practice, because this function is rarely executed, so the
322 		 * potential for sample bias is minimal except in contrived
323 		 * programs.
324 		 */
325 		tdata = prof_tdata_get(tsd, true);
326 		if (tdata != NULL) {
327 			prof_sample_threshold_update(tdata);
328 		}
329 	}
330 
331 	if ((uintptr_t)tctx > (uintptr_t)1U) {
332 		malloc_mutex_lock(tsd_tsdn(tsd), tctx->tdata->lock);
333 		tctx->prepared = false;
334 		if (prof_tctx_should_destroy(tsd_tsdn(tsd), tctx)) {
335 			prof_tctx_destroy(tsd, tctx);
336 		} else {
337 			malloc_mutex_unlock(tsd_tsdn(tsd), tctx->tdata->lock);
338 		}
339 	}
340 }
341 
342 void
343 prof_malloc_sample_object(tsdn_t *tsdn, const void *ptr, size_t usize,
344     prof_tctx_t *tctx) {
345 	prof_tctx_set(tsdn, ptr, usize, NULL, tctx);
346 
347 	/* Get the current time and set this in the extent_t. We'll read this
348 	 * when free() is called. */
349 	nstime_t t = NSTIME_ZERO_INITIALIZER;
350 	nstime_update(&t);
351 	prof_alloc_time_set(tsdn, ptr, NULL, t);
352 
353 	malloc_mutex_lock(tsdn, tctx->tdata->lock);
354 	tctx->cnts.curobjs++;
355 	tctx->cnts.curbytes += usize;
356 	if (opt_prof_accum) {
357 		tctx->cnts.accumobjs++;
358 		tctx->cnts.accumbytes += usize;
359 	}
360 	tctx->prepared = false;
361 	malloc_mutex_unlock(tsdn, tctx->tdata->lock);
362 }
363 
364 static size_t
365 prof_log_bt_index(tsd_t *tsd, prof_bt_t *bt) {
366 	assert(prof_logging_state == prof_logging_state_started);
367 	malloc_mutex_assert_owner(tsd_tsdn(tsd), &log_mtx);
368 
369 	prof_bt_node_t dummy_node;
370 	dummy_node.bt = *bt;
371 	prof_bt_node_t *node;
372 
373 	/* See if this backtrace is already cached in the table. */
374 	if (ckh_search(&log_bt_node_set, (void *)(&dummy_node),
375 	    (void **)(&node), NULL)) {
376 		size_t sz = offsetof(prof_bt_node_t, vec) +
377 			        (bt->len * sizeof(void *));
378 		prof_bt_node_t *new_node = (prof_bt_node_t *)
379 		    iallocztm(tsd_tsdn(tsd), sz, sz_size2index(sz), false, NULL,
380 		    true, arena_get(TSDN_NULL, 0, true), true);
381 		if (log_bt_first == NULL) {
382 			log_bt_first = new_node;
383 			log_bt_last = new_node;
384 		} else {
385 			log_bt_last->next = new_node;
386 			log_bt_last = new_node;
387 		}
388 
389 		new_node->next = NULL;
390 		new_node->index = log_bt_index;
391 		/*
392 		 * Copy the backtrace: bt is inside a tdata or gctx, which
393 		 * might die before prof_log_stop is called.
394 		 */
395 		new_node->bt.len = bt->len;
396 		memcpy(new_node->vec, bt->vec, bt->len * sizeof(void *));
397 		new_node->bt.vec = new_node->vec;
398 
399 		log_bt_index++;
400 		ckh_insert(tsd, &log_bt_node_set, (void *)new_node, NULL);
401 		return new_node->index;
402 	} else {
403 		return node->index;
404 	}
405 }
406 static size_t
407 prof_log_thr_index(tsd_t *tsd, uint64_t thr_uid, const char *name) {
408 	assert(prof_logging_state == prof_logging_state_started);
409 	malloc_mutex_assert_owner(tsd_tsdn(tsd), &log_mtx);
410 
411 	prof_thr_node_t dummy_node;
412 	dummy_node.thr_uid = thr_uid;
413 	prof_thr_node_t *node;
414 
415 	/* See if this thread is already cached in the table. */
416 	if (ckh_search(&log_thr_node_set, (void *)(&dummy_node),
417 	    (void **)(&node), NULL)) {
418 		size_t sz = offsetof(prof_thr_node_t, name) + strlen(name) + 1;
419 		prof_thr_node_t *new_node = (prof_thr_node_t *)
420 		    iallocztm(tsd_tsdn(tsd), sz, sz_size2index(sz), false, NULL,
421 		    true, arena_get(TSDN_NULL, 0, true), true);
422 		if (log_thr_first == NULL) {
423 			log_thr_first = new_node;
424 			log_thr_last = new_node;
425 		} else {
426 			log_thr_last->next = new_node;
427 			log_thr_last = new_node;
428 		}
429 
430 		new_node->next = NULL;
431 		new_node->index = log_thr_index;
432 		new_node->thr_uid = thr_uid;
433 		strcpy(new_node->name, name);
434 
435 		log_thr_index++;
436 		ckh_insert(tsd, &log_thr_node_set, (void *)new_node, NULL);
437 		return new_node->index;
438 	} else {
439 		return node->index;
440 	}
441 }
442 
443 static void
444 prof_try_log(tsd_t *tsd, const void *ptr, size_t usize, prof_tctx_t *tctx) {
445 	malloc_mutex_assert_owner(tsd_tsdn(tsd), tctx->tdata->lock);
446 
447 	prof_tdata_t *cons_tdata = prof_tdata_get(tsd, false);
448 	if (cons_tdata == NULL) {
449 		/*
450 		 * We decide not to log these allocations. cons_tdata will be
451 		 * NULL only when the current thread is in a weird state (e.g.
452 		 * it's being destroyed).
453 		 */
454 		return;
455 	}
456 
457 	malloc_mutex_lock(tsd_tsdn(tsd), &log_mtx);
458 
459 	if (prof_logging_state != prof_logging_state_started) {
460 		goto label_done;
461 	}
462 
463 	if (!log_tables_initialized) {
464 		bool err1 = ckh_new(tsd, &log_bt_node_set, PROF_CKH_MINITEMS,
465 				prof_bt_node_hash, prof_bt_node_keycomp);
466 		bool err2 = ckh_new(tsd, &log_thr_node_set, PROF_CKH_MINITEMS,
467 				prof_thr_node_hash, prof_thr_node_keycomp);
468 		if (err1 || err2) {
469 			goto label_done;
470 		}
471 		log_tables_initialized = true;
472 	}
473 
474 	nstime_t alloc_time = prof_alloc_time_get(tsd_tsdn(tsd), ptr,
475 			          (alloc_ctx_t *)NULL);
476 	nstime_t free_time = NSTIME_ZERO_INITIALIZER;
477 	nstime_update(&free_time);
478 
479 	size_t sz = sizeof(prof_alloc_node_t);
480 	prof_alloc_node_t *new_node = (prof_alloc_node_t *)
481 	    iallocztm(tsd_tsdn(tsd), sz, sz_size2index(sz), false, NULL, true,
482 	    arena_get(TSDN_NULL, 0, true), true);
483 
484 	const char *prod_thr_name = (tctx->tdata->thread_name == NULL)?
485 				        "" : tctx->tdata->thread_name;
486 	const char *cons_thr_name = prof_thread_name_get(tsd);
487 
488 	prof_bt_t bt;
489 	/* Initialize the backtrace, using the buffer in tdata to store it. */
490 	bt_init(&bt, cons_tdata->vec);
491 	prof_backtrace(&bt);
492 	prof_bt_t *cons_bt = &bt;
493 
494 	/* We haven't destroyed tctx yet, so gctx should be good to read. */
495 	prof_bt_t *prod_bt = &tctx->gctx->bt;
496 
497 	new_node->next = NULL;
498 	new_node->alloc_thr_ind = prof_log_thr_index(tsd, tctx->tdata->thr_uid,
499 				      prod_thr_name);
500 	new_node->free_thr_ind = prof_log_thr_index(tsd, cons_tdata->thr_uid,
501 				     cons_thr_name);
502 	new_node->alloc_bt_ind = prof_log_bt_index(tsd, prod_bt);
503 	new_node->free_bt_ind = prof_log_bt_index(tsd, cons_bt);
504 	new_node->alloc_time_ns = nstime_ns(&alloc_time);
505 	new_node->free_time_ns = nstime_ns(&free_time);
506 	new_node->usize = usize;
507 
508 	if (log_alloc_first == NULL) {
509 		log_alloc_first = new_node;
510 		log_alloc_last = new_node;
511 	} else {
512 		log_alloc_last->next = new_node;
513 		log_alloc_last = new_node;
514 	}
515 
516 label_done:
517 	malloc_mutex_unlock(tsd_tsdn(tsd), &log_mtx);
518 }
519 
520 void
521 prof_free_sampled_object(tsd_t *tsd, const void *ptr, size_t usize,
522     prof_tctx_t *tctx) {
523 	malloc_mutex_lock(tsd_tsdn(tsd), tctx->tdata->lock);
524 
525 	assert(tctx->cnts.curobjs > 0);
526 	assert(tctx->cnts.curbytes >= usize);
527 	tctx->cnts.curobjs--;
528 	tctx->cnts.curbytes -= usize;
529 
530 	prof_try_log(tsd, ptr, usize, tctx);
531 
532 	if (prof_tctx_should_destroy(tsd_tsdn(tsd), tctx)) {
533 		prof_tctx_destroy(tsd, tctx);
534 	} else {
535 		malloc_mutex_unlock(tsd_tsdn(tsd), tctx->tdata->lock);
536 	}
537 }
538 
539 void
540 bt_init(prof_bt_t *bt, void **vec) {
541 	cassert(config_prof);
542 
543 	bt->vec = vec;
544 	bt->len = 0;
545 }
546 
547 static void
548 prof_enter(tsd_t *tsd, prof_tdata_t *tdata) {
549 	cassert(config_prof);
550 	assert(tdata == prof_tdata_get(tsd, false));
551 
552 	if (tdata != NULL) {
553 		assert(!tdata->enq);
554 		tdata->enq = true;
555 	}
556 
557 	malloc_mutex_lock(tsd_tsdn(tsd), &bt2gctx_mtx);
558 }
559 
560 static void
561 prof_leave(tsd_t *tsd, prof_tdata_t *tdata) {
562 	cassert(config_prof);
563 	assert(tdata == prof_tdata_get(tsd, false));
564 
565 	malloc_mutex_unlock(tsd_tsdn(tsd), &bt2gctx_mtx);
566 
567 	if (tdata != NULL) {
568 		bool idump, gdump;
569 
570 		assert(tdata->enq);
571 		tdata->enq = false;
572 		idump = tdata->enq_idump;
573 		tdata->enq_idump = false;
574 		gdump = tdata->enq_gdump;
575 		tdata->enq_gdump = false;
576 
577 		if (idump) {
578 			prof_idump(tsd_tsdn(tsd));
579 		}
580 		if (gdump) {
581 			prof_gdump(tsd_tsdn(tsd));
582 		}
583 	}
584 }
585 
586 #ifdef JEMALLOC_PROF_LIBUNWIND
587 void
588 prof_backtrace(prof_bt_t *bt) {
589 	int nframes;
590 
591 	cassert(config_prof);
592 	assert(bt->len == 0);
593 	assert(bt->vec != NULL);
594 
595 	nframes = unw_backtrace(bt->vec, PROF_BT_MAX);
596 	if (nframes <= 0) {
597 		return;
598 	}
599 	bt->len = nframes;
600 }
601 #elif (defined(JEMALLOC_PROF_LIBGCC))
602 static _Unwind_Reason_Code
603 prof_unwind_init_callback(struct _Unwind_Context *context, void *arg) {
604 	cassert(config_prof);
605 
606 	return _URC_NO_REASON;
607 }
608 
609 static _Unwind_Reason_Code
610 prof_unwind_callback(struct _Unwind_Context *context, void *arg) {
611 	prof_unwind_data_t *data = (prof_unwind_data_t *)arg;
612 	void *ip;
613 
614 	cassert(config_prof);
615 
616 	ip = (void *)_Unwind_GetIP(context);
617 	if (ip == NULL) {
618 		return _URC_END_OF_STACK;
619 	}
620 	data->bt->vec[data->bt->len] = ip;
621 	data->bt->len++;
622 	if (data->bt->len == data->max) {
623 		return _URC_END_OF_STACK;
624 	}
625 
626 	return _URC_NO_REASON;
627 }
628 
629 void
630 prof_backtrace(prof_bt_t *bt) {
631 	prof_unwind_data_t data = {bt, PROF_BT_MAX};
632 
633 	cassert(config_prof);
634 
635 	_Unwind_Backtrace(prof_unwind_callback, &data);
636 }
637 #elif (defined(JEMALLOC_PROF_GCC))
638 void
639 prof_backtrace(prof_bt_t *bt) {
640 #define BT_FRAME(i)							\
641 	if ((i) < PROF_BT_MAX) {					\
642 		void *p;						\
643 		if (__builtin_frame_address(i) == 0) {			\
644 			return;						\
645 		}							\
646 		p = __builtin_return_address(i);			\
647 		if (p == NULL) {					\
648 			return;						\
649 		}							\
650 		bt->vec[(i)] = p;					\
651 		bt->len = (i) + 1;					\
652 	} else {							\
653 		return;							\
654 	}
655 
656 	cassert(config_prof);
657 
658 	BT_FRAME(0)
659 	BT_FRAME(1)
660 	BT_FRAME(2)
661 	BT_FRAME(3)
662 	BT_FRAME(4)
663 	BT_FRAME(5)
664 	BT_FRAME(6)
665 	BT_FRAME(7)
666 	BT_FRAME(8)
667 	BT_FRAME(9)
668 
669 	BT_FRAME(10)
670 	BT_FRAME(11)
671 	BT_FRAME(12)
672 	BT_FRAME(13)
673 	BT_FRAME(14)
674 	BT_FRAME(15)
675 	BT_FRAME(16)
676 	BT_FRAME(17)
677 	BT_FRAME(18)
678 	BT_FRAME(19)
679 
680 	BT_FRAME(20)
681 	BT_FRAME(21)
682 	BT_FRAME(22)
683 	BT_FRAME(23)
684 	BT_FRAME(24)
685 	BT_FRAME(25)
686 	BT_FRAME(26)
687 	BT_FRAME(27)
688 	BT_FRAME(28)
689 	BT_FRAME(29)
690 
691 	BT_FRAME(30)
692 	BT_FRAME(31)
693 	BT_FRAME(32)
694 	BT_FRAME(33)
695 	BT_FRAME(34)
696 	BT_FRAME(35)
697 	BT_FRAME(36)
698 	BT_FRAME(37)
699 	BT_FRAME(38)
700 	BT_FRAME(39)
701 
702 	BT_FRAME(40)
703 	BT_FRAME(41)
704 	BT_FRAME(42)
705 	BT_FRAME(43)
706 	BT_FRAME(44)
707 	BT_FRAME(45)
708 	BT_FRAME(46)
709 	BT_FRAME(47)
710 	BT_FRAME(48)
711 	BT_FRAME(49)
712 
713 	BT_FRAME(50)
714 	BT_FRAME(51)
715 	BT_FRAME(52)
716 	BT_FRAME(53)
717 	BT_FRAME(54)
718 	BT_FRAME(55)
719 	BT_FRAME(56)
720 	BT_FRAME(57)
721 	BT_FRAME(58)
722 	BT_FRAME(59)
723 
724 	BT_FRAME(60)
725 	BT_FRAME(61)
726 	BT_FRAME(62)
727 	BT_FRAME(63)
728 	BT_FRAME(64)
729 	BT_FRAME(65)
730 	BT_FRAME(66)
731 	BT_FRAME(67)
732 	BT_FRAME(68)
733 	BT_FRAME(69)
734 
735 	BT_FRAME(70)
736 	BT_FRAME(71)
737 	BT_FRAME(72)
738 	BT_FRAME(73)
739 	BT_FRAME(74)
740 	BT_FRAME(75)
741 	BT_FRAME(76)
742 	BT_FRAME(77)
743 	BT_FRAME(78)
744 	BT_FRAME(79)
745 
746 	BT_FRAME(80)
747 	BT_FRAME(81)
748 	BT_FRAME(82)
749 	BT_FRAME(83)
750 	BT_FRAME(84)
751 	BT_FRAME(85)
752 	BT_FRAME(86)
753 	BT_FRAME(87)
754 	BT_FRAME(88)
755 	BT_FRAME(89)
756 
757 	BT_FRAME(90)
758 	BT_FRAME(91)
759 	BT_FRAME(92)
760 	BT_FRAME(93)
761 	BT_FRAME(94)
762 	BT_FRAME(95)
763 	BT_FRAME(96)
764 	BT_FRAME(97)
765 	BT_FRAME(98)
766 	BT_FRAME(99)
767 
768 	BT_FRAME(100)
769 	BT_FRAME(101)
770 	BT_FRAME(102)
771 	BT_FRAME(103)
772 	BT_FRAME(104)
773 	BT_FRAME(105)
774 	BT_FRAME(106)
775 	BT_FRAME(107)
776 	BT_FRAME(108)
777 	BT_FRAME(109)
778 
779 	BT_FRAME(110)
780 	BT_FRAME(111)
781 	BT_FRAME(112)
782 	BT_FRAME(113)
783 	BT_FRAME(114)
784 	BT_FRAME(115)
785 	BT_FRAME(116)
786 	BT_FRAME(117)
787 	BT_FRAME(118)
788 	BT_FRAME(119)
789 
790 	BT_FRAME(120)
791 	BT_FRAME(121)
792 	BT_FRAME(122)
793 	BT_FRAME(123)
794 	BT_FRAME(124)
795 	BT_FRAME(125)
796 	BT_FRAME(126)
797 	BT_FRAME(127)
798 #undef BT_FRAME
799 }
800 #else
801 void
802 prof_backtrace(prof_bt_t *bt) {
803 	cassert(config_prof);
804 	not_reached();
805 }
806 #endif
807 
808 static malloc_mutex_t *
809 prof_gctx_mutex_choose(void) {
810 	unsigned ngctxs = atomic_fetch_add_u(&cum_gctxs, 1, ATOMIC_RELAXED);
811 
812 	return &gctx_locks[(ngctxs - 1) % PROF_NCTX_LOCKS];
813 }
814 
815 static malloc_mutex_t *
816 prof_tdata_mutex_choose(uint64_t thr_uid) {
817 	return &tdata_locks[thr_uid % PROF_NTDATA_LOCKS];
818 }
819 
820 static prof_gctx_t *
821 prof_gctx_create(tsdn_t *tsdn, prof_bt_t *bt) {
822 	/*
823 	 * Create a single allocation that has space for vec of length bt->len.
824 	 */
825 	size_t size = offsetof(prof_gctx_t, vec) + (bt->len * sizeof(void *));
826 	prof_gctx_t *gctx = (prof_gctx_t *)iallocztm(tsdn, size,
827 	    sz_size2index(size), false, NULL, true, arena_get(TSDN_NULL, 0, true),
828 	    true);
829 	if (gctx == NULL) {
830 		return NULL;
831 	}
832 	gctx->lock = prof_gctx_mutex_choose();
833 	/*
834 	 * Set nlimbo to 1, in order to avoid a race condition with
835 	 * prof_tctx_destroy()/prof_gctx_try_destroy().
836 	 */
837 	gctx->nlimbo = 1;
838 	tctx_tree_new(&gctx->tctxs);
839 	/* Duplicate bt. */
840 	memcpy(gctx->vec, bt->vec, bt->len * sizeof(void *));
841 	gctx->bt.vec = gctx->vec;
842 	gctx->bt.len = bt->len;
843 	return gctx;
844 }
845 
846 static void
847 prof_gctx_try_destroy(tsd_t *tsd, prof_tdata_t *tdata_self, prof_gctx_t *gctx,
848     prof_tdata_t *tdata) {
849 	cassert(config_prof);
850 
851 	/*
852 	 * Check that gctx is still unused by any thread cache before destroying
853 	 * it.  prof_lookup() increments gctx->nlimbo in order to avoid a race
854 	 * condition with this function, as does prof_tctx_destroy() in order to
855 	 * avoid a race between the main body of prof_tctx_destroy() and entry
856 	 * into this function.
857 	 */
858 	prof_enter(tsd, tdata_self);
859 	malloc_mutex_lock(tsd_tsdn(tsd), gctx->lock);
860 	assert(gctx->nlimbo != 0);
861 	if (tctx_tree_empty(&gctx->tctxs) && gctx->nlimbo == 1) {
862 		/* Remove gctx from bt2gctx. */
863 		if (ckh_remove(tsd, &bt2gctx, &gctx->bt, NULL, NULL)) {
864 			not_reached();
865 		}
866 		prof_leave(tsd, tdata_self);
867 		/* Destroy gctx. */
868 		malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);
869 		idalloctm(tsd_tsdn(tsd), gctx, NULL, NULL, true, true);
870 	} else {
871 		/*
872 		 * Compensate for increment in prof_tctx_destroy() or
873 		 * prof_lookup().
874 		 */
875 		gctx->nlimbo--;
876 		malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);
877 		prof_leave(tsd, tdata_self);
878 	}
879 }
880 
881 static bool
882 prof_tctx_should_destroy(tsdn_t *tsdn, prof_tctx_t *tctx) {
883 	malloc_mutex_assert_owner(tsdn, tctx->tdata->lock);
884 
885 	if (opt_prof_accum) {
886 		return false;
887 	}
888 	if (tctx->cnts.curobjs != 0) {
889 		return false;
890 	}
891 	if (tctx->prepared) {
892 		return false;
893 	}
894 	return true;
895 }
896 
897 static bool
898 prof_gctx_should_destroy(prof_gctx_t *gctx) {
899 	if (opt_prof_accum) {
900 		return false;
901 	}
902 	if (!tctx_tree_empty(&gctx->tctxs)) {
903 		return false;
904 	}
905 	if (gctx->nlimbo != 0) {
906 		return false;
907 	}
908 	return true;
909 }
910 
911 static void
912 prof_tctx_destroy(tsd_t *tsd, prof_tctx_t *tctx) {
913 	prof_tdata_t *tdata = tctx->tdata;
914 	prof_gctx_t *gctx = tctx->gctx;
915 	bool destroy_tdata, destroy_tctx, destroy_gctx;
916 
917 	malloc_mutex_assert_owner(tsd_tsdn(tsd), tctx->tdata->lock);
918 
919 	assert(tctx->cnts.curobjs == 0);
920 	assert(tctx->cnts.curbytes == 0);
921 	assert(!opt_prof_accum);
922 	assert(tctx->cnts.accumobjs == 0);
923 	assert(tctx->cnts.accumbytes == 0);
924 
925 	ckh_remove(tsd, &tdata->bt2tctx, &gctx->bt, NULL, NULL);
926 	destroy_tdata = prof_tdata_should_destroy(tsd_tsdn(tsd), tdata, false);
927 	malloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock);
928 
929 	malloc_mutex_lock(tsd_tsdn(tsd), gctx->lock);
930 	switch (tctx->state) {
931 	case prof_tctx_state_nominal:
932 		tctx_tree_remove(&gctx->tctxs, tctx);
933 		destroy_tctx = true;
934 		if (prof_gctx_should_destroy(gctx)) {
935 			/*
936 			 * Increment gctx->nlimbo in order to keep another
937 			 * thread from winning the race to destroy gctx while
938 			 * this one has gctx->lock dropped.  Without this, it
939 			 * would be possible for another thread to:
940 			 *
941 			 * 1) Sample an allocation associated with gctx.
942 			 * 2) Deallocate the sampled object.
943 			 * 3) Successfully prof_gctx_try_destroy(gctx).
944 			 *
945 			 * The result would be that gctx no longer exists by the
946 			 * time this thread accesses it in
947 			 * prof_gctx_try_destroy().
948 			 */
949 			gctx->nlimbo++;
950 			destroy_gctx = true;
951 		} else {
952 			destroy_gctx = false;
953 		}
954 		break;
955 	case prof_tctx_state_dumping:
956 		/*
957 		 * A dumping thread needs tctx to remain valid until dumping
958 		 * has finished.  Change state such that the dumping thread will
959 		 * complete destruction during a late dump iteration phase.
960 		 */
961 		tctx->state = prof_tctx_state_purgatory;
962 		destroy_tctx = false;
963 		destroy_gctx = false;
964 		break;
965 	default:
966 		not_reached();
967 		destroy_tctx = false;
968 		destroy_gctx = false;
969 	}
970 	malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);
971 	if (destroy_gctx) {
972 		prof_gctx_try_destroy(tsd, prof_tdata_get(tsd, false), gctx,
973 		    tdata);
974 	}
975 
976 	malloc_mutex_assert_not_owner(tsd_tsdn(tsd), tctx->tdata->lock);
977 
978 	if (destroy_tdata) {
979 		prof_tdata_destroy(tsd, tdata, false);
980 	}
981 
982 	if (destroy_tctx) {
983 		idalloctm(tsd_tsdn(tsd), tctx, NULL, NULL, true, true);
984 	}
985 }
986 
987 static bool
988 prof_lookup_global(tsd_t *tsd, prof_bt_t *bt, prof_tdata_t *tdata,
989     void **p_btkey, prof_gctx_t **p_gctx, bool *p_new_gctx) {
990 	union {
991 		prof_gctx_t	*p;
992 		void		*v;
993 	} gctx, tgctx;
994 	union {
995 		prof_bt_t	*p;
996 		void		*v;
997 	} btkey;
998 	bool new_gctx;
999 
1000 	prof_enter(tsd, tdata);
1001 	if (ckh_search(&bt2gctx, bt, &btkey.v, &gctx.v)) {
1002 		/* bt has never been seen before.  Insert it. */
1003 		prof_leave(tsd, tdata);
1004 		tgctx.p = prof_gctx_create(tsd_tsdn(tsd), bt);
1005 		if (tgctx.v == NULL) {
1006 			return true;
1007 		}
1008 		prof_enter(tsd, tdata);
1009 		if (ckh_search(&bt2gctx, bt, &btkey.v, &gctx.v)) {
1010 			gctx.p = tgctx.p;
1011 			btkey.p = &gctx.p->bt;
1012 			if (ckh_insert(tsd, &bt2gctx, btkey.v, gctx.v)) {
1013 				/* OOM. */
1014 				prof_leave(tsd, tdata);
1015 				idalloctm(tsd_tsdn(tsd), gctx.v, NULL, NULL,
1016 				    true, true);
1017 				return true;
1018 			}
1019 			new_gctx = true;
1020 		} else {
1021 			new_gctx = false;
1022 		}
1023 	} else {
1024 		tgctx.v = NULL;
1025 		new_gctx = false;
1026 	}
1027 
1028 	if (!new_gctx) {
1029 		/*
1030 		 * Increment nlimbo, in order to avoid a race condition with
1031 		 * prof_tctx_destroy()/prof_gctx_try_destroy().
1032 		 */
1033 		malloc_mutex_lock(tsd_tsdn(tsd), gctx.p->lock);
1034 		gctx.p->nlimbo++;
1035 		malloc_mutex_unlock(tsd_tsdn(tsd), gctx.p->lock);
1036 		new_gctx = false;
1037 
1038 		if (tgctx.v != NULL) {
1039 			/* Lost race to insert. */
1040 			idalloctm(tsd_tsdn(tsd), tgctx.v, NULL, NULL, true,
1041 			    true);
1042 		}
1043 	}
1044 	prof_leave(tsd, tdata);
1045 
1046 	*p_btkey = btkey.v;
1047 	*p_gctx = gctx.p;
1048 	*p_new_gctx = new_gctx;
1049 	return false;
1050 }
1051 
1052 prof_tctx_t *
1053 prof_lookup(tsd_t *tsd, prof_bt_t *bt) {
1054 	union {
1055 		prof_tctx_t	*p;
1056 		void		*v;
1057 	} ret;
1058 	prof_tdata_t *tdata;
1059 	bool not_found;
1060 
1061 	cassert(config_prof);
1062 
1063 	tdata = prof_tdata_get(tsd, false);
1064 	if (tdata == NULL) {
1065 		return NULL;
1066 	}
1067 
1068 	malloc_mutex_lock(tsd_tsdn(tsd), tdata->lock);
1069 	not_found = ckh_search(&tdata->bt2tctx, bt, NULL, &ret.v);
1070 	if (!not_found) { /* Note double negative! */
1071 		ret.p->prepared = true;
1072 	}
1073 	malloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock);
1074 	if (not_found) {
1075 		void *btkey;
1076 		prof_gctx_t *gctx;
1077 		bool new_gctx, error;
1078 
1079 		/*
1080 		 * This thread's cache lacks bt.  Look for it in the global
1081 		 * cache.
1082 		 */
1083 		if (prof_lookup_global(tsd, bt, tdata, &btkey, &gctx,
1084 		    &new_gctx)) {
1085 			return NULL;
1086 		}
1087 
1088 		/* Link a prof_tctx_t into gctx for this thread. */
1089 		ret.v = iallocztm(tsd_tsdn(tsd), sizeof(prof_tctx_t),
1090 		    sz_size2index(sizeof(prof_tctx_t)), false, NULL, true,
1091 		    arena_ichoose(tsd, NULL), true);
1092 		if (ret.p == NULL) {
1093 			if (new_gctx) {
1094 				prof_gctx_try_destroy(tsd, tdata, gctx, tdata);
1095 			}
1096 			return NULL;
1097 		}
1098 		ret.p->tdata = tdata;
1099 		ret.p->thr_uid = tdata->thr_uid;
1100 		ret.p->thr_discrim = tdata->thr_discrim;
1101 		memset(&ret.p->cnts, 0, sizeof(prof_cnt_t));
1102 		ret.p->gctx = gctx;
1103 		ret.p->tctx_uid = tdata->tctx_uid_next++;
1104 		ret.p->prepared = true;
1105 		ret.p->state = prof_tctx_state_initializing;
1106 		malloc_mutex_lock(tsd_tsdn(tsd), tdata->lock);
1107 		error = ckh_insert(tsd, &tdata->bt2tctx, btkey, ret.v);
1108 		malloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock);
1109 		if (error) {
1110 			if (new_gctx) {
1111 				prof_gctx_try_destroy(tsd, tdata, gctx, tdata);
1112 			}
1113 			idalloctm(tsd_tsdn(tsd), ret.v, NULL, NULL, true, true);
1114 			return NULL;
1115 		}
1116 		malloc_mutex_lock(tsd_tsdn(tsd), gctx->lock);
1117 		ret.p->state = prof_tctx_state_nominal;
1118 		tctx_tree_insert(&gctx->tctxs, ret.p);
1119 		gctx->nlimbo--;
1120 		malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);
1121 	}
1122 
1123 	return ret.p;
1124 }
1125 
1126 /*
1127  * The bodies of this function and prof_leakcheck() are compiled out unless heap
1128  * profiling is enabled, so that it is possible to compile jemalloc with
1129  * floating point support completely disabled.  Avoiding floating point code is
1130  * important on memory-constrained systems, but it also enables a workaround for
1131  * versions of glibc that don't properly save/restore floating point registers
1132  * during dynamic lazy symbol loading (which internally calls into whatever
1133  * malloc implementation happens to be integrated into the application).  Note
1134  * that some compilers (e.g.  gcc 4.8) may use floating point registers for fast
1135  * memory moves, so jemalloc must be compiled with such optimizations disabled
1136  * (e.g.
1137  * -mno-sse) in order for the workaround to be complete.
1138  */
1139 void
1140 prof_sample_threshold_update(prof_tdata_t *tdata) {
1141 #ifdef JEMALLOC_PROF
1142 	if (!config_prof) {
1143 		return;
1144 	}
1145 
1146 	if (lg_prof_sample == 0) {
1147 		tsd_bytes_until_sample_set(tsd_fetch(), 0);
1148 		return;
1149 	}
1150 
1151 	/*
1152 	 * Compute sample interval as a geometrically distributed random
1153 	 * variable with mean (2^lg_prof_sample).
1154 	 *
1155 	 *                             __        __
1156 	 *                             |  log(u)  |                     1
1157 	 * tdata->bytes_until_sample = | -------- |, where p = ---------------
1158 	 *                             | log(1-p) |             lg_prof_sample
1159 	 *                                                     2
1160 	 *
1161 	 * For more information on the math, see:
1162 	 *
1163 	 *   Non-Uniform Random Variate Generation
1164 	 *   Luc Devroye
1165 	 *   Springer-Verlag, New York, 1986
1166 	 *   pp 500
1167 	 *   (http://luc.devroye.org/rnbookindex.html)
1168 	 */
1169 	uint64_t r = prng_lg_range_u64(&tdata->prng_state, 53);
1170 	double u = (double)r * (1.0/9007199254740992.0L);
1171 	uint64_t bytes_until_sample = (uint64_t)(log(u) /
1172 	    log(1.0 - (1.0 / (double)((uint64_t)1U << lg_prof_sample))))
1173 	    + (uint64_t)1U;
1174 	if (bytes_until_sample > SSIZE_MAX) {
1175 		bytes_until_sample = SSIZE_MAX;
1176 	}
1177 	tsd_bytes_until_sample_set(tsd_fetch(), bytes_until_sample);
1178 
1179 #endif
1180 }
1181 
1182 #ifdef JEMALLOC_JET
1183 static prof_tdata_t *
1184 prof_tdata_count_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata,
1185     void *arg) {
1186 	size_t *tdata_count = (size_t *)arg;
1187 
1188 	(*tdata_count)++;
1189 
1190 	return NULL;
1191 }
1192 
1193 size_t
1194 prof_tdata_count(void) {
1195 	size_t tdata_count = 0;
1196 	tsdn_t *tsdn;
1197 
1198 	tsdn = tsdn_fetch();
1199 	malloc_mutex_lock(tsdn, &tdatas_mtx);
1200 	tdata_tree_iter(&tdatas, NULL, prof_tdata_count_iter,
1201 	    (void *)&tdata_count);
1202 	malloc_mutex_unlock(tsdn, &tdatas_mtx);
1203 
1204 	return tdata_count;
1205 }
1206 
1207 size_t
1208 prof_bt_count(void) {
1209 	size_t bt_count;
1210 	tsd_t *tsd;
1211 	prof_tdata_t *tdata;
1212 
1213 	tsd = tsd_fetch();
1214 	tdata = prof_tdata_get(tsd, false);
1215 	if (tdata == NULL) {
1216 		return 0;
1217 	}
1218 
1219 	malloc_mutex_lock(tsd_tsdn(tsd), &bt2gctx_mtx);
1220 	bt_count = ckh_count(&bt2gctx);
1221 	malloc_mutex_unlock(tsd_tsdn(tsd), &bt2gctx_mtx);
1222 
1223 	return bt_count;
1224 }
1225 #endif
1226 
1227 static int
1228 prof_dump_open_impl(bool propagate_err, const char *filename) {
1229 	int fd;
1230 
1231 	fd = creat(filename, 0644);
1232 	if (fd == -1 && !propagate_err) {
1233 		malloc_printf("<jemalloc>: creat(\"%s\"), 0644) failed\n",
1234 		    filename);
1235 		if (opt_abort) {
1236 			abort();
1237 		}
1238 	}
1239 
1240 	return fd;
1241 }
1242 prof_dump_open_t *JET_MUTABLE prof_dump_open = prof_dump_open_impl;
1243 
1244 static bool
1245 prof_dump_flush(bool propagate_err) {
1246 	bool ret = false;
1247 	ssize_t err;
1248 
1249 	cassert(config_prof);
1250 
1251 	err = malloc_write_fd(prof_dump_fd, prof_dump_buf, prof_dump_buf_end);
1252 	if (err == -1) {
1253 		if (!propagate_err) {
1254 			malloc_write("<jemalloc>: write() failed during heap "
1255 			    "profile flush\n");
1256 			if (opt_abort) {
1257 				abort();
1258 			}
1259 		}
1260 		ret = true;
1261 	}
1262 	prof_dump_buf_end = 0;
1263 
1264 	return ret;
1265 }
1266 
1267 static bool
1268 prof_dump_close(bool propagate_err) {
1269 	bool ret;
1270 
1271 	assert(prof_dump_fd != -1);
1272 	ret = prof_dump_flush(propagate_err);
1273 	close(prof_dump_fd);
1274 	prof_dump_fd = -1;
1275 
1276 	return ret;
1277 }
1278 
1279 static bool
1280 prof_dump_write(bool propagate_err, const char *s) {
1281 	size_t i, slen, n;
1282 
1283 	cassert(config_prof);
1284 
1285 	i = 0;
1286 	slen = strlen(s);
1287 	while (i < slen) {
1288 		/* Flush the buffer if it is full. */
1289 		if (prof_dump_buf_end == PROF_DUMP_BUFSIZE) {
1290 			if (prof_dump_flush(propagate_err) && propagate_err) {
1291 				return true;
1292 			}
1293 		}
1294 
1295 		if (prof_dump_buf_end + slen - i <= PROF_DUMP_BUFSIZE) {
1296 			/* Finish writing. */
1297 			n = slen - i;
1298 		} else {
1299 			/* Write as much of s as will fit. */
1300 			n = PROF_DUMP_BUFSIZE - prof_dump_buf_end;
1301 		}
1302 		memcpy(&prof_dump_buf[prof_dump_buf_end], &s[i], n);
1303 		prof_dump_buf_end += n;
1304 		i += n;
1305 	}
1306 	assert(i == slen);
1307 
1308 	return false;
1309 }
1310 
1311 JEMALLOC_FORMAT_PRINTF(2, 3)
1312 static bool
1313 prof_dump_printf(bool propagate_err, const char *format, ...) {
1314 	bool ret;
1315 	va_list ap;
1316 	char buf[PROF_PRINTF_BUFSIZE];
1317 
1318 	va_start(ap, format);
1319 	malloc_vsnprintf(buf, sizeof(buf), format, ap);
1320 	va_end(ap);
1321 	ret = prof_dump_write(propagate_err, buf);
1322 
1323 	return ret;
1324 }
1325 
1326 static void
1327 prof_tctx_merge_tdata(tsdn_t *tsdn, prof_tctx_t *tctx, prof_tdata_t *tdata) {
1328 	malloc_mutex_assert_owner(tsdn, tctx->tdata->lock);
1329 
1330 	malloc_mutex_lock(tsdn, tctx->gctx->lock);
1331 
1332 	switch (tctx->state) {
1333 	case prof_tctx_state_initializing:
1334 		malloc_mutex_unlock(tsdn, tctx->gctx->lock);
1335 		return;
1336 	case prof_tctx_state_nominal:
1337 		tctx->state = prof_tctx_state_dumping;
1338 		malloc_mutex_unlock(tsdn, tctx->gctx->lock);
1339 
1340 		memcpy(&tctx->dump_cnts, &tctx->cnts, sizeof(prof_cnt_t));
1341 
1342 		tdata->cnt_summed.curobjs += tctx->dump_cnts.curobjs;
1343 		tdata->cnt_summed.curbytes += tctx->dump_cnts.curbytes;
1344 		if (opt_prof_accum) {
1345 			tdata->cnt_summed.accumobjs +=
1346 			    tctx->dump_cnts.accumobjs;
1347 			tdata->cnt_summed.accumbytes +=
1348 			    tctx->dump_cnts.accumbytes;
1349 		}
1350 		break;
1351 	case prof_tctx_state_dumping:
1352 	case prof_tctx_state_purgatory:
1353 		not_reached();
1354 	}
1355 }
1356 
1357 static void
1358 prof_tctx_merge_gctx(tsdn_t *tsdn, prof_tctx_t *tctx, prof_gctx_t *gctx) {
1359 	malloc_mutex_assert_owner(tsdn, gctx->lock);
1360 
1361 	gctx->cnt_summed.curobjs += tctx->dump_cnts.curobjs;
1362 	gctx->cnt_summed.curbytes += tctx->dump_cnts.curbytes;
1363 	if (opt_prof_accum) {
1364 		gctx->cnt_summed.accumobjs += tctx->dump_cnts.accumobjs;
1365 		gctx->cnt_summed.accumbytes += tctx->dump_cnts.accumbytes;
1366 	}
1367 }
1368 
1369 static prof_tctx_t *
1370 prof_tctx_merge_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *arg) {
1371 	tsdn_t *tsdn = (tsdn_t *)arg;
1372 
1373 	malloc_mutex_assert_owner(tsdn, tctx->gctx->lock);
1374 
1375 	switch (tctx->state) {
1376 	case prof_tctx_state_nominal:
1377 		/* New since dumping started; ignore. */
1378 		break;
1379 	case prof_tctx_state_dumping:
1380 	case prof_tctx_state_purgatory:
1381 		prof_tctx_merge_gctx(tsdn, tctx, tctx->gctx);
1382 		break;
1383 	default:
1384 		not_reached();
1385 	}
1386 
1387 	return NULL;
1388 }
1389 
1390 struct prof_tctx_dump_iter_arg_s {
1391 	tsdn_t	*tsdn;
1392 	bool	propagate_err;
1393 };
1394 
1395 static prof_tctx_t *
1396 prof_tctx_dump_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *opaque) {
1397 	struct prof_tctx_dump_iter_arg_s *arg =
1398 	    (struct prof_tctx_dump_iter_arg_s *)opaque;
1399 
1400 	malloc_mutex_assert_owner(arg->tsdn, tctx->gctx->lock);
1401 
1402 	switch (tctx->state) {
1403 	case prof_tctx_state_initializing:
1404 	case prof_tctx_state_nominal:
1405 		/* Not captured by this dump. */
1406 		break;
1407 	case prof_tctx_state_dumping:
1408 	case prof_tctx_state_purgatory:
1409 		if (prof_dump_printf(arg->propagate_err,
1410 		    "  t%"FMTu64": %"FMTu64": %"FMTu64" [%"FMTu64": "
1411 		    "%"FMTu64"]\n", tctx->thr_uid, tctx->dump_cnts.curobjs,
1412 		    tctx->dump_cnts.curbytes, tctx->dump_cnts.accumobjs,
1413 		    tctx->dump_cnts.accumbytes)) {
1414 			return tctx;
1415 		}
1416 		break;
1417 	default:
1418 		not_reached();
1419 	}
1420 	return NULL;
1421 }
1422 
1423 static prof_tctx_t *
1424 prof_tctx_finish_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *arg) {
1425 	tsdn_t *tsdn = (tsdn_t *)arg;
1426 	prof_tctx_t *ret;
1427 
1428 	malloc_mutex_assert_owner(tsdn, tctx->gctx->lock);
1429 
1430 	switch (tctx->state) {
1431 	case prof_tctx_state_nominal:
1432 		/* New since dumping started; ignore. */
1433 		break;
1434 	case prof_tctx_state_dumping:
1435 		tctx->state = prof_tctx_state_nominal;
1436 		break;
1437 	case prof_tctx_state_purgatory:
1438 		ret = tctx;
1439 		goto label_return;
1440 	default:
1441 		not_reached();
1442 	}
1443 
1444 	ret = NULL;
1445 label_return:
1446 	return ret;
1447 }
1448 
1449 static void
1450 prof_dump_gctx_prep(tsdn_t *tsdn, prof_gctx_t *gctx, prof_gctx_tree_t *gctxs) {
1451 	cassert(config_prof);
1452 
1453 	malloc_mutex_lock(tsdn, gctx->lock);
1454 
1455 	/*
1456 	 * Increment nlimbo so that gctx won't go away before dump.
1457 	 * Additionally, link gctx into the dump list so that it is included in
1458 	 * prof_dump()'s second pass.
1459 	 */
1460 	gctx->nlimbo++;
1461 	gctx_tree_insert(gctxs, gctx);
1462 
1463 	memset(&gctx->cnt_summed, 0, sizeof(prof_cnt_t));
1464 
1465 	malloc_mutex_unlock(tsdn, gctx->lock);
1466 }
1467 
1468 struct prof_gctx_merge_iter_arg_s {
1469 	tsdn_t	*tsdn;
1470 	size_t	leak_ngctx;
1471 };
1472 
1473 static prof_gctx_t *
1474 prof_gctx_merge_iter(prof_gctx_tree_t *gctxs, prof_gctx_t *gctx, void *opaque) {
1475 	struct prof_gctx_merge_iter_arg_s *arg =
1476 	    (struct prof_gctx_merge_iter_arg_s *)opaque;
1477 
1478 	malloc_mutex_lock(arg->tsdn, gctx->lock);
1479 	tctx_tree_iter(&gctx->tctxs, NULL, prof_tctx_merge_iter,
1480 	    (void *)arg->tsdn);
1481 	if (gctx->cnt_summed.curobjs != 0) {
1482 		arg->leak_ngctx++;
1483 	}
1484 	malloc_mutex_unlock(arg->tsdn, gctx->lock);
1485 
1486 	return NULL;
1487 }
1488 
1489 static void
1490 prof_gctx_finish(tsd_t *tsd, prof_gctx_tree_t *gctxs) {
1491 	prof_tdata_t *tdata = prof_tdata_get(tsd, false);
1492 	prof_gctx_t *gctx;
1493 
1494 	/*
1495 	 * Standard tree iteration won't work here, because as soon as we
1496 	 * decrement gctx->nlimbo and unlock gctx, another thread can
1497 	 * concurrently destroy it, which will corrupt the tree.  Therefore,
1498 	 * tear down the tree one node at a time during iteration.
1499 	 */
1500 	while ((gctx = gctx_tree_first(gctxs)) != NULL) {
1501 		gctx_tree_remove(gctxs, gctx);
1502 		malloc_mutex_lock(tsd_tsdn(tsd), gctx->lock);
1503 		{
1504 			prof_tctx_t *next;
1505 
1506 			next = NULL;
1507 			do {
1508 				prof_tctx_t *to_destroy =
1509 				    tctx_tree_iter(&gctx->tctxs, next,
1510 				    prof_tctx_finish_iter,
1511 				    (void *)tsd_tsdn(tsd));
1512 				if (to_destroy != NULL) {
1513 					next = tctx_tree_next(&gctx->tctxs,
1514 					    to_destroy);
1515 					tctx_tree_remove(&gctx->tctxs,
1516 					    to_destroy);
1517 					idalloctm(tsd_tsdn(tsd), to_destroy,
1518 					    NULL, NULL, true, true);
1519 				} else {
1520 					next = NULL;
1521 				}
1522 			} while (next != NULL);
1523 		}
1524 		gctx->nlimbo--;
1525 		if (prof_gctx_should_destroy(gctx)) {
1526 			gctx->nlimbo++;
1527 			malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);
1528 			prof_gctx_try_destroy(tsd, tdata, gctx, tdata);
1529 		} else {
1530 			malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);
1531 		}
1532 	}
1533 }
1534 
1535 struct prof_tdata_merge_iter_arg_s {
1536 	tsdn_t		*tsdn;
1537 	prof_cnt_t	cnt_all;
1538 };
1539 
1540 static prof_tdata_t *
1541 prof_tdata_merge_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata,
1542     void *opaque) {
1543 	struct prof_tdata_merge_iter_arg_s *arg =
1544 	    (struct prof_tdata_merge_iter_arg_s *)opaque;
1545 
1546 	malloc_mutex_lock(arg->tsdn, tdata->lock);
1547 	if (!tdata->expired) {
1548 		size_t tabind;
1549 		union {
1550 			prof_tctx_t	*p;
1551 			void		*v;
1552 		} tctx;
1553 
1554 		tdata->dumping = true;
1555 		memset(&tdata->cnt_summed, 0, sizeof(prof_cnt_t));
1556 		for (tabind = 0; !ckh_iter(&tdata->bt2tctx, &tabind, NULL,
1557 		    &tctx.v);) {
1558 			prof_tctx_merge_tdata(arg->tsdn, tctx.p, tdata);
1559 		}
1560 
1561 		arg->cnt_all.curobjs += tdata->cnt_summed.curobjs;
1562 		arg->cnt_all.curbytes += tdata->cnt_summed.curbytes;
1563 		if (opt_prof_accum) {
1564 			arg->cnt_all.accumobjs += tdata->cnt_summed.accumobjs;
1565 			arg->cnt_all.accumbytes += tdata->cnt_summed.accumbytes;
1566 		}
1567 	} else {
1568 		tdata->dumping = false;
1569 	}
1570 	malloc_mutex_unlock(arg->tsdn, tdata->lock);
1571 
1572 	return NULL;
1573 }
1574 
1575 static prof_tdata_t *
1576 prof_tdata_dump_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata,
1577     void *arg) {
1578 	bool propagate_err = *(bool *)arg;
1579 
1580 	if (!tdata->dumping) {
1581 		return NULL;
1582 	}
1583 
1584 	if (prof_dump_printf(propagate_err,
1585 	    "  t%"FMTu64": %"FMTu64": %"FMTu64" [%"FMTu64": %"FMTu64"]%s%s\n",
1586 	    tdata->thr_uid, tdata->cnt_summed.curobjs,
1587 	    tdata->cnt_summed.curbytes, tdata->cnt_summed.accumobjs,
1588 	    tdata->cnt_summed.accumbytes,
1589 	    (tdata->thread_name != NULL) ? " " : "",
1590 	    (tdata->thread_name != NULL) ? tdata->thread_name : "")) {
1591 		return tdata;
1592 	}
1593 	return NULL;
1594 }
1595 
1596 static bool
1597 prof_dump_header_impl(tsdn_t *tsdn, bool propagate_err,
1598     const prof_cnt_t *cnt_all) {
1599 	bool ret;
1600 
1601 	if (prof_dump_printf(propagate_err,
1602 	    "heap_v2/%"FMTu64"\n"
1603 	    "  t*: %"FMTu64": %"FMTu64" [%"FMTu64": %"FMTu64"]\n",
1604 	    ((uint64_t)1U << lg_prof_sample), cnt_all->curobjs,
1605 	    cnt_all->curbytes, cnt_all->accumobjs, cnt_all->accumbytes)) {
1606 		return true;
1607 	}
1608 
1609 	malloc_mutex_lock(tsdn, &tdatas_mtx);
1610 	ret = (tdata_tree_iter(&tdatas, NULL, prof_tdata_dump_iter,
1611 	    (void *)&propagate_err) != NULL);
1612 	malloc_mutex_unlock(tsdn, &tdatas_mtx);
1613 	return ret;
1614 }
1615 prof_dump_header_t *JET_MUTABLE prof_dump_header = prof_dump_header_impl;
1616 
1617 static bool
1618 prof_dump_gctx(tsdn_t *tsdn, bool propagate_err, prof_gctx_t *gctx,
1619     const prof_bt_t *bt, prof_gctx_tree_t *gctxs) {
1620 	bool ret;
1621 	unsigned i;
1622 	struct prof_tctx_dump_iter_arg_s prof_tctx_dump_iter_arg;
1623 
1624 	cassert(config_prof);
1625 	malloc_mutex_assert_owner(tsdn, gctx->lock);
1626 
1627 	/* Avoid dumping such gctx's that have no useful data. */
1628 	if ((!opt_prof_accum && gctx->cnt_summed.curobjs == 0) ||
1629 	    (opt_prof_accum && gctx->cnt_summed.accumobjs == 0)) {
1630 		assert(gctx->cnt_summed.curobjs == 0);
1631 		assert(gctx->cnt_summed.curbytes == 0);
1632 		assert(gctx->cnt_summed.accumobjs == 0);
1633 		assert(gctx->cnt_summed.accumbytes == 0);
1634 		ret = false;
1635 		goto label_return;
1636 	}
1637 
1638 	if (prof_dump_printf(propagate_err, "@")) {
1639 		ret = true;
1640 		goto label_return;
1641 	}
1642 	for (i = 0; i < bt->len; i++) {
1643 		if (prof_dump_printf(propagate_err, " %#"FMTxPTR,
1644 		    (uintptr_t)bt->vec[i])) {
1645 			ret = true;
1646 			goto label_return;
1647 		}
1648 	}
1649 
1650 	if (prof_dump_printf(propagate_err,
1651 	    "\n"
1652 	    "  t*: %"FMTu64": %"FMTu64" [%"FMTu64": %"FMTu64"]\n",
1653 	    gctx->cnt_summed.curobjs, gctx->cnt_summed.curbytes,
1654 	    gctx->cnt_summed.accumobjs, gctx->cnt_summed.accumbytes)) {
1655 		ret = true;
1656 		goto label_return;
1657 	}
1658 
1659 	prof_tctx_dump_iter_arg.tsdn = tsdn;
1660 	prof_tctx_dump_iter_arg.propagate_err = propagate_err;
1661 	if (tctx_tree_iter(&gctx->tctxs, NULL, prof_tctx_dump_iter,
1662 	    (void *)&prof_tctx_dump_iter_arg) != NULL) {
1663 		ret = true;
1664 		goto label_return;
1665 	}
1666 
1667 	ret = false;
1668 label_return:
1669 	return ret;
1670 }
1671 
1672 #ifndef _WIN32
1673 JEMALLOC_FORMAT_PRINTF(1, 2)
1674 static int
1675 prof_open_maps(const char *format, ...) {
1676 	int mfd;
1677 	va_list ap;
1678 	char filename[PATH_MAX + 1];
1679 
1680 	va_start(ap, format);
1681 	malloc_vsnprintf(filename, sizeof(filename), format, ap);
1682 	va_end(ap);
1683 
1684 #if defined(O_CLOEXEC)
1685 	mfd = open(filename, O_RDONLY | O_CLOEXEC);
1686 #else
1687 	mfd = open(filename, O_RDONLY);
1688 	if (mfd != -1) {
1689 		fcntl(mfd, F_SETFD, fcntl(mfd, F_GETFD) | FD_CLOEXEC);
1690 	}
1691 #endif
1692 
1693 	return mfd;
1694 }
1695 #endif
1696 
1697 static int
1698 prof_getpid(void) {
1699 #ifdef _WIN32
1700 	return GetCurrentProcessId();
1701 #else
1702 	return getpid();
1703 #endif
1704 }
1705 
1706 static bool
1707 prof_dump_maps(bool propagate_err) {
1708 	bool ret;
1709 	int mfd;
1710 
1711 	cassert(config_prof);
1712 #ifdef __FreeBSD__
1713 	mfd = prof_open_maps("/proc/curproc/map");
1714 #elif defined(_WIN32)
1715 	mfd = -1; // Not implemented
1716 #else
1717 	{
1718 		int pid = prof_getpid();
1719 
1720 		mfd = prof_open_maps("/proc/%d/task/%d/maps", pid, pid);
1721 		if (mfd == -1) {
1722 			mfd = prof_open_maps("/proc/%d/maps", pid);
1723 		}
1724 	}
1725 #endif
1726 	if (mfd != -1) {
1727 		ssize_t nread;
1728 
1729 		if (prof_dump_write(propagate_err, "\nMAPPED_LIBRARIES:\n") &&
1730 		    propagate_err) {
1731 			ret = true;
1732 			goto label_return;
1733 		}
1734 		nread = 0;
1735 		do {
1736 			prof_dump_buf_end += nread;
1737 			if (prof_dump_buf_end == PROF_DUMP_BUFSIZE) {
1738 				/* Make space in prof_dump_buf before read(). */
1739 				if (prof_dump_flush(propagate_err) &&
1740 				    propagate_err) {
1741 					ret = true;
1742 					goto label_return;
1743 				}
1744 			}
1745 			nread = malloc_read_fd(mfd,
1746 			    &prof_dump_buf[prof_dump_buf_end], PROF_DUMP_BUFSIZE
1747 			    - prof_dump_buf_end);
1748 		} while (nread > 0);
1749 	} else {
1750 		ret = true;
1751 		goto label_return;
1752 	}
1753 
1754 	ret = false;
1755 label_return:
1756 	if (mfd != -1) {
1757 		close(mfd);
1758 	}
1759 	return ret;
1760 }
1761 
1762 /*
1763  * See prof_sample_threshold_update() comment for why the body of this function
1764  * is conditionally compiled.
1765  */
1766 static void
1767 prof_leakcheck(const prof_cnt_t *cnt_all, size_t leak_ngctx,
1768     const char *filename) {
1769 #ifdef JEMALLOC_PROF
1770 	/*
1771 	 * Scaling is equivalent AdjustSamples() in jeprof, but the result may
1772 	 * differ slightly from what jeprof reports, because here we scale the
1773 	 * summary values, whereas jeprof scales each context individually and
1774 	 * reports the sums of the scaled values.
1775 	 */
1776 	if (cnt_all->curbytes != 0) {
1777 		double sample_period = (double)((uint64_t)1 << lg_prof_sample);
1778 		double ratio = (((double)cnt_all->curbytes) /
1779 		    (double)cnt_all->curobjs) / sample_period;
1780 		double scale_factor = 1.0 / (1.0 - exp(-ratio));
1781 		uint64_t curbytes = (uint64_t)round(((double)cnt_all->curbytes)
1782 		    * scale_factor);
1783 		uint64_t curobjs = (uint64_t)round(((double)cnt_all->curobjs) *
1784 		    scale_factor);
1785 
1786 		malloc_printf("<jemalloc>: Leak approximation summary: ~%"FMTu64
1787 		    " byte%s, ~%"FMTu64" object%s, >= %zu context%s\n",
1788 		    curbytes, (curbytes != 1) ? "s" : "", curobjs, (curobjs !=
1789 		    1) ? "s" : "", leak_ngctx, (leak_ngctx != 1) ? "s" : "");
1790 		malloc_printf(
1791 		    "<jemalloc>: Run jeprof on \"%s\" for leak detail\n",
1792 		    filename);
1793 	}
1794 #endif
1795 }
1796 
1797 struct prof_gctx_dump_iter_arg_s {
1798 	tsdn_t	*tsdn;
1799 	bool	propagate_err;
1800 };
1801 
1802 static prof_gctx_t *
1803 prof_gctx_dump_iter(prof_gctx_tree_t *gctxs, prof_gctx_t *gctx, void *opaque) {
1804 	prof_gctx_t *ret;
1805 	struct prof_gctx_dump_iter_arg_s *arg =
1806 	    (struct prof_gctx_dump_iter_arg_s *)opaque;
1807 
1808 	malloc_mutex_lock(arg->tsdn, gctx->lock);
1809 
1810 	if (prof_dump_gctx(arg->tsdn, arg->propagate_err, gctx, &gctx->bt,
1811 	    gctxs)) {
1812 		ret = gctx;
1813 		goto label_return;
1814 	}
1815 
1816 	ret = NULL;
1817 label_return:
1818 	malloc_mutex_unlock(arg->tsdn, gctx->lock);
1819 	return ret;
1820 }
1821 
1822 static void
1823 prof_dump_prep(tsd_t *tsd, prof_tdata_t *tdata,
1824     struct prof_tdata_merge_iter_arg_s *prof_tdata_merge_iter_arg,
1825     struct prof_gctx_merge_iter_arg_s *prof_gctx_merge_iter_arg,
1826     prof_gctx_tree_t *gctxs) {
1827 	size_t tabind;
1828 	union {
1829 		prof_gctx_t	*p;
1830 		void		*v;
1831 	} gctx;
1832 
1833 	prof_enter(tsd, tdata);
1834 
1835 	/*
1836 	 * Put gctx's in limbo and clear their counters in preparation for
1837 	 * summing.
1838 	 */
1839 	gctx_tree_new(gctxs);
1840 	for (tabind = 0; !ckh_iter(&bt2gctx, &tabind, NULL, &gctx.v);) {
1841 		prof_dump_gctx_prep(tsd_tsdn(tsd), gctx.p, gctxs);
1842 	}
1843 
1844 	/*
1845 	 * Iterate over tdatas, and for the non-expired ones snapshot their tctx
1846 	 * stats and merge them into the associated gctx's.
1847 	 */
1848 	prof_tdata_merge_iter_arg->tsdn = tsd_tsdn(tsd);
1849 	memset(&prof_tdata_merge_iter_arg->cnt_all, 0, sizeof(prof_cnt_t));
1850 	malloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx);
1851 	tdata_tree_iter(&tdatas, NULL, prof_tdata_merge_iter,
1852 	    (void *)prof_tdata_merge_iter_arg);
1853 	malloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx);
1854 
1855 	/* Merge tctx stats into gctx's. */
1856 	prof_gctx_merge_iter_arg->tsdn = tsd_tsdn(tsd);
1857 	prof_gctx_merge_iter_arg->leak_ngctx = 0;
1858 	gctx_tree_iter(gctxs, NULL, prof_gctx_merge_iter,
1859 	    (void *)prof_gctx_merge_iter_arg);
1860 
1861 	prof_leave(tsd, tdata);
1862 }
1863 
1864 static bool
1865 prof_dump_file(tsd_t *tsd, bool propagate_err, const char *filename,
1866     bool leakcheck, prof_tdata_t *tdata,
1867     struct prof_tdata_merge_iter_arg_s *prof_tdata_merge_iter_arg,
1868     struct prof_gctx_merge_iter_arg_s *prof_gctx_merge_iter_arg,
1869     struct prof_gctx_dump_iter_arg_s *prof_gctx_dump_iter_arg,
1870     prof_gctx_tree_t *gctxs) {
1871 	/* Create dump file. */
1872 	if ((prof_dump_fd = prof_dump_open(propagate_err, filename)) == -1) {
1873 		return true;
1874 	}
1875 
1876 	/* Dump profile header. */
1877 	if (prof_dump_header(tsd_tsdn(tsd), propagate_err,
1878 	    &prof_tdata_merge_iter_arg->cnt_all)) {
1879 		goto label_write_error;
1880 	}
1881 
1882 	/* Dump per gctx profile stats. */
1883 	prof_gctx_dump_iter_arg->tsdn = tsd_tsdn(tsd);
1884 	prof_gctx_dump_iter_arg->propagate_err = propagate_err;
1885 	if (gctx_tree_iter(gctxs, NULL, prof_gctx_dump_iter,
1886 	    (void *)prof_gctx_dump_iter_arg) != NULL) {
1887 		goto label_write_error;
1888 	}
1889 
1890 	/* Dump /proc/<pid>/maps if possible. */
1891 	if (prof_dump_maps(propagate_err)) {
1892 		goto label_write_error;
1893 	}
1894 
1895 	if (prof_dump_close(propagate_err)) {
1896 		return true;
1897 	}
1898 
1899 	return false;
1900 label_write_error:
1901 	prof_dump_close(propagate_err);
1902 	return true;
1903 }
1904 
1905 static bool
1906 prof_dump(tsd_t *tsd, bool propagate_err, const char *filename,
1907     bool leakcheck) {
1908 	cassert(config_prof);
1909 	assert(tsd_reentrancy_level_get(tsd) == 0);
1910 
1911 	prof_tdata_t * tdata = prof_tdata_get(tsd, true);
1912 	if (tdata == NULL) {
1913 		return true;
1914 	}
1915 
1916 	pre_reentrancy(tsd, NULL);
1917 	malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_mtx);
1918 
1919 	prof_gctx_tree_t gctxs;
1920 	struct prof_tdata_merge_iter_arg_s prof_tdata_merge_iter_arg;
1921 	struct prof_gctx_merge_iter_arg_s prof_gctx_merge_iter_arg;
1922 	struct prof_gctx_dump_iter_arg_s prof_gctx_dump_iter_arg;
1923 	prof_dump_prep(tsd, tdata, &prof_tdata_merge_iter_arg,
1924 	    &prof_gctx_merge_iter_arg, &gctxs);
1925 	bool err = prof_dump_file(tsd, propagate_err, filename, leakcheck, tdata,
1926 	    &prof_tdata_merge_iter_arg, &prof_gctx_merge_iter_arg,
1927 	    &prof_gctx_dump_iter_arg, &gctxs);
1928 	prof_gctx_finish(tsd, &gctxs);
1929 
1930 	malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_mtx);
1931 	post_reentrancy(tsd);
1932 
1933 	if (err) {
1934 		return true;
1935 	}
1936 
1937 	if (leakcheck) {
1938 		prof_leakcheck(&prof_tdata_merge_iter_arg.cnt_all,
1939 		    prof_gctx_merge_iter_arg.leak_ngctx, filename);
1940 	}
1941 	return false;
1942 }
1943 
1944 #ifdef JEMALLOC_JET
1945 void
1946 prof_cnt_all(uint64_t *curobjs, uint64_t *curbytes, uint64_t *accumobjs,
1947     uint64_t *accumbytes) {
1948 	tsd_t *tsd;
1949 	prof_tdata_t *tdata;
1950 	struct prof_tdata_merge_iter_arg_s prof_tdata_merge_iter_arg;
1951 	struct prof_gctx_merge_iter_arg_s prof_gctx_merge_iter_arg;
1952 	prof_gctx_tree_t gctxs;
1953 
1954 	tsd = tsd_fetch();
1955 	tdata = prof_tdata_get(tsd, false);
1956 	if (tdata == NULL) {
1957 		if (curobjs != NULL) {
1958 			*curobjs = 0;
1959 		}
1960 		if (curbytes != NULL) {
1961 			*curbytes = 0;
1962 		}
1963 		if (accumobjs != NULL) {
1964 			*accumobjs = 0;
1965 		}
1966 		if (accumbytes != NULL) {
1967 			*accumbytes = 0;
1968 		}
1969 		return;
1970 	}
1971 
1972 	prof_dump_prep(tsd, tdata, &prof_tdata_merge_iter_arg,
1973 	    &prof_gctx_merge_iter_arg, &gctxs);
1974 	prof_gctx_finish(tsd, &gctxs);
1975 
1976 	if (curobjs != NULL) {
1977 		*curobjs = prof_tdata_merge_iter_arg.cnt_all.curobjs;
1978 	}
1979 	if (curbytes != NULL) {
1980 		*curbytes = prof_tdata_merge_iter_arg.cnt_all.curbytes;
1981 	}
1982 	if (accumobjs != NULL) {
1983 		*accumobjs = prof_tdata_merge_iter_arg.cnt_all.accumobjs;
1984 	}
1985 	if (accumbytes != NULL) {
1986 		*accumbytes = prof_tdata_merge_iter_arg.cnt_all.accumbytes;
1987 	}
1988 }
1989 #endif
1990 
1991 #define DUMP_FILENAME_BUFSIZE	(PATH_MAX + 1)
1992 #define VSEQ_INVALID		UINT64_C(0xffffffffffffffff)
1993 static void
1994 prof_dump_filename(char *filename, char v, uint64_t vseq) {
1995 	cassert(config_prof);
1996 
1997 	if (vseq != VSEQ_INVALID) {
1998 	        /* "<prefix>.<pid>.<seq>.v<vseq>.heap" */
1999 		malloc_snprintf(filename, DUMP_FILENAME_BUFSIZE,
2000 		    "%s.%d.%"FMTu64".%c%"FMTu64".heap",
2001 		    opt_prof_prefix, prof_getpid(), prof_dump_seq, v, vseq);
2002 	} else {
2003 	        /* "<prefix>.<pid>.<seq>.<v>.heap" */
2004 		malloc_snprintf(filename, DUMP_FILENAME_BUFSIZE,
2005 		    "%s.%d.%"FMTu64".%c.heap",
2006 		    opt_prof_prefix, prof_getpid(), prof_dump_seq, v);
2007 	}
2008 	prof_dump_seq++;
2009 }
2010 
2011 static void
2012 prof_fdump(void) {
2013 	tsd_t *tsd;
2014 	char filename[DUMP_FILENAME_BUFSIZE];
2015 
2016 	cassert(config_prof);
2017 	assert(opt_prof_final);
2018 	assert(opt_prof_prefix[0] != '\0');
2019 
2020 	if (!prof_booted) {
2021 		return;
2022 	}
2023 	tsd = tsd_fetch();
2024 	assert(tsd_reentrancy_level_get(tsd) == 0);
2025 
2026 	malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_seq_mtx);
2027 	prof_dump_filename(filename, 'f', VSEQ_INVALID);
2028 	malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_seq_mtx);
2029 	prof_dump(tsd, false, filename, opt_prof_leak);
2030 }
2031 
2032 bool
2033 prof_accum_init(tsdn_t *tsdn, prof_accum_t *prof_accum) {
2034 	cassert(config_prof);
2035 
2036 #ifndef JEMALLOC_ATOMIC_U64
2037 	if (malloc_mutex_init(&prof_accum->mtx, "prof_accum",
2038 	    WITNESS_RANK_PROF_ACCUM, malloc_mutex_rank_exclusive)) {
2039 		return true;
2040 	}
2041 	prof_accum->accumbytes = 0;
2042 #else
2043 	atomic_store_u64(&prof_accum->accumbytes, 0, ATOMIC_RELAXED);
2044 #endif
2045 	return false;
2046 }
2047 
2048 void
2049 prof_idump(tsdn_t *tsdn) {
2050 	tsd_t *tsd;
2051 	prof_tdata_t *tdata;
2052 
2053 	cassert(config_prof);
2054 
2055 	if (!prof_booted || tsdn_null(tsdn) || !prof_active_get_unlocked()) {
2056 		return;
2057 	}
2058 	tsd = tsdn_tsd(tsdn);
2059 	if (tsd_reentrancy_level_get(tsd) > 0) {
2060 		return;
2061 	}
2062 
2063 	tdata = prof_tdata_get(tsd, false);
2064 	if (tdata == NULL) {
2065 		return;
2066 	}
2067 	if (tdata->enq) {
2068 		tdata->enq_idump = true;
2069 		return;
2070 	}
2071 
2072 	if (opt_prof_prefix[0] != '\0') {
2073 		char filename[PATH_MAX + 1];
2074 		malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_seq_mtx);
2075 		prof_dump_filename(filename, 'i', prof_dump_iseq);
2076 		prof_dump_iseq++;
2077 		malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_seq_mtx);
2078 		prof_dump(tsd, false, filename, false);
2079 	}
2080 }
2081 
2082 bool
2083 prof_mdump(tsd_t *tsd, const char *filename) {
2084 	cassert(config_prof);
2085 	assert(tsd_reentrancy_level_get(tsd) == 0);
2086 
2087 	if (!opt_prof || !prof_booted) {
2088 		return true;
2089 	}
2090 	char filename_buf[DUMP_FILENAME_BUFSIZE];
2091 	if (filename == NULL) {
2092 		/* No filename specified, so automatically generate one. */
2093 		if (opt_prof_prefix[0] == '\0') {
2094 			return true;
2095 		}
2096 		malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_seq_mtx);
2097 		prof_dump_filename(filename_buf, 'm', prof_dump_mseq);
2098 		prof_dump_mseq++;
2099 		malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_seq_mtx);
2100 		filename = filename_buf;
2101 	}
2102 	return prof_dump(tsd, true, filename, false);
2103 }
2104 
2105 void
2106 prof_gdump(tsdn_t *tsdn) {
2107 	tsd_t *tsd;
2108 	prof_tdata_t *tdata;
2109 
2110 	cassert(config_prof);
2111 
2112 	if (!prof_booted || tsdn_null(tsdn) || !prof_active_get_unlocked()) {
2113 		return;
2114 	}
2115 	tsd = tsdn_tsd(tsdn);
2116 	if (tsd_reentrancy_level_get(tsd) > 0) {
2117 		return;
2118 	}
2119 
2120 	tdata = prof_tdata_get(tsd, false);
2121 	if (tdata == NULL) {
2122 		return;
2123 	}
2124 	if (tdata->enq) {
2125 		tdata->enq_gdump = true;
2126 		return;
2127 	}
2128 
2129 	if (opt_prof_prefix[0] != '\0') {
2130 		char filename[DUMP_FILENAME_BUFSIZE];
2131 		malloc_mutex_lock(tsdn, &prof_dump_seq_mtx);
2132 		prof_dump_filename(filename, 'u', prof_dump_useq);
2133 		prof_dump_useq++;
2134 		malloc_mutex_unlock(tsdn, &prof_dump_seq_mtx);
2135 		prof_dump(tsd, false, filename, false);
2136 	}
2137 }
2138 
2139 static void
2140 prof_bt_hash(const void *key, size_t r_hash[2]) {
2141 	prof_bt_t *bt = (prof_bt_t *)key;
2142 
2143 	cassert(config_prof);
2144 
2145 	hash(bt->vec, bt->len * sizeof(void *), 0x94122f33U, r_hash);
2146 }
2147 
2148 static bool
2149 prof_bt_keycomp(const void *k1, const void *k2) {
2150 	const prof_bt_t *bt1 = (prof_bt_t *)k1;
2151 	const prof_bt_t *bt2 = (prof_bt_t *)k2;
2152 
2153 	cassert(config_prof);
2154 
2155 	if (bt1->len != bt2->len) {
2156 		return false;
2157 	}
2158 	return (memcmp(bt1->vec, bt2->vec, bt1->len * sizeof(void *)) == 0);
2159 }
2160 
2161 static void
2162 prof_bt_node_hash(const void *key, size_t r_hash[2]) {
2163 	const prof_bt_node_t *bt_node = (prof_bt_node_t *)key;
2164 	prof_bt_hash((void *)(&bt_node->bt), r_hash);
2165 }
2166 
2167 static bool
2168 prof_bt_node_keycomp(const void *k1, const void *k2) {
2169 	const prof_bt_node_t *bt_node1 = (prof_bt_node_t *)k1;
2170 	const prof_bt_node_t *bt_node2 = (prof_bt_node_t *)k2;
2171 	return prof_bt_keycomp((void *)(&bt_node1->bt),
2172 	    (void *)(&bt_node2->bt));
2173 }
2174 
2175 static void
2176 prof_thr_node_hash(const void *key, size_t r_hash[2]) {
2177 	const prof_thr_node_t *thr_node = (prof_thr_node_t *)key;
2178 	hash(&thr_node->thr_uid, sizeof(uint64_t), 0x94122f35U, r_hash);
2179 }
2180 
2181 static bool
2182 prof_thr_node_keycomp(const void *k1, const void *k2) {
2183 	const prof_thr_node_t *thr_node1 = (prof_thr_node_t *)k1;
2184 	const prof_thr_node_t *thr_node2 = (prof_thr_node_t *)k2;
2185 	return thr_node1->thr_uid == thr_node2->thr_uid;
2186 }
2187 
2188 static uint64_t
2189 prof_thr_uid_alloc(tsdn_t *tsdn) {
2190 	uint64_t thr_uid;
2191 
2192 	malloc_mutex_lock(tsdn, &next_thr_uid_mtx);
2193 	thr_uid = next_thr_uid;
2194 	next_thr_uid++;
2195 	malloc_mutex_unlock(tsdn, &next_thr_uid_mtx);
2196 
2197 	return thr_uid;
2198 }
2199 
2200 static prof_tdata_t *
2201 prof_tdata_init_impl(tsd_t *tsd, uint64_t thr_uid, uint64_t thr_discrim,
2202     char *thread_name, bool active) {
2203 	prof_tdata_t *tdata;
2204 
2205 	cassert(config_prof);
2206 
2207 	/* Initialize an empty cache for this thread. */
2208 	tdata = (prof_tdata_t *)iallocztm(tsd_tsdn(tsd), sizeof(prof_tdata_t),
2209 	    sz_size2index(sizeof(prof_tdata_t)), false, NULL, true,
2210 	    arena_get(TSDN_NULL, 0, true), true);
2211 	if (tdata == NULL) {
2212 		return NULL;
2213 	}
2214 
2215 	tdata->lock = prof_tdata_mutex_choose(thr_uid);
2216 	tdata->thr_uid = thr_uid;
2217 	tdata->thr_discrim = thr_discrim;
2218 	tdata->thread_name = thread_name;
2219 	tdata->attached = true;
2220 	tdata->expired = false;
2221 	tdata->tctx_uid_next = 0;
2222 
2223 	if (ckh_new(tsd, &tdata->bt2tctx, PROF_CKH_MINITEMS, prof_bt_hash,
2224 	    prof_bt_keycomp)) {
2225 		idalloctm(tsd_tsdn(tsd), tdata, NULL, NULL, true, true);
2226 		return NULL;
2227 	}
2228 
2229 	tdata->prng_state = (uint64_t)(uintptr_t)tdata;
2230 	prof_sample_threshold_update(tdata);
2231 
2232 	tdata->enq = false;
2233 	tdata->enq_idump = false;
2234 	tdata->enq_gdump = false;
2235 
2236 	tdata->dumping = false;
2237 	tdata->active = active;
2238 
2239 	malloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx);
2240 	tdata_tree_insert(&tdatas, tdata);
2241 	malloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx);
2242 
2243 	return tdata;
2244 }
2245 
2246 prof_tdata_t *
2247 prof_tdata_init(tsd_t *tsd) {
2248 	return prof_tdata_init_impl(tsd, prof_thr_uid_alloc(tsd_tsdn(tsd)), 0,
2249 	    NULL, prof_thread_active_init_get(tsd_tsdn(tsd)));
2250 }
2251 
2252 static bool
2253 prof_tdata_should_destroy_unlocked(prof_tdata_t *tdata, bool even_if_attached) {
2254 	if (tdata->attached && !even_if_attached) {
2255 		return false;
2256 	}
2257 	if (ckh_count(&tdata->bt2tctx) != 0) {
2258 		return false;
2259 	}
2260 	return true;
2261 }
2262 
2263 static bool
2264 prof_tdata_should_destroy(tsdn_t *tsdn, prof_tdata_t *tdata,
2265     bool even_if_attached) {
2266 	malloc_mutex_assert_owner(tsdn, tdata->lock);
2267 
2268 	return prof_tdata_should_destroy_unlocked(tdata, even_if_attached);
2269 }
2270 
2271 static void
2272 prof_tdata_destroy_locked(tsd_t *tsd, prof_tdata_t *tdata,
2273     bool even_if_attached) {
2274 	malloc_mutex_assert_owner(tsd_tsdn(tsd), &tdatas_mtx);
2275 
2276 	tdata_tree_remove(&tdatas, tdata);
2277 
2278 	assert(prof_tdata_should_destroy_unlocked(tdata, even_if_attached));
2279 
2280 	if (tdata->thread_name != NULL) {
2281 		idalloctm(tsd_tsdn(tsd), tdata->thread_name, NULL, NULL, true,
2282 		    true);
2283 	}
2284 	ckh_delete(tsd, &tdata->bt2tctx);
2285 	idalloctm(tsd_tsdn(tsd), tdata, NULL, NULL, true, true);
2286 }
2287 
2288 static void
2289 prof_tdata_destroy(tsd_t *tsd, prof_tdata_t *tdata, bool even_if_attached) {
2290 	malloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx);
2291 	prof_tdata_destroy_locked(tsd, tdata, even_if_attached);
2292 	malloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx);
2293 }
2294 
2295 static void
2296 prof_tdata_detach(tsd_t *tsd, prof_tdata_t *tdata) {
2297 	bool destroy_tdata;
2298 
2299 	malloc_mutex_lock(tsd_tsdn(tsd), tdata->lock);
2300 	if (tdata->attached) {
2301 		destroy_tdata = prof_tdata_should_destroy(tsd_tsdn(tsd), tdata,
2302 		    true);
2303 		/*
2304 		 * Only detach if !destroy_tdata, because detaching would allow
2305 		 * another thread to win the race to destroy tdata.
2306 		 */
2307 		if (!destroy_tdata) {
2308 			tdata->attached = false;
2309 		}
2310 		tsd_prof_tdata_set(tsd, NULL);
2311 	} else {
2312 		destroy_tdata = false;
2313 	}
2314 	malloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock);
2315 	if (destroy_tdata) {
2316 		prof_tdata_destroy(tsd, tdata, true);
2317 	}
2318 }
2319 
2320 prof_tdata_t *
2321 prof_tdata_reinit(tsd_t *tsd, prof_tdata_t *tdata) {
2322 	uint64_t thr_uid = tdata->thr_uid;
2323 	uint64_t thr_discrim = tdata->thr_discrim + 1;
2324 	char *thread_name = (tdata->thread_name != NULL) ?
2325 	    prof_thread_name_alloc(tsd_tsdn(tsd), tdata->thread_name) : NULL;
2326 	bool active = tdata->active;
2327 
2328 	prof_tdata_detach(tsd, tdata);
2329 	return prof_tdata_init_impl(tsd, thr_uid, thr_discrim, thread_name,
2330 	    active);
2331 }
2332 
2333 static bool
2334 prof_tdata_expire(tsdn_t *tsdn, prof_tdata_t *tdata) {
2335 	bool destroy_tdata;
2336 
2337 	malloc_mutex_lock(tsdn, tdata->lock);
2338 	if (!tdata->expired) {
2339 		tdata->expired = true;
2340 		destroy_tdata = tdata->attached ? false :
2341 		    prof_tdata_should_destroy(tsdn, tdata, false);
2342 	} else {
2343 		destroy_tdata = false;
2344 	}
2345 	malloc_mutex_unlock(tsdn, tdata->lock);
2346 
2347 	return destroy_tdata;
2348 }
2349 
2350 static prof_tdata_t *
2351 prof_tdata_reset_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata,
2352     void *arg) {
2353 	tsdn_t *tsdn = (tsdn_t *)arg;
2354 
2355 	return (prof_tdata_expire(tsdn, tdata) ? tdata : NULL);
2356 }
2357 
2358 void
2359 prof_reset(tsd_t *tsd, size_t lg_sample) {
2360 	prof_tdata_t *next;
2361 
2362 	assert(lg_sample < (sizeof(uint64_t) << 3));
2363 
2364 	malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_mtx);
2365 	malloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx);
2366 
2367 	lg_prof_sample = lg_sample;
2368 
2369 	next = NULL;
2370 	do {
2371 		prof_tdata_t *to_destroy = tdata_tree_iter(&tdatas, next,
2372 		    prof_tdata_reset_iter, (void *)tsd);
2373 		if (to_destroy != NULL) {
2374 			next = tdata_tree_next(&tdatas, to_destroy);
2375 			prof_tdata_destroy_locked(tsd, to_destroy, false);
2376 		} else {
2377 			next = NULL;
2378 		}
2379 	} while (next != NULL);
2380 
2381 	malloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx);
2382 	malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_mtx);
2383 }
2384 
2385 void
2386 prof_tdata_cleanup(tsd_t *tsd) {
2387 	prof_tdata_t *tdata;
2388 
2389 	if (!config_prof) {
2390 		return;
2391 	}
2392 
2393 	tdata = tsd_prof_tdata_get(tsd);
2394 	if (tdata != NULL) {
2395 		prof_tdata_detach(tsd, tdata);
2396 	}
2397 }
2398 
2399 bool
2400 prof_active_get(tsdn_t *tsdn) {
2401 	bool prof_active_current;
2402 
2403 	malloc_mutex_lock(tsdn, &prof_active_mtx);
2404 	prof_active_current = prof_active;
2405 	malloc_mutex_unlock(tsdn, &prof_active_mtx);
2406 	return prof_active_current;
2407 }
2408 
2409 bool
2410 prof_active_set(tsdn_t *tsdn, bool active) {
2411 	bool prof_active_old;
2412 
2413 	malloc_mutex_lock(tsdn, &prof_active_mtx);
2414 	prof_active_old = prof_active;
2415 	prof_active = active;
2416 	malloc_mutex_unlock(tsdn, &prof_active_mtx);
2417 	return prof_active_old;
2418 }
2419 
2420 #ifdef JEMALLOC_JET
2421 size_t
2422 prof_log_bt_count(void) {
2423 	size_t cnt = 0;
2424 	prof_bt_node_t *node = log_bt_first;
2425 	while (node != NULL) {
2426 		cnt++;
2427 		node = node->next;
2428 	}
2429 	return cnt;
2430 }
2431 
2432 size_t
2433 prof_log_alloc_count(void) {
2434 	size_t cnt = 0;
2435 	prof_alloc_node_t *node = log_alloc_first;
2436 	while (node != NULL) {
2437 		cnt++;
2438 		node = node->next;
2439 	}
2440 	return cnt;
2441 }
2442 
2443 size_t
2444 prof_log_thr_count(void) {
2445 	size_t cnt = 0;
2446 	prof_thr_node_t *node = log_thr_first;
2447 	while (node != NULL) {
2448 		cnt++;
2449 		node = node->next;
2450 	}
2451 	return cnt;
2452 }
2453 
2454 bool
2455 prof_log_is_logging(void) {
2456 	return prof_logging_state == prof_logging_state_started;
2457 }
2458 
2459 bool
2460 prof_log_rep_check(void) {
2461 	if (prof_logging_state == prof_logging_state_stopped
2462 	    && log_tables_initialized) {
2463 		return true;
2464 	}
2465 
2466 	if (log_bt_last != NULL && log_bt_last->next != NULL) {
2467 		return true;
2468 	}
2469 	if (log_thr_last != NULL && log_thr_last->next != NULL) {
2470 		return true;
2471 	}
2472 	if (log_alloc_last != NULL && log_alloc_last->next != NULL) {
2473 		return true;
2474 	}
2475 
2476 	size_t bt_count = prof_log_bt_count();
2477 	size_t thr_count = prof_log_thr_count();
2478 	size_t alloc_count = prof_log_alloc_count();
2479 
2480 
2481 	if (prof_logging_state == prof_logging_state_stopped) {
2482 		if (bt_count != 0 || thr_count != 0 || alloc_count || 0) {
2483 			return true;
2484 		}
2485 	}
2486 
2487 	prof_alloc_node_t *node = log_alloc_first;
2488 	while (node != NULL) {
2489 		if (node->alloc_bt_ind >= bt_count) {
2490 			return true;
2491 		}
2492 		if (node->free_bt_ind >= bt_count) {
2493 			return true;
2494 		}
2495 		if (node->alloc_thr_ind >= thr_count) {
2496 			return true;
2497 		}
2498 		if (node->free_thr_ind >= thr_count) {
2499 			return true;
2500 		}
2501 		if (node->alloc_time_ns > node->free_time_ns) {
2502 			return true;
2503 		}
2504 		node = node->next;
2505 	}
2506 
2507 	return false;
2508 }
2509 
2510 void
2511 prof_log_dummy_set(bool new_value) {
2512 	prof_log_dummy = new_value;
2513 }
2514 #endif
2515 
2516 bool
2517 prof_log_start(tsdn_t *tsdn, const char *filename) {
2518 	if (!opt_prof || !prof_booted) {
2519 		return true;
2520 	}
2521 
2522 	bool ret = false;
2523 	size_t buf_size = PATH_MAX + 1;
2524 
2525 	malloc_mutex_lock(tsdn, &log_mtx);
2526 
2527 	if (prof_logging_state != prof_logging_state_stopped) {
2528 		ret = true;
2529 	} else if (filename == NULL) {
2530 		/* Make default name. */
2531 		malloc_snprintf(log_filename, buf_size, "%s.%d.%"FMTu64".json",
2532 		    opt_prof_prefix, prof_getpid(), log_seq);
2533 		log_seq++;
2534 		prof_logging_state = prof_logging_state_started;
2535 	} else if (strlen(filename) >= buf_size) {
2536 		ret = true;
2537 	} else {
2538 		strcpy(log_filename, filename);
2539 		prof_logging_state = prof_logging_state_started;
2540 	}
2541 
2542 	if (!ret) {
2543 		nstime_update(&log_start_timestamp);
2544 	}
2545 
2546 	malloc_mutex_unlock(tsdn, &log_mtx);
2547 
2548 	return ret;
2549 }
2550 
2551 /* Used as an atexit function to stop logging on exit. */
2552 static void
2553 prof_log_stop_final(void) {
2554 	tsd_t *tsd = tsd_fetch();
2555 	prof_log_stop(tsd_tsdn(tsd));
2556 }
2557 
2558 struct prof_emitter_cb_arg_s {
2559 	int fd;
2560 	ssize_t ret;
2561 };
2562 
2563 static void
2564 prof_emitter_write_cb(void *opaque, const char *to_write) {
2565 	struct prof_emitter_cb_arg_s *arg =
2566 	    (struct prof_emitter_cb_arg_s *)opaque;
2567 	size_t bytes = strlen(to_write);
2568 #ifdef JEMALLOC_JET
2569 	if (prof_log_dummy) {
2570 		return;
2571 	}
2572 #endif
2573 	arg->ret = write(arg->fd, (void *)to_write, bytes);
2574 }
2575 
2576 /*
2577  * prof_log_emit_{...} goes through the appropriate linked list, emitting each
2578  * node to the json and deallocating it.
2579  */
2580 static void
2581 prof_log_emit_threads(tsd_t *tsd, emitter_t *emitter) {
2582 	emitter_json_array_kv_begin(emitter, "threads");
2583 	prof_thr_node_t *thr_node = log_thr_first;
2584 	prof_thr_node_t *thr_old_node;
2585 	while (thr_node != NULL) {
2586 		emitter_json_object_begin(emitter);
2587 
2588 		emitter_json_kv(emitter, "thr_uid", emitter_type_uint64,
2589 		    &thr_node->thr_uid);
2590 
2591 		char *thr_name = thr_node->name;
2592 
2593 		emitter_json_kv(emitter, "thr_name", emitter_type_string,
2594 		    &thr_name);
2595 
2596 		emitter_json_object_end(emitter);
2597 		thr_old_node = thr_node;
2598 		thr_node = thr_node->next;
2599 		idalloc(tsd, thr_old_node);
2600 	}
2601 	emitter_json_array_end(emitter);
2602 }
2603 
2604 static void
2605 prof_log_emit_traces(tsd_t *tsd, emitter_t *emitter) {
2606 	emitter_json_array_kv_begin(emitter, "stack_traces");
2607 	prof_bt_node_t *bt_node = log_bt_first;
2608 	prof_bt_node_t *bt_old_node;
2609 	/*
2610 	 * Calculate how many hex digits we need: twice number of bytes, two for
2611 	 * "0x", and then one more for terminating '\0'.
2612 	 */
2613 	char buf[2 * sizeof(intptr_t) + 3];
2614 	size_t buf_sz = sizeof(buf);
2615 	while (bt_node != NULL) {
2616 		emitter_json_array_begin(emitter);
2617 		size_t i;
2618 		for (i = 0; i < bt_node->bt.len; i++) {
2619 			malloc_snprintf(buf, buf_sz, "%p", bt_node->bt.vec[i]);
2620 			char *trace_str = buf;
2621 			emitter_json_value(emitter, emitter_type_string,
2622 			    &trace_str);
2623 		}
2624 		emitter_json_array_end(emitter);
2625 
2626 		bt_old_node = bt_node;
2627 		bt_node = bt_node->next;
2628 		idalloc(tsd, bt_old_node);
2629 	}
2630 	emitter_json_array_end(emitter);
2631 }
2632 
2633 static void
2634 prof_log_emit_allocs(tsd_t *tsd, emitter_t *emitter) {
2635 	emitter_json_array_kv_begin(emitter, "allocations");
2636 	prof_alloc_node_t *alloc_node = log_alloc_first;
2637 	prof_alloc_node_t *alloc_old_node;
2638 	while (alloc_node != NULL) {
2639 		emitter_json_object_begin(emitter);
2640 
2641 		emitter_json_kv(emitter, "alloc_thread", emitter_type_size,
2642 		    &alloc_node->alloc_thr_ind);
2643 
2644 		emitter_json_kv(emitter, "free_thread", emitter_type_size,
2645 		    &alloc_node->free_thr_ind);
2646 
2647 		emitter_json_kv(emitter, "alloc_trace", emitter_type_size,
2648 		    &alloc_node->alloc_bt_ind);
2649 
2650 		emitter_json_kv(emitter, "free_trace", emitter_type_size,
2651 		    &alloc_node->free_bt_ind);
2652 
2653 		emitter_json_kv(emitter, "alloc_timestamp",
2654 		    emitter_type_uint64, &alloc_node->alloc_time_ns);
2655 
2656 		emitter_json_kv(emitter, "free_timestamp", emitter_type_uint64,
2657 		    &alloc_node->free_time_ns);
2658 
2659 		emitter_json_kv(emitter, "usize", emitter_type_uint64,
2660 		    &alloc_node->usize);
2661 
2662 		emitter_json_object_end(emitter);
2663 
2664 		alloc_old_node = alloc_node;
2665 		alloc_node = alloc_node->next;
2666 		idalloc(tsd, alloc_old_node);
2667 	}
2668 	emitter_json_array_end(emitter);
2669 }
2670 
2671 static void
2672 prof_log_emit_metadata(emitter_t *emitter) {
2673 	emitter_json_object_kv_begin(emitter, "info");
2674 
2675 	nstime_t now = NSTIME_ZERO_INITIALIZER;
2676 
2677 	nstime_update(&now);
2678 	uint64_t ns = nstime_ns(&now) - nstime_ns(&log_start_timestamp);
2679 	emitter_json_kv(emitter, "duration", emitter_type_uint64, &ns);
2680 
2681 	char *vers = JEMALLOC_VERSION;
2682 	emitter_json_kv(emitter, "version",
2683 	    emitter_type_string, &vers);
2684 
2685 	emitter_json_kv(emitter, "lg_sample_rate",
2686 	    emitter_type_int, &lg_prof_sample);
2687 
2688 	int pid = prof_getpid();
2689 	emitter_json_kv(emitter, "pid", emitter_type_int, &pid);
2690 
2691 	emitter_json_object_end(emitter);
2692 }
2693 
2694 
2695 bool
2696 prof_log_stop(tsdn_t *tsdn) {
2697 	if (!opt_prof || !prof_booted) {
2698 		return true;
2699 	}
2700 
2701 	tsd_t *tsd = tsdn_tsd(tsdn);
2702 	malloc_mutex_lock(tsdn, &log_mtx);
2703 
2704 	if (prof_logging_state != prof_logging_state_started) {
2705 		malloc_mutex_unlock(tsdn, &log_mtx);
2706 		return true;
2707 	}
2708 
2709 	/*
2710 	 * Set the state to dumping. We'll set it to stopped when we're done.
2711 	 * Since other threads won't be able to start/stop/log when the state is
2712 	 * dumping, we don't have to hold the lock during the whole method.
2713 	 */
2714 	prof_logging_state = prof_logging_state_dumping;
2715 	malloc_mutex_unlock(tsdn, &log_mtx);
2716 
2717 
2718 	emitter_t emitter;
2719 
2720 	/* Create a file. */
2721 
2722 	int fd;
2723 #ifdef JEMALLOC_JET
2724 	if (prof_log_dummy) {
2725 		fd = 0;
2726 	} else {
2727 		fd = creat(log_filename, 0644);
2728 	}
2729 #else
2730 	fd = creat(log_filename, 0644);
2731 #endif
2732 
2733 	if (fd == -1) {
2734 		malloc_printf("<jemalloc>: creat() for log file \"%s\" "
2735 			      " failed with %d\n", log_filename, errno);
2736 		if (opt_abort) {
2737 			abort();
2738 		}
2739 		return true;
2740 	}
2741 
2742 	/* Emit to json. */
2743 	struct prof_emitter_cb_arg_s arg;
2744 	arg.fd = fd;
2745 	emitter_init(&emitter, emitter_output_json, &prof_emitter_write_cb,
2746 	    (void *)(&arg));
2747 
2748 	emitter_begin(&emitter);
2749 	prof_log_emit_metadata(&emitter);
2750 	prof_log_emit_threads(tsd, &emitter);
2751 	prof_log_emit_traces(tsd, &emitter);
2752 	prof_log_emit_allocs(tsd, &emitter);
2753 	emitter_end(&emitter);
2754 
2755 	/* Reset global state. */
2756 	if (log_tables_initialized) {
2757 		ckh_delete(tsd, &log_bt_node_set);
2758 		ckh_delete(tsd, &log_thr_node_set);
2759 	}
2760 	log_tables_initialized = false;
2761 	log_bt_index = 0;
2762 	log_thr_index = 0;
2763 	log_bt_first = NULL;
2764 	log_bt_last = NULL;
2765 	log_thr_first = NULL;
2766 	log_thr_last = NULL;
2767 	log_alloc_first = NULL;
2768 	log_alloc_last = NULL;
2769 
2770 	malloc_mutex_lock(tsdn, &log_mtx);
2771 	prof_logging_state = prof_logging_state_stopped;
2772 	malloc_mutex_unlock(tsdn, &log_mtx);
2773 
2774 #ifdef JEMALLOC_JET
2775 	if (prof_log_dummy) {
2776 		return false;
2777 	}
2778 #endif
2779 	return close(fd);
2780 }
2781 
2782 const char *
2783 prof_thread_name_get(tsd_t *tsd) {
2784 	prof_tdata_t *tdata;
2785 
2786 	tdata = prof_tdata_get(tsd, true);
2787 	if (tdata == NULL) {
2788 		return "";
2789 	}
2790 	return (tdata->thread_name != NULL ? tdata->thread_name : "");
2791 }
2792 
2793 static char *
2794 prof_thread_name_alloc(tsdn_t *tsdn, const char *thread_name) {
2795 	char *ret;
2796 	size_t size;
2797 
2798 	if (thread_name == NULL) {
2799 		return NULL;
2800 	}
2801 
2802 	size = strlen(thread_name) + 1;
2803 	if (size == 1) {
2804 		return "";
2805 	}
2806 
2807 	ret = iallocztm(tsdn, size, sz_size2index(size), false, NULL, true,
2808 	    arena_get(TSDN_NULL, 0, true), true);
2809 	if (ret == NULL) {
2810 		return NULL;
2811 	}
2812 	memcpy(ret, thread_name, size);
2813 	return ret;
2814 }
2815 
2816 int
2817 prof_thread_name_set(tsd_t *tsd, const char *thread_name) {
2818 	prof_tdata_t *tdata;
2819 	unsigned i;
2820 	char *s;
2821 
2822 	tdata = prof_tdata_get(tsd, true);
2823 	if (tdata == NULL) {
2824 		return EAGAIN;
2825 	}
2826 
2827 	/* Validate input. */
2828 	if (thread_name == NULL) {
2829 		return EFAULT;
2830 	}
2831 	for (i = 0; thread_name[i] != '\0'; i++) {
2832 		char c = thread_name[i];
2833 		if (!isgraph(c) && !isblank(c)) {
2834 			return EFAULT;
2835 		}
2836 	}
2837 
2838 	s = prof_thread_name_alloc(tsd_tsdn(tsd), thread_name);
2839 	if (s == NULL) {
2840 		return EAGAIN;
2841 	}
2842 
2843 	if (tdata->thread_name != NULL) {
2844 		idalloctm(tsd_tsdn(tsd), tdata->thread_name, NULL, NULL, true,
2845 		    true);
2846 		tdata->thread_name = NULL;
2847 	}
2848 	if (strlen(s) > 0) {
2849 		tdata->thread_name = s;
2850 	}
2851 	return 0;
2852 }
2853 
2854 bool
2855 prof_thread_active_get(tsd_t *tsd) {
2856 	prof_tdata_t *tdata;
2857 
2858 	tdata = prof_tdata_get(tsd, true);
2859 	if (tdata == NULL) {
2860 		return false;
2861 	}
2862 	return tdata->active;
2863 }
2864 
2865 bool
2866 prof_thread_active_set(tsd_t *tsd, bool active) {
2867 	prof_tdata_t *tdata;
2868 
2869 	tdata = prof_tdata_get(tsd, true);
2870 	if (tdata == NULL) {
2871 		return true;
2872 	}
2873 	tdata->active = active;
2874 	return false;
2875 }
2876 
2877 bool
2878 prof_thread_active_init_get(tsdn_t *tsdn) {
2879 	bool active_init;
2880 
2881 	malloc_mutex_lock(tsdn, &prof_thread_active_init_mtx);
2882 	active_init = prof_thread_active_init;
2883 	malloc_mutex_unlock(tsdn, &prof_thread_active_init_mtx);
2884 	return active_init;
2885 }
2886 
2887 bool
2888 prof_thread_active_init_set(tsdn_t *tsdn, bool active_init) {
2889 	bool active_init_old;
2890 
2891 	malloc_mutex_lock(tsdn, &prof_thread_active_init_mtx);
2892 	active_init_old = prof_thread_active_init;
2893 	prof_thread_active_init = active_init;
2894 	malloc_mutex_unlock(tsdn, &prof_thread_active_init_mtx);
2895 	return active_init_old;
2896 }
2897 
2898 bool
2899 prof_gdump_get(tsdn_t *tsdn) {
2900 	bool prof_gdump_current;
2901 
2902 	malloc_mutex_lock(tsdn, &prof_gdump_mtx);
2903 	prof_gdump_current = prof_gdump_val;
2904 	malloc_mutex_unlock(tsdn, &prof_gdump_mtx);
2905 	return prof_gdump_current;
2906 }
2907 
2908 bool
2909 prof_gdump_set(tsdn_t *tsdn, bool gdump) {
2910 	bool prof_gdump_old;
2911 
2912 	malloc_mutex_lock(tsdn, &prof_gdump_mtx);
2913 	prof_gdump_old = prof_gdump_val;
2914 	prof_gdump_val = gdump;
2915 	malloc_mutex_unlock(tsdn, &prof_gdump_mtx);
2916 	return prof_gdump_old;
2917 }
2918 
2919 void
2920 prof_boot0(void) {
2921 	cassert(config_prof);
2922 
2923 	memcpy(opt_prof_prefix, PROF_PREFIX_DEFAULT,
2924 	    sizeof(PROF_PREFIX_DEFAULT));
2925 }
2926 
2927 void
2928 prof_boot1(void) {
2929 	cassert(config_prof);
2930 
2931 	/*
2932 	 * opt_prof must be in its final state before any arenas are
2933 	 * initialized, so this function must be executed early.
2934 	 */
2935 
2936 	if (opt_prof_leak && !opt_prof) {
2937 		/*
2938 		 * Enable opt_prof, but in such a way that profiles are never
2939 		 * automatically dumped.
2940 		 */
2941 		opt_prof = true;
2942 		opt_prof_gdump = false;
2943 	} else if (opt_prof) {
2944 		if (opt_lg_prof_interval >= 0) {
2945 			prof_interval = (((uint64_t)1U) <<
2946 			    opt_lg_prof_interval);
2947 		}
2948 	}
2949 }
2950 
2951 bool
2952 prof_boot2(tsd_t *tsd) {
2953 	cassert(config_prof);
2954 
2955 	if (opt_prof) {
2956 		unsigned i;
2957 
2958 		lg_prof_sample = opt_lg_prof_sample;
2959 
2960 		prof_active = opt_prof_active;
2961 		if (malloc_mutex_init(&prof_active_mtx, "prof_active",
2962 		    WITNESS_RANK_PROF_ACTIVE, malloc_mutex_rank_exclusive)) {
2963 			return true;
2964 		}
2965 
2966 		prof_gdump_val = opt_prof_gdump;
2967 		if (malloc_mutex_init(&prof_gdump_mtx, "prof_gdump",
2968 		    WITNESS_RANK_PROF_GDUMP, malloc_mutex_rank_exclusive)) {
2969 			return true;
2970 		}
2971 
2972 		prof_thread_active_init = opt_prof_thread_active_init;
2973 		if (malloc_mutex_init(&prof_thread_active_init_mtx,
2974 		    "prof_thread_active_init",
2975 		    WITNESS_RANK_PROF_THREAD_ACTIVE_INIT,
2976 		    malloc_mutex_rank_exclusive)) {
2977 			return true;
2978 		}
2979 
2980 		if (ckh_new(tsd, &bt2gctx, PROF_CKH_MINITEMS, prof_bt_hash,
2981 		    prof_bt_keycomp)) {
2982 			return true;
2983 		}
2984 		if (malloc_mutex_init(&bt2gctx_mtx, "prof_bt2gctx",
2985 		    WITNESS_RANK_PROF_BT2GCTX, malloc_mutex_rank_exclusive)) {
2986 			return true;
2987 		}
2988 
2989 		tdata_tree_new(&tdatas);
2990 		if (malloc_mutex_init(&tdatas_mtx, "prof_tdatas",
2991 		    WITNESS_RANK_PROF_TDATAS, malloc_mutex_rank_exclusive)) {
2992 			return true;
2993 		}
2994 
2995 		next_thr_uid = 0;
2996 		if (malloc_mutex_init(&next_thr_uid_mtx, "prof_next_thr_uid",
2997 		    WITNESS_RANK_PROF_NEXT_THR_UID, malloc_mutex_rank_exclusive)) {
2998 			return true;
2999 		}
3000 
3001 		if (malloc_mutex_init(&prof_dump_seq_mtx, "prof_dump_seq",
3002 		    WITNESS_RANK_PROF_DUMP_SEQ, malloc_mutex_rank_exclusive)) {
3003 			return true;
3004 		}
3005 		if (malloc_mutex_init(&prof_dump_mtx, "prof_dump",
3006 		    WITNESS_RANK_PROF_DUMP, malloc_mutex_rank_exclusive)) {
3007 			return true;
3008 		}
3009 
3010 		if (opt_prof_final && opt_prof_prefix[0] != '\0' &&
3011 		    atexit(prof_fdump) != 0) {
3012 			malloc_write("<jemalloc>: Error in atexit()\n");
3013 			if (opt_abort) {
3014 				abort();
3015 			}
3016 		}
3017 
3018 		if (opt_prof_log) {
3019 			prof_log_start(tsd_tsdn(tsd), NULL);
3020 		}
3021 
3022 		if (atexit(prof_log_stop_final) != 0) {
3023 			malloc_write("<jemalloc>: Error in atexit() "
3024 				     "for logging\n");
3025 			if (opt_abort) {
3026 				abort();
3027 			}
3028 		}
3029 
3030 		if (malloc_mutex_init(&log_mtx, "prof_log",
3031 		    WITNESS_RANK_PROF_LOG, malloc_mutex_rank_exclusive)) {
3032 			return true;
3033 		}
3034 
3035 		if (ckh_new(tsd, &log_bt_node_set, PROF_CKH_MINITEMS,
3036 		    prof_bt_node_hash, prof_bt_node_keycomp)) {
3037 			return true;
3038 		}
3039 
3040 		if (ckh_new(tsd, &log_thr_node_set, PROF_CKH_MINITEMS,
3041 		    prof_thr_node_hash, prof_thr_node_keycomp)) {
3042 			return true;
3043 		}
3044 
3045 		log_tables_initialized = true;
3046 
3047 		gctx_locks = (malloc_mutex_t *)base_alloc(tsd_tsdn(tsd),
3048 		    b0get(), PROF_NCTX_LOCKS * sizeof(malloc_mutex_t),
3049 		    CACHELINE);
3050 		if (gctx_locks == NULL) {
3051 			return true;
3052 		}
3053 		for (i = 0; i < PROF_NCTX_LOCKS; i++) {
3054 			if (malloc_mutex_init(&gctx_locks[i], "prof_gctx",
3055 			    WITNESS_RANK_PROF_GCTX,
3056 			    malloc_mutex_rank_exclusive)) {
3057 				return true;
3058 			}
3059 		}
3060 
3061 		tdata_locks = (malloc_mutex_t *)base_alloc(tsd_tsdn(tsd),
3062 		    b0get(), PROF_NTDATA_LOCKS * sizeof(malloc_mutex_t),
3063 		    CACHELINE);
3064 		if (tdata_locks == NULL) {
3065 			return true;
3066 		}
3067 		for (i = 0; i < PROF_NTDATA_LOCKS; i++) {
3068 			if (malloc_mutex_init(&tdata_locks[i], "prof_tdata",
3069 			    WITNESS_RANK_PROF_TDATA,
3070 			    malloc_mutex_rank_exclusive)) {
3071 				return true;
3072 			}
3073 		}
3074 #ifdef JEMALLOC_PROF_LIBGCC
3075 		/*
3076 		 * Cause the backtracing machinery to allocate its internal
3077 		 * state before enabling profiling.
3078 		 */
3079 		_Unwind_Backtrace(prof_unwind_init_callback, NULL);
3080 #endif
3081 	}
3082 	prof_booted = true;
3083 
3084 	return false;
3085 }
3086 
3087 void
3088 prof_prefork0(tsdn_t *tsdn) {
3089 	if (config_prof && opt_prof) {
3090 		unsigned i;
3091 
3092 		malloc_mutex_prefork(tsdn, &prof_dump_mtx);
3093 		malloc_mutex_prefork(tsdn, &bt2gctx_mtx);
3094 		malloc_mutex_prefork(tsdn, &tdatas_mtx);
3095 		for (i = 0; i < PROF_NTDATA_LOCKS; i++) {
3096 			malloc_mutex_prefork(tsdn, &tdata_locks[i]);
3097 		}
3098 		for (i = 0; i < PROF_NCTX_LOCKS; i++) {
3099 			malloc_mutex_prefork(tsdn, &gctx_locks[i]);
3100 		}
3101 	}
3102 }
3103 
3104 void
3105 prof_prefork1(tsdn_t *tsdn) {
3106 	if (config_prof && opt_prof) {
3107 		malloc_mutex_prefork(tsdn, &prof_active_mtx);
3108 		malloc_mutex_prefork(tsdn, &prof_dump_seq_mtx);
3109 		malloc_mutex_prefork(tsdn, &prof_gdump_mtx);
3110 		malloc_mutex_prefork(tsdn, &next_thr_uid_mtx);
3111 		malloc_mutex_prefork(tsdn, &prof_thread_active_init_mtx);
3112 	}
3113 }
3114 
3115 void
3116 prof_postfork_parent(tsdn_t *tsdn) {
3117 	if (config_prof && opt_prof) {
3118 		unsigned i;
3119 
3120 		malloc_mutex_postfork_parent(tsdn,
3121 		    &prof_thread_active_init_mtx);
3122 		malloc_mutex_postfork_parent(tsdn, &next_thr_uid_mtx);
3123 		malloc_mutex_postfork_parent(tsdn, &prof_gdump_mtx);
3124 		malloc_mutex_postfork_parent(tsdn, &prof_dump_seq_mtx);
3125 		malloc_mutex_postfork_parent(tsdn, &prof_active_mtx);
3126 		for (i = 0; i < PROF_NCTX_LOCKS; i++) {
3127 			malloc_mutex_postfork_parent(tsdn, &gctx_locks[i]);
3128 		}
3129 		for (i = 0; i < PROF_NTDATA_LOCKS; i++) {
3130 			malloc_mutex_postfork_parent(tsdn, &tdata_locks[i]);
3131 		}
3132 		malloc_mutex_postfork_parent(tsdn, &tdatas_mtx);
3133 		malloc_mutex_postfork_parent(tsdn, &bt2gctx_mtx);
3134 		malloc_mutex_postfork_parent(tsdn, &prof_dump_mtx);
3135 	}
3136 }
3137 
3138 void
3139 prof_postfork_child(tsdn_t *tsdn) {
3140 	if (config_prof && opt_prof) {
3141 		unsigned i;
3142 
3143 		malloc_mutex_postfork_child(tsdn, &prof_thread_active_init_mtx);
3144 		malloc_mutex_postfork_child(tsdn, &next_thr_uid_mtx);
3145 		malloc_mutex_postfork_child(tsdn, &prof_gdump_mtx);
3146 		malloc_mutex_postfork_child(tsdn, &prof_dump_seq_mtx);
3147 		malloc_mutex_postfork_child(tsdn, &prof_active_mtx);
3148 		for (i = 0; i < PROF_NCTX_LOCKS; i++) {
3149 			malloc_mutex_postfork_child(tsdn, &gctx_locks[i]);
3150 		}
3151 		for (i = 0; i < PROF_NTDATA_LOCKS; i++) {
3152 			malloc_mutex_postfork_child(tsdn, &tdata_locks[i]);
3153 		}
3154 		malloc_mutex_postfork_child(tsdn, &tdatas_mtx);
3155 		malloc_mutex_postfork_child(tsdn, &bt2gctx_mtx);
3156 		malloc_mutex_postfork_child(tsdn, &prof_dump_mtx);
3157 	}
3158 }
3159 
3160 /******************************************************************************/
3161