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