Home | History | Annotate | Line # | Download | only in internal
      1  1.1  christos #ifndef JEMALLOC_INTERNAL_PROF_INLINES_H
      2  1.1  christos #define JEMALLOC_INTERNAL_PROF_INLINES_H
      3  1.1  christos 
      4  1.1  christos #include "jemalloc/internal/safety_check.h"
      5  1.1  christos #include "jemalloc/internal/sz.h"
      6  1.1  christos #include "jemalloc/internal/thread_event.h"
      7  1.1  christos 
      8  1.1  christos JEMALLOC_ALWAYS_INLINE void
      9  1.1  christos prof_active_assert(void) {
     10  1.1  christos 	cassert(config_prof);
     11  1.1  christos 	/*
     12  1.1  christos 	 * If opt_prof is off, then prof_active must always be off, regardless
     13  1.1  christos 	 * of whether prof_active_mtx is in effect or not.
     14  1.1  christos 	 */
     15  1.1  christos 	assert(opt_prof || !prof_active_state);
     16  1.1  christos }
     17  1.1  christos 
     18  1.1  christos JEMALLOC_ALWAYS_INLINE bool
     19  1.1  christos prof_active_get_unlocked(void) {
     20  1.1  christos 	prof_active_assert();
     21  1.1  christos 	/*
     22  1.1  christos 	 * Even if opt_prof is true, sampling can be temporarily disabled by
     23  1.1  christos 	 * setting prof_active to false.  No locking is used when reading
     24  1.1  christos 	 * prof_active in the fast path, so there are no guarantees regarding
     25  1.1  christos 	 * how long it will take for all threads to notice state changes.
     26  1.1  christos 	 */
     27  1.1  christos 	return prof_active_state;
     28  1.1  christos }
     29  1.1  christos 
     30  1.1  christos JEMALLOC_ALWAYS_INLINE bool
     31  1.1  christos prof_gdump_get_unlocked(void) {
     32  1.1  christos 	/*
     33  1.1  christos 	 * No locking is used when reading prof_gdump_val in the fast path, so
     34  1.1  christos 	 * there are no guarantees regarding how long it will take for all
     35  1.1  christos 	 * threads to notice state changes.
     36  1.1  christos 	 */
     37  1.1  christos 	return prof_gdump_val;
     38  1.1  christos }
     39  1.1  christos 
     40  1.1  christos JEMALLOC_ALWAYS_INLINE prof_tdata_t *
     41  1.1  christos prof_tdata_get(tsd_t *tsd, bool create) {
     42  1.1  christos 	prof_tdata_t *tdata;
     43  1.1  christos 
     44  1.1  christos 	cassert(config_prof);
     45  1.1  christos 
     46  1.1  christos 	tdata = tsd_prof_tdata_get(tsd);
     47  1.1  christos 	if (create) {
     48  1.1  christos 		assert(tsd_reentrancy_level_get(tsd) == 0);
     49  1.1  christos 		if (unlikely(tdata == NULL)) {
     50  1.1  christos 			if (tsd_nominal(tsd)) {
     51  1.1  christos 				tdata = prof_tdata_init(tsd);
     52  1.1  christos 				tsd_prof_tdata_set(tsd, tdata);
     53  1.1  christos 			}
     54  1.1  christos 		} else if (unlikely(tdata->expired)) {
     55  1.1  christos 			tdata = prof_tdata_reinit(tsd, tdata);
     56  1.1  christos 			tsd_prof_tdata_set(tsd, tdata);
     57  1.1  christos 		}
     58  1.1  christos 		assert(tdata == NULL || tdata->attached);
     59  1.1  christos 	}
     60  1.1  christos 
     61  1.1  christos 	return tdata;
     62  1.1  christos }
     63  1.1  christos 
     64  1.1  christos JEMALLOC_ALWAYS_INLINE void
     65  1.1  christos prof_info_get(tsd_t *tsd, const void *ptr, emap_alloc_ctx_t *alloc_ctx,
     66  1.1  christos     prof_info_t *prof_info) {
     67  1.1  christos 	cassert(config_prof);
     68  1.1  christos 	assert(ptr != NULL);
     69  1.1  christos 	assert(prof_info != NULL);
     70  1.1  christos 
     71  1.1  christos 	arena_prof_info_get(tsd, ptr, alloc_ctx, prof_info, false);
     72  1.1  christos }
     73  1.1  christos 
     74  1.1  christos JEMALLOC_ALWAYS_INLINE void
     75  1.1  christos prof_info_get_and_reset_recent(tsd_t *tsd, const void *ptr,
     76  1.1  christos     emap_alloc_ctx_t *alloc_ctx, prof_info_t *prof_info) {
     77  1.1  christos 	cassert(config_prof);
     78  1.1  christos 	assert(ptr != NULL);
     79  1.1  christos 	assert(prof_info != NULL);
     80  1.1  christos 
     81  1.1  christos 	arena_prof_info_get(tsd, ptr, alloc_ctx, prof_info, true);
     82  1.1  christos }
     83  1.1  christos 
     84  1.1  christos JEMALLOC_ALWAYS_INLINE void
     85  1.1  christos prof_tctx_reset(tsd_t *tsd, const void *ptr, emap_alloc_ctx_t *alloc_ctx) {
     86  1.1  christos 	cassert(config_prof);
     87  1.1  christos 	assert(ptr != NULL);
     88  1.1  christos 
     89  1.1  christos 	arena_prof_tctx_reset(tsd, ptr, alloc_ctx);
     90  1.1  christos }
     91  1.1  christos 
     92  1.1  christos JEMALLOC_ALWAYS_INLINE void
     93  1.1  christos prof_tctx_reset_sampled(tsd_t *tsd, const void *ptr) {
     94  1.1  christos 	cassert(config_prof);
     95  1.1  christos 	assert(ptr != NULL);
     96  1.1  christos 
     97  1.1  christos 	arena_prof_tctx_reset_sampled(tsd, ptr);
     98  1.1  christos }
     99  1.1  christos 
    100  1.1  christos JEMALLOC_ALWAYS_INLINE void
    101  1.1  christos prof_info_set(tsd_t *tsd, edata_t *edata, prof_tctx_t *tctx, size_t size) {
    102  1.1  christos 	cassert(config_prof);
    103  1.1  christos 	assert(edata != NULL);
    104  1.1  christos 	assert((uintptr_t)tctx > (uintptr_t)1U);
    105  1.1  christos 
    106  1.1  christos 	arena_prof_info_set(tsd, edata, tctx, size);
    107  1.1  christos }
    108  1.1  christos 
    109  1.1  christos JEMALLOC_ALWAYS_INLINE bool
    110  1.1  christos prof_sample_should_skip(tsd_t *tsd, bool sample_event) {
    111  1.1  christos 	cassert(config_prof);
    112  1.1  christos 
    113  1.1  christos 	/* Fastpath: no need to load tdata */
    114  1.1  christos 	if (likely(!sample_event)) {
    115  1.1  christos 		return true;
    116  1.1  christos 	}
    117  1.1  christos 
    118  1.1  christos 	/*
    119  1.1  christos 	 * sample_event is always obtained from the thread event module, and
    120  1.1  christos 	 * whenever it's true, it means that the thread event module has
    121  1.1  christos 	 * already checked the reentrancy level.
    122  1.1  christos 	 */
    123  1.1  christos 	assert(tsd_reentrancy_level_get(tsd) == 0);
    124  1.1  christos 
    125  1.1  christos 	prof_tdata_t *tdata = prof_tdata_get(tsd, true);
    126  1.1  christos 	if (unlikely(tdata == NULL)) {
    127  1.1  christos 		return true;
    128  1.1  christos 	}
    129  1.1  christos 
    130  1.1  christos 	return !tdata->active;
    131  1.1  christos }
    132  1.1  christos 
    133  1.1  christos JEMALLOC_ALWAYS_INLINE prof_tctx_t *
    134  1.1  christos prof_alloc_prep(tsd_t *tsd, bool prof_active, bool sample_event) {
    135  1.1  christos 	prof_tctx_t *ret;
    136  1.1  christos 
    137  1.1  christos 	if (!prof_active ||
    138  1.1  christos 	    likely(prof_sample_should_skip(tsd, sample_event))) {
    139  1.1  christos 		ret = (prof_tctx_t *)(uintptr_t)1U;
    140  1.1  christos 	} else {
    141  1.1  christos 		ret = prof_tctx_create(tsd);
    142  1.1  christos 	}
    143  1.1  christos 
    144  1.1  christos 	return ret;
    145  1.1  christos }
    146  1.1  christos 
    147  1.1  christos JEMALLOC_ALWAYS_INLINE void
    148  1.1  christos prof_malloc(tsd_t *tsd, const void *ptr, size_t size, size_t usize,
    149  1.1  christos     emap_alloc_ctx_t *alloc_ctx, prof_tctx_t *tctx) {
    150  1.1  christos 	cassert(config_prof);
    151  1.1  christos 	assert(ptr != NULL);
    152  1.1  christos 	assert(usize == isalloc(tsd_tsdn(tsd), ptr));
    153  1.1  christos 
    154  1.1  christos 	if (unlikely((uintptr_t)tctx > (uintptr_t)1U)) {
    155  1.1  christos 		prof_malloc_sample_object(tsd, ptr, size, usize, tctx);
    156  1.1  christos 	} else {
    157  1.1  christos 		prof_tctx_reset(tsd, ptr, alloc_ctx);
    158  1.1  christos 	}
    159  1.1  christos }
    160  1.1  christos 
    161  1.1  christos JEMALLOC_ALWAYS_INLINE void
    162  1.1  christos prof_realloc(tsd_t *tsd, const void *ptr, size_t size, size_t usize,
    163  1.1  christos     prof_tctx_t *tctx, bool prof_active, const void *old_ptr, size_t old_usize,
    164  1.1  christos     prof_info_t *old_prof_info, bool sample_event) {
    165  1.1  christos 	bool sampled, old_sampled, moved;
    166  1.1  christos 
    167  1.1  christos 	cassert(config_prof);
    168  1.1  christos 	assert(ptr != NULL || (uintptr_t)tctx <= (uintptr_t)1U);
    169  1.1  christos 
    170  1.1  christos 	if (prof_active && ptr != NULL) {
    171  1.1  christos 		assert(usize == isalloc(tsd_tsdn(tsd), ptr));
    172  1.1  christos 		if (prof_sample_should_skip(tsd, sample_event)) {
    173  1.1  christos 			/*
    174  1.1  christos 			 * Don't sample.  The usize passed to prof_alloc_prep()
    175  1.1  christos 			 * was larger than what actually got allocated, so a
    176  1.1  christos 			 * backtrace was captured for this allocation, even
    177  1.1  christos 			 * though its actual usize was insufficient to cross the
    178  1.1  christos 			 * sample threshold.
    179  1.1  christos 			 */
    180  1.1  christos 			prof_alloc_rollback(tsd, tctx);
    181  1.1  christos 			tctx = (prof_tctx_t *)(uintptr_t)1U;
    182  1.1  christos 		}
    183  1.1  christos 	}
    184  1.1  christos 
    185  1.1  christos 	sampled = ((uintptr_t)tctx > (uintptr_t)1U);
    186  1.1  christos 	old_sampled = ((uintptr_t)old_prof_info->alloc_tctx > (uintptr_t)1U);
    187  1.1  christos 	moved = (ptr != old_ptr);
    188  1.1  christos 
    189  1.1  christos 	if (unlikely(sampled)) {
    190  1.1  christos 		prof_malloc_sample_object(tsd, ptr, size, usize, tctx);
    191  1.1  christos 	} else if (moved) {
    192  1.1  christos 		prof_tctx_reset(tsd, ptr, NULL);
    193  1.1  christos 	} else if (unlikely(old_sampled)) {
    194  1.1  christos 		/*
    195  1.1  christos 		 * prof_tctx_reset() would work for the !moved case as well,
    196  1.1  christos 		 * but prof_tctx_reset_sampled() is slightly cheaper, and the
    197  1.1  christos 		 * proper thing to do here in the presence of explicit
    198  1.1  christos 		 * knowledge re: moved state.
    199  1.1  christos 		 */
    200  1.1  christos 		prof_tctx_reset_sampled(tsd, ptr);
    201  1.1  christos 	} else {
    202  1.1  christos 		prof_info_t prof_info;
    203  1.1  christos 		prof_info_get(tsd, ptr, NULL, &prof_info);
    204  1.1  christos 		assert((uintptr_t)prof_info.alloc_tctx == (uintptr_t)1U);
    205  1.1  christos 	}
    206  1.1  christos 
    207  1.1  christos 	/*
    208  1.1  christos 	 * The prof_free_sampled_object() call must come after the
    209  1.1  christos 	 * prof_malloc_sample_object() call, because tctx and old_tctx may be
    210  1.1  christos 	 * the same, in which case reversing the call order could cause the tctx
    211  1.1  christos 	 * to be prematurely destroyed as a side effect of momentarily zeroed
    212  1.1  christos 	 * counters.
    213  1.1  christos 	 */
    214  1.1  christos 	if (unlikely(old_sampled)) {
    215  1.1  christos 		prof_free_sampled_object(tsd, old_usize, old_prof_info);
    216  1.1  christos 	}
    217  1.1  christos }
    218  1.1  christos 
    219  1.1  christos JEMALLOC_ALWAYS_INLINE size_t
    220  1.1  christos prof_sample_align(size_t orig_align) {
    221  1.1  christos 	/*
    222  1.1  christos 	 * Enforce page alignment, so that sampled allocations can be identified
    223  1.1  christos 	 * w/o metadata lookup.
    224  1.1  christos 	 */
    225  1.1  christos 	assert(opt_prof);
    226  1.1  christos 	return (opt_cache_oblivious && orig_align < PAGE) ? PAGE :
    227  1.1  christos 	    orig_align;
    228  1.1  christos }
    229  1.1  christos 
    230  1.1  christos JEMALLOC_ALWAYS_INLINE bool
    231  1.1  christos prof_sample_aligned(const void *ptr) {
    232  1.1  christos 	return ((uintptr_t)ptr & PAGE_MASK) == 0;
    233  1.1  christos }
    234  1.1  christos 
    235  1.1  christos JEMALLOC_ALWAYS_INLINE bool
    236  1.1  christos prof_sampled(tsd_t *tsd, const void *ptr) {
    237  1.1  christos 	prof_info_t prof_info;
    238  1.1  christos 	prof_info_get(tsd, ptr, NULL, &prof_info);
    239  1.1  christos 	bool sampled = (uintptr_t)prof_info.alloc_tctx > (uintptr_t)1U;
    240  1.1  christos 	if (sampled) {
    241  1.1  christos 		assert(prof_sample_aligned(ptr));
    242  1.1  christos 	}
    243  1.1  christos 	return sampled;
    244  1.1  christos }
    245  1.1  christos 
    246  1.1  christos JEMALLOC_ALWAYS_INLINE void
    247  1.1  christos prof_free(tsd_t *tsd, const void *ptr, size_t usize,
    248  1.1  christos     emap_alloc_ctx_t *alloc_ctx) {
    249  1.1  christos 	prof_info_t prof_info;
    250  1.1  christos 	prof_info_get_and_reset_recent(tsd, ptr, alloc_ctx, &prof_info);
    251  1.1  christos 
    252  1.1  christos 	cassert(config_prof);
    253  1.1  christos 	assert(usize == isalloc(tsd_tsdn(tsd), ptr));
    254  1.1  christos 
    255  1.1  christos 	if (unlikely((uintptr_t)prof_info.alloc_tctx > (uintptr_t)1U)) {
    256  1.1  christos 		assert(prof_sample_aligned(ptr));
    257  1.1  christos 		prof_free_sampled_object(tsd, usize, &prof_info);
    258  1.1  christos 	}
    259  1.1  christos }
    260  1.1  christos 
    261  1.1  christos #endif /* JEMALLOC_INTERNAL_PROF_INLINES_H */
    262