1*8e33eff8Schristos #ifndef JEMALLOC_INTERNAL_PROF_INLINES_B_H 2*8e33eff8Schristos #define JEMALLOC_INTERNAL_PROF_INLINES_B_H 3*8e33eff8Schristos 4*8e33eff8Schristos #include "jemalloc/internal/sz.h" 5*8e33eff8Schristos 6*8e33eff8Schristos JEMALLOC_ALWAYS_INLINE bool 7*8e33eff8Schristos prof_gdump_get_unlocked(void) { 8*8e33eff8Schristos /* 9*8e33eff8Schristos * No locking is used when reading prof_gdump_val in the fast path, so 10*8e33eff8Schristos * there are no guarantees regarding how long it will take for all 11*8e33eff8Schristos * threads to notice state changes. 12*8e33eff8Schristos */ 13*8e33eff8Schristos return prof_gdump_val; 14*8e33eff8Schristos } 15*8e33eff8Schristos 16*8e33eff8Schristos JEMALLOC_ALWAYS_INLINE prof_tdata_t * 17*8e33eff8Schristos prof_tdata_get(tsd_t *tsd, bool create) { 18*8e33eff8Schristos prof_tdata_t *tdata; 19*8e33eff8Schristos 20*8e33eff8Schristos cassert(config_prof); 21*8e33eff8Schristos 22*8e33eff8Schristos tdata = tsd_prof_tdata_get(tsd); 23*8e33eff8Schristos if (create) { 24*8e33eff8Schristos if (unlikely(tdata == NULL)) { 25*8e33eff8Schristos if (tsd_nominal(tsd)) { 26*8e33eff8Schristos tdata = prof_tdata_init(tsd); 27*8e33eff8Schristos tsd_prof_tdata_set(tsd, tdata); 28*8e33eff8Schristos } 29*8e33eff8Schristos } else if (unlikely(tdata->expired)) { 30*8e33eff8Schristos tdata = prof_tdata_reinit(tsd, tdata); 31*8e33eff8Schristos tsd_prof_tdata_set(tsd, tdata); 32*8e33eff8Schristos } 33*8e33eff8Schristos assert(tdata == NULL || tdata->attached); 34*8e33eff8Schristos } 35*8e33eff8Schristos 36*8e33eff8Schristos return tdata; 37*8e33eff8Schristos } 38*8e33eff8Schristos 39*8e33eff8Schristos JEMALLOC_ALWAYS_INLINE prof_tctx_t * 40*8e33eff8Schristos prof_tctx_get(tsdn_t *tsdn, const void *ptr, alloc_ctx_t *alloc_ctx) { 41*8e33eff8Schristos cassert(config_prof); 42*8e33eff8Schristos assert(ptr != NULL); 43*8e33eff8Schristos 44*8e33eff8Schristos return arena_prof_tctx_get(tsdn, ptr, alloc_ctx); 45*8e33eff8Schristos } 46*8e33eff8Schristos 47*8e33eff8Schristos JEMALLOC_NORETURN JEMALLOC_ALWAYS_INLINE void 48*8e33eff8Schristos prof_tctx_set(tsdn_t *tsdn, const void *ptr, size_t usize, 49*8e33eff8Schristos alloc_ctx_t *alloc_ctx, prof_tctx_t *tctx) { 50*8e33eff8Schristos cassert(config_prof); 51*8e33eff8Schristos assert(ptr != NULL); 52*8e33eff8Schristos 53*8e33eff8Schristos arena_prof_tctx_set(tsdn, ptr, usize, alloc_ctx, tctx); 54*8e33eff8Schristos } 55*8e33eff8Schristos 56*8e33eff8Schristos JEMALLOC_NORETURN JEMALLOC_ALWAYS_INLINE void 57*8e33eff8Schristos prof_tctx_reset(tsdn_t *tsdn, const void *ptr, prof_tctx_t *tctx) { 58*8e33eff8Schristos cassert(config_prof); 59*8e33eff8Schristos assert(ptr != NULL); 60*8e33eff8Schristos 61*8e33eff8Schristos arena_prof_tctx_reset(tsdn, ptr, tctx); 62*8e33eff8Schristos } 63*8e33eff8Schristos 64*8e33eff8Schristos JEMALLOC_ALWAYS_INLINE bool 65*8e33eff8Schristos prof_sample_accum_update(tsd_t *tsd, size_t usize, bool update, 66*8e33eff8Schristos prof_tdata_t **tdata_out) { 67*8e33eff8Schristos prof_tdata_t *tdata; 68*8e33eff8Schristos 69*8e33eff8Schristos cassert(config_prof); 70*8e33eff8Schristos 71*8e33eff8Schristos tdata = prof_tdata_get(tsd, true); 72*8e33eff8Schristos if (unlikely((uintptr_t)tdata <= (uintptr_t)PROF_TDATA_STATE_MAX)) { 73*8e33eff8Schristos tdata = NULL; 74*8e33eff8Schristos } 75*8e33eff8Schristos 76*8e33eff8Schristos if (tdata_out != NULL) { 77*8e33eff8Schristos *tdata_out = tdata; 78*8e33eff8Schristos } 79*8e33eff8Schristos 80*8e33eff8Schristos if (unlikely(tdata == NULL)) { 81*8e33eff8Schristos return true; 82*8e33eff8Schristos } 83*8e33eff8Schristos 84*8e33eff8Schristos if (likely(tdata->bytes_until_sample >= usize)) { 85*8e33eff8Schristos if (update) { 86*8e33eff8Schristos tdata->bytes_until_sample -= usize; 87*8e33eff8Schristos } 88*8e33eff8Schristos return true; 89*8e33eff8Schristos } else { 90*8e33eff8Schristos if (tsd_reentrancy_level_get(tsd) > 0) { 91*8e33eff8Schristos return true; 92*8e33eff8Schristos } 93*8e33eff8Schristos /* Compute new sample threshold. */ 94*8e33eff8Schristos if (update) { 95*8e33eff8Schristos prof_sample_threshold_update(tdata); 96*8e33eff8Schristos } 97*8e33eff8Schristos return !tdata->active; 98*8e33eff8Schristos } 99*8e33eff8Schristos } 100*8e33eff8Schristos 101*8e33eff8Schristos JEMALLOC_ALWAYS_INLINE prof_tctx_t * 102*8e33eff8Schristos prof_alloc_prep(tsd_t *tsd, size_t usize, bool _prof_active, bool update) { 103*8e33eff8Schristos prof_tctx_t *ret; 104*8e33eff8Schristos prof_tdata_t *tdata; 105*8e33eff8Schristos prof_bt_t bt; 106*8e33eff8Schristos 107*8e33eff8Schristos assert(usize == sz_s2u(usize)); 108*8e33eff8Schristos 109*8e33eff8Schristos if (!_prof_active || likely(prof_sample_accum_update(tsd, usize, update, 110*8e33eff8Schristos &tdata))) { 111*8e33eff8Schristos ret = (prof_tctx_t *)(uintptr_t)1U; 112*8e33eff8Schristos } else { 113*8e33eff8Schristos bt_init(&bt, tdata->vec); 114*8e33eff8Schristos prof_backtrace(&bt); 115*8e33eff8Schristos ret = prof_lookup(tsd, &bt); 116*8e33eff8Schristos } 117*8e33eff8Schristos 118*8e33eff8Schristos return ret; 119*8e33eff8Schristos } 120*8e33eff8Schristos 121*8e33eff8Schristos JEMALLOC_NORETURN JEMALLOC_ALWAYS_INLINE void 122*8e33eff8Schristos prof_malloc(tsdn_t *tsdn, const void *ptr, size_t usize, alloc_ctx_t *alloc_ctx, 123*8e33eff8Schristos prof_tctx_t *tctx) { 124*8e33eff8Schristos cassert(config_prof); 125*8e33eff8Schristos assert(ptr != NULL); 126*8e33eff8Schristos assert(usize == isalloc(tsdn, ptr)); 127*8e33eff8Schristos 128*8e33eff8Schristos if (unlikely((uintptr_t)tctx > (uintptr_t)1U)) { 129*8e33eff8Schristos prof_malloc_sample_object(tsdn, ptr, usize, tctx); 130*8e33eff8Schristos } else { 131*8e33eff8Schristos prof_tctx_set(tsdn, ptr, usize, alloc_ctx, 132*8e33eff8Schristos (prof_tctx_t *)(uintptr_t)1U); 133*8e33eff8Schristos } 134*8e33eff8Schristos } 135*8e33eff8Schristos 136*8e33eff8Schristos JEMALLOC_NORETURN JEMALLOC_ALWAYS_INLINE void 137*8e33eff8Schristos prof_realloc(tsd_t *tsd, const void *ptr, size_t usize, prof_tctx_t *tctx, 138*8e33eff8Schristos bool _prof_active, bool updated, const void *old_ptr, size_t old_usize, 139*8e33eff8Schristos prof_tctx_t *old_tctx) { 140*8e33eff8Schristos bool sampled, old_sampled, moved; 141*8e33eff8Schristos 142*8e33eff8Schristos cassert(config_prof); 143*8e33eff8Schristos assert(ptr != NULL || (uintptr_t)tctx <= (uintptr_t)1U); 144*8e33eff8Schristos 145*8e33eff8Schristos if (_prof_active && !updated && ptr != NULL) { 146*8e33eff8Schristos assert(usize == isalloc(tsd_tsdn(tsd), ptr)); 147*8e33eff8Schristos if (prof_sample_accum_update(tsd, usize, true, NULL)) { 148*8e33eff8Schristos /* 149*8e33eff8Schristos * Don't sample. The usize passed to prof_alloc_prep() 150*8e33eff8Schristos * was larger than what actually got allocated, so a 151*8e33eff8Schristos * backtrace was captured for this allocation, even 152*8e33eff8Schristos * though its actual usize was insufficient to cross the 153*8e33eff8Schristos * sample threshold. 154*8e33eff8Schristos */ 155*8e33eff8Schristos prof_alloc_rollback(tsd, tctx, true); 156*8e33eff8Schristos tctx = (prof_tctx_t *)(uintptr_t)1U; 157*8e33eff8Schristos } 158*8e33eff8Schristos } 159*8e33eff8Schristos 160*8e33eff8Schristos sampled = ((uintptr_t)tctx > (uintptr_t)1U); 161*8e33eff8Schristos old_sampled = ((uintptr_t)old_tctx > (uintptr_t)1U); 162*8e33eff8Schristos moved = (ptr != old_ptr); 163*8e33eff8Schristos 164*8e33eff8Schristos if (unlikely(sampled)) { 165*8e33eff8Schristos prof_malloc_sample_object(tsd_tsdn(tsd), ptr, usize, tctx); 166*8e33eff8Schristos } else if (moved) { 167*8e33eff8Schristos prof_tctx_set(tsd_tsdn(tsd), ptr, usize, NULL, 168*8e33eff8Schristos (prof_tctx_t *)(uintptr_t)1U); 169*8e33eff8Schristos } else if (unlikely(old_sampled)) { 170*8e33eff8Schristos /* 171*8e33eff8Schristos * prof_tctx_set() would work for the !moved case as well, but 172*8e33eff8Schristos * prof_tctx_reset() is slightly cheaper, and the proper thing 173*8e33eff8Schristos * to do here in the presence of explicit knowledge re: moved 174*8e33eff8Schristos * state. 175*8e33eff8Schristos */ 176*8e33eff8Schristos prof_tctx_reset(tsd_tsdn(tsd), ptr, tctx); 177*8e33eff8Schristos } else { 178*8e33eff8Schristos assert((uintptr_t)prof_tctx_get(tsd_tsdn(tsd), ptr, NULL) == 179*8e33eff8Schristos (uintptr_t)1U); 180*8e33eff8Schristos } 181*8e33eff8Schristos 182*8e33eff8Schristos /* 183*8e33eff8Schristos * The prof_free_sampled_object() call must come after the 184*8e33eff8Schristos * prof_malloc_sample_object() call, because tctx and old_tctx may be 185*8e33eff8Schristos * the same, in which case reversing the call order could cause the tctx 186*8e33eff8Schristos * to be prematurely destroyed as a side effect of momentarily zeroed 187*8e33eff8Schristos * counters. 188*8e33eff8Schristos */ 189*8e33eff8Schristos if (unlikely(old_sampled)) { 190*8e33eff8Schristos prof_free_sampled_object(tsd, old_usize, old_tctx); 191*8e33eff8Schristos } 192*8e33eff8Schristos } 193*8e33eff8Schristos 194*8e33eff8Schristos JEMALLOC_NORETURN JEMALLOC_ALWAYS_INLINE void 195*8e33eff8Schristos prof_free(tsd_t *tsd, const void *ptr, size_t usize, alloc_ctx_t *alloc_ctx) { 196*8e33eff8Schristos prof_tctx_t *tctx = prof_tctx_get(tsd_tsdn(tsd), ptr, alloc_ctx); 197*8e33eff8Schristos 198*8e33eff8Schristos cassert(config_prof); 199*8e33eff8Schristos assert(usize == isalloc(tsd_tsdn(tsd), ptr)); 200*8e33eff8Schristos 201*8e33eff8Schristos if (unlikely((uintptr_t)tctx > (uintptr_t)1U)) { 202*8e33eff8Schristos prof_free_sampled_object(tsd, usize, tctx); 203*8e33eff8Schristos } 204*8e33eff8Schristos } 205*8e33eff8Schristos 206*8e33eff8Schristos #endif /* JEMALLOC_INTERNAL_PROF_INLINES_B_H */ 207