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