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