1 #ifndef ARENA_RESET_PROF_C_ 2 #include "test/jemalloc_test.h" 3 #endif 4 5 #include "jemalloc/internal/extent_mmap.h" 6 #include "jemalloc/internal/rtree.h" 7 8 #include "test/extent_hooks.h" 9 10 static unsigned 11 get_nsizes_impl(const char *cmd) { 12 unsigned ret; 13 size_t z; 14 15 z = sizeof(unsigned); 16 assert_d_eq(mallctl(cmd, (void *)&ret, &z, NULL, 0), 0, 17 "Unexpected mallctl(\"%s\", ...) failure", cmd); 18 19 return ret; 20 } 21 22 static unsigned 23 get_nsmall(void) { 24 return get_nsizes_impl("arenas.nbins"); 25 } 26 27 static unsigned 28 get_nlarge(void) { 29 return get_nsizes_impl("arenas.nlextents"); 30 } 31 32 static size_t 33 get_size_impl(const char *cmd, size_t ind) { 34 size_t ret; 35 size_t z; 36 size_t mib[4]; 37 size_t miblen = 4; 38 39 z = sizeof(size_t); 40 assert_d_eq(mallctlnametomib(cmd, mib, &miblen), 41 0, "Unexpected mallctlnametomib(\"%s\", ...) failure", cmd); 42 mib[2] = ind; 43 z = sizeof(size_t); 44 assert_d_eq(mallctlbymib(mib, miblen, (void *)&ret, &z, NULL, 0), 45 0, "Unexpected mallctlbymib([\"%s\", %zu], ...) failure", cmd, ind); 46 47 return ret; 48 } 49 50 static size_t 51 get_small_size(size_t ind) { 52 return get_size_impl("arenas.bin.0.size", ind); 53 } 54 55 static size_t 56 get_large_size(size_t ind) { 57 return get_size_impl("arenas.lextent.0.size", ind); 58 } 59 60 /* Like ivsalloc(), but safe to call on discarded allocations. */ 61 static size_t 62 vsalloc(tsdn_t *tsdn, const void *ptr) { 63 rtree_ctx_t rtree_ctx_fallback; 64 rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback); 65 66 extent_t *extent; 67 szind_t szind; 68 if (rtree_extent_szind_read(tsdn, &extents_rtree, rtree_ctx, 69 (uintptr_t)ptr, false, &extent, &szind)) { 70 return 0; 71 } 72 73 if (extent == NULL) { 74 return 0; 75 } 76 if (extent_state_get(extent) != extent_state_active) { 77 return 0; 78 } 79 80 if (szind == NSIZES) { 81 return 0; 82 } 83 84 return sz_index2size(szind); 85 } 86 87 static unsigned 88 do_arena_create(extent_hooks_t *h) { 89 unsigned arena_ind; 90 size_t sz = sizeof(unsigned); 91 assert_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz, 92 (void *)(h != NULL ? &h : NULL), (h != NULL ? sizeof(h) : 0)), 0, 93 "Unexpected mallctl() failure"); 94 return arena_ind; 95 } 96 97 static void 98 do_arena_reset_pre(unsigned arena_ind, void ***ptrs, unsigned *nptrs) { 99 #define NLARGE 32 100 unsigned nsmall, nlarge, i; 101 size_t sz; 102 int flags; 103 tsdn_t *tsdn; 104 105 flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE; 106 107 nsmall = get_nsmall(); 108 nlarge = get_nlarge() > NLARGE ? NLARGE : get_nlarge(); 109 *nptrs = nsmall + nlarge; 110 *ptrs = (void **)malloc(*nptrs * sizeof(void *)); 111 assert_ptr_not_null(*ptrs, "Unexpected malloc() failure"); 112 113 /* Allocate objects with a wide range of sizes. */ 114 for (i = 0; i < nsmall; i++) { 115 sz = get_small_size(i); 116 (*ptrs)[i] = mallocx(sz, flags); 117 assert_ptr_not_null((*ptrs)[i], 118 "Unexpected mallocx(%zu, %#x) failure", sz, flags); 119 } 120 for (i = 0; i < nlarge; i++) { 121 sz = get_large_size(i); 122 (*ptrs)[nsmall + i] = mallocx(sz, flags); 123 assert_ptr_not_null((*ptrs)[i], 124 "Unexpected mallocx(%zu, %#x) failure", sz, flags); 125 } 126 127 tsdn = tsdn_fetch(); 128 129 /* Verify allocations. */ 130 for (i = 0; i < *nptrs; i++) { 131 assert_zu_gt(ivsalloc(tsdn, (*ptrs)[i]), 0, 132 "Allocation should have queryable size"); 133 } 134 } 135 136 static void 137 do_arena_reset_post(void **ptrs, unsigned nptrs, unsigned arena_ind) { 138 tsdn_t *tsdn; 139 unsigned i; 140 141 tsdn = tsdn_fetch(); 142 143 if (have_background_thread) { 144 malloc_mutex_lock(tsdn, 145 &background_thread_info[arena_ind % ncpus].mtx); 146 } 147 /* Verify allocations no longer exist. */ 148 for (i = 0; i < nptrs; i++) { 149 assert_zu_eq(vsalloc(tsdn, ptrs[i]), 0, 150 "Allocation should no longer exist"); 151 } 152 if (have_background_thread) { 153 malloc_mutex_unlock(tsdn, 154 &background_thread_info[arena_ind % ncpus].mtx); 155 } 156 157 free(ptrs); 158 } 159 160 static void 161 do_arena_reset_destroy(const char *name, unsigned arena_ind) { 162 size_t mib[3]; 163 size_t miblen; 164 165 miblen = sizeof(mib)/sizeof(size_t); 166 assert_d_eq(mallctlnametomib(name, mib, &miblen), 0, 167 "Unexpected mallctlnametomib() failure"); 168 mib[1] = (size_t)arena_ind; 169 assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0, 170 "Unexpected mallctlbymib() failure"); 171 } 172 173 static void 174 do_arena_reset(unsigned arena_ind) { 175 do_arena_reset_destroy("arena.0.reset", arena_ind); 176 } 177 178 static void 179 do_arena_destroy(unsigned arena_ind) { 180 do_arena_reset_destroy("arena.0.destroy", arena_ind); 181 } 182 183 TEST_BEGIN(test_arena_reset) { 184 unsigned arena_ind; 185 void **ptrs; 186 unsigned nptrs; 187 188 arena_ind = do_arena_create(NULL); 189 do_arena_reset_pre(arena_ind, &ptrs, &nptrs); 190 do_arena_reset(arena_ind); 191 do_arena_reset_post(ptrs, nptrs, arena_ind); 192 } 193 TEST_END 194 195 static bool 196 arena_i_initialized(unsigned arena_ind, bool refresh) { 197 bool initialized; 198 size_t mib[3]; 199 size_t miblen, sz; 200 201 if (refresh) { 202 uint64_t epoch = 1; 203 assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, 204 sizeof(epoch)), 0, "Unexpected mallctl() failure"); 205 } 206 207 miblen = sizeof(mib)/sizeof(size_t); 208 assert_d_eq(mallctlnametomib("arena.0.initialized", mib, &miblen), 0, 209 "Unexpected mallctlnametomib() failure"); 210 mib[1] = (size_t)arena_ind; 211 sz = sizeof(initialized); 212 assert_d_eq(mallctlbymib(mib, miblen, (void *)&initialized, &sz, NULL, 213 0), 0, "Unexpected mallctlbymib() failure"); 214 215 return initialized; 216 } 217 218 TEST_BEGIN(test_arena_destroy_initial) { 219 assert_false(arena_i_initialized(MALLCTL_ARENAS_DESTROYED, false), 220 "Destroyed arena stats should not be initialized"); 221 } 222 TEST_END 223 224 TEST_BEGIN(test_arena_destroy_hooks_default) { 225 unsigned arena_ind, arena_ind_another, arena_ind_prev; 226 void **ptrs; 227 unsigned nptrs; 228 229 arena_ind = do_arena_create(NULL); 230 do_arena_reset_pre(arena_ind, &ptrs, &nptrs); 231 232 assert_false(arena_i_initialized(arena_ind, false), 233 "Arena stats should not be initialized"); 234 assert_true(arena_i_initialized(arena_ind, true), 235 "Arena stats should be initialized"); 236 237 /* 238 * Create another arena before destroying one, to better verify arena 239 * index reuse. 240 */ 241 arena_ind_another = do_arena_create(NULL); 242 243 do_arena_destroy(arena_ind); 244 245 assert_false(arena_i_initialized(arena_ind, true), 246 "Arena stats should not be initialized"); 247 assert_true(arena_i_initialized(MALLCTL_ARENAS_DESTROYED, false), 248 "Destroyed arena stats should be initialized"); 249 250 do_arena_reset_post(ptrs, nptrs, arena_ind); 251 252 arena_ind_prev = arena_ind; 253 arena_ind = do_arena_create(NULL); 254 do_arena_reset_pre(arena_ind, &ptrs, &nptrs); 255 assert_u_eq(arena_ind, arena_ind_prev, 256 "Arena index should have been recycled"); 257 do_arena_destroy(arena_ind); 258 do_arena_reset_post(ptrs, nptrs, arena_ind); 259 260 do_arena_destroy(arena_ind_another); 261 } 262 TEST_END 263 264 /* 265 * Actually unmap extents, regardless of opt_retain, so that attempts to access 266 * a destroyed arena's memory will segfault. 267 */ 268 static bool 269 extent_dalloc_unmap(extent_hooks_t *extent_hooks, void *addr, size_t size, 270 bool committed, unsigned arena_ind) { 271 TRACE_HOOK("%s(extent_hooks=%p, addr=%p, size=%zu, committed=%s, " 272 "arena_ind=%u)\n", __func__, extent_hooks, addr, size, committed ? 273 "true" : "false", arena_ind); 274 assert_ptr_eq(extent_hooks, &hooks, 275 "extent_hooks should be same as pointer used to set hooks"); 276 assert_ptr_eq(extent_hooks->dalloc, extent_dalloc_unmap, 277 "Wrong hook function"); 278 called_dalloc = true; 279 if (!try_dalloc) { 280 return true; 281 } 282 pages_unmap(addr, size); 283 did_dalloc = true; 284 return false; 285 } 286 287 static extent_hooks_t hooks_orig; 288 289 static extent_hooks_t hooks_unmap = { 290 extent_alloc_hook, 291 extent_dalloc_unmap, /* dalloc */ 292 extent_destroy_hook, 293 extent_commit_hook, 294 extent_decommit_hook, 295 extent_purge_lazy_hook, 296 extent_purge_forced_hook, 297 extent_split_hook, 298 extent_merge_hook 299 }; 300 301 TEST_BEGIN(test_arena_destroy_hooks_unmap) { 302 unsigned arena_ind; 303 void **ptrs; 304 unsigned nptrs; 305 306 extent_hooks_prep(); 307 try_decommit = false; 308 memcpy(&hooks_orig, &hooks, sizeof(extent_hooks_t)); 309 memcpy(&hooks, &hooks_unmap, sizeof(extent_hooks_t)); 310 311 did_alloc = false; 312 arena_ind = do_arena_create(&hooks); 313 do_arena_reset_pre(arena_ind, &ptrs, &nptrs); 314 315 assert_true(did_alloc, "Expected alloc"); 316 317 assert_false(arena_i_initialized(arena_ind, false), 318 "Arena stats should not be initialized"); 319 assert_true(arena_i_initialized(arena_ind, true), 320 "Arena stats should be initialized"); 321 322 did_dalloc = false; 323 do_arena_destroy(arena_ind); 324 assert_true(did_dalloc, "Expected dalloc"); 325 326 assert_false(arena_i_initialized(arena_ind, true), 327 "Arena stats should not be initialized"); 328 assert_true(arena_i_initialized(MALLCTL_ARENAS_DESTROYED, false), 329 "Destroyed arena stats should be initialized"); 330 331 do_arena_reset_post(ptrs, nptrs, arena_ind); 332 333 memcpy(&hooks, &hooks_orig, sizeof(extent_hooks_t)); 334 } 335 TEST_END 336 337 int 338 main(void) { 339 return test( 340 test_arena_reset, 341 test_arena_destroy_initial, 342 test_arena_destroy_hooks_default, 343 test_arena_destroy_hooks_unmap); 344 } 345