xref: /freebsd/contrib/jemalloc/src/prof_sys.c (revision c43cad87172039ccf38172129c79755ea79e6102)
1*c43cad87SWarner Losh #define JEMALLOC_PROF_SYS_C_
2*c43cad87SWarner Losh #include "jemalloc/internal/jemalloc_preamble.h"
3*c43cad87SWarner Losh #include "jemalloc/internal/jemalloc_internal_includes.h"
4*c43cad87SWarner Losh 
5*c43cad87SWarner Losh #include "jemalloc/internal/buf_writer.h"
6*c43cad87SWarner Losh #include "jemalloc/internal/ctl.h"
7*c43cad87SWarner Losh #include "jemalloc/internal/prof_data.h"
8*c43cad87SWarner Losh #include "jemalloc/internal/prof_sys.h"
9*c43cad87SWarner Losh 
10*c43cad87SWarner Losh #ifdef JEMALLOC_PROF_LIBUNWIND
11*c43cad87SWarner Losh #define UNW_LOCAL_ONLY
12*c43cad87SWarner Losh #include <libunwind.h>
13*c43cad87SWarner Losh #endif
14*c43cad87SWarner Losh 
15*c43cad87SWarner Losh #ifdef JEMALLOC_PROF_LIBGCC
16*c43cad87SWarner Losh /*
17*c43cad87SWarner Losh  * We have a circular dependency -- jemalloc_internal.h tells us if we should
18*c43cad87SWarner Losh  * use libgcc's unwinding functionality, but after we've included that, we've
19*c43cad87SWarner Losh  * already hooked _Unwind_Backtrace.  We'll temporarily disable hooking.
20*c43cad87SWarner Losh  */
21*c43cad87SWarner Losh #undef _Unwind_Backtrace
22*c43cad87SWarner Losh #include <unwind.h>
23*c43cad87SWarner Losh #define _Unwind_Backtrace JEMALLOC_TEST_HOOK(_Unwind_Backtrace, test_hooks_libc_hook)
24*c43cad87SWarner Losh #endif
25*c43cad87SWarner Losh 
26*c43cad87SWarner Losh /******************************************************************************/
27*c43cad87SWarner Losh 
28*c43cad87SWarner Losh malloc_mutex_t prof_dump_filename_mtx;
29*c43cad87SWarner Losh 
30*c43cad87SWarner Losh bool prof_do_mock = false;
31*c43cad87SWarner Losh 
32*c43cad87SWarner Losh static uint64_t prof_dump_seq;
33*c43cad87SWarner Losh static uint64_t prof_dump_iseq;
34*c43cad87SWarner Losh static uint64_t prof_dump_mseq;
35*c43cad87SWarner Losh static uint64_t prof_dump_useq;
36*c43cad87SWarner Losh 
37*c43cad87SWarner Losh static char *prof_prefix = NULL;
38*c43cad87SWarner Losh 
39*c43cad87SWarner Losh /* The fallback allocator profiling functionality will use. */
40*c43cad87SWarner Losh base_t *prof_base;
41*c43cad87SWarner Losh 
42*c43cad87SWarner Losh void
43*c43cad87SWarner Losh bt_init(prof_bt_t *bt, void **vec) {
44*c43cad87SWarner Losh 	cassert(config_prof);
45*c43cad87SWarner Losh 
46*c43cad87SWarner Losh 	bt->vec = vec;
47*c43cad87SWarner Losh 	bt->len = 0;
48*c43cad87SWarner Losh }
49*c43cad87SWarner Losh 
50*c43cad87SWarner Losh #ifdef JEMALLOC_PROF_LIBUNWIND
51*c43cad87SWarner Losh static void
52*c43cad87SWarner Losh prof_backtrace_impl(void **vec, unsigned *len, unsigned max_len) {
53*c43cad87SWarner Losh 	int nframes;
54*c43cad87SWarner Losh 
55*c43cad87SWarner Losh 	cassert(config_prof);
56*c43cad87SWarner Losh 	assert(*len == 0);
57*c43cad87SWarner Losh 	assert(vec != NULL);
58*c43cad87SWarner Losh 	assert(max_len == PROF_BT_MAX);
59*c43cad87SWarner Losh 
60*c43cad87SWarner Losh 	nframes = unw_backtrace(vec, PROF_BT_MAX);
61*c43cad87SWarner Losh 	if (nframes <= 0) {
62*c43cad87SWarner Losh 		return;
63*c43cad87SWarner Losh 	}
64*c43cad87SWarner Losh 	*len = nframes;
65*c43cad87SWarner Losh }
66*c43cad87SWarner Losh #elif (defined(JEMALLOC_PROF_LIBGCC))
67*c43cad87SWarner Losh static _Unwind_Reason_Code
68*c43cad87SWarner Losh prof_unwind_init_callback(struct _Unwind_Context *context, void *arg) {
69*c43cad87SWarner Losh 	cassert(config_prof);
70*c43cad87SWarner Losh 
71*c43cad87SWarner Losh 	return _URC_NO_REASON;
72*c43cad87SWarner Losh }
73*c43cad87SWarner Losh 
74*c43cad87SWarner Losh static _Unwind_Reason_Code
75*c43cad87SWarner Losh prof_unwind_callback(struct _Unwind_Context *context, void *arg) {
76*c43cad87SWarner Losh 	prof_unwind_data_t *data = (prof_unwind_data_t *)arg;
77*c43cad87SWarner Losh 	void *ip;
78*c43cad87SWarner Losh 
79*c43cad87SWarner Losh 	cassert(config_prof);
80*c43cad87SWarner Losh 
81*c43cad87SWarner Losh 	ip = (void *)_Unwind_GetIP(context);
82*c43cad87SWarner Losh 	if (ip == NULL) {
83*c43cad87SWarner Losh 		return _URC_END_OF_STACK;
84*c43cad87SWarner Losh 	}
85*c43cad87SWarner Losh 	data->vec[*data->len] = ip;
86*c43cad87SWarner Losh 	(*data->len)++;
87*c43cad87SWarner Losh 	if (*data->len == data->max) {
88*c43cad87SWarner Losh 		return _URC_END_OF_STACK;
89*c43cad87SWarner Losh 	}
90*c43cad87SWarner Losh 
91*c43cad87SWarner Losh 	return _URC_NO_REASON;
92*c43cad87SWarner Losh }
93*c43cad87SWarner Losh 
94*c43cad87SWarner Losh static void
95*c43cad87SWarner Losh prof_backtrace_impl(void **vec, unsigned *len, unsigned max_len) {
96*c43cad87SWarner Losh 	prof_unwind_data_t data = {vec, len, max_len};
97*c43cad87SWarner Losh 
98*c43cad87SWarner Losh 	cassert(config_prof);
99*c43cad87SWarner Losh 	assert(vec != NULL);
100*c43cad87SWarner Losh 	assert(max_len == PROF_BT_MAX);
101*c43cad87SWarner Losh 
102*c43cad87SWarner Losh 	_Unwind_Backtrace(prof_unwind_callback, &data);
103*c43cad87SWarner Losh }
104*c43cad87SWarner Losh #elif (defined(JEMALLOC_PROF_GCC))
105*c43cad87SWarner Losh static void
106*c43cad87SWarner Losh prof_backtrace_impl(void **vec, unsigned *len, unsigned max_len) {
107*c43cad87SWarner Losh #define BT_FRAME(i)							\
108*c43cad87SWarner Losh 	if ((i) < max_len) {						\
109*c43cad87SWarner Losh 		void *p;						\
110*c43cad87SWarner Losh 		if (__builtin_frame_address(i) == 0) {			\
111*c43cad87SWarner Losh 			return;						\
112*c43cad87SWarner Losh 		}							\
113*c43cad87SWarner Losh 		p = __builtin_return_address(i);			\
114*c43cad87SWarner Losh 		if (p == NULL) {					\
115*c43cad87SWarner Losh 			return;						\
116*c43cad87SWarner Losh 		}							\
117*c43cad87SWarner Losh 		vec[(i)] = p;						\
118*c43cad87SWarner Losh 		*len = (i) + 1;						\
119*c43cad87SWarner Losh 	} else {							\
120*c43cad87SWarner Losh 		return;							\
121*c43cad87SWarner Losh 	}
122*c43cad87SWarner Losh 
123*c43cad87SWarner Losh 	cassert(config_prof);
124*c43cad87SWarner Losh 	assert(vec != NULL);
125*c43cad87SWarner Losh 	assert(max_len == PROF_BT_MAX);
126*c43cad87SWarner Losh 
127*c43cad87SWarner Losh 	BT_FRAME(0)
128*c43cad87SWarner Losh 	BT_FRAME(1)
129*c43cad87SWarner Losh 	BT_FRAME(2)
130*c43cad87SWarner Losh 	BT_FRAME(3)
131*c43cad87SWarner Losh 	BT_FRAME(4)
132*c43cad87SWarner Losh 	BT_FRAME(5)
133*c43cad87SWarner Losh 	BT_FRAME(6)
134*c43cad87SWarner Losh 	BT_FRAME(7)
135*c43cad87SWarner Losh 	BT_FRAME(8)
136*c43cad87SWarner Losh 	BT_FRAME(9)
137*c43cad87SWarner Losh 
138*c43cad87SWarner Losh 	BT_FRAME(10)
139*c43cad87SWarner Losh 	BT_FRAME(11)
140*c43cad87SWarner Losh 	BT_FRAME(12)
141*c43cad87SWarner Losh 	BT_FRAME(13)
142*c43cad87SWarner Losh 	BT_FRAME(14)
143*c43cad87SWarner Losh 	BT_FRAME(15)
144*c43cad87SWarner Losh 	BT_FRAME(16)
145*c43cad87SWarner Losh 	BT_FRAME(17)
146*c43cad87SWarner Losh 	BT_FRAME(18)
147*c43cad87SWarner Losh 	BT_FRAME(19)
148*c43cad87SWarner Losh 
149*c43cad87SWarner Losh 	BT_FRAME(20)
150*c43cad87SWarner Losh 	BT_FRAME(21)
151*c43cad87SWarner Losh 	BT_FRAME(22)
152*c43cad87SWarner Losh 	BT_FRAME(23)
153*c43cad87SWarner Losh 	BT_FRAME(24)
154*c43cad87SWarner Losh 	BT_FRAME(25)
155*c43cad87SWarner Losh 	BT_FRAME(26)
156*c43cad87SWarner Losh 	BT_FRAME(27)
157*c43cad87SWarner Losh 	BT_FRAME(28)
158*c43cad87SWarner Losh 	BT_FRAME(29)
159*c43cad87SWarner Losh 
160*c43cad87SWarner Losh 	BT_FRAME(30)
161*c43cad87SWarner Losh 	BT_FRAME(31)
162*c43cad87SWarner Losh 	BT_FRAME(32)
163*c43cad87SWarner Losh 	BT_FRAME(33)
164*c43cad87SWarner Losh 	BT_FRAME(34)
165*c43cad87SWarner Losh 	BT_FRAME(35)
166*c43cad87SWarner Losh 	BT_FRAME(36)
167*c43cad87SWarner Losh 	BT_FRAME(37)
168*c43cad87SWarner Losh 	BT_FRAME(38)
169*c43cad87SWarner Losh 	BT_FRAME(39)
170*c43cad87SWarner Losh 
171*c43cad87SWarner Losh 	BT_FRAME(40)
172*c43cad87SWarner Losh 	BT_FRAME(41)
173*c43cad87SWarner Losh 	BT_FRAME(42)
174*c43cad87SWarner Losh 	BT_FRAME(43)
175*c43cad87SWarner Losh 	BT_FRAME(44)
176*c43cad87SWarner Losh 	BT_FRAME(45)
177*c43cad87SWarner Losh 	BT_FRAME(46)
178*c43cad87SWarner Losh 	BT_FRAME(47)
179*c43cad87SWarner Losh 	BT_FRAME(48)
180*c43cad87SWarner Losh 	BT_FRAME(49)
181*c43cad87SWarner Losh 
182*c43cad87SWarner Losh 	BT_FRAME(50)
183*c43cad87SWarner Losh 	BT_FRAME(51)
184*c43cad87SWarner Losh 	BT_FRAME(52)
185*c43cad87SWarner Losh 	BT_FRAME(53)
186*c43cad87SWarner Losh 	BT_FRAME(54)
187*c43cad87SWarner Losh 	BT_FRAME(55)
188*c43cad87SWarner Losh 	BT_FRAME(56)
189*c43cad87SWarner Losh 	BT_FRAME(57)
190*c43cad87SWarner Losh 	BT_FRAME(58)
191*c43cad87SWarner Losh 	BT_FRAME(59)
192*c43cad87SWarner Losh 
193*c43cad87SWarner Losh 	BT_FRAME(60)
194*c43cad87SWarner Losh 	BT_FRAME(61)
195*c43cad87SWarner Losh 	BT_FRAME(62)
196*c43cad87SWarner Losh 	BT_FRAME(63)
197*c43cad87SWarner Losh 	BT_FRAME(64)
198*c43cad87SWarner Losh 	BT_FRAME(65)
199*c43cad87SWarner Losh 	BT_FRAME(66)
200*c43cad87SWarner Losh 	BT_FRAME(67)
201*c43cad87SWarner Losh 	BT_FRAME(68)
202*c43cad87SWarner Losh 	BT_FRAME(69)
203*c43cad87SWarner Losh 
204*c43cad87SWarner Losh 	BT_FRAME(70)
205*c43cad87SWarner Losh 	BT_FRAME(71)
206*c43cad87SWarner Losh 	BT_FRAME(72)
207*c43cad87SWarner Losh 	BT_FRAME(73)
208*c43cad87SWarner Losh 	BT_FRAME(74)
209*c43cad87SWarner Losh 	BT_FRAME(75)
210*c43cad87SWarner Losh 	BT_FRAME(76)
211*c43cad87SWarner Losh 	BT_FRAME(77)
212*c43cad87SWarner Losh 	BT_FRAME(78)
213*c43cad87SWarner Losh 	BT_FRAME(79)
214*c43cad87SWarner Losh 
215*c43cad87SWarner Losh 	BT_FRAME(80)
216*c43cad87SWarner Losh 	BT_FRAME(81)
217*c43cad87SWarner Losh 	BT_FRAME(82)
218*c43cad87SWarner Losh 	BT_FRAME(83)
219*c43cad87SWarner Losh 	BT_FRAME(84)
220*c43cad87SWarner Losh 	BT_FRAME(85)
221*c43cad87SWarner Losh 	BT_FRAME(86)
222*c43cad87SWarner Losh 	BT_FRAME(87)
223*c43cad87SWarner Losh 	BT_FRAME(88)
224*c43cad87SWarner Losh 	BT_FRAME(89)
225*c43cad87SWarner Losh 
226*c43cad87SWarner Losh 	BT_FRAME(90)
227*c43cad87SWarner Losh 	BT_FRAME(91)
228*c43cad87SWarner Losh 	BT_FRAME(92)
229*c43cad87SWarner Losh 	BT_FRAME(93)
230*c43cad87SWarner Losh 	BT_FRAME(94)
231*c43cad87SWarner Losh 	BT_FRAME(95)
232*c43cad87SWarner Losh 	BT_FRAME(96)
233*c43cad87SWarner Losh 	BT_FRAME(97)
234*c43cad87SWarner Losh 	BT_FRAME(98)
235*c43cad87SWarner Losh 	BT_FRAME(99)
236*c43cad87SWarner Losh 
237*c43cad87SWarner Losh 	BT_FRAME(100)
238*c43cad87SWarner Losh 	BT_FRAME(101)
239*c43cad87SWarner Losh 	BT_FRAME(102)
240*c43cad87SWarner Losh 	BT_FRAME(103)
241*c43cad87SWarner Losh 	BT_FRAME(104)
242*c43cad87SWarner Losh 	BT_FRAME(105)
243*c43cad87SWarner Losh 	BT_FRAME(106)
244*c43cad87SWarner Losh 	BT_FRAME(107)
245*c43cad87SWarner Losh 	BT_FRAME(108)
246*c43cad87SWarner Losh 	BT_FRAME(109)
247*c43cad87SWarner Losh 
248*c43cad87SWarner Losh 	BT_FRAME(110)
249*c43cad87SWarner Losh 	BT_FRAME(111)
250*c43cad87SWarner Losh 	BT_FRAME(112)
251*c43cad87SWarner Losh 	BT_FRAME(113)
252*c43cad87SWarner Losh 	BT_FRAME(114)
253*c43cad87SWarner Losh 	BT_FRAME(115)
254*c43cad87SWarner Losh 	BT_FRAME(116)
255*c43cad87SWarner Losh 	BT_FRAME(117)
256*c43cad87SWarner Losh 	BT_FRAME(118)
257*c43cad87SWarner Losh 	BT_FRAME(119)
258*c43cad87SWarner Losh 
259*c43cad87SWarner Losh 	BT_FRAME(120)
260*c43cad87SWarner Losh 	BT_FRAME(121)
261*c43cad87SWarner Losh 	BT_FRAME(122)
262*c43cad87SWarner Losh 	BT_FRAME(123)
263*c43cad87SWarner Losh 	BT_FRAME(124)
264*c43cad87SWarner Losh 	BT_FRAME(125)
265*c43cad87SWarner Losh 	BT_FRAME(126)
266*c43cad87SWarner Losh 	BT_FRAME(127)
267*c43cad87SWarner Losh #undef BT_FRAME
268*c43cad87SWarner Losh }
269*c43cad87SWarner Losh #else
270*c43cad87SWarner Losh static void
271*c43cad87SWarner Losh prof_backtrace_impl(void **vec, unsigned *len, unsigned max_len) {
272*c43cad87SWarner Losh 	cassert(config_prof);
273*c43cad87SWarner Losh 	not_reached();
274*c43cad87SWarner Losh }
275*c43cad87SWarner Losh #endif
276*c43cad87SWarner Losh 
277*c43cad87SWarner Losh void
278*c43cad87SWarner Losh prof_backtrace(tsd_t *tsd, prof_bt_t *bt) {
279*c43cad87SWarner Losh 	cassert(config_prof);
280*c43cad87SWarner Losh 	prof_backtrace_hook_t prof_backtrace_hook = prof_backtrace_hook_get();
281*c43cad87SWarner Losh 	assert(prof_backtrace_hook != NULL);
282*c43cad87SWarner Losh 
283*c43cad87SWarner Losh 	pre_reentrancy(tsd, NULL);
284*c43cad87SWarner Losh 	prof_backtrace_hook(bt->vec, &bt->len, PROF_BT_MAX);
285*c43cad87SWarner Losh 	post_reentrancy(tsd);
286*c43cad87SWarner Losh }
287*c43cad87SWarner Losh 
288*c43cad87SWarner Losh void
289*c43cad87SWarner Losh prof_hooks_init() {
290*c43cad87SWarner Losh 	prof_backtrace_hook_set(&prof_backtrace_impl);
291*c43cad87SWarner Losh 	prof_dump_hook_set(NULL);
292*c43cad87SWarner Losh }
293*c43cad87SWarner Losh 
294*c43cad87SWarner Losh void
295*c43cad87SWarner Losh prof_unwind_init() {
296*c43cad87SWarner Losh #ifdef JEMALLOC_PROF_LIBGCC
297*c43cad87SWarner Losh 	/*
298*c43cad87SWarner Losh 	 * Cause the backtracing machinery to allocate its internal
299*c43cad87SWarner Losh 	 * state before enabling profiling.
300*c43cad87SWarner Losh 	 */
301*c43cad87SWarner Losh 	_Unwind_Backtrace(prof_unwind_init_callback, NULL);
302*c43cad87SWarner Losh #endif
303*c43cad87SWarner Losh }
304*c43cad87SWarner Losh 
305*c43cad87SWarner Losh static int
306*c43cad87SWarner Losh prof_sys_thread_name_read_impl(char *buf, size_t limit) {
307*c43cad87SWarner Losh #if defined(JEMALLOC_HAVE_PTHREAD_GETNAME_NP)
308*c43cad87SWarner Losh 	return pthread_getname_np(pthread_self(), buf, limit);
309*c43cad87SWarner Losh #elif defined(JEMALLOC_HAVE_PTHREAD_GET_NAME_NP)
310*c43cad87SWarner Losh 	pthread_get_name_np(pthread_self(), buf, limit);
311*c43cad87SWarner Losh 	return 0;
312*c43cad87SWarner Losh #else
313*c43cad87SWarner Losh 	return ENOSYS;
314*c43cad87SWarner Losh #endif
315*c43cad87SWarner Losh }
316*c43cad87SWarner Losh prof_sys_thread_name_read_t *JET_MUTABLE prof_sys_thread_name_read =
317*c43cad87SWarner Losh     prof_sys_thread_name_read_impl;
318*c43cad87SWarner Losh 
319*c43cad87SWarner Losh void
320*c43cad87SWarner Losh prof_sys_thread_name_fetch(tsd_t *tsd) {
321*c43cad87SWarner Losh #define THREAD_NAME_MAX_LEN 16
322*c43cad87SWarner Losh 	char buf[THREAD_NAME_MAX_LEN];
323*c43cad87SWarner Losh 	if (!prof_sys_thread_name_read(buf, THREAD_NAME_MAX_LEN)) {
324*c43cad87SWarner Losh 		prof_thread_name_set_impl(tsd, buf);
325*c43cad87SWarner Losh 	}
326*c43cad87SWarner Losh #undef THREAD_NAME_MAX_LEN
327*c43cad87SWarner Losh }
328*c43cad87SWarner Losh 
329*c43cad87SWarner Losh int
330*c43cad87SWarner Losh prof_getpid(void) {
331*c43cad87SWarner Losh #ifdef _WIN32
332*c43cad87SWarner Losh 	return GetCurrentProcessId();
333*c43cad87SWarner Losh #else
334*c43cad87SWarner Losh 	return getpid();
335*c43cad87SWarner Losh #endif
336*c43cad87SWarner Losh }
337*c43cad87SWarner Losh 
338*c43cad87SWarner Losh /*
339*c43cad87SWarner Losh  * This buffer is rather large for stack allocation, so use a single buffer for
340*c43cad87SWarner Losh  * all profile dumps; protected by prof_dump_mtx.
341*c43cad87SWarner Losh  */
342*c43cad87SWarner Losh static char prof_dump_buf[PROF_DUMP_BUFSIZE];
343*c43cad87SWarner Losh 
344*c43cad87SWarner Losh typedef struct prof_dump_arg_s prof_dump_arg_t;
345*c43cad87SWarner Losh struct prof_dump_arg_s {
346*c43cad87SWarner Losh 	/*
347*c43cad87SWarner Losh 	 * Whether error should be handled locally: if true, then we print out
348*c43cad87SWarner Losh 	 * error message as well as abort (if opt_abort is true) when an error
349*c43cad87SWarner Losh 	 * occurred, and we also report the error back to the caller in the end;
350*c43cad87SWarner Losh 	 * if false, then we only report the error back to the caller in the
351*c43cad87SWarner Losh 	 * end.
352*c43cad87SWarner Losh 	 */
353*c43cad87SWarner Losh 	const bool handle_error_locally;
354*c43cad87SWarner Losh 	/*
355*c43cad87SWarner Losh 	 * Whether there has been an error in the dumping process, which could
356*c43cad87SWarner Losh 	 * have happened either in file opening or in file writing.  When an
357*c43cad87SWarner Losh 	 * error has already occurred, we will stop further writing to the file.
358*c43cad87SWarner Losh 	 */
359*c43cad87SWarner Losh 	bool error;
360*c43cad87SWarner Losh 	/* File descriptor of the dump file. */
361*c43cad87SWarner Losh 	int prof_dump_fd;
362*c43cad87SWarner Losh };
363*c43cad87SWarner Losh 
364*c43cad87SWarner Losh static void
365*c43cad87SWarner Losh prof_dump_check_possible_error(prof_dump_arg_t *arg, bool err_cond,
366*c43cad87SWarner Losh     const char *format, ...) {
367*c43cad87SWarner Losh 	assert(!arg->error);
368*c43cad87SWarner Losh 	if (!err_cond) {
369*c43cad87SWarner Losh 		return;
370*c43cad87SWarner Losh 	}
371*c43cad87SWarner Losh 
372*c43cad87SWarner Losh 	arg->error = true;
373*c43cad87SWarner Losh 	if (!arg->handle_error_locally) {
374*c43cad87SWarner Losh 		return;
375*c43cad87SWarner Losh 	}
376*c43cad87SWarner Losh 
377*c43cad87SWarner Losh 	va_list ap;
378*c43cad87SWarner Losh 	char buf[PROF_PRINTF_BUFSIZE];
379*c43cad87SWarner Losh 	va_start(ap, format);
380*c43cad87SWarner Losh 	malloc_vsnprintf(buf, sizeof(buf), format, ap);
381*c43cad87SWarner Losh 	va_end(ap);
382*c43cad87SWarner Losh 	malloc_write(buf);
383*c43cad87SWarner Losh 
384*c43cad87SWarner Losh 	if (opt_abort) {
385*c43cad87SWarner Losh 		abort();
386*c43cad87SWarner Losh 	}
387*c43cad87SWarner Losh }
388*c43cad87SWarner Losh 
389*c43cad87SWarner Losh static int
390*c43cad87SWarner Losh prof_dump_open_file_impl(const char *filename, int mode) {
391*c43cad87SWarner Losh 	return creat(filename, mode);
392*c43cad87SWarner Losh }
393*c43cad87SWarner Losh prof_dump_open_file_t *JET_MUTABLE prof_dump_open_file =
394*c43cad87SWarner Losh     prof_dump_open_file_impl;
395*c43cad87SWarner Losh 
396*c43cad87SWarner Losh static void
397*c43cad87SWarner Losh prof_dump_open(prof_dump_arg_t *arg, const char *filename) {
398*c43cad87SWarner Losh 	arg->prof_dump_fd = prof_dump_open_file(filename, 0644);
399*c43cad87SWarner Losh 	prof_dump_check_possible_error(arg, arg->prof_dump_fd == -1,
400*c43cad87SWarner Losh 	    "<jemalloc>: failed to open \"%s\"\n", filename);
401*c43cad87SWarner Losh }
402*c43cad87SWarner Losh 
403*c43cad87SWarner Losh prof_dump_write_file_t *JET_MUTABLE prof_dump_write_file = malloc_write_fd;
404*c43cad87SWarner Losh 
405*c43cad87SWarner Losh static void
406*c43cad87SWarner Losh prof_dump_flush(void *opaque, const char *s) {
407*c43cad87SWarner Losh 	cassert(config_prof);
408*c43cad87SWarner Losh 	prof_dump_arg_t *arg = (prof_dump_arg_t *)opaque;
409*c43cad87SWarner Losh 	if (!arg->error) {
410*c43cad87SWarner Losh 		ssize_t err = prof_dump_write_file(arg->prof_dump_fd, s,
411*c43cad87SWarner Losh 		    strlen(s));
412*c43cad87SWarner Losh 		prof_dump_check_possible_error(arg, err == -1,
413*c43cad87SWarner Losh 		    "<jemalloc>: failed to write during heap profile flush\n");
414*c43cad87SWarner Losh 	}
415*c43cad87SWarner Losh }
416*c43cad87SWarner Losh 
417*c43cad87SWarner Losh static void
418*c43cad87SWarner Losh prof_dump_close(prof_dump_arg_t *arg) {
419*c43cad87SWarner Losh 	if (arg->prof_dump_fd != -1) {
420*c43cad87SWarner Losh 		close(arg->prof_dump_fd);
421*c43cad87SWarner Losh 	}
422*c43cad87SWarner Losh }
423*c43cad87SWarner Losh 
424*c43cad87SWarner Losh #ifndef _WIN32
425*c43cad87SWarner Losh JEMALLOC_FORMAT_PRINTF(1, 2)
426*c43cad87SWarner Losh static int
427*c43cad87SWarner Losh prof_open_maps_internal(const char *format, ...) {
428*c43cad87SWarner Losh 	int mfd;
429*c43cad87SWarner Losh 	va_list ap;
430*c43cad87SWarner Losh 	char filename[PATH_MAX + 1];
431*c43cad87SWarner Losh 
432*c43cad87SWarner Losh 	va_start(ap, format);
433*c43cad87SWarner Losh 	malloc_vsnprintf(filename, sizeof(filename), format, ap);
434*c43cad87SWarner Losh 	va_end(ap);
435*c43cad87SWarner Losh 
436*c43cad87SWarner Losh #if defined(O_CLOEXEC)
437*c43cad87SWarner Losh 	mfd = open(filename, O_RDONLY | O_CLOEXEC);
438*c43cad87SWarner Losh #else
439*c43cad87SWarner Losh 	mfd = open(filename, O_RDONLY);
440*c43cad87SWarner Losh 	if (mfd != -1) {
441*c43cad87SWarner Losh 		fcntl(mfd, F_SETFD, fcntl(mfd, F_GETFD) | FD_CLOEXEC);
442*c43cad87SWarner Losh 	}
443*c43cad87SWarner Losh #endif
444*c43cad87SWarner Losh 
445*c43cad87SWarner Losh 	return mfd;
446*c43cad87SWarner Losh }
447*c43cad87SWarner Losh #endif
448*c43cad87SWarner Losh 
449*c43cad87SWarner Losh static int
450*c43cad87SWarner Losh prof_dump_open_maps_impl() {
451*c43cad87SWarner Losh 	int mfd;
452*c43cad87SWarner Losh 
453*c43cad87SWarner Losh 	cassert(config_prof);
454*c43cad87SWarner Losh #if defined(__FreeBSD__) || defined(__DragonFly__)
455*c43cad87SWarner Losh 	mfd = prof_open_maps_internal("/proc/curproc/map");
456*c43cad87SWarner Losh #elif defined(_WIN32)
457*c43cad87SWarner Losh 	mfd = -1; // Not implemented
458*c43cad87SWarner Losh #else
459*c43cad87SWarner Losh 	int pid = prof_getpid();
460*c43cad87SWarner Losh 
461*c43cad87SWarner Losh 	mfd = prof_open_maps_internal("/proc/%d/task/%d/maps", pid, pid);
462*c43cad87SWarner Losh 	if (mfd == -1) {
463*c43cad87SWarner Losh 		mfd = prof_open_maps_internal("/proc/%d/maps", pid);
464*c43cad87SWarner Losh 	}
465*c43cad87SWarner Losh #endif
466*c43cad87SWarner Losh 	return mfd;
467*c43cad87SWarner Losh }
468*c43cad87SWarner Losh prof_dump_open_maps_t *JET_MUTABLE prof_dump_open_maps =
469*c43cad87SWarner Losh     prof_dump_open_maps_impl;
470*c43cad87SWarner Losh 
471*c43cad87SWarner Losh static ssize_t
472*c43cad87SWarner Losh prof_dump_read_maps_cb(void *read_cbopaque, void *buf, size_t limit) {
473*c43cad87SWarner Losh 	int mfd = *(int *)read_cbopaque;
474*c43cad87SWarner Losh 	assert(mfd != -1);
475*c43cad87SWarner Losh 	return malloc_read_fd(mfd, buf, limit);
476*c43cad87SWarner Losh }
477*c43cad87SWarner Losh 
478*c43cad87SWarner Losh static void
479*c43cad87SWarner Losh prof_dump_maps(buf_writer_t *buf_writer) {
480*c43cad87SWarner Losh 	int mfd = prof_dump_open_maps();
481*c43cad87SWarner Losh 	if (mfd == -1) {
482*c43cad87SWarner Losh 		return;
483*c43cad87SWarner Losh 	}
484*c43cad87SWarner Losh 
485*c43cad87SWarner Losh 	buf_writer_cb(buf_writer, "\nMAPPED_LIBRARIES:\n");
486*c43cad87SWarner Losh 	buf_writer_pipe(buf_writer, prof_dump_read_maps_cb, &mfd);
487*c43cad87SWarner Losh 	close(mfd);
488*c43cad87SWarner Losh }
489*c43cad87SWarner Losh 
490*c43cad87SWarner Losh static bool
491*c43cad87SWarner Losh prof_dump(tsd_t *tsd, bool propagate_err, const char *filename,
492*c43cad87SWarner Losh     bool leakcheck) {
493*c43cad87SWarner Losh 	cassert(config_prof);
494*c43cad87SWarner Losh 	assert(tsd_reentrancy_level_get(tsd) == 0);
495*c43cad87SWarner Losh 
496*c43cad87SWarner Losh 	prof_tdata_t * tdata = prof_tdata_get(tsd, true);
497*c43cad87SWarner Losh 	if (tdata == NULL) {
498*c43cad87SWarner Losh 		return true;
499*c43cad87SWarner Losh 	}
500*c43cad87SWarner Losh 
501*c43cad87SWarner Losh 	prof_dump_arg_t arg = {/* handle_error_locally */ !propagate_err,
502*c43cad87SWarner Losh 	    /* error */ false, /* prof_dump_fd */ -1};
503*c43cad87SWarner Losh 
504*c43cad87SWarner Losh 	pre_reentrancy(tsd, NULL);
505*c43cad87SWarner Losh 	malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_mtx);
506*c43cad87SWarner Losh 
507*c43cad87SWarner Losh 	prof_dump_open(&arg, filename);
508*c43cad87SWarner Losh 	buf_writer_t buf_writer;
509*c43cad87SWarner Losh 	bool err = buf_writer_init(tsd_tsdn(tsd), &buf_writer, prof_dump_flush,
510*c43cad87SWarner Losh 	    &arg, prof_dump_buf, PROF_DUMP_BUFSIZE);
511*c43cad87SWarner Losh 	assert(!err);
512*c43cad87SWarner Losh 	prof_dump_impl(tsd, buf_writer_cb, &buf_writer, tdata, leakcheck);
513*c43cad87SWarner Losh 	prof_dump_maps(&buf_writer);
514*c43cad87SWarner Losh 	buf_writer_terminate(tsd_tsdn(tsd), &buf_writer);
515*c43cad87SWarner Losh 	prof_dump_close(&arg);
516*c43cad87SWarner Losh 
517*c43cad87SWarner Losh 	prof_dump_hook_t dump_hook = prof_dump_hook_get();
518*c43cad87SWarner Losh 	if (dump_hook != NULL) {
519*c43cad87SWarner Losh 		dump_hook(filename);
520*c43cad87SWarner Losh 	}
521*c43cad87SWarner Losh 	malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_mtx);
522*c43cad87SWarner Losh 	post_reentrancy(tsd);
523*c43cad87SWarner Losh 
524*c43cad87SWarner Losh 	return arg.error;
525*c43cad87SWarner Losh }
526*c43cad87SWarner Losh 
527*c43cad87SWarner Losh /*
528*c43cad87SWarner Losh  * If profiling is off, then PROF_DUMP_FILENAME_LEN is 1, so we'll end up
529*c43cad87SWarner Losh  * calling strncpy with a size of 0, which triggers a -Wstringop-truncation
530*c43cad87SWarner Losh  * warning (strncpy can never actually be called in this case, since we bail out
531*c43cad87SWarner Losh  * much earlier when config_prof is false).  This function works around the
532*c43cad87SWarner Losh  * warning to let us leave the warning on.
533*c43cad87SWarner Losh  */
534*c43cad87SWarner Losh static inline void
535*c43cad87SWarner Losh prof_strncpy(char *UNUSED dest, const char *UNUSED src, size_t UNUSED size) {
536*c43cad87SWarner Losh 	cassert(config_prof);
537*c43cad87SWarner Losh #ifdef JEMALLOC_PROF
538*c43cad87SWarner Losh 	strncpy(dest, src, size);
539*c43cad87SWarner Losh #endif
540*c43cad87SWarner Losh }
541*c43cad87SWarner Losh 
542*c43cad87SWarner Losh static const char *
543*c43cad87SWarner Losh prof_prefix_get(tsdn_t* tsdn) {
544*c43cad87SWarner Losh 	malloc_mutex_assert_owner(tsdn, &prof_dump_filename_mtx);
545*c43cad87SWarner Losh 
546*c43cad87SWarner Losh 	return prof_prefix == NULL ? opt_prof_prefix : prof_prefix;
547*c43cad87SWarner Losh }
548*c43cad87SWarner Losh 
549*c43cad87SWarner Losh static bool
550*c43cad87SWarner Losh prof_prefix_is_empty(tsdn_t *tsdn) {
551*c43cad87SWarner Losh 	malloc_mutex_lock(tsdn, &prof_dump_filename_mtx);
552*c43cad87SWarner Losh 	bool ret = (prof_prefix_get(tsdn)[0] == '\0');
553*c43cad87SWarner Losh 	malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx);
554*c43cad87SWarner Losh 	return ret;
555*c43cad87SWarner Losh }
556*c43cad87SWarner Losh 
557*c43cad87SWarner Losh #define DUMP_FILENAME_BUFSIZE (PATH_MAX + 1)
558*c43cad87SWarner Losh #define VSEQ_INVALID UINT64_C(0xffffffffffffffff)
559*c43cad87SWarner Losh static void
560*c43cad87SWarner Losh prof_dump_filename(tsd_t *tsd, char *filename, char v, uint64_t vseq) {
561*c43cad87SWarner Losh 	cassert(config_prof);
562*c43cad87SWarner Losh 
563*c43cad87SWarner Losh 	assert(tsd_reentrancy_level_get(tsd) == 0);
564*c43cad87SWarner Losh 	const char *prefix = prof_prefix_get(tsd_tsdn(tsd));
565*c43cad87SWarner Losh 
566*c43cad87SWarner Losh 	if (vseq != VSEQ_INVALID) {
567*c43cad87SWarner Losh 	        /* "<prefix>.<pid>.<seq>.v<vseq>.heap" */
568*c43cad87SWarner Losh 		malloc_snprintf(filename, DUMP_FILENAME_BUFSIZE,
569*c43cad87SWarner Losh 		    "%s.%d.%"FMTu64".%c%"FMTu64".heap", prefix, prof_getpid(),
570*c43cad87SWarner Losh 		    prof_dump_seq, v, vseq);
571*c43cad87SWarner Losh 	} else {
572*c43cad87SWarner Losh 	        /* "<prefix>.<pid>.<seq>.<v>.heap" */
573*c43cad87SWarner Losh 		malloc_snprintf(filename, DUMP_FILENAME_BUFSIZE,
574*c43cad87SWarner Losh 		    "%s.%d.%"FMTu64".%c.heap", prefix, prof_getpid(),
575*c43cad87SWarner Losh 		    prof_dump_seq, v);
576*c43cad87SWarner Losh 	}
577*c43cad87SWarner Losh 	prof_dump_seq++;
578*c43cad87SWarner Losh }
579*c43cad87SWarner Losh 
580*c43cad87SWarner Losh void
581*c43cad87SWarner Losh prof_get_default_filename(tsdn_t *tsdn, char *filename, uint64_t ind) {
582*c43cad87SWarner Losh 	malloc_mutex_lock(tsdn, &prof_dump_filename_mtx);
583*c43cad87SWarner Losh 	malloc_snprintf(filename, PROF_DUMP_FILENAME_LEN,
584*c43cad87SWarner Losh 	    "%s.%d.%"FMTu64".json", prof_prefix_get(tsdn), prof_getpid(), ind);
585*c43cad87SWarner Losh 	malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx);
586*c43cad87SWarner Losh }
587*c43cad87SWarner Losh 
588*c43cad87SWarner Losh void
589*c43cad87SWarner Losh prof_fdump_impl(tsd_t *tsd) {
590*c43cad87SWarner Losh 	char filename[DUMP_FILENAME_BUFSIZE];
591*c43cad87SWarner Losh 
592*c43cad87SWarner Losh 	assert(!prof_prefix_is_empty(tsd_tsdn(tsd)));
593*c43cad87SWarner Losh 	malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_filename_mtx);
594*c43cad87SWarner Losh 	prof_dump_filename(tsd, filename, 'f', VSEQ_INVALID);
595*c43cad87SWarner Losh 	malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx);
596*c43cad87SWarner Losh 	prof_dump(tsd, false, filename, opt_prof_leak);
597*c43cad87SWarner Losh }
598*c43cad87SWarner Losh 
599*c43cad87SWarner Losh bool
600*c43cad87SWarner Losh prof_prefix_set(tsdn_t *tsdn, const char *prefix) {
601*c43cad87SWarner Losh 	cassert(config_prof);
602*c43cad87SWarner Losh 	ctl_mtx_assert_held(tsdn);
603*c43cad87SWarner Losh 	malloc_mutex_lock(tsdn, &prof_dump_filename_mtx);
604*c43cad87SWarner Losh 	if (prof_prefix == NULL) {
605*c43cad87SWarner Losh 		malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx);
606*c43cad87SWarner Losh 		/* Everything is still guarded by ctl_mtx. */
607*c43cad87SWarner Losh 		char *buffer = base_alloc(tsdn, prof_base,
608*c43cad87SWarner Losh 		    PROF_DUMP_FILENAME_LEN, QUANTUM);
609*c43cad87SWarner Losh 		if (buffer == NULL) {
610*c43cad87SWarner Losh 			return true;
611*c43cad87SWarner Losh 		}
612*c43cad87SWarner Losh 		malloc_mutex_lock(tsdn, &prof_dump_filename_mtx);
613*c43cad87SWarner Losh 		prof_prefix = buffer;
614*c43cad87SWarner Losh 	}
615*c43cad87SWarner Losh 	assert(prof_prefix != NULL);
616*c43cad87SWarner Losh 
617*c43cad87SWarner Losh 	prof_strncpy(prof_prefix, prefix, PROF_DUMP_FILENAME_LEN - 1);
618*c43cad87SWarner Losh 	prof_prefix[PROF_DUMP_FILENAME_LEN - 1] = '\0';
619*c43cad87SWarner Losh 	malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx);
620*c43cad87SWarner Losh 
621*c43cad87SWarner Losh 	return false;
622*c43cad87SWarner Losh }
623*c43cad87SWarner Losh 
624*c43cad87SWarner Losh void
625*c43cad87SWarner Losh prof_idump_impl(tsd_t *tsd) {
626*c43cad87SWarner Losh 	malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_filename_mtx);
627*c43cad87SWarner Losh 	if (prof_prefix_get(tsd_tsdn(tsd))[0] == '\0') {
628*c43cad87SWarner Losh 		malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx);
629*c43cad87SWarner Losh 		return;
630*c43cad87SWarner Losh 	}
631*c43cad87SWarner Losh 	char filename[PATH_MAX + 1];
632*c43cad87SWarner Losh 	prof_dump_filename(tsd, filename, 'i', prof_dump_iseq);
633*c43cad87SWarner Losh 	prof_dump_iseq++;
634*c43cad87SWarner Losh 	malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx);
635*c43cad87SWarner Losh 	prof_dump(tsd, false, filename, false);
636*c43cad87SWarner Losh }
637*c43cad87SWarner Losh 
638*c43cad87SWarner Losh bool
639*c43cad87SWarner Losh prof_mdump_impl(tsd_t *tsd, const char *filename) {
640*c43cad87SWarner Losh 	char filename_buf[DUMP_FILENAME_BUFSIZE];
641*c43cad87SWarner Losh 	if (filename == NULL) {
642*c43cad87SWarner Losh 		/* No filename specified, so automatically generate one. */
643*c43cad87SWarner Losh 		malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_filename_mtx);
644*c43cad87SWarner Losh 		if (prof_prefix_get(tsd_tsdn(tsd))[0] == '\0') {
645*c43cad87SWarner Losh 			malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx);
646*c43cad87SWarner Losh 			return true;
647*c43cad87SWarner Losh 		}
648*c43cad87SWarner Losh 		prof_dump_filename(tsd, filename_buf, 'm', prof_dump_mseq);
649*c43cad87SWarner Losh 		prof_dump_mseq++;
650*c43cad87SWarner Losh 		malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx);
651*c43cad87SWarner Losh 		filename = filename_buf;
652*c43cad87SWarner Losh 	}
653*c43cad87SWarner Losh 	return prof_dump(tsd, true, filename, false);
654*c43cad87SWarner Losh }
655*c43cad87SWarner Losh 
656*c43cad87SWarner Losh void
657*c43cad87SWarner Losh prof_gdump_impl(tsd_t *tsd) {
658*c43cad87SWarner Losh 	tsdn_t *tsdn = tsd_tsdn(tsd);
659*c43cad87SWarner Losh 	malloc_mutex_lock(tsdn, &prof_dump_filename_mtx);
660*c43cad87SWarner Losh 	if (prof_prefix_get(tsdn)[0] == '\0') {
661*c43cad87SWarner Losh 		malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx);
662*c43cad87SWarner Losh 		return;
663*c43cad87SWarner Losh 	}
664*c43cad87SWarner Losh 	char filename[DUMP_FILENAME_BUFSIZE];
665*c43cad87SWarner Losh 	prof_dump_filename(tsd, filename, 'u', prof_dump_useq);
666*c43cad87SWarner Losh 	prof_dump_useq++;
667*c43cad87SWarner Losh 	malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx);
668*c43cad87SWarner Losh 	prof_dump(tsd, false, filename, false);
669*c43cad87SWarner Losh }
670