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