Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/jemalloc/src/prof_log.c
39478 views
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
719