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