xref: /freebsd/contrib/jemalloc/src/prof.c (revision 8df8b2d3e51d1b816201d8a1fe8bc29fe192e562)
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 
11 /******************************************************************************/
12 
13 #ifdef JEMALLOC_PROF_LIBUNWIND
14 #define UNW_LOCAL_ONLY
15 #include <libunwind.h>
16 #endif
17 
18 #ifdef JEMALLOC_PROF_LIBGCC
19 /*
20  * We have a circular dependency -- jemalloc_internal.h tells us if we should
21  * use libgcc's unwinding functionality, but after we've included that, we've
22  * already hooked _Unwind_Backtrace.  We'll temporarily disable hooking.
23  */
24 #undef _Unwind_Backtrace
25 #include <unwind.h>
26 #define _Unwind_Backtrace JEMALLOC_HOOK(_Unwind_Backtrace, hooks_libc_hook)
27 #endif
28 
29 /******************************************************************************/
30 /* Data. */
31 
32 bool		opt_prof = false;
33 bool		opt_prof_active = true;
34 bool		opt_prof_thread_active_init = true;
35 size_t		opt_lg_prof_sample = LG_PROF_SAMPLE_DEFAULT;
36 ssize_t		opt_lg_prof_interval = LG_PROF_INTERVAL_DEFAULT;
37 bool		opt_prof_gdump = false;
38 bool		opt_prof_final = false;
39 bool		opt_prof_leak = false;
40 bool		opt_prof_accum = false;
41 char		opt_prof_prefix[
42     /* Minimize memory bloat for non-prof builds. */
43 #ifdef JEMALLOC_PROF
44     PATH_MAX +
45 #endif
46     1];
47 
48 /*
49  * Initialized as opt_prof_active, and accessed via
50  * prof_active_[gs]et{_unlocked,}().
51  */
52 bool			prof_active;
53 static malloc_mutex_t	prof_active_mtx;
54 
55 /*
56  * Initialized as opt_prof_thread_active_init, and accessed via
57  * prof_thread_active_init_[gs]et().
58  */
59 static bool		prof_thread_active_init;
60 static malloc_mutex_t	prof_thread_active_init_mtx;
61 
62 /*
63  * Initialized as opt_prof_gdump, and accessed via
64  * prof_gdump_[gs]et{_unlocked,}().
65  */
66 bool			prof_gdump_val;
67 static malloc_mutex_t	prof_gdump_mtx;
68 
69 uint64_t	prof_interval = 0;
70 
71 size_t		lg_prof_sample;
72 
73 /*
74  * Table of mutexes that are shared among gctx's.  These are leaf locks, so
75  * there is no problem with using them for more than one gctx at the same time.
76  * The primary motivation for this sharing though is that gctx's are ephemeral,
77  * and destroying mutexes causes complications for systems that allocate when
78  * creating/destroying mutexes.
79  */
80 static malloc_mutex_t	*gctx_locks;
81 static atomic_u_t	cum_gctxs; /* Atomic counter. */
82 
83 /*
84  * Table of mutexes that are shared among tdata's.  No operations require
85  * holding multiple tdata locks, so there is no problem with using them for more
86  * than one tdata at the same time, even though a gctx lock may be acquired
87  * while holding a tdata lock.
88  */
89 static malloc_mutex_t	*tdata_locks;
90 
91 /*
92  * Global hash of (prof_bt_t *)-->(prof_gctx_t *).  This is the master data
93  * structure that knows about all backtraces currently captured.
94  */
95 static ckh_t		bt2gctx;
96 /* Non static to enable profiling. */
97 malloc_mutex_t		bt2gctx_mtx;
98 
99 /*
100  * Tree of all extant prof_tdata_t structures, regardless of state,
101  * {attached,detached,expired}.
102  */
103 static prof_tdata_tree_t	tdatas;
104 static malloc_mutex_t	tdatas_mtx;
105 
106 static uint64_t		next_thr_uid;
107 static malloc_mutex_t	next_thr_uid_mtx;
108 
109 static malloc_mutex_t	prof_dump_seq_mtx;
110 static uint64_t		prof_dump_seq;
111 static uint64_t		prof_dump_iseq;
112 static uint64_t		prof_dump_mseq;
113 static uint64_t		prof_dump_useq;
114 
115 /*
116  * This buffer is rather large for stack allocation, so use a single buffer for
117  * all profile dumps.
118  */
119 static malloc_mutex_t	prof_dump_mtx;
120 static char		prof_dump_buf[
121     /* Minimize memory bloat for non-prof builds. */
122 #ifdef JEMALLOC_PROF
123     PROF_DUMP_BUFSIZE
124 #else
125     1
126 #endif
127 ];
128 static size_t		prof_dump_buf_end;
129 static int		prof_dump_fd;
130 
131 /* Do not dump any profiles until bootstrapping is complete. */
132 static bool		prof_booted = false;
133 
134 /******************************************************************************/
135 /*
136  * Function prototypes for static functions that are referenced prior to
137  * definition.
138  */
139 
140 static bool	prof_tctx_should_destroy(tsdn_t *tsdn, prof_tctx_t *tctx);
141 static void	prof_tctx_destroy(tsd_t *tsd, prof_tctx_t *tctx);
142 static bool	prof_tdata_should_destroy(tsdn_t *tsdn, prof_tdata_t *tdata,
143     bool even_if_attached);
144 static void	prof_tdata_destroy(tsd_t *tsd, prof_tdata_t *tdata,
145     bool even_if_attached);
146 static char	*prof_thread_name_alloc(tsdn_t *tsdn, const char *thread_name);
147 
148 /******************************************************************************/
149 /* Red-black trees. */
150 
151 static int
152 prof_tctx_comp(const prof_tctx_t *a, const prof_tctx_t *b) {
153 	uint64_t a_thr_uid = a->thr_uid;
154 	uint64_t b_thr_uid = b->thr_uid;
155 	int ret = (a_thr_uid > b_thr_uid) - (a_thr_uid < b_thr_uid);
156 	if (ret == 0) {
157 		uint64_t a_thr_discrim = a->thr_discrim;
158 		uint64_t b_thr_discrim = b->thr_discrim;
159 		ret = (a_thr_discrim > b_thr_discrim) - (a_thr_discrim <
160 		    b_thr_discrim);
161 		if (ret == 0) {
162 			uint64_t a_tctx_uid = a->tctx_uid;
163 			uint64_t b_tctx_uid = b->tctx_uid;
164 			ret = (a_tctx_uid > b_tctx_uid) - (a_tctx_uid <
165 			    b_tctx_uid);
166 		}
167 	}
168 	return ret;
169 }
170 
171 rb_gen(static UNUSED, tctx_tree_, prof_tctx_tree_t, prof_tctx_t,
172     tctx_link, prof_tctx_comp)
173 
174 static int
175 prof_gctx_comp(const prof_gctx_t *a, const prof_gctx_t *b) {
176 	unsigned a_len = a->bt.len;
177 	unsigned b_len = b->bt.len;
178 	unsigned comp_len = (a_len < b_len) ? a_len : b_len;
179 	int ret = memcmp(a->bt.vec, b->bt.vec, comp_len * sizeof(void *));
180 	if (ret == 0) {
181 		ret = (a_len > b_len) - (a_len < b_len);
182 	}
183 	return ret;
184 }
185 
186 rb_gen(static UNUSED, gctx_tree_, prof_gctx_tree_t, prof_gctx_t, dump_link,
187     prof_gctx_comp)
188 
189 static int
190 prof_tdata_comp(const prof_tdata_t *a, const prof_tdata_t *b) {
191 	int ret;
192 	uint64_t a_uid = a->thr_uid;
193 	uint64_t b_uid = b->thr_uid;
194 
195 	ret = ((a_uid > b_uid) - (a_uid < b_uid));
196 	if (ret == 0) {
197 		uint64_t a_discrim = a->thr_discrim;
198 		uint64_t b_discrim = b->thr_discrim;
199 
200 		ret = ((a_discrim > b_discrim) - (a_discrim < b_discrim));
201 	}
202 	return ret;
203 }
204 
205 rb_gen(static UNUSED, tdata_tree_, prof_tdata_tree_t, prof_tdata_t, tdata_link,
206     prof_tdata_comp)
207 
208 /******************************************************************************/
209 
210 void
211 prof_alloc_rollback(tsd_t *tsd, prof_tctx_t *tctx, bool updated) {
212 	prof_tdata_t *tdata;
213 
214 	cassert(config_prof);
215 
216 	if (updated) {
217 		/*
218 		 * Compute a new sample threshold.  This isn't very important in
219 		 * practice, because this function is rarely executed, so the
220 		 * potential for sample bias is minimal except in contrived
221 		 * programs.
222 		 */
223 		tdata = prof_tdata_get(tsd, true);
224 		if (tdata != NULL) {
225 			prof_sample_threshold_update(tdata);
226 		}
227 	}
228 
229 	if ((uintptr_t)tctx > (uintptr_t)1U) {
230 		malloc_mutex_lock(tsd_tsdn(tsd), tctx->tdata->lock);
231 		tctx->prepared = false;
232 		if (prof_tctx_should_destroy(tsd_tsdn(tsd), tctx)) {
233 			prof_tctx_destroy(tsd, tctx);
234 		} else {
235 			malloc_mutex_unlock(tsd_tsdn(tsd), tctx->tdata->lock);
236 		}
237 	}
238 }
239 
240 void
241 prof_malloc_sample_object(tsdn_t *tsdn, const void *ptr, size_t usize,
242     prof_tctx_t *tctx) {
243 	prof_tctx_set(tsdn, ptr, usize, NULL, tctx);
244 
245 	malloc_mutex_lock(tsdn, tctx->tdata->lock);
246 	tctx->cnts.curobjs++;
247 	tctx->cnts.curbytes += usize;
248 	if (opt_prof_accum) {
249 		tctx->cnts.accumobjs++;
250 		tctx->cnts.accumbytes += usize;
251 	}
252 	tctx->prepared = false;
253 	malloc_mutex_unlock(tsdn, tctx->tdata->lock);
254 }
255 
256 void
257 prof_free_sampled_object(tsd_t *tsd, size_t usize, prof_tctx_t *tctx) {
258 	malloc_mutex_lock(tsd_tsdn(tsd), tctx->tdata->lock);
259 	assert(tctx->cnts.curobjs > 0);
260 	assert(tctx->cnts.curbytes >= usize);
261 	tctx->cnts.curobjs--;
262 	tctx->cnts.curbytes -= usize;
263 
264 	if (prof_tctx_should_destroy(tsd_tsdn(tsd), tctx)) {
265 		prof_tctx_destroy(tsd, tctx);
266 	} else {
267 		malloc_mutex_unlock(tsd_tsdn(tsd), tctx->tdata->lock);
268 	}
269 }
270 
271 void
272 bt_init(prof_bt_t *bt, void **vec) {
273 	cassert(config_prof);
274 
275 	bt->vec = vec;
276 	bt->len = 0;
277 }
278 
279 static void
280 prof_enter(tsd_t *tsd, prof_tdata_t *tdata) {
281 	cassert(config_prof);
282 	assert(tdata == prof_tdata_get(tsd, false));
283 
284 	if (tdata != NULL) {
285 		assert(!tdata->enq);
286 		tdata->enq = true;
287 	}
288 
289 	malloc_mutex_lock(tsd_tsdn(tsd), &bt2gctx_mtx);
290 }
291 
292 static void
293 prof_leave(tsd_t *tsd, prof_tdata_t *tdata) {
294 	cassert(config_prof);
295 	assert(tdata == prof_tdata_get(tsd, false));
296 
297 	malloc_mutex_unlock(tsd_tsdn(tsd), &bt2gctx_mtx);
298 
299 	if (tdata != NULL) {
300 		bool idump, gdump;
301 
302 		assert(tdata->enq);
303 		tdata->enq = false;
304 		idump = tdata->enq_idump;
305 		tdata->enq_idump = false;
306 		gdump = tdata->enq_gdump;
307 		tdata->enq_gdump = false;
308 
309 		if (idump) {
310 			prof_idump(tsd_tsdn(tsd));
311 		}
312 		if (gdump) {
313 			prof_gdump(tsd_tsdn(tsd));
314 		}
315 	}
316 }
317 
318 #ifdef JEMALLOC_PROF_LIBUNWIND
319 void
320 prof_backtrace(prof_bt_t *bt) {
321 	int nframes;
322 
323 	cassert(config_prof);
324 	assert(bt->len == 0);
325 	assert(bt->vec != NULL);
326 
327 	nframes = unw_backtrace(bt->vec, PROF_BT_MAX);
328 	if (nframes <= 0) {
329 		return;
330 	}
331 	bt->len = nframes;
332 }
333 #elif (defined(JEMALLOC_PROF_LIBGCC))
334 static _Unwind_Reason_Code
335 prof_unwind_init_callback(struct _Unwind_Context *context, void *arg) {
336 	cassert(config_prof);
337 
338 	return _URC_NO_REASON;
339 }
340 
341 static _Unwind_Reason_Code
342 prof_unwind_callback(struct _Unwind_Context *context, void *arg) {
343 	prof_unwind_data_t *data = (prof_unwind_data_t *)arg;
344 	void *ip;
345 
346 	cassert(config_prof);
347 
348 	ip = (void *)_Unwind_GetIP(context);
349 	if (ip == NULL) {
350 		return _URC_END_OF_STACK;
351 	}
352 	data->bt->vec[data->bt->len] = ip;
353 	data->bt->len++;
354 	if (data->bt->len == data->max) {
355 		return _URC_END_OF_STACK;
356 	}
357 
358 	return _URC_NO_REASON;
359 }
360 
361 void
362 prof_backtrace(prof_bt_t *bt) {
363 	prof_unwind_data_t data = {bt, PROF_BT_MAX};
364 
365 	cassert(config_prof);
366 
367 	_Unwind_Backtrace(prof_unwind_callback, &data);
368 }
369 #elif (defined(JEMALLOC_PROF_GCC))
370 void
371 prof_backtrace(prof_bt_t *bt) {
372 #define BT_FRAME(i)							\
373 	if ((i) < PROF_BT_MAX) {					\
374 		void *p;						\
375 		if (__builtin_frame_address(i) == 0) {			\
376 			return;						\
377 		}							\
378 		p = __builtin_return_address(i);			\
379 		if (p == NULL) {					\
380 			return;						\
381 		}							\
382 		bt->vec[(i)] = p;					\
383 		bt->len = (i) + 1;					\
384 	} else {							\
385 		return;							\
386 	}
387 
388 	cassert(config_prof);
389 
390 	BT_FRAME(0)
391 	BT_FRAME(1)
392 	BT_FRAME(2)
393 	BT_FRAME(3)
394 	BT_FRAME(4)
395 	BT_FRAME(5)
396 	BT_FRAME(6)
397 	BT_FRAME(7)
398 	BT_FRAME(8)
399 	BT_FRAME(9)
400 
401 	BT_FRAME(10)
402 	BT_FRAME(11)
403 	BT_FRAME(12)
404 	BT_FRAME(13)
405 	BT_FRAME(14)
406 	BT_FRAME(15)
407 	BT_FRAME(16)
408 	BT_FRAME(17)
409 	BT_FRAME(18)
410 	BT_FRAME(19)
411 
412 	BT_FRAME(20)
413 	BT_FRAME(21)
414 	BT_FRAME(22)
415 	BT_FRAME(23)
416 	BT_FRAME(24)
417 	BT_FRAME(25)
418 	BT_FRAME(26)
419 	BT_FRAME(27)
420 	BT_FRAME(28)
421 	BT_FRAME(29)
422 
423 	BT_FRAME(30)
424 	BT_FRAME(31)
425 	BT_FRAME(32)
426 	BT_FRAME(33)
427 	BT_FRAME(34)
428 	BT_FRAME(35)
429 	BT_FRAME(36)
430 	BT_FRAME(37)
431 	BT_FRAME(38)
432 	BT_FRAME(39)
433 
434 	BT_FRAME(40)
435 	BT_FRAME(41)
436 	BT_FRAME(42)
437 	BT_FRAME(43)
438 	BT_FRAME(44)
439 	BT_FRAME(45)
440 	BT_FRAME(46)
441 	BT_FRAME(47)
442 	BT_FRAME(48)
443 	BT_FRAME(49)
444 
445 	BT_FRAME(50)
446 	BT_FRAME(51)
447 	BT_FRAME(52)
448 	BT_FRAME(53)
449 	BT_FRAME(54)
450 	BT_FRAME(55)
451 	BT_FRAME(56)
452 	BT_FRAME(57)
453 	BT_FRAME(58)
454 	BT_FRAME(59)
455 
456 	BT_FRAME(60)
457 	BT_FRAME(61)
458 	BT_FRAME(62)
459 	BT_FRAME(63)
460 	BT_FRAME(64)
461 	BT_FRAME(65)
462 	BT_FRAME(66)
463 	BT_FRAME(67)
464 	BT_FRAME(68)
465 	BT_FRAME(69)
466 
467 	BT_FRAME(70)
468 	BT_FRAME(71)
469 	BT_FRAME(72)
470 	BT_FRAME(73)
471 	BT_FRAME(74)
472 	BT_FRAME(75)
473 	BT_FRAME(76)
474 	BT_FRAME(77)
475 	BT_FRAME(78)
476 	BT_FRAME(79)
477 
478 	BT_FRAME(80)
479 	BT_FRAME(81)
480 	BT_FRAME(82)
481 	BT_FRAME(83)
482 	BT_FRAME(84)
483 	BT_FRAME(85)
484 	BT_FRAME(86)
485 	BT_FRAME(87)
486 	BT_FRAME(88)
487 	BT_FRAME(89)
488 
489 	BT_FRAME(90)
490 	BT_FRAME(91)
491 	BT_FRAME(92)
492 	BT_FRAME(93)
493 	BT_FRAME(94)
494 	BT_FRAME(95)
495 	BT_FRAME(96)
496 	BT_FRAME(97)
497 	BT_FRAME(98)
498 	BT_FRAME(99)
499 
500 	BT_FRAME(100)
501 	BT_FRAME(101)
502 	BT_FRAME(102)
503 	BT_FRAME(103)
504 	BT_FRAME(104)
505 	BT_FRAME(105)
506 	BT_FRAME(106)
507 	BT_FRAME(107)
508 	BT_FRAME(108)
509 	BT_FRAME(109)
510 
511 	BT_FRAME(110)
512 	BT_FRAME(111)
513 	BT_FRAME(112)
514 	BT_FRAME(113)
515 	BT_FRAME(114)
516 	BT_FRAME(115)
517 	BT_FRAME(116)
518 	BT_FRAME(117)
519 	BT_FRAME(118)
520 	BT_FRAME(119)
521 
522 	BT_FRAME(120)
523 	BT_FRAME(121)
524 	BT_FRAME(122)
525 	BT_FRAME(123)
526 	BT_FRAME(124)
527 	BT_FRAME(125)
528 	BT_FRAME(126)
529 	BT_FRAME(127)
530 #undef BT_FRAME
531 }
532 #else
533 void
534 prof_backtrace(prof_bt_t *bt) {
535 	cassert(config_prof);
536 	not_reached();
537 }
538 #endif
539 
540 static malloc_mutex_t *
541 prof_gctx_mutex_choose(void) {
542 	unsigned ngctxs = atomic_fetch_add_u(&cum_gctxs, 1, ATOMIC_RELAXED);
543 
544 	return &gctx_locks[(ngctxs - 1) % PROF_NCTX_LOCKS];
545 }
546 
547 static malloc_mutex_t *
548 prof_tdata_mutex_choose(uint64_t thr_uid) {
549 	return &tdata_locks[thr_uid % PROF_NTDATA_LOCKS];
550 }
551 
552 static prof_gctx_t *
553 prof_gctx_create(tsdn_t *tsdn, prof_bt_t *bt) {
554 	/*
555 	 * Create a single allocation that has space for vec of length bt->len.
556 	 */
557 	size_t size = offsetof(prof_gctx_t, vec) + (bt->len * sizeof(void *));
558 	prof_gctx_t *gctx = (prof_gctx_t *)iallocztm(tsdn, size,
559 	    sz_size2index(size), false, NULL, true, arena_get(TSDN_NULL, 0, true),
560 	    true);
561 	if (gctx == NULL) {
562 		return NULL;
563 	}
564 	gctx->lock = prof_gctx_mutex_choose();
565 	/*
566 	 * Set nlimbo to 1, in order to avoid a race condition with
567 	 * prof_tctx_destroy()/prof_gctx_try_destroy().
568 	 */
569 	gctx->nlimbo = 1;
570 	tctx_tree_new(&gctx->tctxs);
571 	/* Duplicate bt. */
572 	memcpy(gctx->vec, bt->vec, bt->len * sizeof(void *));
573 	gctx->bt.vec = gctx->vec;
574 	gctx->bt.len = bt->len;
575 	return gctx;
576 }
577 
578 static void
579 prof_gctx_try_destroy(tsd_t *tsd, prof_tdata_t *tdata_self, prof_gctx_t *gctx,
580     prof_tdata_t *tdata) {
581 	cassert(config_prof);
582 
583 	/*
584 	 * Check that gctx is still unused by any thread cache before destroying
585 	 * it.  prof_lookup() increments gctx->nlimbo in order to avoid a race
586 	 * condition with this function, as does prof_tctx_destroy() in order to
587 	 * avoid a race between the main body of prof_tctx_destroy() and entry
588 	 * into this function.
589 	 */
590 	prof_enter(tsd, tdata_self);
591 	malloc_mutex_lock(tsd_tsdn(tsd), gctx->lock);
592 	assert(gctx->nlimbo != 0);
593 	if (tctx_tree_empty(&gctx->tctxs) && gctx->nlimbo == 1) {
594 		/* Remove gctx from bt2gctx. */
595 		if (ckh_remove(tsd, &bt2gctx, &gctx->bt, NULL, NULL)) {
596 			not_reached();
597 		}
598 		prof_leave(tsd, tdata_self);
599 		/* Destroy gctx. */
600 		malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);
601 		idalloctm(tsd_tsdn(tsd), gctx, NULL, NULL, true, true);
602 	} else {
603 		/*
604 		 * Compensate for increment in prof_tctx_destroy() or
605 		 * prof_lookup().
606 		 */
607 		gctx->nlimbo--;
608 		malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);
609 		prof_leave(tsd, tdata_self);
610 	}
611 }
612 
613 static bool
614 prof_tctx_should_destroy(tsdn_t *tsdn, prof_tctx_t *tctx) {
615 	malloc_mutex_assert_owner(tsdn, tctx->tdata->lock);
616 
617 	if (opt_prof_accum) {
618 		return false;
619 	}
620 	if (tctx->cnts.curobjs != 0) {
621 		return false;
622 	}
623 	if (tctx->prepared) {
624 		return false;
625 	}
626 	return true;
627 }
628 
629 static bool
630 prof_gctx_should_destroy(prof_gctx_t *gctx) {
631 	if (opt_prof_accum) {
632 		return false;
633 	}
634 	if (!tctx_tree_empty(&gctx->tctxs)) {
635 		return false;
636 	}
637 	if (gctx->nlimbo != 0) {
638 		return false;
639 	}
640 	return true;
641 }
642 
643 static void
644 prof_tctx_destroy(tsd_t *tsd, prof_tctx_t *tctx) {
645 	prof_tdata_t *tdata = tctx->tdata;
646 	prof_gctx_t *gctx = tctx->gctx;
647 	bool destroy_tdata, destroy_tctx, destroy_gctx;
648 
649 	malloc_mutex_assert_owner(tsd_tsdn(tsd), tctx->tdata->lock);
650 
651 	assert(tctx->cnts.curobjs == 0);
652 	assert(tctx->cnts.curbytes == 0);
653 	assert(!opt_prof_accum);
654 	assert(tctx->cnts.accumobjs == 0);
655 	assert(tctx->cnts.accumbytes == 0);
656 
657 	ckh_remove(tsd, &tdata->bt2tctx, &gctx->bt, NULL, NULL);
658 	destroy_tdata = prof_tdata_should_destroy(tsd_tsdn(tsd), tdata, false);
659 	malloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock);
660 
661 	malloc_mutex_lock(tsd_tsdn(tsd), gctx->lock);
662 	switch (tctx->state) {
663 	case prof_tctx_state_nominal:
664 		tctx_tree_remove(&gctx->tctxs, tctx);
665 		destroy_tctx = true;
666 		if (prof_gctx_should_destroy(gctx)) {
667 			/*
668 			 * Increment gctx->nlimbo in order to keep another
669 			 * thread from winning the race to destroy gctx while
670 			 * this one has gctx->lock dropped.  Without this, it
671 			 * would be possible for another thread to:
672 			 *
673 			 * 1) Sample an allocation associated with gctx.
674 			 * 2) Deallocate the sampled object.
675 			 * 3) Successfully prof_gctx_try_destroy(gctx).
676 			 *
677 			 * The result would be that gctx no longer exists by the
678 			 * time this thread accesses it in
679 			 * prof_gctx_try_destroy().
680 			 */
681 			gctx->nlimbo++;
682 			destroy_gctx = true;
683 		} else {
684 			destroy_gctx = false;
685 		}
686 		break;
687 	case prof_tctx_state_dumping:
688 		/*
689 		 * A dumping thread needs tctx to remain valid until dumping
690 		 * has finished.  Change state such that the dumping thread will
691 		 * complete destruction during a late dump iteration phase.
692 		 */
693 		tctx->state = prof_tctx_state_purgatory;
694 		destroy_tctx = false;
695 		destroy_gctx = false;
696 		break;
697 	default:
698 		not_reached();
699 		destroy_tctx = false;
700 		destroy_gctx = false;
701 	}
702 	malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);
703 	if (destroy_gctx) {
704 		prof_gctx_try_destroy(tsd, prof_tdata_get(tsd, false), gctx,
705 		    tdata);
706 	}
707 
708 	malloc_mutex_assert_not_owner(tsd_tsdn(tsd), tctx->tdata->lock);
709 
710 	if (destroy_tdata) {
711 		prof_tdata_destroy(tsd, tdata, false);
712 	}
713 
714 	if (destroy_tctx) {
715 		idalloctm(tsd_tsdn(tsd), tctx, NULL, NULL, true, true);
716 	}
717 }
718 
719 static bool
720 prof_lookup_global(tsd_t *tsd, prof_bt_t *bt, prof_tdata_t *tdata,
721     void **p_btkey, prof_gctx_t **p_gctx, bool *p_new_gctx) {
722 	union {
723 		prof_gctx_t	*p;
724 		void		*v;
725 	} gctx, tgctx;
726 	union {
727 		prof_bt_t	*p;
728 		void		*v;
729 	} btkey;
730 	bool new_gctx;
731 
732 	prof_enter(tsd, tdata);
733 	if (ckh_search(&bt2gctx, bt, &btkey.v, &gctx.v)) {
734 		/* bt has never been seen before.  Insert it. */
735 		prof_leave(tsd, tdata);
736 		tgctx.p = prof_gctx_create(tsd_tsdn(tsd), bt);
737 		if (tgctx.v == NULL) {
738 			return true;
739 		}
740 		prof_enter(tsd, tdata);
741 		if (ckh_search(&bt2gctx, bt, &btkey.v, &gctx.v)) {
742 			gctx.p = tgctx.p;
743 			btkey.p = &gctx.p->bt;
744 			if (ckh_insert(tsd, &bt2gctx, btkey.v, gctx.v)) {
745 				/* OOM. */
746 				prof_leave(tsd, tdata);
747 				idalloctm(tsd_tsdn(tsd), gctx.v, NULL, NULL,
748 				    true, true);
749 				return true;
750 			}
751 			new_gctx = true;
752 		} else {
753 			new_gctx = false;
754 		}
755 	} else {
756 		tgctx.v = NULL;
757 		new_gctx = false;
758 	}
759 
760 	if (!new_gctx) {
761 		/*
762 		 * Increment nlimbo, in order to avoid a race condition with
763 		 * prof_tctx_destroy()/prof_gctx_try_destroy().
764 		 */
765 		malloc_mutex_lock(tsd_tsdn(tsd), gctx.p->lock);
766 		gctx.p->nlimbo++;
767 		malloc_mutex_unlock(tsd_tsdn(tsd), gctx.p->lock);
768 		new_gctx = false;
769 
770 		if (tgctx.v != NULL) {
771 			/* Lost race to insert. */
772 			idalloctm(tsd_tsdn(tsd), tgctx.v, NULL, NULL, true,
773 			    true);
774 		}
775 	}
776 	prof_leave(tsd, tdata);
777 
778 	*p_btkey = btkey.v;
779 	*p_gctx = gctx.p;
780 	*p_new_gctx = new_gctx;
781 	return false;
782 }
783 
784 prof_tctx_t *
785 prof_lookup(tsd_t *tsd, prof_bt_t *bt) {
786 	union {
787 		prof_tctx_t	*p;
788 		void		*v;
789 	} ret;
790 	prof_tdata_t *tdata;
791 	bool not_found;
792 
793 	cassert(config_prof);
794 
795 	tdata = prof_tdata_get(tsd, false);
796 	if (tdata == NULL) {
797 		return NULL;
798 	}
799 
800 	malloc_mutex_lock(tsd_tsdn(tsd), tdata->lock);
801 	not_found = ckh_search(&tdata->bt2tctx, bt, NULL, &ret.v);
802 	if (!not_found) { /* Note double negative! */
803 		ret.p->prepared = true;
804 	}
805 	malloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock);
806 	if (not_found) {
807 		void *btkey;
808 		prof_gctx_t *gctx;
809 		bool new_gctx, error;
810 
811 		/*
812 		 * This thread's cache lacks bt.  Look for it in the global
813 		 * cache.
814 		 */
815 		if (prof_lookup_global(tsd, bt, tdata, &btkey, &gctx,
816 		    &new_gctx)) {
817 			return NULL;
818 		}
819 
820 		/* Link a prof_tctx_t into gctx for this thread. */
821 		ret.v = iallocztm(tsd_tsdn(tsd), sizeof(prof_tctx_t),
822 		    sz_size2index(sizeof(prof_tctx_t)), false, NULL, true,
823 		    arena_ichoose(tsd, NULL), true);
824 		if (ret.p == NULL) {
825 			if (new_gctx) {
826 				prof_gctx_try_destroy(tsd, tdata, gctx, tdata);
827 			}
828 			return NULL;
829 		}
830 		ret.p->tdata = tdata;
831 		ret.p->thr_uid = tdata->thr_uid;
832 		ret.p->thr_discrim = tdata->thr_discrim;
833 		memset(&ret.p->cnts, 0, sizeof(prof_cnt_t));
834 		ret.p->gctx = gctx;
835 		ret.p->tctx_uid = tdata->tctx_uid_next++;
836 		ret.p->prepared = true;
837 		ret.p->state = prof_tctx_state_initializing;
838 		malloc_mutex_lock(tsd_tsdn(tsd), tdata->lock);
839 		error = ckh_insert(tsd, &tdata->bt2tctx, btkey, ret.v);
840 		malloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock);
841 		if (error) {
842 			if (new_gctx) {
843 				prof_gctx_try_destroy(tsd, tdata, gctx, tdata);
844 			}
845 			idalloctm(tsd_tsdn(tsd), ret.v, NULL, NULL, true, true);
846 			return NULL;
847 		}
848 		malloc_mutex_lock(tsd_tsdn(tsd), gctx->lock);
849 		ret.p->state = prof_tctx_state_nominal;
850 		tctx_tree_insert(&gctx->tctxs, ret.p);
851 		gctx->nlimbo--;
852 		malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);
853 	}
854 
855 	return ret.p;
856 }
857 
858 /*
859  * The bodies of this function and prof_leakcheck() are compiled out unless heap
860  * profiling is enabled, so that it is possible to compile jemalloc with
861  * floating point support completely disabled.  Avoiding floating point code is
862  * important on memory-constrained systems, but it also enables a workaround for
863  * versions of glibc that don't properly save/restore floating point registers
864  * during dynamic lazy symbol loading (which internally calls into whatever
865  * malloc implementation happens to be integrated into the application).  Note
866  * that some compilers (e.g.  gcc 4.8) may use floating point registers for fast
867  * memory moves, so jemalloc must be compiled with such optimizations disabled
868  * (e.g.
869  * -mno-sse) in order for the workaround to be complete.
870  */
871 void
872 prof_sample_threshold_update(prof_tdata_t *tdata) {
873 #ifdef JEMALLOC_PROF
874 	uint64_t r;
875 	double u;
876 
877 	if (!config_prof) {
878 		return;
879 	}
880 
881 	if (lg_prof_sample == 0) {
882 		tdata->bytes_until_sample = 0;
883 		return;
884 	}
885 
886 	/*
887 	 * Compute sample interval as a geometrically distributed random
888 	 * variable with mean (2^lg_prof_sample).
889 	 *
890 	 *                             __        __
891 	 *                             |  log(u)  |                     1
892 	 * tdata->bytes_until_sample = | -------- |, where p = ---------------
893 	 *                             | log(1-p) |             lg_prof_sample
894 	 *                                                     2
895 	 *
896 	 * For more information on the math, see:
897 	 *
898 	 *   Non-Uniform Random Variate Generation
899 	 *   Luc Devroye
900 	 *   Springer-Verlag, New York, 1986
901 	 *   pp 500
902 	 *   (http://luc.devroye.org/rnbookindex.html)
903 	 */
904 	r = prng_lg_range_u64(&tdata->prng_state, 53);
905 	u = (double)r * (1.0/9007199254740992.0L);
906 	tdata->bytes_until_sample = (uint64_t)(log(u) /
907 	    log(1.0 - (1.0 / (double)((uint64_t)1U << lg_prof_sample))))
908 	    + (uint64_t)1U;
909 #endif
910 }
911 
912 #ifdef JEMALLOC_JET
913 static prof_tdata_t *
914 prof_tdata_count_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata,
915     void *arg) {
916 	size_t *tdata_count = (size_t *)arg;
917 
918 	(*tdata_count)++;
919 
920 	return NULL;
921 }
922 
923 size_t
924 prof_tdata_count(void) {
925 	size_t tdata_count = 0;
926 	tsdn_t *tsdn;
927 
928 	tsdn = tsdn_fetch();
929 	malloc_mutex_lock(tsdn, &tdatas_mtx);
930 	tdata_tree_iter(&tdatas, NULL, prof_tdata_count_iter,
931 	    (void *)&tdata_count);
932 	malloc_mutex_unlock(tsdn, &tdatas_mtx);
933 
934 	return tdata_count;
935 }
936 
937 size_t
938 prof_bt_count(void) {
939 	size_t bt_count;
940 	tsd_t *tsd;
941 	prof_tdata_t *tdata;
942 
943 	tsd = tsd_fetch();
944 	tdata = prof_tdata_get(tsd, false);
945 	if (tdata == NULL) {
946 		return 0;
947 	}
948 
949 	malloc_mutex_lock(tsd_tsdn(tsd), &bt2gctx_mtx);
950 	bt_count = ckh_count(&bt2gctx);
951 	malloc_mutex_unlock(tsd_tsdn(tsd), &bt2gctx_mtx);
952 
953 	return bt_count;
954 }
955 #endif
956 
957 static int
958 prof_dump_open_impl(bool propagate_err, const char *filename) {
959 	int fd;
960 
961 	fd = creat(filename, 0644);
962 	if (fd == -1 && !propagate_err) {
963 		malloc_printf("<jemalloc>: creat(\"%s\"), 0644) failed\n",
964 		    filename);
965 		if (opt_abort) {
966 			abort();
967 		}
968 	}
969 
970 	return fd;
971 }
972 prof_dump_open_t *JET_MUTABLE prof_dump_open = prof_dump_open_impl;
973 
974 static bool
975 prof_dump_flush(bool propagate_err) {
976 	bool ret = false;
977 	ssize_t err;
978 
979 	cassert(config_prof);
980 
981 	err = malloc_write_fd(prof_dump_fd, prof_dump_buf, prof_dump_buf_end);
982 	if (err == -1) {
983 		if (!propagate_err) {
984 			malloc_write("<jemalloc>: write() failed during heap "
985 			    "profile flush\n");
986 			if (opt_abort) {
987 				abort();
988 			}
989 		}
990 		ret = true;
991 	}
992 	prof_dump_buf_end = 0;
993 
994 	return ret;
995 }
996 
997 static bool
998 prof_dump_close(bool propagate_err) {
999 	bool ret;
1000 
1001 	assert(prof_dump_fd != -1);
1002 	ret = prof_dump_flush(propagate_err);
1003 	close(prof_dump_fd);
1004 	prof_dump_fd = -1;
1005 
1006 	return ret;
1007 }
1008 
1009 static bool
1010 prof_dump_write(bool propagate_err, const char *s) {
1011 	size_t i, slen, n;
1012 
1013 	cassert(config_prof);
1014 
1015 	i = 0;
1016 	slen = strlen(s);
1017 	while (i < slen) {
1018 		/* Flush the buffer if it is full. */
1019 		if (prof_dump_buf_end == PROF_DUMP_BUFSIZE) {
1020 			if (prof_dump_flush(propagate_err) && propagate_err) {
1021 				return true;
1022 			}
1023 		}
1024 
1025 		if (prof_dump_buf_end + slen <= PROF_DUMP_BUFSIZE) {
1026 			/* Finish writing. */
1027 			n = slen - i;
1028 		} else {
1029 			/* Write as much of s as will fit. */
1030 			n = PROF_DUMP_BUFSIZE - prof_dump_buf_end;
1031 		}
1032 		memcpy(&prof_dump_buf[prof_dump_buf_end], &s[i], n);
1033 		prof_dump_buf_end += n;
1034 		i += n;
1035 	}
1036 
1037 	return false;
1038 }
1039 
1040 JEMALLOC_FORMAT_PRINTF(2, 3)
1041 static bool
1042 prof_dump_printf(bool propagate_err, const char *format, ...) {
1043 	bool ret;
1044 	va_list ap;
1045 	char buf[PROF_PRINTF_BUFSIZE];
1046 
1047 	va_start(ap, format);
1048 	malloc_vsnprintf(buf, sizeof(buf), format, ap);
1049 	va_end(ap);
1050 	ret = prof_dump_write(propagate_err, buf);
1051 
1052 	return ret;
1053 }
1054 
1055 static void
1056 prof_tctx_merge_tdata(tsdn_t *tsdn, prof_tctx_t *tctx, prof_tdata_t *tdata) {
1057 	malloc_mutex_assert_owner(tsdn, tctx->tdata->lock);
1058 
1059 	malloc_mutex_lock(tsdn, tctx->gctx->lock);
1060 
1061 	switch (tctx->state) {
1062 	case prof_tctx_state_initializing:
1063 		malloc_mutex_unlock(tsdn, tctx->gctx->lock);
1064 		return;
1065 	case prof_tctx_state_nominal:
1066 		tctx->state = prof_tctx_state_dumping;
1067 		malloc_mutex_unlock(tsdn, tctx->gctx->lock);
1068 
1069 		memcpy(&tctx->dump_cnts, &tctx->cnts, sizeof(prof_cnt_t));
1070 
1071 		tdata->cnt_summed.curobjs += tctx->dump_cnts.curobjs;
1072 		tdata->cnt_summed.curbytes += tctx->dump_cnts.curbytes;
1073 		if (opt_prof_accum) {
1074 			tdata->cnt_summed.accumobjs +=
1075 			    tctx->dump_cnts.accumobjs;
1076 			tdata->cnt_summed.accumbytes +=
1077 			    tctx->dump_cnts.accumbytes;
1078 		}
1079 		break;
1080 	case prof_tctx_state_dumping:
1081 	case prof_tctx_state_purgatory:
1082 		not_reached();
1083 	}
1084 }
1085 
1086 static void
1087 prof_tctx_merge_gctx(tsdn_t *tsdn, prof_tctx_t *tctx, prof_gctx_t *gctx) {
1088 	malloc_mutex_assert_owner(tsdn, gctx->lock);
1089 
1090 	gctx->cnt_summed.curobjs += tctx->dump_cnts.curobjs;
1091 	gctx->cnt_summed.curbytes += tctx->dump_cnts.curbytes;
1092 	if (opt_prof_accum) {
1093 		gctx->cnt_summed.accumobjs += tctx->dump_cnts.accumobjs;
1094 		gctx->cnt_summed.accumbytes += tctx->dump_cnts.accumbytes;
1095 	}
1096 }
1097 
1098 static prof_tctx_t *
1099 prof_tctx_merge_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *arg) {
1100 	tsdn_t *tsdn = (tsdn_t *)arg;
1101 
1102 	malloc_mutex_assert_owner(tsdn, tctx->gctx->lock);
1103 
1104 	switch (tctx->state) {
1105 	case prof_tctx_state_nominal:
1106 		/* New since dumping started; ignore. */
1107 		break;
1108 	case prof_tctx_state_dumping:
1109 	case prof_tctx_state_purgatory:
1110 		prof_tctx_merge_gctx(tsdn, tctx, tctx->gctx);
1111 		break;
1112 	default:
1113 		not_reached();
1114 	}
1115 
1116 	return NULL;
1117 }
1118 
1119 struct prof_tctx_dump_iter_arg_s {
1120 	tsdn_t	*tsdn;
1121 	bool	propagate_err;
1122 };
1123 
1124 static prof_tctx_t *
1125 prof_tctx_dump_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *opaque) {
1126 	struct prof_tctx_dump_iter_arg_s *arg =
1127 	    (struct prof_tctx_dump_iter_arg_s *)opaque;
1128 
1129 	malloc_mutex_assert_owner(arg->tsdn, tctx->gctx->lock);
1130 
1131 	switch (tctx->state) {
1132 	case prof_tctx_state_initializing:
1133 	case prof_tctx_state_nominal:
1134 		/* Not captured by this dump. */
1135 		break;
1136 	case prof_tctx_state_dumping:
1137 	case prof_tctx_state_purgatory:
1138 		if (prof_dump_printf(arg->propagate_err,
1139 		    "  t%"FMTu64": %"FMTu64": %"FMTu64" [%"FMTu64": "
1140 		    "%"FMTu64"]\n", tctx->thr_uid, tctx->dump_cnts.curobjs,
1141 		    tctx->dump_cnts.curbytes, tctx->dump_cnts.accumobjs,
1142 		    tctx->dump_cnts.accumbytes)) {
1143 			return tctx;
1144 		}
1145 		break;
1146 	default:
1147 		not_reached();
1148 	}
1149 	return NULL;
1150 }
1151 
1152 static prof_tctx_t *
1153 prof_tctx_finish_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *arg) {
1154 	tsdn_t *tsdn = (tsdn_t *)arg;
1155 	prof_tctx_t *ret;
1156 
1157 	malloc_mutex_assert_owner(tsdn, tctx->gctx->lock);
1158 
1159 	switch (tctx->state) {
1160 	case prof_tctx_state_nominal:
1161 		/* New since dumping started; ignore. */
1162 		break;
1163 	case prof_tctx_state_dumping:
1164 		tctx->state = prof_tctx_state_nominal;
1165 		break;
1166 	case prof_tctx_state_purgatory:
1167 		ret = tctx;
1168 		goto label_return;
1169 	default:
1170 		not_reached();
1171 	}
1172 
1173 	ret = NULL;
1174 label_return:
1175 	return ret;
1176 }
1177 
1178 static void
1179 prof_dump_gctx_prep(tsdn_t *tsdn, prof_gctx_t *gctx, prof_gctx_tree_t *gctxs) {
1180 	cassert(config_prof);
1181 
1182 	malloc_mutex_lock(tsdn, gctx->lock);
1183 
1184 	/*
1185 	 * Increment nlimbo so that gctx won't go away before dump.
1186 	 * Additionally, link gctx into the dump list so that it is included in
1187 	 * prof_dump()'s second pass.
1188 	 */
1189 	gctx->nlimbo++;
1190 	gctx_tree_insert(gctxs, gctx);
1191 
1192 	memset(&gctx->cnt_summed, 0, sizeof(prof_cnt_t));
1193 
1194 	malloc_mutex_unlock(tsdn, gctx->lock);
1195 }
1196 
1197 struct prof_gctx_merge_iter_arg_s {
1198 	tsdn_t	*tsdn;
1199 	size_t	leak_ngctx;
1200 };
1201 
1202 static prof_gctx_t *
1203 prof_gctx_merge_iter(prof_gctx_tree_t *gctxs, prof_gctx_t *gctx, void *opaque) {
1204 	struct prof_gctx_merge_iter_arg_s *arg =
1205 	    (struct prof_gctx_merge_iter_arg_s *)opaque;
1206 
1207 	malloc_mutex_lock(arg->tsdn, gctx->lock);
1208 	tctx_tree_iter(&gctx->tctxs, NULL, prof_tctx_merge_iter,
1209 	    (void *)arg->tsdn);
1210 	if (gctx->cnt_summed.curobjs != 0) {
1211 		arg->leak_ngctx++;
1212 	}
1213 	malloc_mutex_unlock(arg->tsdn, gctx->lock);
1214 
1215 	return NULL;
1216 }
1217 
1218 static void
1219 prof_gctx_finish(tsd_t *tsd, prof_gctx_tree_t *gctxs) {
1220 	prof_tdata_t *tdata = prof_tdata_get(tsd, false);
1221 	prof_gctx_t *gctx;
1222 
1223 	/*
1224 	 * Standard tree iteration won't work here, because as soon as we
1225 	 * decrement gctx->nlimbo and unlock gctx, another thread can
1226 	 * concurrently destroy it, which will corrupt the tree.  Therefore,
1227 	 * tear down the tree one node at a time during iteration.
1228 	 */
1229 	while ((gctx = gctx_tree_first(gctxs)) != NULL) {
1230 		gctx_tree_remove(gctxs, gctx);
1231 		malloc_mutex_lock(tsd_tsdn(tsd), gctx->lock);
1232 		{
1233 			prof_tctx_t *next;
1234 
1235 			next = NULL;
1236 			do {
1237 				prof_tctx_t *to_destroy =
1238 				    tctx_tree_iter(&gctx->tctxs, next,
1239 				    prof_tctx_finish_iter,
1240 				    (void *)tsd_tsdn(tsd));
1241 				if (to_destroy != NULL) {
1242 					next = tctx_tree_next(&gctx->tctxs,
1243 					    to_destroy);
1244 					tctx_tree_remove(&gctx->tctxs,
1245 					    to_destroy);
1246 					idalloctm(tsd_tsdn(tsd), to_destroy,
1247 					    NULL, NULL, true, true);
1248 				} else {
1249 					next = NULL;
1250 				}
1251 			} while (next != NULL);
1252 		}
1253 		gctx->nlimbo--;
1254 		if (prof_gctx_should_destroy(gctx)) {
1255 			gctx->nlimbo++;
1256 			malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);
1257 			prof_gctx_try_destroy(tsd, tdata, gctx, tdata);
1258 		} else {
1259 			malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);
1260 		}
1261 	}
1262 }
1263 
1264 struct prof_tdata_merge_iter_arg_s {
1265 	tsdn_t		*tsdn;
1266 	prof_cnt_t	cnt_all;
1267 };
1268 
1269 static prof_tdata_t *
1270 prof_tdata_merge_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata,
1271     void *opaque) {
1272 	struct prof_tdata_merge_iter_arg_s *arg =
1273 	    (struct prof_tdata_merge_iter_arg_s *)opaque;
1274 
1275 	malloc_mutex_lock(arg->tsdn, tdata->lock);
1276 	if (!tdata->expired) {
1277 		size_t tabind;
1278 		union {
1279 			prof_tctx_t	*p;
1280 			void		*v;
1281 		} tctx;
1282 
1283 		tdata->dumping = true;
1284 		memset(&tdata->cnt_summed, 0, sizeof(prof_cnt_t));
1285 		for (tabind = 0; !ckh_iter(&tdata->bt2tctx, &tabind, NULL,
1286 		    &tctx.v);) {
1287 			prof_tctx_merge_tdata(arg->tsdn, tctx.p, tdata);
1288 		}
1289 
1290 		arg->cnt_all.curobjs += tdata->cnt_summed.curobjs;
1291 		arg->cnt_all.curbytes += tdata->cnt_summed.curbytes;
1292 		if (opt_prof_accum) {
1293 			arg->cnt_all.accumobjs += tdata->cnt_summed.accumobjs;
1294 			arg->cnt_all.accumbytes += tdata->cnt_summed.accumbytes;
1295 		}
1296 	} else {
1297 		tdata->dumping = false;
1298 	}
1299 	malloc_mutex_unlock(arg->tsdn, tdata->lock);
1300 
1301 	return NULL;
1302 }
1303 
1304 static prof_tdata_t *
1305 prof_tdata_dump_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata,
1306     void *arg) {
1307 	bool propagate_err = *(bool *)arg;
1308 
1309 	if (!tdata->dumping) {
1310 		return NULL;
1311 	}
1312 
1313 	if (prof_dump_printf(propagate_err,
1314 	    "  t%"FMTu64": %"FMTu64": %"FMTu64" [%"FMTu64": %"FMTu64"]%s%s\n",
1315 	    tdata->thr_uid, tdata->cnt_summed.curobjs,
1316 	    tdata->cnt_summed.curbytes, tdata->cnt_summed.accumobjs,
1317 	    tdata->cnt_summed.accumbytes,
1318 	    (tdata->thread_name != NULL) ? " " : "",
1319 	    (tdata->thread_name != NULL) ? tdata->thread_name : "")) {
1320 		return tdata;
1321 	}
1322 	return NULL;
1323 }
1324 
1325 static bool
1326 prof_dump_header_impl(tsdn_t *tsdn, bool propagate_err,
1327     const prof_cnt_t *cnt_all) {
1328 	bool ret;
1329 
1330 	if (prof_dump_printf(propagate_err,
1331 	    "heap_v2/%"FMTu64"\n"
1332 	    "  t*: %"FMTu64": %"FMTu64" [%"FMTu64": %"FMTu64"]\n",
1333 	    ((uint64_t)1U << lg_prof_sample), cnt_all->curobjs,
1334 	    cnt_all->curbytes, cnt_all->accumobjs, cnt_all->accumbytes)) {
1335 		return true;
1336 	}
1337 
1338 	malloc_mutex_lock(tsdn, &tdatas_mtx);
1339 	ret = (tdata_tree_iter(&tdatas, NULL, prof_tdata_dump_iter,
1340 	    (void *)&propagate_err) != NULL);
1341 	malloc_mutex_unlock(tsdn, &tdatas_mtx);
1342 	return ret;
1343 }
1344 prof_dump_header_t *JET_MUTABLE prof_dump_header = prof_dump_header_impl;
1345 
1346 static bool
1347 prof_dump_gctx(tsdn_t *tsdn, bool propagate_err, prof_gctx_t *gctx,
1348     const prof_bt_t *bt, prof_gctx_tree_t *gctxs) {
1349 	bool ret;
1350 	unsigned i;
1351 	struct prof_tctx_dump_iter_arg_s prof_tctx_dump_iter_arg;
1352 
1353 	cassert(config_prof);
1354 	malloc_mutex_assert_owner(tsdn, gctx->lock);
1355 
1356 	/* Avoid dumping such gctx's that have no useful data. */
1357 	if ((!opt_prof_accum && gctx->cnt_summed.curobjs == 0) ||
1358 	    (opt_prof_accum && gctx->cnt_summed.accumobjs == 0)) {
1359 		assert(gctx->cnt_summed.curobjs == 0);
1360 		assert(gctx->cnt_summed.curbytes == 0);
1361 		assert(gctx->cnt_summed.accumobjs == 0);
1362 		assert(gctx->cnt_summed.accumbytes == 0);
1363 		ret = false;
1364 		goto label_return;
1365 	}
1366 
1367 	if (prof_dump_printf(propagate_err, "@")) {
1368 		ret = true;
1369 		goto label_return;
1370 	}
1371 	for (i = 0; i < bt->len; i++) {
1372 		if (prof_dump_printf(propagate_err, " %#"FMTxPTR,
1373 		    (uintptr_t)bt->vec[i])) {
1374 			ret = true;
1375 			goto label_return;
1376 		}
1377 	}
1378 
1379 	if (prof_dump_printf(propagate_err,
1380 	    "\n"
1381 	    "  t*: %"FMTu64": %"FMTu64" [%"FMTu64": %"FMTu64"]\n",
1382 	    gctx->cnt_summed.curobjs, gctx->cnt_summed.curbytes,
1383 	    gctx->cnt_summed.accumobjs, gctx->cnt_summed.accumbytes)) {
1384 		ret = true;
1385 		goto label_return;
1386 	}
1387 
1388 	prof_tctx_dump_iter_arg.tsdn = tsdn;
1389 	prof_tctx_dump_iter_arg.propagate_err = propagate_err;
1390 	if (tctx_tree_iter(&gctx->tctxs, NULL, prof_tctx_dump_iter,
1391 	    (void *)&prof_tctx_dump_iter_arg) != NULL) {
1392 		ret = true;
1393 		goto label_return;
1394 	}
1395 
1396 	ret = false;
1397 label_return:
1398 	return ret;
1399 }
1400 
1401 #ifndef _WIN32
1402 JEMALLOC_FORMAT_PRINTF(1, 2)
1403 static int
1404 prof_open_maps(const char *format, ...) {
1405 	int mfd;
1406 	va_list ap;
1407 	char filename[PATH_MAX + 1];
1408 
1409 	va_start(ap, format);
1410 	malloc_vsnprintf(filename, sizeof(filename), format, ap);
1411 	va_end(ap);
1412 
1413 #if defined(O_CLOEXEC)
1414 	mfd = open(filename, O_RDONLY | O_CLOEXEC);
1415 #else
1416 	mfd = open(filename, O_RDONLY);
1417 	if (mfd != -1) {
1418 		fcntl(mfd, F_SETFD, fcntl(mfd, F_GETFD) | FD_CLOEXEC);
1419 	}
1420 #endif
1421 
1422 	return mfd;
1423 }
1424 #endif
1425 
1426 static int
1427 prof_getpid(void) {
1428 #ifdef _WIN32
1429 	return GetCurrentProcessId();
1430 #else
1431 	return getpid();
1432 #endif
1433 }
1434 
1435 static bool
1436 prof_dump_maps(bool propagate_err) {
1437 	bool ret;
1438 	int mfd;
1439 
1440 	cassert(config_prof);
1441 #ifdef __FreeBSD__
1442 	mfd = prof_open_maps("/proc/curproc/map");
1443 #elif defined(_WIN32)
1444 	mfd = -1; // Not implemented
1445 #else
1446 	{
1447 		int pid = prof_getpid();
1448 
1449 		mfd = prof_open_maps("/proc/%d/task/%d/maps", pid, pid);
1450 		if (mfd == -1) {
1451 			mfd = prof_open_maps("/proc/%d/maps", pid);
1452 		}
1453 	}
1454 #endif
1455 	if (mfd != -1) {
1456 		ssize_t nread;
1457 
1458 		if (prof_dump_write(propagate_err, "\nMAPPED_LIBRARIES:\n") &&
1459 		    propagate_err) {
1460 			ret = true;
1461 			goto label_return;
1462 		}
1463 		nread = 0;
1464 		do {
1465 			prof_dump_buf_end += nread;
1466 			if (prof_dump_buf_end == PROF_DUMP_BUFSIZE) {
1467 				/* Make space in prof_dump_buf before read(). */
1468 				if (prof_dump_flush(propagate_err) &&
1469 				    propagate_err) {
1470 					ret = true;
1471 					goto label_return;
1472 				}
1473 			}
1474 			nread = malloc_read_fd(mfd,
1475 			    &prof_dump_buf[prof_dump_buf_end], PROF_DUMP_BUFSIZE
1476 			    - prof_dump_buf_end);
1477 		} while (nread > 0);
1478 	} else {
1479 		ret = true;
1480 		goto label_return;
1481 	}
1482 
1483 	ret = false;
1484 label_return:
1485 	if (mfd != -1) {
1486 		close(mfd);
1487 	}
1488 	return ret;
1489 }
1490 
1491 /*
1492  * See prof_sample_threshold_update() comment for why the body of this function
1493  * is conditionally compiled.
1494  */
1495 static void
1496 prof_leakcheck(const prof_cnt_t *cnt_all, size_t leak_ngctx,
1497     const char *filename) {
1498 #ifdef JEMALLOC_PROF
1499 	/*
1500 	 * Scaling is equivalent AdjustSamples() in jeprof, but the result may
1501 	 * differ slightly from what jeprof reports, because here we scale the
1502 	 * summary values, whereas jeprof scales each context individually and
1503 	 * reports the sums of the scaled values.
1504 	 */
1505 	if (cnt_all->curbytes != 0) {
1506 		double sample_period = (double)((uint64_t)1 << lg_prof_sample);
1507 		double ratio = (((double)cnt_all->curbytes) /
1508 		    (double)cnt_all->curobjs) / sample_period;
1509 		double scale_factor = 1.0 / (1.0 - exp(-ratio));
1510 		uint64_t curbytes = (uint64_t)round(((double)cnt_all->curbytes)
1511 		    * scale_factor);
1512 		uint64_t curobjs = (uint64_t)round(((double)cnt_all->curobjs) *
1513 		    scale_factor);
1514 
1515 		malloc_printf("<jemalloc>: Leak approximation summary: ~%"FMTu64
1516 		    " byte%s, ~%"FMTu64" object%s, >= %zu context%s\n",
1517 		    curbytes, (curbytes != 1) ? "s" : "", curobjs, (curobjs !=
1518 		    1) ? "s" : "", leak_ngctx, (leak_ngctx != 1) ? "s" : "");
1519 		malloc_printf(
1520 		    "<jemalloc>: Run jeprof on \"%s\" for leak detail\n",
1521 		    filename);
1522 	}
1523 #endif
1524 }
1525 
1526 struct prof_gctx_dump_iter_arg_s {
1527 	tsdn_t	*tsdn;
1528 	bool	propagate_err;
1529 };
1530 
1531 static prof_gctx_t *
1532 prof_gctx_dump_iter(prof_gctx_tree_t *gctxs, prof_gctx_t *gctx, void *opaque) {
1533 	prof_gctx_t *ret;
1534 	struct prof_gctx_dump_iter_arg_s *arg =
1535 	    (struct prof_gctx_dump_iter_arg_s *)opaque;
1536 
1537 	malloc_mutex_lock(arg->tsdn, gctx->lock);
1538 
1539 	if (prof_dump_gctx(arg->tsdn, arg->propagate_err, gctx, &gctx->bt,
1540 	    gctxs)) {
1541 		ret = gctx;
1542 		goto label_return;
1543 	}
1544 
1545 	ret = NULL;
1546 label_return:
1547 	malloc_mutex_unlock(arg->tsdn, gctx->lock);
1548 	return ret;
1549 }
1550 
1551 static void
1552 prof_dump_prep(tsd_t *tsd, prof_tdata_t *tdata,
1553     struct prof_tdata_merge_iter_arg_s *prof_tdata_merge_iter_arg,
1554     struct prof_gctx_merge_iter_arg_s *prof_gctx_merge_iter_arg,
1555     prof_gctx_tree_t *gctxs) {
1556 	size_t tabind;
1557 	union {
1558 		prof_gctx_t	*p;
1559 		void		*v;
1560 	} gctx;
1561 
1562 	prof_enter(tsd, tdata);
1563 
1564 	/*
1565 	 * Put gctx's in limbo and clear their counters in preparation for
1566 	 * summing.
1567 	 */
1568 	gctx_tree_new(gctxs);
1569 	for (tabind = 0; !ckh_iter(&bt2gctx, &tabind, NULL, &gctx.v);) {
1570 		prof_dump_gctx_prep(tsd_tsdn(tsd), gctx.p, gctxs);
1571 	}
1572 
1573 	/*
1574 	 * Iterate over tdatas, and for the non-expired ones snapshot their tctx
1575 	 * stats and merge them into the associated gctx's.
1576 	 */
1577 	prof_tdata_merge_iter_arg->tsdn = tsd_tsdn(tsd);
1578 	memset(&prof_tdata_merge_iter_arg->cnt_all, 0, sizeof(prof_cnt_t));
1579 	malloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx);
1580 	tdata_tree_iter(&tdatas, NULL, prof_tdata_merge_iter,
1581 	    (void *)prof_tdata_merge_iter_arg);
1582 	malloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx);
1583 
1584 	/* Merge tctx stats into gctx's. */
1585 	prof_gctx_merge_iter_arg->tsdn = tsd_tsdn(tsd);
1586 	prof_gctx_merge_iter_arg->leak_ngctx = 0;
1587 	gctx_tree_iter(gctxs, NULL, prof_gctx_merge_iter,
1588 	    (void *)prof_gctx_merge_iter_arg);
1589 
1590 	prof_leave(tsd, tdata);
1591 }
1592 
1593 static bool
1594 prof_dump_file(tsd_t *tsd, bool propagate_err, const char *filename,
1595     bool leakcheck, prof_tdata_t *tdata,
1596     struct prof_tdata_merge_iter_arg_s *prof_tdata_merge_iter_arg,
1597     struct prof_gctx_merge_iter_arg_s *prof_gctx_merge_iter_arg,
1598     struct prof_gctx_dump_iter_arg_s *prof_gctx_dump_iter_arg,
1599     prof_gctx_tree_t *gctxs) {
1600 	/* Create dump file. */
1601 	if ((prof_dump_fd = prof_dump_open(propagate_err, filename)) == -1) {
1602 		return true;
1603 	}
1604 
1605 	/* Dump profile header. */
1606 	if (prof_dump_header(tsd_tsdn(tsd), propagate_err,
1607 	    &prof_tdata_merge_iter_arg->cnt_all)) {
1608 		goto label_write_error;
1609 	}
1610 
1611 	/* Dump per gctx profile stats. */
1612 	prof_gctx_dump_iter_arg->tsdn = tsd_tsdn(tsd);
1613 	prof_gctx_dump_iter_arg->propagate_err = propagate_err;
1614 	if (gctx_tree_iter(gctxs, NULL, prof_gctx_dump_iter,
1615 	    (void *)prof_gctx_dump_iter_arg) != NULL) {
1616 		goto label_write_error;
1617 	}
1618 
1619 	/* Dump /proc/<pid>/maps if possible. */
1620 	if (prof_dump_maps(propagate_err)) {
1621 		goto label_write_error;
1622 	}
1623 
1624 	if (prof_dump_close(propagate_err)) {
1625 		return true;
1626 	}
1627 
1628 	return false;
1629 label_write_error:
1630 	prof_dump_close(propagate_err);
1631 	return true;
1632 }
1633 
1634 static bool
1635 prof_dump(tsd_t *tsd, bool propagate_err, const char *filename,
1636     bool leakcheck) {
1637 	cassert(config_prof);
1638 	assert(tsd_reentrancy_level_get(tsd) == 0);
1639 
1640 	prof_tdata_t * tdata = prof_tdata_get(tsd, true);
1641 	if (tdata == NULL) {
1642 		return true;
1643 	}
1644 
1645 	pre_reentrancy(tsd, NULL);
1646 	malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_mtx);
1647 
1648 	prof_gctx_tree_t gctxs;
1649 	struct prof_tdata_merge_iter_arg_s prof_tdata_merge_iter_arg;
1650 	struct prof_gctx_merge_iter_arg_s prof_gctx_merge_iter_arg;
1651 	struct prof_gctx_dump_iter_arg_s prof_gctx_dump_iter_arg;
1652 	prof_dump_prep(tsd, tdata, &prof_tdata_merge_iter_arg,
1653 	    &prof_gctx_merge_iter_arg, &gctxs);
1654 	bool err = prof_dump_file(tsd, propagate_err, filename, leakcheck, tdata,
1655 	    &prof_tdata_merge_iter_arg, &prof_gctx_merge_iter_arg,
1656 	    &prof_gctx_dump_iter_arg, &gctxs);
1657 	prof_gctx_finish(tsd, &gctxs);
1658 
1659 	malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_mtx);
1660 	post_reentrancy(tsd);
1661 
1662 	if (err) {
1663 		return true;
1664 	}
1665 
1666 	if (leakcheck) {
1667 		prof_leakcheck(&prof_tdata_merge_iter_arg.cnt_all,
1668 		    prof_gctx_merge_iter_arg.leak_ngctx, filename);
1669 	}
1670 	return false;
1671 }
1672 
1673 #ifdef JEMALLOC_JET
1674 void
1675 prof_cnt_all(uint64_t *curobjs, uint64_t *curbytes, uint64_t *accumobjs,
1676     uint64_t *accumbytes) {
1677 	tsd_t *tsd;
1678 	prof_tdata_t *tdata;
1679 	struct prof_tdata_merge_iter_arg_s prof_tdata_merge_iter_arg;
1680 	struct prof_gctx_merge_iter_arg_s prof_gctx_merge_iter_arg;
1681 	prof_gctx_tree_t gctxs;
1682 
1683 	tsd = tsd_fetch();
1684 	tdata = prof_tdata_get(tsd, false);
1685 	if (tdata == NULL) {
1686 		if (curobjs != NULL) {
1687 			*curobjs = 0;
1688 		}
1689 		if (curbytes != NULL) {
1690 			*curbytes = 0;
1691 		}
1692 		if (accumobjs != NULL) {
1693 			*accumobjs = 0;
1694 		}
1695 		if (accumbytes != NULL) {
1696 			*accumbytes = 0;
1697 		}
1698 		return;
1699 	}
1700 
1701 	prof_dump_prep(tsd, tdata, &prof_tdata_merge_iter_arg,
1702 	    &prof_gctx_merge_iter_arg, &gctxs);
1703 	prof_gctx_finish(tsd, &gctxs);
1704 
1705 	if (curobjs != NULL) {
1706 		*curobjs = prof_tdata_merge_iter_arg.cnt_all.curobjs;
1707 	}
1708 	if (curbytes != NULL) {
1709 		*curbytes = prof_tdata_merge_iter_arg.cnt_all.curbytes;
1710 	}
1711 	if (accumobjs != NULL) {
1712 		*accumobjs = prof_tdata_merge_iter_arg.cnt_all.accumobjs;
1713 	}
1714 	if (accumbytes != NULL) {
1715 		*accumbytes = prof_tdata_merge_iter_arg.cnt_all.accumbytes;
1716 	}
1717 }
1718 #endif
1719 
1720 #define DUMP_FILENAME_BUFSIZE	(PATH_MAX + 1)
1721 #define VSEQ_INVALID		UINT64_C(0xffffffffffffffff)
1722 static void
1723 prof_dump_filename(char *filename, char v, uint64_t vseq) {
1724 	cassert(config_prof);
1725 
1726 	if (vseq != VSEQ_INVALID) {
1727 	        /* "<prefix>.<pid>.<seq>.v<vseq>.heap" */
1728 		malloc_snprintf(filename, DUMP_FILENAME_BUFSIZE,
1729 		    "%s.%d.%"FMTu64".%c%"FMTu64".heap",
1730 		    opt_prof_prefix, prof_getpid(), prof_dump_seq, v, vseq);
1731 	} else {
1732 	        /* "<prefix>.<pid>.<seq>.<v>.heap" */
1733 		malloc_snprintf(filename, DUMP_FILENAME_BUFSIZE,
1734 		    "%s.%d.%"FMTu64".%c.heap",
1735 		    opt_prof_prefix, prof_getpid(), prof_dump_seq, v);
1736 	}
1737 	prof_dump_seq++;
1738 }
1739 
1740 static void
1741 prof_fdump(void) {
1742 	tsd_t *tsd;
1743 	char filename[DUMP_FILENAME_BUFSIZE];
1744 
1745 	cassert(config_prof);
1746 	assert(opt_prof_final);
1747 	assert(opt_prof_prefix[0] != '\0');
1748 
1749 	if (!prof_booted) {
1750 		return;
1751 	}
1752 	tsd = tsd_fetch();
1753 	assert(tsd_reentrancy_level_get(tsd) == 0);
1754 
1755 	malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_seq_mtx);
1756 	prof_dump_filename(filename, 'f', VSEQ_INVALID);
1757 	malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_seq_mtx);
1758 	prof_dump(tsd, false, filename, opt_prof_leak);
1759 }
1760 
1761 bool
1762 prof_accum_init(tsdn_t *tsdn, prof_accum_t *prof_accum) {
1763 	cassert(config_prof);
1764 
1765 #ifndef JEMALLOC_ATOMIC_U64
1766 	if (malloc_mutex_init(&prof_accum->mtx, "prof_accum",
1767 	    WITNESS_RANK_PROF_ACCUM, malloc_mutex_rank_exclusive)) {
1768 		return true;
1769 	}
1770 	prof_accum->accumbytes = 0;
1771 #else
1772 	atomic_store_u64(&prof_accum->accumbytes, 0, ATOMIC_RELAXED);
1773 #endif
1774 	return false;
1775 }
1776 
1777 void
1778 prof_idump(tsdn_t *tsdn) {
1779 	tsd_t *tsd;
1780 	prof_tdata_t *tdata;
1781 
1782 	cassert(config_prof);
1783 
1784 	if (!prof_booted || tsdn_null(tsdn) || !prof_active_get_unlocked()) {
1785 		return;
1786 	}
1787 	tsd = tsdn_tsd(tsdn);
1788 	if (tsd_reentrancy_level_get(tsd) > 0) {
1789 		return;
1790 	}
1791 
1792 	tdata = prof_tdata_get(tsd, false);
1793 	if (tdata == NULL) {
1794 		return;
1795 	}
1796 	if (tdata->enq) {
1797 		tdata->enq_idump = true;
1798 		return;
1799 	}
1800 
1801 	if (opt_prof_prefix[0] != '\0') {
1802 		char filename[PATH_MAX + 1];
1803 		malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_seq_mtx);
1804 		prof_dump_filename(filename, 'i', prof_dump_iseq);
1805 		prof_dump_iseq++;
1806 		malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_seq_mtx);
1807 		prof_dump(tsd, false, filename, false);
1808 	}
1809 }
1810 
1811 bool
1812 prof_mdump(tsd_t *tsd, const char *filename) {
1813 	cassert(config_prof);
1814 	assert(tsd_reentrancy_level_get(tsd) == 0);
1815 
1816 	if (!opt_prof || !prof_booted) {
1817 		return true;
1818 	}
1819 	char filename_buf[DUMP_FILENAME_BUFSIZE];
1820 	if (filename == NULL) {
1821 		/* No filename specified, so automatically generate one. */
1822 		if (opt_prof_prefix[0] == '\0') {
1823 			return true;
1824 		}
1825 		malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_seq_mtx);
1826 		prof_dump_filename(filename_buf, 'm', prof_dump_mseq);
1827 		prof_dump_mseq++;
1828 		malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_seq_mtx);
1829 		filename = filename_buf;
1830 	}
1831 	return prof_dump(tsd, true, filename, false);
1832 }
1833 
1834 void
1835 prof_gdump(tsdn_t *tsdn) {
1836 	tsd_t *tsd;
1837 	prof_tdata_t *tdata;
1838 
1839 	cassert(config_prof);
1840 
1841 	if (!prof_booted || tsdn_null(tsdn) || !prof_active_get_unlocked()) {
1842 		return;
1843 	}
1844 	tsd = tsdn_tsd(tsdn);
1845 	if (tsd_reentrancy_level_get(tsd) > 0) {
1846 		return;
1847 	}
1848 
1849 	tdata = prof_tdata_get(tsd, false);
1850 	if (tdata == NULL) {
1851 		return;
1852 	}
1853 	if (tdata->enq) {
1854 		tdata->enq_gdump = true;
1855 		return;
1856 	}
1857 
1858 	if (opt_prof_prefix[0] != '\0') {
1859 		char filename[DUMP_FILENAME_BUFSIZE];
1860 		malloc_mutex_lock(tsdn, &prof_dump_seq_mtx);
1861 		prof_dump_filename(filename, 'u', prof_dump_useq);
1862 		prof_dump_useq++;
1863 		malloc_mutex_unlock(tsdn, &prof_dump_seq_mtx);
1864 		prof_dump(tsd, false, filename, false);
1865 	}
1866 }
1867 
1868 static void
1869 prof_bt_hash(const void *key, size_t r_hash[2]) {
1870 	prof_bt_t *bt = (prof_bt_t *)key;
1871 
1872 	cassert(config_prof);
1873 
1874 	hash(bt->vec, bt->len * sizeof(void *), 0x94122f33U, r_hash);
1875 }
1876 
1877 static bool
1878 prof_bt_keycomp(const void *k1, const void *k2) {
1879 	const prof_bt_t *bt1 = (prof_bt_t *)k1;
1880 	const prof_bt_t *bt2 = (prof_bt_t *)k2;
1881 
1882 	cassert(config_prof);
1883 
1884 	if (bt1->len != bt2->len) {
1885 		return false;
1886 	}
1887 	return (memcmp(bt1->vec, bt2->vec, bt1->len * sizeof(void *)) == 0);
1888 }
1889 
1890 static uint64_t
1891 prof_thr_uid_alloc(tsdn_t *tsdn) {
1892 	uint64_t thr_uid;
1893 
1894 	malloc_mutex_lock(tsdn, &next_thr_uid_mtx);
1895 	thr_uid = next_thr_uid;
1896 	next_thr_uid++;
1897 	malloc_mutex_unlock(tsdn, &next_thr_uid_mtx);
1898 
1899 	return thr_uid;
1900 }
1901 
1902 static prof_tdata_t *
1903 prof_tdata_init_impl(tsd_t *tsd, uint64_t thr_uid, uint64_t thr_discrim,
1904     char *thread_name, bool active) {
1905 	prof_tdata_t *tdata;
1906 
1907 	cassert(config_prof);
1908 
1909 	/* Initialize an empty cache for this thread. */
1910 	tdata = (prof_tdata_t *)iallocztm(tsd_tsdn(tsd), sizeof(prof_tdata_t),
1911 	    sz_size2index(sizeof(prof_tdata_t)), false, NULL, true,
1912 	    arena_get(TSDN_NULL, 0, true), true);
1913 	if (tdata == NULL) {
1914 		return NULL;
1915 	}
1916 
1917 	tdata->lock = prof_tdata_mutex_choose(thr_uid);
1918 	tdata->thr_uid = thr_uid;
1919 	tdata->thr_discrim = thr_discrim;
1920 	tdata->thread_name = thread_name;
1921 	tdata->attached = true;
1922 	tdata->expired = false;
1923 	tdata->tctx_uid_next = 0;
1924 
1925 	if (ckh_new(tsd, &tdata->bt2tctx, PROF_CKH_MINITEMS, prof_bt_hash,
1926 	    prof_bt_keycomp)) {
1927 		idalloctm(tsd_tsdn(tsd), tdata, NULL, NULL, true, true);
1928 		return NULL;
1929 	}
1930 
1931 	tdata->prng_state = (uint64_t)(uintptr_t)tdata;
1932 	prof_sample_threshold_update(tdata);
1933 
1934 	tdata->enq = false;
1935 	tdata->enq_idump = false;
1936 	tdata->enq_gdump = false;
1937 
1938 	tdata->dumping = false;
1939 	tdata->active = active;
1940 
1941 	malloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx);
1942 	tdata_tree_insert(&tdatas, tdata);
1943 	malloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx);
1944 
1945 	return tdata;
1946 }
1947 
1948 prof_tdata_t *
1949 prof_tdata_init(tsd_t *tsd) {
1950 	return prof_tdata_init_impl(tsd, prof_thr_uid_alloc(tsd_tsdn(tsd)), 0,
1951 	    NULL, prof_thread_active_init_get(tsd_tsdn(tsd)));
1952 }
1953 
1954 static bool
1955 prof_tdata_should_destroy_unlocked(prof_tdata_t *tdata, bool even_if_attached) {
1956 	if (tdata->attached && !even_if_attached) {
1957 		return false;
1958 	}
1959 	if (ckh_count(&tdata->bt2tctx) != 0) {
1960 		return false;
1961 	}
1962 	return true;
1963 }
1964 
1965 static bool
1966 prof_tdata_should_destroy(tsdn_t *tsdn, prof_tdata_t *tdata,
1967     bool even_if_attached) {
1968 	malloc_mutex_assert_owner(tsdn, tdata->lock);
1969 
1970 	return prof_tdata_should_destroy_unlocked(tdata, even_if_attached);
1971 }
1972 
1973 static void
1974 prof_tdata_destroy_locked(tsd_t *tsd, prof_tdata_t *tdata,
1975     bool even_if_attached) {
1976 	malloc_mutex_assert_owner(tsd_tsdn(tsd), &tdatas_mtx);
1977 
1978 	tdata_tree_remove(&tdatas, tdata);
1979 
1980 	assert(prof_tdata_should_destroy_unlocked(tdata, even_if_attached));
1981 
1982 	if (tdata->thread_name != NULL) {
1983 		idalloctm(tsd_tsdn(tsd), tdata->thread_name, NULL, NULL, true,
1984 		    true);
1985 	}
1986 	ckh_delete(tsd, &tdata->bt2tctx);
1987 	idalloctm(tsd_tsdn(tsd), tdata, NULL, NULL, true, true);
1988 }
1989 
1990 static void
1991 prof_tdata_destroy(tsd_t *tsd, prof_tdata_t *tdata, bool even_if_attached) {
1992 	malloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx);
1993 	prof_tdata_destroy_locked(tsd, tdata, even_if_attached);
1994 	malloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx);
1995 }
1996 
1997 static void
1998 prof_tdata_detach(tsd_t *tsd, prof_tdata_t *tdata) {
1999 	bool destroy_tdata;
2000 
2001 	malloc_mutex_lock(tsd_tsdn(tsd), tdata->lock);
2002 	if (tdata->attached) {
2003 		destroy_tdata = prof_tdata_should_destroy(tsd_tsdn(tsd), tdata,
2004 		    true);
2005 		/*
2006 		 * Only detach if !destroy_tdata, because detaching would allow
2007 		 * another thread to win the race to destroy tdata.
2008 		 */
2009 		if (!destroy_tdata) {
2010 			tdata->attached = false;
2011 		}
2012 		tsd_prof_tdata_set(tsd, NULL);
2013 	} else {
2014 		destroy_tdata = false;
2015 	}
2016 	malloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock);
2017 	if (destroy_tdata) {
2018 		prof_tdata_destroy(tsd, tdata, true);
2019 	}
2020 }
2021 
2022 prof_tdata_t *
2023 prof_tdata_reinit(tsd_t *tsd, prof_tdata_t *tdata) {
2024 	uint64_t thr_uid = tdata->thr_uid;
2025 	uint64_t thr_discrim = tdata->thr_discrim + 1;
2026 	char *thread_name = (tdata->thread_name != NULL) ?
2027 	    prof_thread_name_alloc(tsd_tsdn(tsd), tdata->thread_name) : NULL;
2028 	bool active = tdata->active;
2029 
2030 	prof_tdata_detach(tsd, tdata);
2031 	return prof_tdata_init_impl(tsd, thr_uid, thr_discrim, thread_name,
2032 	    active);
2033 }
2034 
2035 static bool
2036 prof_tdata_expire(tsdn_t *tsdn, prof_tdata_t *tdata) {
2037 	bool destroy_tdata;
2038 
2039 	malloc_mutex_lock(tsdn, tdata->lock);
2040 	if (!tdata->expired) {
2041 		tdata->expired = true;
2042 		destroy_tdata = tdata->attached ? false :
2043 		    prof_tdata_should_destroy(tsdn, tdata, false);
2044 	} else {
2045 		destroy_tdata = false;
2046 	}
2047 	malloc_mutex_unlock(tsdn, tdata->lock);
2048 
2049 	return destroy_tdata;
2050 }
2051 
2052 static prof_tdata_t *
2053 prof_tdata_reset_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata,
2054     void *arg) {
2055 	tsdn_t *tsdn = (tsdn_t *)arg;
2056 
2057 	return (prof_tdata_expire(tsdn, tdata) ? tdata : NULL);
2058 }
2059 
2060 void
2061 prof_reset(tsd_t *tsd, size_t lg_sample) {
2062 	prof_tdata_t *next;
2063 
2064 	assert(lg_sample < (sizeof(uint64_t) << 3));
2065 
2066 	malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_mtx);
2067 	malloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx);
2068 
2069 	lg_prof_sample = lg_sample;
2070 
2071 	next = NULL;
2072 	do {
2073 		prof_tdata_t *to_destroy = tdata_tree_iter(&tdatas, next,
2074 		    prof_tdata_reset_iter, (void *)tsd);
2075 		if (to_destroy != NULL) {
2076 			next = tdata_tree_next(&tdatas, to_destroy);
2077 			prof_tdata_destroy_locked(tsd, to_destroy, false);
2078 		} else {
2079 			next = NULL;
2080 		}
2081 	} while (next != NULL);
2082 
2083 	malloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx);
2084 	malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_mtx);
2085 }
2086 
2087 void
2088 prof_tdata_cleanup(tsd_t *tsd) {
2089 	prof_tdata_t *tdata;
2090 
2091 	if (!config_prof) {
2092 		return;
2093 	}
2094 
2095 	tdata = tsd_prof_tdata_get(tsd);
2096 	if (tdata != NULL) {
2097 		prof_tdata_detach(tsd, tdata);
2098 	}
2099 }
2100 
2101 bool
2102 prof_active_get(tsdn_t *tsdn) {
2103 	bool prof_active_current;
2104 
2105 	malloc_mutex_lock(tsdn, &prof_active_mtx);
2106 	prof_active_current = prof_active;
2107 	malloc_mutex_unlock(tsdn, &prof_active_mtx);
2108 	return prof_active_current;
2109 }
2110 
2111 bool
2112 prof_active_set(tsdn_t *tsdn, bool active) {
2113 	bool prof_active_old;
2114 
2115 	malloc_mutex_lock(tsdn, &prof_active_mtx);
2116 	prof_active_old = prof_active;
2117 	prof_active = active;
2118 	malloc_mutex_unlock(tsdn, &prof_active_mtx);
2119 	return prof_active_old;
2120 }
2121 
2122 const char *
2123 prof_thread_name_get(tsd_t *tsd) {
2124 	prof_tdata_t *tdata;
2125 
2126 	tdata = prof_tdata_get(tsd, true);
2127 	if (tdata == NULL) {
2128 		return "";
2129 	}
2130 	return (tdata->thread_name != NULL ? tdata->thread_name : "");
2131 }
2132 
2133 static char *
2134 prof_thread_name_alloc(tsdn_t *tsdn, const char *thread_name) {
2135 	char *ret;
2136 	size_t size;
2137 
2138 	if (thread_name == NULL) {
2139 		return NULL;
2140 	}
2141 
2142 	size = strlen(thread_name) + 1;
2143 	if (size == 1) {
2144 		return "";
2145 	}
2146 
2147 	ret = iallocztm(tsdn, size, sz_size2index(size), false, NULL, true,
2148 	    arena_get(TSDN_NULL, 0, true), true);
2149 	if (ret == NULL) {
2150 		return NULL;
2151 	}
2152 	memcpy(ret, thread_name, size);
2153 	return ret;
2154 }
2155 
2156 int
2157 prof_thread_name_set(tsd_t *tsd, const char *thread_name) {
2158 	prof_tdata_t *tdata;
2159 	unsigned i;
2160 	char *s;
2161 
2162 	tdata = prof_tdata_get(tsd, true);
2163 	if (tdata == NULL) {
2164 		return EAGAIN;
2165 	}
2166 
2167 	/* Validate input. */
2168 	if (thread_name == NULL) {
2169 		return EFAULT;
2170 	}
2171 	for (i = 0; thread_name[i] != '\0'; i++) {
2172 		char c = thread_name[i];
2173 		if (!isgraph(c) && !isblank(c)) {
2174 			return EFAULT;
2175 		}
2176 	}
2177 
2178 	s = prof_thread_name_alloc(tsd_tsdn(tsd), thread_name);
2179 	if (s == NULL) {
2180 		return EAGAIN;
2181 	}
2182 
2183 	if (tdata->thread_name != NULL) {
2184 		idalloctm(tsd_tsdn(tsd), tdata->thread_name, NULL, NULL, true,
2185 		    true);
2186 		tdata->thread_name = NULL;
2187 	}
2188 	if (strlen(s) > 0) {
2189 		tdata->thread_name = s;
2190 	}
2191 	return 0;
2192 }
2193 
2194 bool
2195 prof_thread_active_get(tsd_t *tsd) {
2196 	prof_tdata_t *tdata;
2197 
2198 	tdata = prof_tdata_get(tsd, true);
2199 	if (tdata == NULL) {
2200 		return false;
2201 	}
2202 	return tdata->active;
2203 }
2204 
2205 bool
2206 prof_thread_active_set(tsd_t *tsd, bool active) {
2207 	prof_tdata_t *tdata;
2208 
2209 	tdata = prof_tdata_get(tsd, true);
2210 	if (tdata == NULL) {
2211 		return true;
2212 	}
2213 	tdata->active = active;
2214 	return false;
2215 }
2216 
2217 bool
2218 prof_thread_active_init_get(tsdn_t *tsdn) {
2219 	bool active_init;
2220 
2221 	malloc_mutex_lock(tsdn, &prof_thread_active_init_mtx);
2222 	active_init = prof_thread_active_init;
2223 	malloc_mutex_unlock(tsdn, &prof_thread_active_init_mtx);
2224 	return active_init;
2225 }
2226 
2227 bool
2228 prof_thread_active_init_set(tsdn_t *tsdn, bool active_init) {
2229 	bool active_init_old;
2230 
2231 	malloc_mutex_lock(tsdn, &prof_thread_active_init_mtx);
2232 	active_init_old = prof_thread_active_init;
2233 	prof_thread_active_init = active_init;
2234 	malloc_mutex_unlock(tsdn, &prof_thread_active_init_mtx);
2235 	return active_init_old;
2236 }
2237 
2238 bool
2239 prof_gdump_get(tsdn_t *tsdn) {
2240 	bool prof_gdump_current;
2241 
2242 	malloc_mutex_lock(tsdn, &prof_gdump_mtx);
2243 	prof_gdump_current = prof_gdump_val;
2244 	malloc_mutex_unlock(tsdn, &prof_gdump_mtx);
2245 	return prof_gdump_current;
2246 }
2247 
2248 bool
2249 prof_gdump_set(tsdn_t *tsdn, bool gdump) {
2250 	bool prof_gdump_old;
2251 
2252 	malloc_mutex_lock(tsdn, &prof_gdump_mtx);
2253 	prof_gdump_old = prof_gdump_val;
2254 	prof_gdump_val = gdump;
2255 	malloc_mutex_unlock(tsdn, &prof_gdump_mtx);
2256 	return prof_gdump_old;
2257 }
2258 
2259 void
2260 prof_boot0(void) {
2261 	cassert(config_prof);
2262 
2263 	memcpy(opt_prof_prefix, PROF_PREFIX_DEFAULT,
2264 	    sizeof(PROF_PREFIX_DEFAULT));
2265 }
2266 
2267 void
2268 prof_boot1(void) {
2269 	cassert(config_prof);
2270 
2271 	/*
2272 	 * opt_prof must be in its final state before any arenas are
2273 	 * initialized, so this function must be executed early.
2274 	 */
2275 
2276 	if (opt_prof_leak && !opt_prof) {
2277 		/*
2278 		 * Enable opt_prof, but in such a way that profiles are never
2279 		 * automatically dumped.
2280 		 */
2281 		opt_prof = true;
2282 		opt_prof_gdump = false;
2283 	} else if (opt_prof) {
2284 		if (opt_lg_prof_interval >= 0) {
2285 			prof_interval = (((uint64_t)1U) <<
2286 			    opt_lg_prof_interval);
2287 		}
2288 	}
2289 }
2290 
2291 bool
2292 prof_boot2(tsd_t *tsd) {
2293 	cassert(config_prof);
2294 
2295 	if (opt_prof) {
2296 		unsigned i;
2297 
2298 		lg_prof_sample = opt_lg_prof_sample;
2299 
2300 		prof_active = opt_prof_active;
2301 		if (malloc_mutex_init(&prof_active_mtx, "prof_active",
2302 		    WITNESS_RANK_PROF_ACTIVE, malloc_mutex_rank_exclusive)) {
2303 			return true;
2304 		}
2305 
2306 		prof_gdump_val = opt_prof_gdump;
2307 		if (malloc_mutex_init(&prof_gdump_mtx, "prof_gdump",
2308 		    WITNESS_RANK_PROF_GDUMP, malloc_mutex_rank_exclusive)) {
2309 			return true;
2310 		}
2311 
2312 		prof_thread_active_init = opt_prof_thread_active_init;
2313 		if (malloc_mutex_init(&prof_thread_active_init_mtx,
2314 		    "prof_thread_active_init",
2315 		    WITNESS_RANK_PROF_THREAD_ACTIVE_INIT,
2316 		    malloc_mutex_rank_exclusive)) {
2317 			return true;
2318 		}
2319 
2320 		if (ckh_new(tsd, &bt2gctx, PROF_CKH_MINITEMS, prof_bt_hash,
2321 		    prof_bt_keycomp)) {
2322 			return true;
2323 		}
2324 		if (malloc_mutex_init(&bt2gctx_mtx, "prof_bt2gctx",
2325 		    WITNESS_RANK_PROF_BT2GCTX, malloc_mutex_rank_exclusive)) {
2326 			return true;
2327 		}
2328 
2329 		tdata_tree_new(&tdatas);
2330 		if (malloc_mutex_init(&tdatas_mtx, "prof_tdatas",
2331 		    WITNESS_RANK_PROF_TDATAS, malloc_mutex_rank_exclusive)) {
2332 			return true;
2333 		}
2334 
2335 		next_thr_uid = 0;
2336 		if (malloc_mutex_init(&next_thr_uid_mtx, "prof_next_thr_uid",
2337 		    WITNESS_RANK_PROF_NEXT_THR_UID, malloc_mutex_rank_exclusive)) {
2338 			return true;
2339 		}
2340 
2341 		if (malloc_mutex_init(&prof_dump_seq_mtx, "prof_dump_seq",
2342 		    WITNESS_RANK_PROF_DUMP_SEQ, malloc_mutex_rank_exclusive)) {
2343 			return true;
2344 		}
2345 		if (malloc_mutex_init(&prof_dump_mtx, "prof_dump",
2346 		    WITNESS_RANK_PROF_DUMP, malloc_mutex_rank_exclusive)) {
2347 			return true;
2348 		}
2349 
2350 		if (opt_prof_final && opt_prof_prefix[0] != '\0' &&
2351 		    atexit(prof_fdump) != 0) {
2352 			malloc_write("<jemalloc>: Error in atexit()\n");
2353 			if (opt_abort) {
2354 				abort();
2355 			}
2356 		}
2357 
2358 		gctx_locks = (malloc_mutex_t *)base_alloc(tsd_tsdn(tsd),
2359 		    b0get(), PROF_NCTX_LOCKS * sizeof(malloc_mutex_t),
2360 		    CACHELINE);
2361 		if (gctx_locks == NULL) {
2362 			return true;
2363 		}
2364 		for (i = 0; i < PROF_NCTX_LOCKS; i++) {
2365 			if (malloc_mutex_init(&gctx_locks[i], "prof_gctx",
2366 			    WITNESS_RANK_PROF_GCTX,
2367 			    malloc_mutex_rank_exclusive)) {
2368 				return true;
2369 			}
2370 		}
2371 
2372 		tdata_locks = (malloc_mutex_t *)base_alloc(tsd_tsdn(tsd),
2373 		    b0get(), PROF_NTDATA_LOCKS * sizeof(malloc_mutex_t),
2374 		    CACHELINE);
2375 		if (tdata_locks == NULL) {
2376 			return true;
2377 		}
2378 		for (i = 0; i < PROF_NTDATA_LOCKS; i++) {
2379 			if (malloc_mutex_init(&tdata_locks[i], "prof_tdata",
2380 			    WITNESS_RANK_PROF_TDATA,
2381 			    malloc_mutex_rank_exclusive)) {
2382 				return true;
2383 			}
2384 		}
2385 	}
2386 
2387 #ifdef JEMALLOC_PROF_LIBGCC
2388 	/*
2389 	 * Cause the backtracing machinery to allocate its internal state
2390 	 * before enabling profiling.
2391 	 */
2392 	_Unwind_Backtrace(prof_unwind_init_callback, NULL);
2393 #endif
2394 
2395 	prof_booted = true;
2396 
2397 	return false;
2398 }
2399 
2400 void
2401 prof_prefork0(tsdn_t *tsdn) {
2402 	if (config_prof && opt_prof) {
2403 		unsigned i;
2404 
2405 		malloc_mutex_prefork(tsdn, &prof_dump_mtx);
2406 		malloc_mutex_prefork(tsdn, &bt2gctx_mtx);
2407 		malloc_mutex_prefork(tsdn, &tdatas_mtx);
2408 		for (i = 0; i < PROF_NTDATA_LOCKS; i++) {
2409 			malloc_mutex_prefork(tsdn, &tdata_locks[i]);
2410 		}
2411 		for (i = 0; i < PROF_NCTX_LOCKS; i++) {
2412 			malloc_mutex_prefork(tsdn, &gctx_locks[i]);
2413 		}
2414 	}
2415 }
2416 
2417 void
2418 prof_prefork1(tsdn_t *tsdn) {
2419 	if (config_prof && opt_prof) {
2420 		malloc_mutex_prefork(tsdn, &prof_active_mtx);
2421 		malloc_mutex_prefork(tsdn, &prof_dump_seq_mtx);
2422 		malloc_mutex_prefork(tsdn, &prof_gdump_mtx);
2423 		malloc_mutex_prefork(tsdn, &next_thr_uid_mtx);
2424 		malloc_mutex_prefork(tsdn, &prof_thread_active_init_mtx);
2425 	}
2426 }
2427 
2428 void
2429 prof_postfork_parent(tsdn_t *tsdn) {
2430 	if (config_prof && opt_prof) {
2431 		unsigned i;
2432 
2433 		malloc_mutex_postfork_parent(tsdn,
2434 		    &prof_thread_active_init_mtx);
2435 		malloc_mutex_postfork_parent(tsdn, &next_thr_uid_mtx);
2436 		malloc_mutex_postfork_parent(tsdn, &prof_gdump_mtx);
2437 		malloc_mutex_postfork_parent(tsdn, &prof_dump_seq_mtx);
2438 		malloc_mutex_postfork_parent(tsdn, &prof_active_mtx);
2439 		for (i = 0; i < PROF_NCTX_LOCKS; i++) {
2440 			malloc_mutex_postfork_parent(tsdn, &gctx_locks[i]);
2441 		}
2442 		for (i = 0; i < PROF_NTDATA_LOCKS; i++) {
2443 			malloc_mutex_postfork_parent(tsdn, &tdata_locks[i]);
2444 		}
2445 		malloc_mutex_postfork_parent(tsdn, &tdatas_mtx);
2446 		malloc_mutex_postfork_parent(tsdn, &bt2gctx_mtx);
2447 		malloc_mutex_postfork_parent(tsdn, &prof_dump_mtx);
2448 	}
2449 }
2450 
2451 void
2452 prof_postfork_child(tsdn_t *tsdn) {
2453 	if (config_prof && opt_prof) {
2454 		unsigned i;
2455 
2456 		malloc_mutex_postfork_child(tsdn, &prof_thread_active_init_mtx);
2457 		malloc_mutex_postfork_child(tsdn, &next_thr_uid_mtx);
2458 		malloc_mutex_postfork_child(tsdn, &prof_gdump_mtx);
2459 		malloc_mutex_postfork_child(tsdn, &prof_dump_seq_mtx);
2460 		malloc_mutex_postfork_child(tsdn, &prof_active_mtx);
2461 		for (i = 0; i < PROF_NCTX_LOCKS; i++) {
2462 			malloc_mutex_postfork_child(tsdn, &gctx_locks[i]);
2463 		}
2464 		for (i = 0; i < PROF_NTDATA_LOCKS; i++) {
2465 			malloc_mutex_postfork_child(tsdn, &tdata_locks[i]);
2466 		}
2467 		malloc_mutex_postfork_child(tsdn, &tdatas_mtx);
2468 		malloc_mutex_postfork_child(tsdn, &bt2gctx_mtx);
2469 		malloc_mutex_postfork_child(tsdn, &prof_dump_mtx);
2470 	}
2471 }
2472 
2473 /******************************************************************************/
2474