1 #define JEMALLOC_PROF_C_
2 #include "jemalloc/internal/jemalloc_internal.h"
3 /******************************************************************************/
5 #ifdef JEMALLOC_PROF_LIBUNWIND
10 #ifdef JEMALLOC_PROF_LIBGCC
14 /******************************************************************************/
17 bool opt_prof
= false;
18 bool opt_prof_active
= true;
19 bool opt_prof_thread_active_init
= true;
20 size_t opt_lg_prof_sample
= LG_PROF_SAMPLE_DEFAULT
;
21 ssize_t opt_lg_prof_interval
= LG_PROF_INTERVAL_DEFAULT
;
22 bool opt_prof_gdump
= false;
23 bool opt_prof_final
= true;
24 bool opt_prof_leak
= false;
25 bool opt_prof_accum
= false;
27 /* Minimize memory bloat for non-prof builds. */
34 * Initialized as opt_prof_active, and accessed via
35 * prof_active_[gs]et{_unlocked,}().
38 static malloc_mutex_t prof_active_mtx
;
41 * Initialized as opt_prof_thread_active_init, and accessed via
42 * prof_thread_active_init_[gs]et().
44 static bool prof_thread_active_init
;
45 static malloc_mutex_t prof_thread_active_init_mtx
;
47 uint64_t prof_interval
= 0;
49 size_t lg_prof_sample
;
52 * Table of mutexes that are shared among gctx's. These are leaf locks, so
53 * there is no problem with using them for more than one gctx at the same time.
54 * The primary motivation for this sharing though is that gctx's are ephemeral,
55 * and destroying mutexes causes complications for systems that allocate when
56 * creating/destroying mutexes.
58 static malloc_mutex_t
*gctx_locks
;
59 static unsigned cum_gctxs
; /* Atomic counter. */
62 * Table of mutexes that are shared among tdata's. No operations require
63 * holding multiple tdata locks, so there is no problem with using them for more
64 * than one tdata at the same time, even though a gctx lock may be acquired
65 * while holding a tdata lock.
67 static malloc_mutex_t
*tdata_locks
;
70 * Global hash of (prof_bt_t *)-->(prof_gctx_t *). This is the master data
71 * structure that knows about all backtraces currently captured.
74 static malloc_mutex_t bt2gctx_mtx
;
77 * Tree of all extant prof_tdata_t structures, regardless of state,
78 * {attached,detached,expired}.
80 static prof_tdata_tree_t tdatas
;
81 static malloc_mutex_t tdatas_mtx
;
83 static uint64_t next_thr_uid
;
84 static malloc_mutex_t next_thr_uid_mtx
;
86 static malloc_mutex_t prof_dump_seq_mtx
;
87 static uint64_t prof_dump_seq
;
88 static uint64_t prof_dump_iseq
;
89 static uint64_t prof_dump_mseq
;
90 static uint64_t prof_dump_useq
;
93 * This buffer is rather large for stack allocation, so use a single buffer for
96 static malloc_mutex_t prof_dump_mtx
;
97 static char prof_dump_buf
[
98 /* Minimize memory bloat for non-prof builds. */
105 static unsigned prof_dump_buf_end
;
106 static int prof_dump_fd
;
108 /* Do not dump any profiles until bootstrapping is complete. */
109 static bool prof_booted
= false;
111 /******************************************************************************/
113 * Function prototypes for static functions that are referenced prior to
117 static bool prof_tctx_should_destroy(prof_tctx_t
*tctx
);
118 static void prof_tctx_destroy(tsd_t
*tsd
, prof_tctx_t
*tctx
);
119 static bool prof_tdata_should_destroy(prof_tdata_t
*tdata
,
120 bool even_if_attached
);
121 static void prof_tdata_destroy(tsd_t
*tsd
, prof_tdata_t
*tdata
,
122 bool even_if_attached
);
123 static char *prof_thread_name_alloc(tsd_t
*tsd
, const char *thread_name
);
125 /******************************************************************************/
126 /* Red-black trees. */
128 JEMALLOC_INLINE_C
int
129 prof_tctx_comp(const prof_tctx_t
*a
, const prof_tctx_t
*b
)
131 uint64_t a_uid
= a
->tdata
->thr_uid
;
132 uint64_t b_uid
= b
->tdata
->thr_uid
;
134 return ((a_uid
> b_uid
) - (a_uid
< b_uid
));
137 rb_gen(static UNUSED
, tctx_tree_
, prof_tctx_tree_t
, prof_tctx_t
,
138 tctx_link
, prof_tctx_comp
)
140 JEMALLOC_INLINE_C
int
141 prof_gctx_comp(const prof_gctx_t
*a
, const prof_gctx_t
*b
)
143 unsigned a_len
= a
->bt
.len
;
144 unsigned b_len
= b
->bt
.len
;
145 unsigned comp_len
= (a_len
< b_len
) ? a_len
: b_len
;
146 int ret
= memcmp(a
->bt
.vec
, b
->bt
.vec
, comp_len
* sizeof(void *));
148 ret
= (a_len
> b_len
) - (a_len
< b_len
);
152 rb_gen(static UNUSED
, gctx_tree_
, prof_gctx_tree_t
, prof_gctx_t
, dump_link
,
155 JEMALLOC_INLINE_C
int
156 prof_tdata_comp(const prof_tdata_t
*a
, const prof_tdata_t
*b
)
159 uint64_t a_uid
= a
->thr_uid
;
160 uint64_t b_uid
= b
->thr_uid
;
162 ret
= ((a_uid
> b_uid
) - (a_uid
< b_uid
));
164 uint64_t a_discrim
= a
->thr_discrim
;
165 uint64_t b_discrim
= b
->thr_discrim
;
167 ret
= ((a_discrim
> b_discrim
) - (a_discrim
< b_discrim
));
172 rb_gen(static UNUSED
, tdata_tree_
, prof_tdata_tree_t
, prof_tdata_t
, tdata_link
,
175 /******************************************************************************/
178 prof_alloc_rollback(tsd_t
*tsd
, prof_tctx_t
*tctx
, bool updated
)
182 cassert(config_prof
);
186 * Compute a new sample threshold. This isn't very important in
187 * practice, because this function is rarely executed, so the
188 * potential for sample bias is minimal except in contrived
191 tdata
= prof_tdata_get(tsd
, true);
193 prof_sample_threshold_update(tctx
->tdata
);
196 if ((uintptr_t)tctx
> (uintptr_t)1U) {
197 malloc_mutex_lock(tctx
->tdata
->lock
);
198 tctx
->prepared
= false;
199 if (prof_tctx_should_destroy(tctx
))
200 prof_tctx_destroy(tsd
, tctx
);
202 malloc_mutex_unlock(tctx
->tdata
->lock
);
207 prof_malloc_sample_object(const void *ptr
, size_t usize
, prof_tctx_t
*tctx
) {
208 prof_tctx_set(ptr
, tctx
);
210 malloc_mutex_lock(tctx
->tdata
->lock
);
211 tctx
->cnts
.curobjs
++;
212 tctx
->cnts
.curbytes
+= usize
;
213 if (opt_prof_accum
) {
214 tctx
->cnts
.accumobjs
++;
215 tctx
->cnts
.accumbytes
+= usize
;
217 tctx
->prepared
= false;
218 malloc_mutex_unlock(tctx
->tdata
->lock
);
222 prof_free_sampled_object(tsd_t
*tsd
, size_t usize
, prof_tctx_t
*tctx
)
225 malloc_mutex_lock(tctx
->tdata
->lock
);
226 assert(tctx
->cnts
.curobjs
> 0);
227 assert(tctx
->cnts
.curbytes
>= usize
);
228 tctx
->cnts
.curobjs
--;
229 tctx
->cnts
.curbytes
-= usize
;
231 if (prof_tctx_should_destroy(tctx
))
232 prof_tctx_destroy(tsd
, tctx
);
234 malloc_mutex_unlock(tctx
->tdata
->lock
);
238 bt_init(prof_bt_t
*bt
, void **vec
)
241 cassert(config_prof
);
248 prof_enter(prof_tdata_t
*tdata
)
251 cassert(config_prof
);
256 malloc_mutex_lock(&bt2gctx_mtx
);
260 prof_leave(prof_tdata_t
*tdata
)
264 cassert(config_prof
);
266 malloc_mutex_unlock(&bt2gctx_mtx
);
270 idump
= tdata
->enq_idump
;
271 tdata
->enq_idump
= false;
272 gdump
= tdata
->enq_gdump
;
273 tdata
->enq_gdump
= false;
281 #ifdef JEMALLOC_PROF_LIBUNWIND
283 prof_backtrace(prof_bt_t
*bt
)
287 cassert(config_prof
);
288 assert(bt
->len
== 0);
289 assert(bt
->vec
!= NULL
);
291 nframes
= unw_backtrace(bt
->vec
, PROF_BT_MAX
);
296 #elif (defined(JEMALLOC_PROF_LIBGCC))
297 static _Unwind_Reason_Code
298 prof_unwind_init_callback(struct _Unwind_Context
*context
, void *arg
)
301 cassert(config_prof
);
303 return (_URC_NO_REASON
);
306 static _Unwind_Reason_Code
307 prof_unwind_callback(struct _Unwind_Context
*context
, void *arg
)
309 prof_unwind_data_t
*data
= (prof_unwind_data_t
*)arg
;
312 cassert(config_prof
);
314 ip
= (void *)_Unwind_GetIP(context
);
316 return (_URC_END_OF_STACK
);
317 data
->bt
->vec
[data
->bt
->len
] = ip
;
319 if (data
->bt
->len
== data
->max
)
320 return (_URC_END_OF_STACK
);
322 return (_URC_NO_REASON
);
326 prof_backtrace(prof_bt_t
*bt
)
328 prof_unwind_data_t data
= {bt
, PROF_BT_MAX
};
330 cassert(config_prof
);
332 _Unwind_Backtrace(prof_unwind_callback
, &data
);
334 #elif (defined(JEMALLOC_PROF_GCC))
336 prof_backtrace(prof_bt_t
*bt
)
338 #define BT_FRAME(i) \
339 if ((i) < PROF_BT_MAX) { \
341 if (__builtin_frame_address(i) == 0) \
343 p = __builtin_return_address(i); \
351 cassert(config_prof
);
497 prof_backtrace(prof_bt_t
*bt
)
500 cassert(config_prof
);
505 static malloc_mutex_t
*
506 prof_gctx_mutex_choose(void)
508 unsigned ngctxs
= atomic_add_u(&cum_gctxs
, 1);
510 return (&gctx_locks
[(ngctxs
- 1) % PROF_NCTX_LOCKS
]);
513 static malloc_mutex_t
*
514 prof_tdata_mutex_choose(uint64_t thr_uid
)
517 return (&tdata_locks
[thr_uid
% PROF_NTDATA_LOCKS
]);
521 prof_gctx_create(tsd_t
*tsd
, prof_bt_t
*bt
)
524 * Create a single allocation that has space for vec of length bt->len.
526 prof_gctx_t
*gctx
= (prof_gctx_t
*)imalloc(tsd
, offsetof(prof_gctx_t
,
527 vec
) + (bt
->len
* sizeof(void *)));
530 gctx
->lock
= prof_gctx_mutex_choose();
532 * Set nlimbo to 1, in order to avoid a race condition with
533 * prof_tctx_destroy()/prof_gctx_try_destroy().
536 tctx_tree_new(&gctx
->tctxs
);
538 memcpy(gctx
->vec
, bt
->vec
, bt
->len
* sizeof(void *));
539 gctx
->bt
.vec
= gctx
->vec
;
540 gctx
->bt
.len
= bt
->len
;
545 prof_gctx_try_destroy(tsd_t
*tsd
, prof_gctx_t
*gctx
, prof_tdata_t
*tdata
)
548 cassert(config_prof
);
551 * Check that gctx is still unused by any thread cache before destroying
552 * it. prof_lookup() increments gctx->nlimbo in order to avoid a race
553 * condition with this function, as does prof_tctx_destroy() in order to
554 * avoid a race between the main body of prof_tctx_destroy() and entry
555 * into this function.
558 malloc_mutex_lock(gctx
->lock
);
559 assert(gctx
->nlimbo
!= 0);
560 if (tctx_tree_empty(&gctx
->tctxs
) && gctx
->nlimbo
== 1) {
561 /* Remove gctx from bt2gctx. */
562 if (ckh_remove(tsd
, &bt2gctx
, &gctx
->bt
, NULL
, NULL
))
566 malloc_mutex_unlock(gctx
->lock
);
570 * Compensate for increment in prof_tctx_destroy() or
574 malloc_mutex_unlock(gctx
->lock
);
579 /* tctx->tdata->lock must be held. */
581 prof_tctx_should_destroy(prof_tctx_t
*tctx
)
586 if (tctx
->cnts
.curobjs
!= 0)
594 prof_gctx_should_destroy(prof_gctx_t
*gctx
)
599 if (!tctx_tree_empty(&gctx
->tctxs
))
601 if (gctx
->nlimbo
!= 0)
606 /* tctx->tdata->lock is held upon entry, and released before return. */
608 prof_tctx_destroy(tsd_t
*tsd
, prof_tctx_t
*tctx
)
610 prof_tdata_t
*tdata
= tctx
->tdata
;
611 prof_gctx_t
*gctx
= tctx
->gctx
;
612 bool destroy_tdata
, destroy_gctx
;
614 assert(tctx
->cnts
.curobjs
== 0);
615 assert(tctx
->cnts
.curbytes
== 0);
616 assert(!opt_prof_accum
);
617 assert(tctx
->cnts
.accumobjs
== 0);
618 assert(tctx
->cnts
.accumbytes
== 0);
620 ckh_remove(tsd
, &tdata
->bt2tctx
, &gctx
->bt
, NULL
, NULL
);
621 destroy_tdata
= prof_tdata_should_destroy(tdata
, false);
622 malloc_mutex_unlock(tdata
->lock
);
624 malloc_mutex_lock(gctx
->lock
);
625 tctx_tree_remove(&gctx
->tctxs
, tctx
);
626 if (prof_gctx_should_destroy(gctx
)) {
628 * Increment gctx->nlimbo in order to keep another thread from
629 * winning the race to destroy gctx while this one has
630 * gctx->lock dropped. Without this, it would be possible for
633 * 1) Sample an allocation associated with gctx.
634 * 2) Deallocate the sampled object.
635 * 3) Successfully prof_gctx_try_destroy(gctx).
637 * The result would be that gctx no longer exists by the time
638 * this thread accesses it in prof_gctx_try_destroy().
643 destroy_gctx
= false;
644 malloc_mutex_unlock(gctx
->lock
);
646 prof_gctx_try_destroy(tsd
, gctx
, tdata
);
649 prof_tdata_destroy(tsd
, tdata
, false);
655 prof_lookup_global(tsd_t
*tsd
, prof_bt_t
*bt
, prof_tdata_t
*tdata
,
656 void **p_btkey
, prof_gctx_t
**p_gctx
, bool *p_new_gctx
)
669 if (ckh_search(&bt2gctx
, bt
, &btkey
.v
, &gctx
.v
)) {
670 /* bt has never been seen before. Insert it. */
671 gctx
.p
= prof_gctx_create(tsd
, bt
);
672 if (gctx
.v
== NULL
) {
676 btkey
.p
= &gctx
.p
->bt
;
677 if (ckh_insert(tsd
, &bt2gctx
, btkey
.v
, gctx
.v
)) {
680 idalloc(tsd
, gctx
.v
);
686 * Increment nlimbo, in order to avoid a race condition with
687 * prof_tctx_destroy()/prof_gctx_try_destroy().
689 malloc_mutex_lock(gctx
.p
->lock
);
691 malloc_mutex_unlock(gctx
.p
->lock
);
698 *p_new_gctx
= new_gctx
;
703 prof_lookup(tsd_t
*tsd
, prof_bt_t
*bt
)
712 cassert(config_prof
);
714 tdata
= prof_tdata_get(tsd
, false);
718 malloc_mutex_lock(tdata
->lock
);
719 not_found
= ckh_search(&tdata
->bt2tctx
, bt
, NULL
, &ret
.v
);
720 if (!not_found
) /* Note double negative! */
721 ret
.p
->prepared
= true;
722 malloc_mutex_unlock(tdata
->lock
);
726 bool new_gctx
, error
;
729 * This thread's cache lacks bt. Look for it in the global
732 if (prof_lookup_global(tsd
, bt
, tdata
, &btkey
, &gctx
,
736 /* Link a prof_tctx_t into gctx for this thread. */
737 ret
.v
= imalloc(tsd
, sizeof(prof_tctx_t
));
740 prof_gctx_try_destroy(tsd
, gctx
, tdata
);
743 ret
.p
->tdata
= tdata
;
744 memset(&ret
.p
->cnts
, 0, sizeof(prof_cnt_t
));
746 ret
.p
->prepared
= true;
747 ret
.p
->state
= prof_tctx_state_initializing
;
748 malloc_mutex_lock(tdata
->lock
);
749 error
= ckh_insert(tsd
, &tdata
->bt2tctx
, btkey
, ret
.v
);
750 malloc_mutex_unlock(tdata
->lock
);
753 prof_gctx_try_destroy(tsd
, gctx
, tdata
);
757 malloc_mutex_lock(gctx
->lock
);
758 ret
.p
->state
= prof_tctx_state_nominal
;
759 tctx_tree_insert(&gctx
->tctxs
, ret
.p
);
761 malloc_mutex_unlock(gctx
->lock
);
768 prof_sample_threshold_update(prof_tdata_t
*tdata
)
771 * The body of this function is compiled out unless heap profiling is
772 * enabled, so that it is possible to compile jemalloc with floating
773 * point support completely disabled. Avoiding floating point code is
774 * important on memory-constrained systems, but it also enables a
775 * workaround for versions of glibc that don't properly save/restore
776 * floating point registers during dynamic lazy symbol loading (which
777 * internally calls into whatever malloc implementation happens to be
778 * integrated into the application). Note that some compilers (e.g.
779 * gcc 4.8) may use floating point registers for fast memory moves, so
780 * jemalloc must be compiled with such optimizations disabled (e.g.
781 * -mno-sse) in order for the workaround to be complete.
790 if (lg_prof_sample
== 0) {
791 tdata
->bytes_until_sample
= 0;
796 * Compute sample interval as a geometrically distributed random
797 * variable with mean (2^lg_prof_sample).
801 * tdata->bytes_until_sample = | -------- |, where p = ---------------
802 * | log(1-p) | lg_prof_sample
805 * For more information on the math, see:
807 * Non-Uniform Random Variate Generation
809 * Springer-Verlag, New York, 1986
811 * (http://luc.devroye.org/rnbookindex.html)
813 prng64(r
, 53, tdata
->prng_state
, UINT64_C(6364136223846793005),
814 UINT64_C(1442695040888963407));
815 u
= (double)r
* (1.0/9007199254740992.0L);
816 tdata
->bytes_until_sample
= (uint64_t)(log(u
) /
817 log(1.0 - (1.0 / (double)((uint64_t)1U << lg_prof_sample
))))
823 static prof_tdata_t
*
824 prof_tdata_count_iter(prof_tdata_tree_t
*tdatas
, prof_tdata_t
*tdata
, void *arg
)
826 size_t *tdata_count
= (size_t *)arg
;
834 prof_tdata_count(void)
836 size_t tdata_count
= 0;
838 malloc_mutex_lock(&tdatas_mtx
);
839 tdata_tree_iter(&tdatas
, NULL
, prof_tdata_count_iter
,
840 (void *)&tdata_count
);
841 malloc_mutex_unlock(&tdatas_mtx
);
843 return (tdata_count
);
856 tdata
= prof_tdata_get(tsd
, false);
861 bt_count
= ckh_count(&bt2gctx
);
869 #undef prof_dump_open
870 #define prof_dump_open JEMALLOC_N(prof_dump_open_impl)
873 prof_dump_open(bool propagate_err
, const char *filename
)
877 fd
= creat(filename
, 0644);
878 if (fd
== -1 && !propagate_err
) {
879 malloc_printf("<jemalloc>: creat(\"%s\"), 0644) failed\n",
888 #undef prof_dump_open
889 #define prof_dump_open JEMALLOC_N(prof_dump_open)
890 prof_dump_open_t
*prof_dump_open
= JEMALLOC_N(prof_dump_open_impl
);
894 prof_dump_flush(bool propagate_err
)
899 cassert(config_prof
);
901 err
= write(prof_dump_fd
, prof_dump_buf
, prof_dump_buf_end
);
903 if (!propagate_err
) {
904 malloc_write("<jemalloc>: write() failed during heap "
911 prof_dump_buf_end
= 0;
917 prof_dump_close(bool propagate_err
)
921 assert(prof_dump_fd
!= -1);
922 ret
= prof_dump_flush(propagate_err
);
930 prof_dump_write(bool propagate_err
, const char *s
)
934 cassert(config_prof
);
939 /* Flush the buffer if it is full. */
940 if (prof_dump_buf_end
== PROF_DUMP_BUFSIZE
)
941 if (prof_dump_flush(propagate_err
) && propagate_err
)
944 if (prof_dump_buf_end
+ slen
<= PROF_DUMP_BUFSIZE
) {
945 /* Finish writing. */
948 /* Write as much of s as will fit. */
949 n
= PROF_DUMP_BUFSIZE
- prof_dump_buf_end
;
951 memcpy(&prof_dump_buf
[prof_dump_buf_end
], &s
[i
], n
);
952 prof_dump_buf_end
+= n
;
959 JEMALLOC_ATTR(format(printf
, 2, 3))
961 prof_dump_printf(bool propagate_err
, const char *format
, ...)
965 char buf
[PROF_PRINTF_BUFSIZE
];
967 va_start(ap
, format
);
968 malloc_vsnprintf(buf
, sizeof(buf
), format
, ap
);
970 ret
= prof_dump_write(propagate_err
, buf
);
975 /* tctx->tdata->lock is held. */
977 prof_tctx_merge_tdata(prof_tctx_t
*tctx
, prof_tdata_t
*tdata
)
980 malloc_mutex_lock(tctx
->gctx
->lock
);
981 if (tctx
->state
== prof_tctx_state_initializing
) {
982 malloc_mutex_unlock(tctx
->gctx
->lock
);
985 assert(tctx
->state
== prof_tctx_state_nominal
);
986 tctx
->state
= prof_tctx_state_dumping
;
987 malloc_mutex_unlock(tctx
->gctx
->lock
);
989 memcpy(&tctx
->dump_cnts
, &tctx
->cnts
, sizeof(prof_cnt_t
));
991 tdata
->cnt_summed
.curobjs
+= tctx
->dump_cnts
.curobjs
;
992 tdata
->cnt_summed
.curbytes
+= tctx
->dump_cnts
.curbytes
;
993 if (opt_prof_accum
) {
994 tdata
->cnt_summed
.accumobjs
+= tctx
->dump_cnts
.accumobjs
;
995 tdata
->cnt_summed
.accumbytes
+= tctx
->dump_cnts
.accumbytes
;
999 /* gctx->lock is held. */
1001 prof_tctx_merge_gctx(prof_tctx_t
*tctx
, prof_gctx_t
*gctx
)
1004 gctx
->cnt_summed
.curobjs
+= tctx
->dump_cnts
.curobjs
;
1005 gctx
->cnt_summed
.curbytes
+= tctx
->dump_cnts
.curbytes
;
1006 if (opt_prof_accum
) {
1007 gctx
->cnt_summed
.accumobjs
+= tctx
->dump_cnts
.accumobjs
;
1008 gctx
->cnt_summed
.accumbytes
+= tctx
->dump_cnts
.accumbytes
;
1012 /* tctx->gctx is held. */
1013 static prof_tctx_t
*
1014 prof_tctx_merge_iter(prof_tctx_tree_t
*tctxs
, prof_tctx_t
*tctx
, void *arg
)
1017 switch (tctx
->state
) {
1018 case prof_tctx_state_nominal
:
1019 /* New since dumping started; ignore. */
1021 case prof_tctx_state_dumping
:
1022 case prof_tctx_state_purgatory
:
1023 prof_tctx_merge_gctx(tctx
, tctx
->gctx
);
1032 /* gctx->lock is held. */
1033 static prof_tctx_t
*
1034 prof_tctx_dump_iter(prof_tctx_tree_t
*tctxs
, prof_tctx_t
*tctx
, void *arg
)
1036 bool propagate_err
= *(bool *)arg
;
1038 if (prof_dump_printf(propagate_err
,
1039 " t%"PRIu64
": %"PRIu64
": %"PRIu64
" [%"PRIu64
": %"PRIu64
"]\n",
1040 tctx
->tdata
->thr_uid
, tctx
->dump_cnts
.curobjs
,
1041 tctx
->dump_cnts
.curbytes
, tctx
->dump_cnts
.accumobjs
,
1042 tctx
->dump_cnts
.accumbytes
))
1047 /* tctx->gctx is held. */
1048 static prof_tctx_t
*
1049 prof_tctx_finish_iter(prof_tctx_tree_t
*tctxs
, prof_tctx_t
*tctx
, void *arg
)
1053 switch (tctx
->state
) {
1054 case prof_tctx_state_nominal
:
1055 /* New since dumping started; ignore. */
1057 case prof_tctx_state_dumping
:
1058 tctx
->state
= prof_tctx_state_nominal
;
1060 case prof_tctx_state_purgatory
:
1073 prof_dump_gctx_prep(prof_gctx_t
*gctx
, prof_gctx_tree_t
*gctxs
)
1076 cassert(config_prof
);
1078 malloc_mutex_lock(gctx
->lock
);
1081 * Increment nlimbo so that gctx won't go away before dump.
1082 * Additionally, link gctx into the dump list so that it is included in
1083 * prof_dump()'s second pass.
1086 gctx_tree_insert(gctxs
, gctx
);
1088 memset(&gctx
->cnt_summed
, 0, sizeof(prof_cnt_t
));
1090 malloc_mutex_unlock(gctx
->lock
);
1093 static prof_gctx_t
*
1094 prof_gctx_merge_iter(prof_gctx_tree_t
*gctxs
, prof_gctx_t
*gctx
, void *arg
)
1096 size_t *leak_ngctx
= (size_t *)arg
;
1098 malloc_mutex_lock(gctx
->lock
);
1099 tctx_tree_iter(&gctx
->tctxs
, NULL
, prof_tctx_merge_iter
, NULL
);
1100 if (gctx
->cnt_summed
.curobjs
!= 0)
1102 malloc_mutex_unlock(gctx
->lock
);
1108 prof_gctx_finish(tsd_t
*tsd
, prof_gctx_tree_t
*gctxs
)
1110 prof_tdata_t
*tdata
= prof_tdata_get(tsd
, false);
1114 * Standard tree iteration won't work here, because as soon as we
1115 * decrement gctx->nlimbo and unlock gctx, another thread can
1116 * concurrently destroy it, which will corrupt the tree. Therefore,
1117 * tear down the tree one node at a time during iteration.
1119 while ((gctx
= gctx_tree_first(gctxs
)) != NULL
) {
1120 gctx_tree_remove(gctxs
, gctx
);
1121 malloc_mutex_lock(gctx
->lock
);
1127 prof_tctx_t
*to_destroy
=
1128 tctx_tree_iter(&gctx
->tctxs
, next
,
1129 prof_tctx_finish_iter
, NULL
);
1130 if (to_destroy
!= NULL
) {
1131 next
= tctx_tree_next(&gctx
->tctxs
,
1133 tctx_tree_remove(&gctx
->tctxs
,
1135 idalloc(tsd
, to_destroy
);
1138 } while (next
!= NULL
);
1141 if (prof_gctx_should_destroy(gctx
)) {
1143 malloc_mutex_unlock(gctx
->lock
);
1144 prof_gctx_try_destroy(tsd
, gctx
, tdata
);
1146 malloc_mutex_unlock(gctx
->lock
);
1150 static prof_tdata_t
*
1151 prof_tdata_merge_iter(prof_tdata_tree_t
*tdatas
, prof_tdata_t
*tdata
, void *arg
)
1153 prof_cnt_t
*cnt_all
= (prof_cnt_t
*)arg
;
1155 malloc_mutex_lock(tdata
->lock
);
1156 if (!tdata
->expired
) {
1163 tdata
->dumping
= true;
1164 memset(&tdata
->cnt_summed
, 0, sizeof(prof_cnt_t
));
1165 for (tabind
= 0; !ckh_iter(&tdata
->bt2tctx
, &tabind
, NULL
,
1167 prof_tctx_merge_tdata(tctx
.p
, tdata
);
1169 cnt_all
->curobjs
+= tdata
->cnt_summed
.curobjs
;
1170 cnt_all
->curbytes
+= tdata
->cnt_summed
.curbytes
;
1171 if (opt_prof_accum
) {
1172 cnt_all
->accumobjs
+= tdata
->cnt_summed
.accumobjs
;
1173 cnt_all
->accumbytes
+= tdata
->cnt_summed
.accumbytes
;
1176 tdata
->dumping
= false;
1177 malloc_mutex_unlock(tdata
->lock
);
1182 static prof_tdata_t
*
1183 prof_tdata_dump_iter(prof_tdata_tree_t
*tdatas
, prof_tdata_t
*tdata
, void *arg
)
1185 bool propagate_err
= *(bool *)arg
;
1187 if (!tdata
->dumping
)
1190 if (prof_dump_printf(propagate_err
,
1191 " t%"PRIu64
": %"PRIu64
": %"PRIu64
" [%"PRIu64
": %"PRIu64
"]%s%s\n",
1192 tdata
->thr_uid
, tdata
->cnt_summed
.curobjs
,
1193 tdata
->cnt_summed
.curbytes
, tdata
->cnt_summed
.accumobjs
,
1194 tdata
->cnt_summed
.accumbytes
,
1195 (tdata
->thread_name
!= NULL
) ? " " : "",
1196 (tdata
->thread_name
!= NULL
) ? tdata
->thread_name
: ""))
1202 #undef prof_dump_header
1203 #define prof_dump_header JEMALLOC_N(prof_dump_header_impl)
1206 prof_dump_header(bool propagate_err
, const prof_cnt_t
*cnt_all
)
1210 if (prof_dump_printf(propagate_err
,
1211 "heap_v2/%"PRIu64
"\n"
1212 " t*: %"PRIu64
": %"PRIu64
" [%"PRIu64
": %"PRIu64
"]\n",
1213 ((uint64_t)1U << lg_prof_sample
), cnt_all
->curobjs
,
1214 cnt_all
->curbytes
, cnt_all
->accumobjs
, cnt_all
->accumbytes
))
1217 malloc_mutex_lock(&tdatas_mtx
);
1218 ret
= (tdata_tree_iter(&tdatas
, NULL
, prof_tdata_dump_iter
,
1219 (void *)&propagate_err
) != NULL
);
1220 malloc_mutex_unlock(&tdatas_mtx
);
1224 #undef prof_dump_header
1225 #define prof_dump_header JEMALLOC_N(prof_dump_header)
1226 prof_dump_header_t
*prof_dump_header
= JEMALLOC_N(prof_dump_header_impl
);
1229 /* gctx->lock is held. */
1231 prof_dump_gctx(bool propagate_err
, prof_gctx_t
*gctx
, const prof_bt_t
*bt
,
1232 prof_gctx_tree_t
*gctxs
)
1237 cassert(config_prof
);
1239 /* Avoid dumping such gctx's that have no useful data. */
1240 if ((!opt_prof_accum
&& gctx
->cnt_summed
.curobjs
== 0) ||
1241 (opt_prof_accum
&& gctx
->cnt_summed
.accumobjs
== 0)) {
1242 assert(gctx
->cnt_summed
.curobjs
== 0);
1243 assert(gctx
->cnt_summed
.curbytes
== 0);
1244 assert(gctx
->cnt_summed
.accumobjs
== 0);
1245 assert(gctx
->cnt_summed
.accumbytes
== 0);
1250 if (prof_dump_printf(propagate_err
, "@")) {
1254 for (i
= 0; i
< bt
->len
; i
++) {
1255 if (prof_dump_printf(propagate_err
, " %#"PRIxPTR
,
1256 (uintptr_t)bt
->vec
[i
])) {
1262 if (prof_dump_printf(propagate_err
,
1264 " t*: %"PRIu64
": %"PRIu64
" [%"PRIu64
": %"PRIu64
"]\n",
1265 gctx
->cnt_summed
.curobjs
, gctx
->cnt_summed
.curbytes
,
1266 gctx
->cnt_summed
.accumobjs
, gctx
->cnt_summed
.accumbytes
)) {
1271 if (tctx_tree_iter(&gctx
->tctxs
, NULL
, prof_tctx_dump_iter
,
1272 (void *)&propagate_err
) != NULL
) {
1283 prof_dump_maps(bool propagate_err
)
1287 char filename
[PATH_MAX
+ 1];
1289 cassert(config_prof
);
1291 malloc_snprintf(filename
, sizeof(filename
), "/proc/curproc/map");
1293 malloc_snprintf(filename
, sizeof(filename
), "/proc/%d/maps",
1296 mfd
= open(filename
, O_RDONLY
);
1300 if (prof_dump_write(propagate_err
, "\nMAPPED_LIBRARIES:\n") &&
1307 prof_dump_buf_end
+= nread
;
1308 if (prof_dump_buf_end
== PROF_DUMP_BUFSIZE
) {
1309 /* Make space in prof_dump_buf before read(). */
1310 if (prof_dump_flush(propagate_err
) &&
1316 nread
= read(mfd
, &prof_dump_buf
[prof_dump_buf_end
],
1317 PROF_DUMP_BUFSIZE
- prof_dump_buf_end
);
1318 } while (nread
> 0);
1332 prof_leakcheck(const prof_cnt_t
*cnt_all
, size_t leak_ngctx
,
1333 const char *filename
)
1336 if (cnt_all
->curbytes
!= 0) {
1337 malloc_printf("<jemalloc>: Leak summary: %"PRIu64
" byte%s, %"
1338 PRIu64
" object%s, %zu context%s\n",
1339 cnt_all
->curbytes
, (cnt_all
->curbytes
!= 1) ? "s" : "",
1340 cnt_all
->curobjs
, (cnt_all
->curobjs
!= 1) ? "s" : "",
1341 leak_ngctx
, (leak_ngctx
!= 1) ? "s" : "");
1343 "<jemalloc>: Run pprof on \"%s\" for leak detail\n",
1348 static prof_gctx_t
*
1349 prof_gctx_dump_iter(prof_gctx_tree_t
*gctxs
, prof_gctx_t
*gctx
, void *arg
)
1352 bool propagate_err
= *(bool *)arg
;
1354 malloc_mutex_lock(gctx
->lock
);
1356 if (prof_dump_gctx(propagate_err
, gctx
, &gctx
->bt
, gctxs
)) {
1363 malloc_mutex_unlock(gctx
->lock
);
1368 prof_dump(tsd_t
*tsd
, bool propagate_err
, const char *filename
, bool leakcheck
)
1370 prof_tdata_t
*tdata
;
1378 prof_gctx_tree_t gctxs
;
1380 cassert(config_prof
);
1382 tdata
= prof_tdata_get(tsd
, true);
1386 malloc_mutex_lock(&prof_dump_mtx
);
1390 * Put gctx's in limbo and clear their counters in preparation for
1393 gctx_tree_new(&gctxs
);
1394 for (tabind
= 0; !ckh_iter(&bt2gctx
, &tabind
, NULL
, &gctx
.v
);)
1395 prof_dump_gctx_prep(gctx
.p
, &gctxs
);
1398 * Iterate over tdatas, and for the non-expired ones snapshot their tctx
1399 * stats and merge them into the associated gctx's.
1401 memset(&cnt_all
, 0, sizeof(prof_cnt_t
));
1402 malloc_mutex_lock(&tdatas_mtx
);
1403 tdata_tree_iter(&tdatas
, NULL
, prof_tdata_merge_iter
, (void *)&cnt_all
);
1404 malloc_mutex_unlock(&tdatas_mtx
);
1406 /* Merge tctx stats into gctx's. */
1408 gctx_tree_iter(&gctxs
, NULL
, prof_gctx_merge_iter
, (void *)&leak_ngctx
);
1412 /* Create dump file. */
1413 if ((prof_dump_fd
= prof_dump_open(propagate_err
, filename
)) == -1)
1414 goto label_open_close_error
;
1416 /* Dump profile header. */
1417 if (prof_dump_header(propagate_err
, &cnt_all
))
1418 goto label_write_error
;
1420 /* Dump per gctx profile stats. */
1421 if (gctx_tree_iter(&gctxs
, NULL
, prof_gctx_dump_iter
,
1422 (void *)&propagate_err
) != NULL
)
1423 goto label_write_error
;
1425 /* Dump /proc/<pid>/maps if possible. */
1426 if (prof_dump_maps(propagate_err
))
1427 goto label_write_error
;
1429 if (prof_dump_close(propagate_err
))
1430 goto label_open_close_error
;
1432 prof_gctx_finish(tsd
, &gctxs
);
1433 malloc_mutex_unlock(&prof_dump_mtx
);
1436 prof_leakcheck(&cnt_all
, leak_ngctx
, filename
);
1440 prof_dump_close(propagate_err
);
1441 label_open_close_error
:
1442 prof_gctx_finish(tsd
, &gctxs
);
1443 malloc_mutex_unlock(&prof_dump_mtx
);
1447 #define DUMP_FILENAME_BUFSIZE (PATH_MAX + 1)
1448 #define VSEQ_INVALID UINT64_C(0xffffffffffffffff)
1450 prof_dump_filename(char *filename
, char v
, uint64_t vseq
)
1453 cassert(config_prof
);
1455 if (vseq
!= VSEQ_INVALID
) {
1456 /* "<prefix>.<pid>.<seq>.v<vseq>.heap" */
1457 malloc_snprintf(filename
, DUMP_FILENAME_BUFSIZE
,
1458 "%s.%d.%"PRIu64
".%c%"PRIu64
".heap",
1459 opt_prof_prefix
, (int)getpid(), prof_dump_seq
, v
, vseq
);
1461 /* "<prefix>.<pid>.<seq>.<v>.heap" */
1462 malloc_snprintf(filename
, DUMP_FILENAME_BUFSIZE
,
1463 "%s.%d.%"PRIu64
".%c.heap",
1464 opt_prof_prefix
, (int)getpid(), prof_dump_seq
, v
);
1473 char filename
[DUMP_FILENAME_BUFSIZE
];
1475 cassert(config_prof
);
1481 if (opt_prof_final
&& opt_prof_prefix
[0] != '\0') {
1482 malloc_mutex_lock(&prof_dump_seq_mtx
);
1483 prof_dump_filename(filename
, 'f', VSEQ_INVALID
);
1484 malloc_mutex_unlock(&prof_dump_seq_mtx
);
1485 prof_dump(tsd
, false, filename
, opt_prof_leak
);
1493 prof_tdata_t
*tdata
;
1494 char filename
[PATH_MAX
+ 1];
1496 cassert(config_prof
);
1501 tdata
= prof_tdata_get(tsd
, false);
1505 tdata
->enq_idump
= true;
1509 if (opt_prof_prefix
[0] != '\0') {
1510 malloc_mutex_lock(&prof_dump_seq_mtx
);
1511 prof_dump_filename(filename
, 'i', prof_dump_iseq
);
1513 malloc_mutex_unlock(&prof_dump_seq_mtx
);
1514 prof_dump(tsd
, false, filename
, false);
1519 prof_mdump(const char *filename
)
1522 char filename_buf
[DUMP_FILENAME_BUFSIZE
];
1524 cassert(config_prof
);
1526 if (!opt_prof
|| !prof_booted
)
1530 if (filename
== NULL
) {
1531 /* No filename specified, so automatically generate one. */
1532 if (opt_prof_prefix
[0] == '\0')
1534 malloc_mutex_lock(&prof_dump_seq_mtx
);
1535 prof_dump_filename(filename_buf
, 'm', prof_dump_mseq
);
1537 malloc_mutex_unlock(&prof_dump_seq_mtx
);
1538 filename
= filename_buf
;
1540 return (prof_dump(tsd
, true, filename
, false));
1547 prof_tdata_t
*tdata
;
1548 char filename
[DUMP_FILENAME_BUFSIZE
];
1550 cassert(config_prof
);
1555 tdata
= prof_tdata_get(tsd
, false);
1559 tdata
->enq_gdump
= true;
1563 if (opt_prof_prefix
[0] != '\0') {
1564 malloc_mutex_lock(&prof_dump_seq_mtx
);
1565 prof_dump_filename(filename
, 'u', prof_dump_useq
);
1567 malloc_mutex_unlock(&prof_dump_seq_mtx
);
1568 prof_dump(tsd
, false, filename
, false);
1573 prof_bt_hash(const void *key
, size_t r_hash
[2])
1575 prof_bt_t
*bt
= (prof_bt_t
*)key
;
1577 cassert(config_prof
);
1579 hash(bt
->vec
, bt
->len
* sizeof(void *), 0x94122f33U
, r_hash
);
1583 prof_bt_keycomp(const void *k1
, const void *k2
)
1585 const prof_bt_t
*bt1
= (prof_bt_t
*)k1
;
1586 const prof_bt_t
*bt2
= (prof_bt_t
*)k2
;
1588 cassert(config_prof
);
1590 if (bt1
->len
!= bt2
->len
)
1592 return (memcmp(bt1
->vec
, bt2
->vec
, bt1
->len
* sizeof(void *)) == 0);
1595 JEMALLOC_INLINE_C
uint64_t
1596 prof_thr_uid_alloc(void)
1600 malloc_mutex_lock(&next_thr_uid_mtx
);
1601 thr_uid
= next_thr_uid
;
1603 malloc_mutex_unlock(&next_thr_uid_mtx
);
1608 static prof_tdata_t
*
1609 prof_tdata_init_impl(tsd_t
*tsd
, uint64_t thr_uid
, uint64_t thr_discrim
,
1610 char *thread_name
, bool active
)
1612 prof_tdata_t
*tdata
;
1614 cassert(config_prof
);
1616 /* Initialize an empty cache for this thread. */
1617 tdata
= (prof_tdata_t
*)imalloc(tsd
, sizeof(prof_tdata_t
));
1621 tdata
->lock
= prof_tdata_mutex_choose(thr_uid
);
1622 tdata
->thr_uid
= thr_uid
;
1623 tdata
->thr_discrim
= thr_discrim
;
1624 tdata
->thread_name
= thread_name
;
1625 tdata
->attached
= true;
1626 tdata
->expired
= false;
1628 if (ckh_new(tsd
, &tdata
->bt2tctx
, PROF_CKH_MINITEMS
,
1629 prof_bt_hash
, prof_bt_keycomp
)) {
1630 idalloc(tsd
, tdata
);
1634 tdata
->prng_state
= (uint64_t)(uintptr_t)tdata
;
1635 prof_sample_threshold_update(tdata
);
1638 tdata
->enq_idump
= false;
1639 tdata
->enq_gdump
= false;
1641 tdata
->dumping
= false;
1642 tdata
->active
= active
;
1644 malloc_mutex_lock(&tdatas_mtx
);
1645 tdata_tree_insert(&tdatas
, tdata
);
1646 malloc_mutex_unlock(&tdatas_mtx
);
1652 prof_tdata_init(tsd_t
*tsd
)
1655 return (prof_tdata_init_impl(tsd
, prof_thr_uid_alloc(), 0, NULL
,
1656 prof_thread_active_init_get()));
1659 /* tdata->lock must be held. */
1661 prof_tdata_should_destroy(prof_tdata_t
*tdata
, bool even_if_attached
)
1664 if (tdata
->attached
&& !even_if_attached
)
1666 if (ckh_count(&tdata
->bt2tctx
) != 0)
1671 /* tdatas_mtx must be held. */
1673 prof_tdata_destroy_locked(tsd_t
*tsd
, prof_tdata_t
*tdata
,
1674 bool even_if_attached
)
1677 assert(prof_tdata_should_destroy(tdata
, even_if_attached
));
1678 assert(tsd_prof_tdata_get(tsd
) != tdata
);
1680 tdata_tree_remove(&tdatas
, tdata
);
1682 if (tdata
->thread_name
!= NULL
)
1683 idalloc(tsd
, tdata
->thread_name
);
1684 ckh_delete(tsd
, &tdata
->bt2tctx
);
1685 idalloc(tsd
, tdata
);
1689 prof_tdata_destroy(tsd_t
*tsd
, prof_tdata_t
*tdata
, bool even_if_attached
)
1692 malloc_mutex_lock(&tdatas_mtx
);
1693 prof_tdata_destroy_locked(tsd
, tdata
, even_if_attached
);
1694 malloc_mutex_unlock(&tdatas_mtx
);
1698 prof_tdata_detach(tsd_t
*tsd
, prof_tdata_t
*tdata
)
1702 malloc_mutex_lock(tdata
->lock
);
1703 if (tdata
->attached
) {
1704 destroy_tdata
= prof_tdata_should_destroy(tdata
, true);
1706 * Only detach if !destroy_tdata, because detaching would allow
1707 * another thread to win the race to destroy tdata.
1710 tdata
->attached
= false;
1711 tsd_prof_tdata_set(tsd
, NULL
);
1713 destroy_tdata
= false;
1714 malloc_mutex_unlock(tdata
->lock
);
1716 prof_tdata_destroy(tsd
, tdata
, true);
1720 prof_tdata_reinit(tsd_t
*tsd
, prof_tdata_t
*tdata
)
1722 uint64_t thr_uid
= tdata
->thr_uid
;
1723 uint64_t thr_discrim
= tdata
->thr_discrim
+ 1;
1724 char *thread_name
= (tdata
->thread_name
!= NULL
) ?
1725 prof_thread_name_alloc(tsd
, tdata
->thread_name
) : NULL
;
1726 bool active
= tdata
->active
;
1728 prof_tdata_detach(tsd
, tdata
);
1729 return (prof_tdata_init_impl(tsd
, thr_uid
, thr_discrim
, thread_name
,
1734 prof_tdata_expire(prof_tdata_t
*tdata
)
1738 malloc_mutex_lock(tdata
->lock
);
1739 if (!tdata
->expired
) {
1740 tdata
->expired
= true;
1741 destroy_tdata
= tdata
->attached
? false :
1742 prof_tdata_should_destroy(tdata
, false);
1744 destroy_tdata
= false;
1745 malloc_mutex_unlock(tdata
->lock
);
1747 return (destroy_tdata
);
1750 static prof_tdata_t
*
1751 prof_tdata_reset_iter(prof_tdata_tree_t
*tdatas
, prof_tdata_t
*tdata
, void *arg
)
1754 return (prof_tdata_expire(tdata
) ? tdata
: NULL
);
1758 prof_reset(tsd_t
*tsd
, size_t lg_sample
)
1762 assert(lg_sample
< (sizeof(uint64_t) << 3));
1764 malloc_mutex_lock(&prof_dump_mtx
);
1765 malloc_mutex_lock(&tdatas_mtx
);
1767 lg_prof_sample
= lg_sample
;
1771 prof_tdata_t
*to_destroy
= tdata_tree_iter(&tdatas
, next
,
1772 prof_tdata_reset_iter
, NULL
);
1773 if (to_destroy
!= NULL
) {
1774 next
= tdata_tree_next(&tdatas
, to_destroy
);
1775 prof_tdata_destroy_locked(tsd
, to_destroy
, false);
1778 } while (next
!= NULL
);
1780 malloc_mutex_unlock(&tdatas_mtx
);
1781 malloc_mutex_unlock(&prof_dump_mtx
);
1785 prof_tdata_cleanup(tsd_t
*tsd
)
1787 prof_tdata_t
*tdata
;
1792 tdata
= tsd_prof_tdata_get(tsd
);
1794 prof_tdata_detach(tsd
, tdata
);
1798 prof_active_get(void)
1800 bool prof_active_current
;
1802 malloc_mutex_lock(&prof_active_mtx
);
1803 prof_active_current
= prof_active
;
1804 malloc_mutex_unlock(&prof_active_mtx
);
1805 return (prof_active_current
);
1809 prof_active_set(bool active
)
1811 bool prof_active_old
;
1813 malloc_mutex_lock(&prof_active_mtx
);
1814 prof_active_old
= prof_active
;
1815 prof_active
= active
;
1816 malloc_mutex_unlock(&prof_active_mtx
);
1817 return (prof_active_old
);
1821 prof_thread_name_get(void)
1824 prof_tdata_t
*tdata
;
1827 tdata
= prof_tdata_get(tsd
, true);
1830 return (tdata
->thread_name
!= NULL
? tdata
->thread_name
: "");
1834 prof_thread_name_alloc(tsd_t
*tsd
, const char *thread_name
)
1839 if (thread_name
== NULL
)
1842 size
= strlen(thread_name
) + 1;
1846 ret
= imalloc(tsd
, size
);
1849 memcpy(ret
, thread_name
, size
);
1854 prof_thread_name_set(tsd_t
*tsd
, const char *thread_name
)
1856 prof_tdata_t
*tdata
;
1860 tdata
= prof_tdata_get(tsd
, true);
1864 /* Validate input. */
1865 if (thread_name
== NULL
)
1867 for (i
= 0; thread_name
[i
] != '\0'; i
++) {
1868 char c
= thread_name
[i
];
1869 if (!isgraph(c
) && !isblank(c
))
1873 s
= prof_thread_name_alloc(tsd
, thread_name
);
1877 if (tdata
->thread_name
!= NULL
) {
1878 idalloc(tsd
, tdata
->thread_name
);
1879 tdata
->thread_name
= NULL
;
1882 tdata
->thread_name
= s
;
1887 prof_thread_active_get(void)
1890 prof_tdata_t
*tdata
;
1893 tdata
= prof_tdata_get(tsd
, true);
1896 return (tdata
->active
);
1900 prof_thread_active_set(bool active
)
1903 prof_tdata_t
*tdata
;
1906 tdata
= prof_tdata_get(tsd
, true);
1909 tdata
->active
= active
;
1914 prof_thread_active_init_get(void)
1918 malloc_mutex_lock(&prof_thread_active_init_mtx
);
1919 active_init
= prof_thread_active_init
;
1920 malloc_mutex_unlock(&prof_thread_active_init_mtx
);
1921 return (active_init
);
1925 prof_thread_active_init_set(bool active_init
)
1927 bool active_init_old
;
1929 malloc_mutex_lock(&prof_thread_active_init_mtx
);
1930 active_init_old
= prof_thread_active_init
;
1931 prof_thread_active_init
= active_init
;
1932 malloc_mutex_unlock(&prof_thread_active_init_mtx
);
1933 return (active_init_old
);
1940 cassert(config_prof
);
1942 memcpy(opt_prof_prefix
, PROF_PREFIX_DEFAULT
,
1943 sizeof(PROF_PREFIX_DEFAULT
));
1950 cassert(config_prof
);
1953 * opt_prof must be in its final state before any arenas are
1954 * initialized, so this function must be executed early.
1957 if (opt_prof_leak
&& !opt_prof
) {
1959 * Enable opt_prof, but in such a way that profiles are never
1960 * automatically dumped.
1963 opt_prof_gdump
= false;
1964 } else if (opt_prof
) {
1965 if (opt_lg_prof_interval
>= 0) {
1966 prof_interval
= (((uint64_t)1U) <<
1967 opt_lg_prof_interval
);
1976 cassert(config_prof
);
1982 lg_prof_sample
= opt_lg_prof_sample
;
1984 prof_active
= opt_prof_active
;
1985 if (malloc_mutex_init(&prof_active_mtx
))
1988 prof_thread_active_init
= opt_prof_thread_active_init
;
1989 if (malloc_mutex_init(&prof_thread_active_init_mtx
))
1993 if (ckh_new(tsd
, &bt2gctx
, PROF_CKH_MINITEMS
, prof_bt_hash
,
1996 if (malloc_mutex_init(&bt2gctx_mtx
))
1999 tdata_tree_new(&tdatas
);
2000 if (malloc_mutex_init(&tdatas_mtx
))
2004 if (malloc_mutex_init(&next_thr_uid_mtx
))
2007 if (malloc_mutex_init(&prof_dump_seq_mtx
))
2009 if (malloc_mutex_init(&prof_dump_mtx
))
2012 if (atexit(prof_fdump
) != 0) {
2013 malloc_write("<jemalloc>: Error in atexit()\n");
2018 gctx_locks
= (malloc_mutex_t
*)base_alloc(PROF_NCTX_LOCKS
*
2019 sizeof(malloc_mutex_t
));
2020 if (gctx_locks
== NULL
)
2022 for (i
= 0; i
< PROF_NCTX_LOCKS
; i
++) {
2023 if (malloc_mutex_init(&gctx_locks
[i
]))
2027 tdata_locks
= (malloc_mutex_t
*)base_alloc(PROF_NTDATA_LOCKS
*
2028 sizeof(malloc_mutex_t
));
2029 if (tdata_locks
== NULL
)
2031 for (i
= 0; i
< PROF_NTDATA_LOCKS
; i
++) {
2032 if (malloc_mutex_init(&tdata_locks
[i
]))
2037 #ifdef JEMALLOC_PROF_LIBGCC
2039 * Cause the backtracing machinery to allocate its internal state
2040 * before enabling profiling.
2042 _Unwind_Backtrace(prof_unwind_init_callback
, NULL
);
2057 malloc_mutex_prefork(&tdatas_mtx
);
2058 malloc_mutex_prefork(&bt2gctx_mtx
);
2059 malloc_mutex_prefork(&next_thr_uid_mtx
);
2060 malloc_mutex_prefork(&prof_dump_seq_mtx
);
2061 for (i
= 0; i
< PROF_NCTX_LOCKS
; i
++)
2062 malloc_mutex_prefork(&gctx_locks
[i
]);
2063 for (i
= 0; i
< PROF_NTDATA_LOCKS
; i
++)
2064 malloc_mutex_prefork(&tdata_locks
[i
]);
2069 prof_postfork_parent(void)
2075 for (i
= 0; i
< PROF_NTDATA_LOCKS
; i
++)
2076 malloc_mutex_postfork_parent(&tdata_locks
[i
]);
2077 for (i
= 0; i
< PROF_NCTX_LOCKS
; i
++)
2078 malloc_mutex_postfork_parent(&gctx_locks
[i
]);
2079 malloc_mutex_postfork_parent(&prof_dump_seq_mtx
);
2080 malloc_mutex_postfork_parent(&next_thr_uid_mtx
);
2081 malloc_mutex_postfork_parent(&bt2gctx_mtx
);
2082 malloc_mutex_postfork_parent(&tdatas_mtx
);
2087 prof_postfork_child(void)
2093 for (i
= 0; i
< PROF_NTDATA_LOCKS
; i
++)
2094 malloc_mutex_postfork_child(&tdata_locks
[i
]);
2095 for (i
= 0; i
< PROF_NCTX_LOCKS
; i
++)
2096 malloc_mutex_postfork_child(&gctx_locks
[i
]);
2097 malloc_mutex_postfork_child(&prof_dump_seq_mtx
);
2098 malloc_mutex_postfork_child(&next_thr_uid_mtx
);
2099 malloc_mutex_postfork_child(&bt2gctx_mtx
);
2100 malloc_mutex_postfork_child(&tdatas_mtx
);
2104 /******************************************************************************/