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