1 #include "jemalloc/internal/jemalloc_preamble.h"
2 #include "jemalloc/internal/jemalloc_internal_includes.h"
3
4 #include "jemalloc/internal/assert.h"
5 #include "jemalloc/internal/buf_writer.h"
6 #include "jemalloc/internal/ckh.h"
7 #include "jemalloc/internal/emitter.h"
8 #include "jemalloc/internal/hash.h"
9 #include "jemalloc/internal/malloc_io.h"
10 #include "jemalloc/internal/mutex.h"
11 #include "jemalloc/internal/prof_data.h"
12 #include "jemalloc/internal/prof_log.h"
13 #include "jemalloc/internal/prof_sys.h"
14
15 bool opt_prof_log = false;
16 typedef enum prof_logging_state_e prof_logging_state_t;
17 enum prof_logging_state_e {
18 prof_logging_state_stopped,
19 prof_logging_state_started,
20 prof_logging_state_dumping
21 };
22
23 /*
24 * - stopped: log_start never called, or previous log_stop has completed.
25 * - started: log_start called, log_stop not called yet. Allocations are logged.
26 * - dumping: log_stop called but not finished; samples are not logged anymore.
27 */
28 prof_logging_state_t prof_logging_state = prof_logging_state_stopped;
29
30 /* Used in unit tests. */
31 static bool prof_log_dummy = false;
32
33 /* Incremented for every log file that is output. */
34 static uint64_t log_seq = 0;
35 static char log_filename[
36 /* Minimize memory bloat for non-prof builds. */
37 #ifdef JEMALLOC_PROF
38 PATH_MAX +
39 #endif
40 1];
41
42 /* Timestamp for most recent call to log_start(). */
43 static nstime_t log_start_timestamp;
44
45 /* Increment these when adding to the log_bt and log_thr linked lists. */
46 static size_t log_bt_index = 0;
47 static size_t log_thr_index = 0;
48
49 /* Linked list node definitions. These are only used in this file. */
50 typedef struct prof_bt_node_s prof_bt_node_t;
51
52 struct prof_bt_node_s {
53 prof_bt_node_t *next;
54 size_t index;
55 prof_bt_t bt;
56 /* Variable size backtrace vector pointed to by bt. */
57 void *vec[1];
58 };
59
60 typedef struct prof_thr_node_s prof_thr_node_t;
61
62 struct prof_thr_node_s {
63 prof_thr_node_t *next;
64 size_t index;
65 uint64_t thr_uid;
66 /* Variable size based on thr_name_sz. */
67 char name[1];
68 };
69
70 typedef struct prof_alloc_node_s prof_alloc_node_t;
71
72 /* This is output when logging sampled allocations. */
73 struct prof_alloc_node_s {
74 prof_alloc_node_t *next;
75 /* Indices into an array of thread data. */
76 size_t alloc_thr_ind;
77 size_t free_thr_ind;
78
79 /* Indices into an array of backtraces. */
80 size_t alloc_bt_ind;
81 size_t free_bt_ind;
82
83 uint64_t alloc_time_ns;
84 uint64_t free_time_ns;
85
86 size_t usize;
87 };
88
89 /*
90 * Created on the first call to prof_try_log and deleted on prof_log_stop.
91 * These are the backtraces and threads that have already been logged by an
92 * allocation.
93 */
94 static bool log_tables_initialized = false;
95 static ckh_t log_bt_node_set;
96 static ckh_t log_thr_node_set;
97
98 /* Store linked lists for logged data. */
99 static prof_bt_node_t *log_bt_first = NULL;
100 static prof_bt_node_t *log_bt_last = NULL;
101 static prof_thr_node_t *log_thr_first = NULL;
102 static prof_thr_node_t *log_thr_last = NULL;
103 static prof_alloc_node_t *log_alloc_first = NULL;
104 static prof_alloc_node_t *log_alloc_last = NULL;
105
106 /* Protects the prof_logging_state and any log_{...} variable. */
107 malloc_mutex_t log_mtx;
108
109 /******************************************************************************/
110 /*
111 * Function prototypes for static functions that are referenced prior to
112 * definition.
113 */
114
115 /* Hashtable functions for log_bt_node_set and log_thr_node_set. */
116 static void prof_thr_node_hash(const void *key, size_t r_hash[2]);
117 static bool prof_thr_node_keycomp(const void *k1, const void *k2);
118 static void prof_bt_node_hash(const void *key, size_t r_hash[2]);
119 static bool prof_bt_node_keycomp(const void *k1, const void *k2);
120
121 /******************************************************************************/
122
123 static size_t
prof_log_bt_index(tsd_t * tsd,prof_bt_t * bt)124 prof_log_bt_index(tsd_t *tsd, prof_bt_t *bt) {
125 assert(prof_logging_state == prof_logging_state_started);
126 malloc_mutex_assert_owner(tsd_tsdn(tsd), &log_mtx);
127
128 prof_bt_node_t dummy_node;
129 dummy_node.bt = *bt;
130 prof_bt_node_t *node;
131
132 /* See if this backtrace is already cached in the table. */
133 if (ckh_search(&log_bt_node_set, (void *)(&dummy_node),
134 (void **)(&node), NULL)) {
135 size_t sz = offsetof(prof_bt_node_t, vec) +
136 (bt->len * sizeof(void *));
137 prof_bt_node_t *new_node = (prof_bt_node_t *)
138 iallocztm(tsd_tsdn(tsd), sz, sz_size2index(sz), false, NULL,
139 true, arena_get(TSDN_NULL, 0, true), true);
140 if (log_bt_first == NULL) {
141 log_bt_first = new_node;
142 log_bt_last = new_node;
143 } else {
144 log_bt_last->next = new_node;
145 log_bt_last = new_node;
146 }
147
148 new_node->next = NULL;
149 new_node->index = log_bt_index;
150 /*
151 * Copy the backtrace: bt is inside a tdata or gctx, which
152 * might die before prof_log_stop is called.
153 */
154 new_node->bt.len = bt->len;
155 memcpy(new_node->vec, bt->vec, bt->len * sizeof(void *));
156 new_node->bt.vec = new_node->vec;
157
158 log_bt_index++;
159 ckh_insert(tsd, &log_bt_node_set, (void *)new_node, NULL);
160 return new_node->index;
161 } else {
162 return node->index;
163 }
164 }
165
166 static size_t
prof_log_thr_index(tsd_t * tsd,uint64_t thr_uid,const char * name)167 prof_log_thr_index(tsd_t *tsd, uint64_t thr_uid, const char *name) {
168 assert(prof_logging_state == prof_logging_state_started);
169 malloc_mutex_assert_owner(tsd_tsdn(tsd), &log_mtx);
170
171 prof_thr_node_t dummy_node;
172 dummy_node.thr_uid = thr_uid;
173 prof_thr_node_t *node;
174
175 /* See if this thread is already cached in the table. */
176 if (ckh_search(&log_thr_node_set, (void *)(&dummy_node),
177 (void **)(&node), NULL)) {
178 size_t sz = offsetof(prof_thr_node_t, name) + strlen(name) + 1;
179 prof_thr_node_t *new_node = (prof_thr_node_t *)
180 iallocztm(tsd_tsdn(tsd), sz, sz_size2index(sz), false, NULL,
181 true, arena_get(TSDN_NULL, 0, true), true);
182 if (log_thr_first == NULL) {
183 log_thr_first = new_node;
184 log_thr_last = new_node;
185 } else {
186 log_thr_last->next = new_node;
187 log_thr_last = new_node;
188 }
189
190 new_node->next = NULL;
191 new_node->index = log_thr_index;
192 new_node->thr_uid = thr_uid;
193 strcpy(new_node->name, name);
194
195 log_thr_index++;
196 ckh_insert(tsd, &log_thr_node_set, (void *)new_node, NULL);
197 return new_node->index;
198 } else {
199 return node->index;
200 }
201 }
202
203 JEMALLOC_COLD
204 void
prof_try_log(tsd_t * tsd,size_t usize,prof_info_t * prof_info)205 prof_try_log(tsd_t *tsd, size_t usize, prof_info_t *prof_info) {
206 cassert(config_prof);
207 prof_tctx_t *tctx = prof_info->alloc_tctx;
208 malloc_mutex_assert_owner(tsd_tsdn(tsd), tctx->tdata->lock);
209
210 prof_tdata_t *cons_tdata = prof_tdata_get(tsd, false);
211 if (cons_tdata == NULL) {
212 /*
213 * We decide not to log these allocations. cons_tdata will be
214 * NULL only when the current thread is in a weird state (e.g.
215 * it's being destroyed).
216 */
217 return;
218 }
219
220 malloc_mutex_lock(tsd_tsdn(tsd), &log_mtx);
221
222 if (prof_logging_state != prof_logging_state_started) {
223 goto label_done;
224 }
225
226 if (!log_tables_initialized) {
227 bool err1 = ckh_new(tsd, &log_bt_node_set, PROF_CKH_MINITEMS,
228 prof_bt_node_hash, prof_bt_node_keycomp);
229 bool err2 = ckh_new(tsd, &log_thr_node_set, PROF_CKH_MINITEMS,
230 prof_thr_node_hash, prof_thr_node_keycomp);
231 if (err1 || err2) {
232 goto label_done;
233 }
234 log_tables_initialized = true;
235 }
236
237 nstime_t alloc_time = prof_info->alloc_time;
238 nstime_t free_time;
239 nstime_prof_init_update(&free_time);
240
241 size_t sz = sizeof(prof_alloc_node_t);
242 prof_alloc_node_t *new_node = (prof_alloc_node_t *)
243 iallocztm(tsd_tsdn(tsd), sz, sz_size2index(sz), false, NULL, true,
244 arena_get(TSDN_NULL, 0, true), true);
245
246 const char *prod_thr_name = (tctx->tdata->thread_name == NULL)?
247 "" : tctx->tdata->thread_name;
248 const char *cons_thr_name = prof_thread_name_get(tsd);
249
250 prof_bt_t bt;
251 /* Initialize the backtrace, using the buffer in tdata to store it. */
252 bt_init(&bt, cons_tdata->vec);
253 prof_backtrace(tsd, &bt);
254 prof_bt_t *cons_bt = &bt;
255
256 /* We haven't destroyed tctx yet, so gctx should be good to read. */
257 prof_bt_t *prod_bt = &tctx->gctx->bt;
258
259 new_node->next = NULL;
260 new_node->alloc_thr_ind = prof_log_thr_index(tsd, tctx->tdata->thr_uid,
261 prod_thr_name);
262 new_node->free_thr_ind = prof_log_thr_index(tsd, cons_tdata->thr_uid,
263 cons_thr_name);
264 new_node->alloc_bt_ind = prof_log_bt_index(tsd, prod_bt);
265 new_node->free_bt_ind = prof_log_bt_index(tsd, cons_bt);
266 new_node->alloc_time_ns = nstime_ns(&alloc_time);
267 new_node->free_time_ns = nstime_ns(&free_time);
268 new_node->usize = usize;
269
270 if (log_alloc_first == NULL) {
271 log_alloc_first = new_node;
272 log_alloc_last = new_node;
273 } else {
274 log_alloc_last->next = new_node;
275 log_alloc_last = new_node;
276 }
277
278 label_done:
279 malloc_mutex_unlock(tsd_tsdn(tsd), &log_mtx);
280 }
281
282 static void
prof_bt_node_hash(const void * key,size_t r_hash[2])283 prof_bt_node_hash(const void *key, size_t r_hash[2]) {
284 const prof_bt_node_t *bt_node = (prof_bt_node_t *)key;
285 prof_bt_hash((void *)(&bt_node->bt), r_hash);
286 }
287
288 static bool
prof_bt_node_keycomp(const void * k1,const void * k2)289 prof_bt_node_keycomp(const void *k1, const void *k2) {
290 const prof_bt_node_t *bt_node1 = (prof_bt_node_t *)k1;
291 const prof_bt_node_t *bt_node2 = (prof_bt_node_t *)k2;
292 return prof_bt_keycomp((void *)(&bt_node1->bt),
293 (void *)(&bt_node2->bt));
294 }
295
296 static void
prof_thr_node_hash(const void * key,size_t r_hash[2])297 prof_thr_node_hash(const void *key, size_t r_hash[2]) {
298 const prof_thr_node_t *thr_node = (prof_thr_node_t *)key;
299 hash(&thr_node->thr_uid, sizeof(uint64_t), 0x94122f35U, r_hash);
300 }
301
302 static bool
prof_thr_node_keycomp(const void * k1,const void * k2)303 prof_thr_node_keycomp(const void *k1, const void *k2) {
304 const prof_thr_node_t *thr_node1 = (prof_thr_node_t *)k1;
305 const prof_thr_node_t *thr_node2 = (prof_thr_node_t *)k2;
306 return thr_node1->thr_uid == thr_node2->thr_uid;
307 }
308
309 /* Used in unit tests. */
310 size_t
prof_log_bt_count(void)311 prof_log_bt_count(void) {
312 cassert(config_prof);
313 size_t cnt = 0;
314 prof_bt_node_t *node = log_bt_first;
315 while (node != NULL) {
316 cnt++;
317 node = node->next;
318 }
319 return cnt;
320 }
321
322 /* Used in unit tests. */
323 size_t
prof_log_alloc_count(void)324 prof_log_alloc_count(void) {
325 cassert(config_prof);
326 size_t cnt = 0;
327 prof_alloc_node_t *node = log_alloc_first;
328 while (node != NULL) {
329 cnt++;
330 node = node->next;
331 }
332 return cnt;
333 }
334
335 /* Used in unit tests. */
336 size_t
prof_log_thr_count(void)337 prof_log_thr_count(void) {
338 cassert(config_prof);
339 size_t cnt = 0;
340 prof_thr_node_t *node = log_thr_first;
341 while (node != NULL) {
342 cnt++;
343 node = node->next;
344 }
345 return cnt;
346 }
347
348 /* Used in unit tests. */
349 bool
prof_log_is_logging(void)350 prof_log_is_logging(void) {
351 cassert(config_prof);
352 return prof_logging_state == prof_logging_state_started;
353 }
354
355 /* Used in unit tests. */
356 bool
prof_log_rep_check(void)357 prof_log_rep_check(void) {
358 cassert(config_prof);
359 if (prof_logging_state == prof_logging_state_stopped
360 && log_tables_initialized) {
361 return true;
362 }
363
364 if (log_bt_last != NULL && log_bt_last->next != NULL) {
365 return true;
366 }
367 if (log_thr_last != NULL && log_thr_last->next != NULL) {
368 return true;
369 }
370 if (log_alloc_last != NULL && log_alloc_last->next != NULL) {
371 return true;
372 }
373
374 size_t bt_count = prof_log_bt_count();
375 size_t thr_count = prof_log_thr_count();
376 size_t alloc_count = prof_log_alloc_count();
377
378
379 if (prof_logging_state == prof_logging_state_stopped) {
380 if (bt_count != 0 || thr_count != 0 || alloc_count || 0) {
381 return true;
382 }
383 }
384
385 prof_alloc_node_t *node = log_alloc_first;
386 while (node != NULL) {
387 if (node->alloc_bt_ind >= bt_count) {
388 return true;
389 }
390 if (node->free_bt_ind >= bt_count) {
391 return true;
392 }
393 if (node->alloc_thr_ind >= thr_count) {
394 return true;
395 }
396 if (node->free_thr_ind >= thr_count) {
397 return true;
398 }
399 if (node->alloc_time_ns > node->free_time_ns) {
400 return true;
401 }
402 node = node->next;
403 }
404
405 return false;
406 }
407
408 /* Used in unit tests. */
409 void
prof_log_dummy_set(bool new_value)410 prof_log_dummy_set(bool new_value) {
411 cassert(config_prof);
412 prof_log_dummy = new_value;
413 }
414
415 /* Used as an atexit function to stop logging on exit. */
416 static void
prof_log_stop_final(void)417 prof_log_stop_final(void) {
418 tsd_t *tsd = tsd_fetch();
419 prof_log_stop(tsd_tsdn(tsd));
420 }
421
422 JEMALLOC_COLD
423 bool
prof_log_start(tsdn_t * tsdn,const char * filename)424 prof_log_start(tsdn_t *tsdn, const char *filename) {
425 cassert(config_prof);
426
427 if (!opt_prof) {
428 return true;
429 }
430
431 bool ret = false;
432
433 malloc_mutex_lock(tsdn, &log_mtx);
434
435 static bool prof_log_atexit_called = false;
436 if (!prof_log_atexit_called) {
437 prof_log_atexit_called = true;
438 if (atexit(prof_log_stop_final) != 0) {
439 malloc_write("<jemalloc>: Error in atexit() "
440 "for logging\n");
441 if (opt_abort) {
442 abort();
443 }
444 ret = true;
445 goto label_done;
446 }
447 }
448
449 if (prof_logging_state != prof_logging_state_stopped) {
450 ret = true;
451 } else if (filename == NULL) {
452 /* Make default name. */
453 prof_get_default_filename(tsdn, log_filename, log_seq);
454 log_seq++;
455 prof_logging_state = prof_logging_state_started;
456 } else if (strlen(filename) >= PROF_DUMP_FILENAME_LEN) {
457 ret = true;
458 } else {
459 strcpy(log_filename, filename);
460 prof_logging_state = prof_logging_state_started;
461 }
462
463 if (!ret) {
464 nstime_prof_init_update(&log_start_timestamp);
465 }
466 label_done:
467 malloc_mutex_unlock(tsdn, &log_mtx);
468
469 return ret;
470 }
471
472 struct prof_emitter_cb_arg_s {
473 int fd;
474 ssize_t ret;
475 };
476
477 static void
prof_emitter_write_cb(void * opaque,const char * to_write)478 prof_emitter_write_cb(void *opaque, const char *to_write) {
479 struct prof_emitter_cb_arg_s *arg =
480 (struct prof_emitter_cb_arg_s *)opaque;
481 size_t bytes = strlen(to_write);
482 if (prof_log_dummy) {
483 return;
484 }
485 arg->ret = malloc_write_fd(arg->fd, to_write, bytes);
486 }
487
488 /*
489 * prof_log_emit_{...} goes through the appropriate linked list, emitting each
490 * node to the json and deallocating it.
491 */
492 static void
prof_log_emit_threads(tsd_t * tsd,emitter_t * emitter)493 prof_log_emit_threads(tsd_t *tsd, emitter_t *emitter) {
494 emitter_json_array_kv_begin(emitter, "threads");
495 prof_thr_node_t *thr_node = log_thr_first;
496 prof_thr_node_t *thr_old_node;
497 while (thr_node != NULL) {
498 emitter_json_object_begin(emitter);
499
500 emitter_json_kv(emitter, "thr_uid", emitter_type_uint64,
501 &thr_node->thr_uid);
502
503 char *thr_name = thr_node->name;
504
505 emitter_json_kv(emitter, "thr_name", emitter_type_string,
506 &thr_name);
507
508 emitter_json_object_end(emitter);
509 thr_old_node = thr_node;
510 thr_node = thr_node->next;
511 idalloctm(tsd_tsdn(tsd), thr_old_node, NULL, NULL, true, true);
512 }
513 emitter_json_array_end(emitter);
514 }
515
516 static void
prof_log_emit_traces(tsd_t * tsd,emitter_t * emitter)517 prof_log_emit_traces(tsd_t *tsd, emitter_t *emitter) {
518 emitter_json_array_kv_begin(emitter, "stack_traces");
519 prof_bt_node_t *bt_node = log_bt_first;
520 prof_bt_node_t *bt_old_node;
521 /*
522 * Calculate how many hex digits we need: twice number of bytes, two for
523 * "0x", and then one more for terminating '\0'.
524 */
525 char buf[2 * sizeof(intptr_t) + 3];
526 size_t buf_sz = sizeof(buf);
527 while (bt_node != NULL) {
528 emitter_json_array_begin(emitter);
529 size_t i;
530 for (i = 0; i < bt_node->bt.len; i++) {
531 malloc_snprintf(buf, buf_sz, "%p", bt_node->bt.vec[i]);
532 char *trace_str = buf;
533 emitter_json_value(emitter, emitter_type_string,
534 &trace_str);
535 }
536 emitter_json_array_end(emitter);
537
538 bt_old_node = bt_node;
539 bt_node = bt_node->next;
540 idalloctm(tsd_tsdn(tsd), bt_old_node, NULL, NULL, true, true);
541 }
542 emitter_json_array_end(emitter);
543 }
544
545 static void
prof_log_emit_allocs(tsd_t * tsd,emitter_t * emitter)546 prof_log_emit_allocs(tsd_t *tsd, emitter_t *emitter) {
547 emitter_json_array_kv_begin(emitter, "allocations");
548 prof_alloc_node_t *alloc_node = log_alloc_first;
549 prof_alloc_node_t *alloc_old_node;
550 while (alloc_node != NULL) {
551 emitter_json_object_begin(emitter);
552
553 emitter_json_kv(emitter, "alloc_thread", emitter_type_size,
554 &alloc_node->alloc_thr_ind);
555
556 emitter_json_kv(emitter, "free_thread", emitter_type_size,
557 &alloc_node->free_thr_ind);
558
559 emitter_json_kv(emitter, "alloc_trace", emitter_type_size,
560 &alloc_node->alloc_bt_ind);
561
562 emitter_json_kv(emitter, "free_trace", emitter_type_size,
563 &alloc_node->free_bt_ind);
564
565 emitter_json_kv(emitter, "alloc_timestamp",
566 emitter_type_uint64, &alloc_node->alloc_time_ns);
567
568 emitter_json_kv(emitter, "free_timestamp", emitter_type_uint64,
569 &alloc_node->free_time_ns);
570
571 emitter_json_kv(emitter, "usize", emitter_type_uint64,
572 &alloc_node->usize);
573
574 emitter_json_object_end(emitter);
575
576 alloc_old_node = alloc_node;
577 alloc_node = alloc_node->next;
578 idalloctm(tsd_tsdn(tsd), alloc_old_node, NULL, NULL, true,
579 true);
580 }
581 emitter_json_array_end(emitter);
582 }
583
584 static void
prof_log_emit_metadata(emitter_t * emitter)585 prof_log_emit_metadata(emitter_t *emitter) {
586 emitter_json_object_kv_begin(emitter, "info");
587
588 nstime_t now;
589
590 nstime_prof_init_update(&now);
591 uint64_t ns = nstime_ns(&now) - nstime_ns(&log_start_timestamp);
592 emitter_json_kv(emitter, "duration", emitter_type_uint64, &ns);
593
594 char *vers = JEMALLOC_VERSION;
595 emitter_json_kv(emitter, "version",
596 emitter_type_string, &vers);
597
598 emitter_json_kv(emitter, "lg_sample_rate",
599 emitter_type_int, &lg_prof_sample);
600
601 const char *res_type = prof_time_res_mode_names[opt_prof_time_res];
602 emitter_json_kv(emitter, "prof_time_resolution", emitter_type_string,
603 &res_type);
604
605 int pid = prof_getpid();
606 emitter_json_kv(emitter, "pid", emitter_type_int, &pid);
607
608 emitter_json_object_end(emitter);
609 }
610
611 #define PROF_LOG_STOP_BUFSIZE PROF_DUMP_BUFSIZE
612 JEMALLOC_COLD
613 bool
prof_log_stop(tsdn_t * tsdn)614 prof_log_stop(tsdn_t *tsdn) {
615 cassert(config_prof);
616 if (!opt_prof || !prof_booted) {
617 return true;
618 }
619
620 tsd_t *tsd = tsdn_tsd(tsdn);
621 malloc_mutex_lock(tsdn, &log_mtx);
622
623 if (prof_logging_state != prof_logging_state_started) {
624 malloc_mutex_unlock(tsdn, &log_mtx);
625 return true;
626 }
627
628 /*
629 * Set the state to dumping. We'll set it to stopped when we're done.
630 * Since other threads won't be able to start/stop/log when the state is
631 * dumping, we don't have to hold the lock during the whole method.
632 */
633 prof_logging_state = prof_logging_state_dumping;
634 malloc_mutex_unlock(tsdn, &log_mtx);
635
636
637 emitter_t emitter;
638
639 /* Create a file. */
640
641 int fd;
642 if (prof_log_dummy) {
643 fd = 0;
644 } else {
645 fd = creat(log_filename, 0644);
646 }
647
648 if (fd == -1) {
649 malloc_printf("<jemalloc>: creat() for log file \"%s\" "
650 " failed with %d\n", log_filename, errno);
651 if (opt_abort) {
652 abort();
653 }
654 return true;
655 }
656
657 struct prof_emitter_cb_arg_s arg;
658 arg.fd = fd;
659
660 buf_writer_t buf_writer;
661 buf_writer_init(tsdn, &buf_writer, prof_emitter_write_cb, &arg, NULL,
662 PROF_LOG_STOP_BUFSIZE);
663 emitter_init(&emitter, emitter_output_json_compact, buf_writer_cb,
664 &buf_writer);
665
666 emitter_begin(&emitter);
667 prof_log_emit_metadata(&emitter);
668 prof_log_emit_threads(tsd, &emitter);
669 prof_log_emit_traces(tsd, &emitter);
670 prof_log_emit_allocs(tsd, &emitter);
671 emitter_end(&emitter);
672
673 buf_writer_terminate(tsdn, &buf_writer);
674
675 /* Reset global state. */
676 if (log_tables_initialized) {
677 ckh_delete(tsd, &log_bt_node_set);
678 ckh_delete(tsd, &log_thr_node_set);
679 }
680 log_tables_initialized = false;
681 log_bt_index = 0;
682 log_thr_index = 0;
683 log_bt_first = NULL;
684 log_bt_last = NULL;
685 log_thr_first = NULL;
686 log_thr_last = NULL;
687 log_alloc_first = NULL;
688 log_alloc_last = NULL;
689
690 malloc_mutex_lock(tsdn, &log_mtx);
691 prof_logging_state = prof_logging_state_stopped;
692 malloc_mutex_unlock(tsdn, &log_mtx);
693
694 if (prof_log_dummy) {
695 return false;
696 }
697 return close(fd) || arg.ret == -1;
698 }
699 #undef PROF_LOG_STOP_BUFSIZE
700
701 JEMALLOC_COLD
702 bool
prof_log_init(tsd_t * tsd)703 prof_log_init(tsd_t *tsd) {
704 cassert(config_prof);
705 if (malloc_mutex_init(&log_mtx, "prof_log",
706 WITNESS_RANK_PROF_LOG, malloc_mutex_rank_exclusive)) {
707 return true;
708 }
709
710 if (opt_prof_log) {
711 prof_log_start(tsd_tsdn(tsd), NULL);
712 }
713
714 return false;
715 }
716
717 /******************************************************************************/
718