1*8e33eff8Schristos #include "test/jemalloc_test.h" 2*8e33eff8Schristos 3*8e33eff8Schristos #include "jemalloc/internal/ticker.h" 4*8e33eff8Schristos 5*8e33eff8Schristos static nstime_monotonic_t *nstime_monotonic_orig; 6*8e33eff8Schristos static nstime_update_t *nstime_update_orig; 7*8e33eff8Schristos 8*8e33eff8Schristos static unsigned nupdates_mock; 9*8e33eff8Schristos static nstime_t time_mock; 10*8e33eff8Schristos static bool monotonic_mock; 11*8e33eff8Schristos 12*8e33eff8Schristos static bool 13*8e33eff8Schristos check_background_thread_enabled(void) { 14*8e33eff8Schristos bool enabled; 15*8e33eff8Schristos size_t sz = sizeof(bool); 16*8e33eff8Schristos int ret = mallctl("background_thread", (void *)&enabled, &sz, NULL,0); 17*8e33eff8Schristos if (ret == ENOENT) { 18*8e33eff8Schristos return false; 19*8e33eff8Schristos } 20*8e33eff8Schristos assert_d_eq(ret, 0, "Unexpected mallctl error"); 21*8e33eff8Schristos return enabled; 22*8e33eff8Schristos } 23*8e33eff8Schristos 24*8e33eff8Schristos static bool 25*8e33eff8Schristos nstime_monotonic_mock(void) { 26*8e33eff8Schristos return monotonic_mock; 27*8e33eff8Schristos } 28*8e33eff8Schristos 29*8e33eff8Schristos static bool 30*8e33eff8Schristos nstime_update_mock(nstime_t *time) { 31*8e33eff8Schristos nupdates_mock++; 32*8e33eff8Schristos if (monotonic_mock) { 33*8e33eff8Schristos nstime_copy(time, &time_mock); 34*8e33eff8Schristos } 35*8e33eff8Schristos return !monotonic_mock; 36*8e33eff8Schristos } 37*8e33eff8Schristos 38*8e33eff8Schristos static unsigned 39*8e33eff8Schristos do_arena_create(ssize_t dirty_decay_ms, ssize_t muzzy_decay_ms) { 40*8e33eff8Schristos unsigned arena_ind; 41*8e33eff8Schristos size_t sz = sizeof(unsigned); 42*8e33eff8Schristos assert_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz, NULL, 0), 43*8e33eff8Schristos 0, "Unexpected mallctl() failure"); 44*8e33eff8Schristos size_t mib[3]; 45*8e33eff8Schristos size_t miblen = sizeof(mib)/sizeof(size_t); 46*8e33eff8Schristos 47*8e33eff8Schristos assert_d_eq(mallctlnametomib("arena.0.dirty_decay_ms", mib, &miblen), 48*8e33eff8Schristos 0, "Unexpected mallctlnametomib() failure"); 49*8e33eff8Schristos mib[1] = (size_t)arena_ind; 50*8e33eff8Schristos assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, 51*8e33eff8Schristos (void *)&dirty_decay_ms, sizeof(dirty_decay_ms)), 0, 52*8e33eff8Schristos "Unexpected mallctlbymib() failure"); 53*8e33eff8Schristos 54*8e33eff8Schristos assert_d_eq(mallctlnametomib("arena.0.muzzy_decay_ms", mib, &miblen), 55*8e33eff8Schristos 0, "Unexpected mallctlnametomib() failure"); 56*8e33eff8Schristos mib[1] = (size_t)arena_ind; 57*8e33eff8Schristos assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, 58*8e33eff8Schristos (void *)&muzzy_decay_ms, sizeof(muzzy_decay_ms)), 0, 59*8e33eff8Schristos "Unexpected mallctlbymib() failure"); 60*8e33eff8Schristos 61*8e33eff8Schristos return arena_ind; 62*8e33eff8Schristos } 63*8e33eff8Schristos 64*8e33eff8Schristos static void 65*8e33eff8Schristos do_arena_destroy(unsigned arena_ind) { 66*8e33eff8Schristos size_t mib[3]; 67*8e33eff8Schristos size_t miblen = sizeof(mib)/sizeof(size_t); 68*8e33eff8Schristos assert_d_eq(mallctlnametomib("arena.0.destroy", mib, &miblen), 0, 69*8e33eff8Schristos "Unexpected mallctlnametomib() failure"); 70*8e33eff8Schristos mib[1] = (size_t)arena_ind; 71*8e33eff8Schristos assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0, 72*8e33eff8Schristos "Unexpected mallctlbymib() failure"); 73*8e33eff8Schristos } 74*8e33eff8Schristos 75*8e33eff8Schristos void 76*8e33eff8Schristos do_epoch(void) { 77*8e33eff8Schristos uint64_t epoch = 1; 78*8e33eff8Schristos assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)), 79*8e33eff8Schristos 0, "Unexpected mallctl() failure"); 80*8e33eff8Schristos } 81*8e33eff8Schristos 82*8e33eff8Schristos void 83*8e33eff8Schristos do_purge(unsigned arena_ind) { 84*8e33eff8Schristos size_t mib[3]; 85*8e33eff8Schristos size_t miblen = sizeof(mib)/sizeof(size_t); 86*8e33eff8Schristos assert_d_eq(mallctlnametomib("arena.0.purge", mib, &miblen), 0, 87*8e33eff8Schristos "Unexpected mallctlnametomib() failure"); 88*8e33eff8Schristos mib[1] = (size_t)arena_ind; 89*8e33eff8Schristos assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0, 90*8e33eff8Schristos "Unexpected mallctlbymib() failure"); 91*8e33eff8Schristos } 92*8e33eff8Schristos 93*8e33eff8Schristos void 94*8e33eff8Schristos do_decay(unsigned arena_ind) { 95*8e33eff8Schristos size_t mib[3]; 96*8e33eff8Schristos size_t miblen = sizeof(mib)/sizeof(size_t); 97*8e33eff8Schristos assert_d_eq(mallctlnametomib("arena.0.decay", mib, &miblen), 0, 98*8e33eff8Schristos "Unexpected mallctlnametomib() failure"); 99*8e33eff8Schristos mib[1] = (size_t)arena_ind; 100*8e33eff8Schristos assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0, 101*8e33eff8Schristos "Unexpected mallctlbymib() failure"); 102*8e33eff8Schristos } 103*8e33eff8Schristos 104*8e33eff8Schristos static uint64_t 105*8e33eff8Schristos get_arena_npurge_impl(const char *mibname, unsigned arena_ind) { 106*8e33eff8Schristos size_t mib[4]; 107*8e33eff8Schristos size_t miblen = sizeof(mib)/sizeof(size_t); 108*8e33eff8Schristos assert_d_eq(mallctlnametomib(mibname, mib, &miblen), 0, 109*8e33eff8Schristos "Unexpected mallctlnametomib() failure"); 110*8e33eff8Schristos mib[2] = (size_t)arena_ind; 111*8e33eff8Schristos uint64_t npurge = 0; 112*8e33eff8Schristos size_t sz = sizeof(npurge); 113*8e33eff8Schristos assert_d_eq(mallctlbymib(mib, miblen, (void *)&npurge, &sz, NULL, 0), 114*8e33eff8Schristos config_stats ? 0 : ENOENT, "Unexpected mallctlbymib() failure"); 115*8e33eff8Schristos return npurge; 116*8e33eff8Schristos } 117*8e33eff8Schristos 118*8e33eff8Schristos static uint64_t 119*8e33eff8Schristos get_arena_dirty_npurge(unsigned arena_ind) { 120*8e33eff8Schristos do_epoch(); 121*8e33eff8Schristos return get_arena_npurge_impl("stats.arenas.0.dirty_npurge", arena_ind); 122*8e33eff8Schristos } 123*8e33eff8Schristos 124*8e33eff8Schristos static uint64_t 125*8e33eff8Schristos get_arena_muzzy_npurge(unsigned arena_ind) { 126*8e33eff8Schristos do_epoch(); 127*8e33eff8Schristos return get_arena_npurge_impl("stats.arenas.0.muzzy_npurge", arena_ind); 128*8e33eff8Schristos } 129*8e33eff8Schristos 130*8e33eff8Schristos static uint64_t 131*8e33eff8Schristos get_arena_npurge(unsigned arena_ind) { 132*8e33eff8Schristos do_epoch(); 133*8e33eff8Schristos return get_arena_npurge_impl("stats.arenas.0.dirty_npurge", arena_ind) + 134*8e33eff8Schristos get_arena_npurge_impl("stats.arenas.0.muzzy_npurge", arena_ind); 135*8e33eff8Schristos } 136*8e33eff8Schristos 137*8e33eff8Schristos static size_t 138*8e33eff8Schristos get_arena_pdirty(unsigned arena_ind) { 139*8e33eff8Schristos do_epoch(); 140*8e33eff8Schristos size_t mib[4]; 141*8e33eff8Schristos size_t miblen = sizeof(mib)/sizeof(size_t); 142*8e33eff8Schristos assert_d_eq(mallctlnametomib("stats.arenas.0.pdirty", mib, &miblen), 0, 143*8e33eff8Schristos "Unexpected mallctlnametomib() failure"); 144*8e33eff8Schristos mib[2] = (size_t)arena_ind; 145*8e33eff8Schristos size_t pdirty; 146*8e33eff8Schristos size_t sz = sizeof(pdirty); 147*8e33eff8Schristos assert_d_eq(mallctlbymib(mib, miblen, (void *)&pdirty, &sz, NULL, 0), 0, 148*8e33eff8Schristos "Unexpected mallctlbymib() failure"); 149*8e33eff8Schristos return pdirty; 150*8e33eff8Schristos } 151*8e33eff8Schristos 152*8e33eff8Schristos static size_t 153*8e33eff8Schristos get_arena_pmuzzy(unsigned arena_ind) { 154*8e33eff8Schristos do_epoch(); 155*8e33eff8Schristos size_t mib[4]; 156*8e33eff8Schristos size_t miblen = sizeof(mib)/sizeof(size_t); 157*8e33eff8Schristos assert_d_eq(mallctlnametomib("stats.arenas.0.pmuzzy", mib, &miblen), 0, 158*8e33eff8Schristos "Unexpected mallctlnametomib() failure"); 159*8e33eff8Schristos mib[2] = (size_t)arena_ind; 160*8e33eff8Schristos size_t pmuzzy; 161*8e33eff8Schristos size_t sz = sizeof(pmuzzy); 162*8e33eff8Schristos assert_d_eq(mallctlbymib(mib, miblen, (void *)&pmuzzy, &sz, NULL, 0), 0, 163*8e33eff8Schristos "Unexpected mallctlbymib() failure"); 164*8e33eff8Schristos return pmuzzy; 165*8e33eff8Schristos } 166*8e33eff8Schristos 167*8e33eff8Schristos static void * 168*8e33eff8Schristos do_mallocx(size_t size, int flags) { 169*8e33eff8Schristos void *p = mallocx(size, flags); 170*8e33eff8Schristos assert_ptr_not_null(p, "Unexpected mallocx() failure"); 171*8e33eff8Schristos return p; 172*8e33eff8Schristos } 173*8e33eff8Schristos 174*8e33eff8Schristos static void 175*8e33eff8Schristos generate_dirty(unsigned arena_ind, size_t size) { 176*8e33eff8Schristos int flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE; 177*8e33eff8Schristos void *p = do_mallocx(size, flags); 178*8e33eff8Schristos dallocx(p, flags); 179*8e33eff8Schristos } 180*8e33eff8Schristos 181*8e33eff8Schristos TEST_BEGIN(test_decay_ticks) { 182*8e33eff8Schristos test_skip_if(check_background_thread_enabled()); 183*8e33eff8Schristos 184*8e33eff8Schristos ticker_t *decay_ticker; 185*8e33eff8Schristos unsigned tick0, tick1, arena_ind; 186*8e33eff8Schristos size_t sz, large0; 187*8e33eff8Schristos void *p; 188*8e33eff8Schristos 189*8e33eff8Schristos sz = sizeof(size_t); 190*8e33eff8Schristos assert_d_eq(mallctl("arenas.lextent.0.size", (void *)&large0, &sz, NULL, 191*8e33eff8Schristos 0), 0, "Unexpected mallctl failure"); 192*8e33eff8Schristos 193*8e33eff8Schristos /* Set up a manually managed arena for test. */ 194*8e33eff8Schristos arena_ind = do_arena_create(0, 0); 195*8e33eff8Schristos 196*8e33eff8Schristos /* Migrate to the new arena, and get the ticker. */ 197*8e33eff8Schristos unsigned old_arena_ind; 198*8e33eff8Schristos size_t sz_arena_ind = sizeof(old_arena_ind); 199*8e33eff8Schristos assert_d_eq(mallctl("thread.arena", (void *)&old_arena_ind, 200*8e33eff8Schristos &sz_arena_ind, (void *)&arena_ind, sizeof(arena_ind)), 0, 201*8e33eff8Schristos "Unexpected mallctl() failure"); 202*8e33eff8Schristos decay_ticker = decay_ticker_get(tsd_fetch(), arena_ind); 203*8e33eff8Schristos assert_ptr_not_null(decay_ticker, 204*8e33eff8Schristos "Unexpected failure getting decay ticker"); 205*8e33eff8Schristos 206*8e33eff8Schristos /* 207*8e33eff8Schristos * Test the standard APIs using a large size class, since we can't 208*8e33eff8Schristos * control tcache interactions for small size classes (except by 209*8e33eff8Schristos * completely disabling tcache for the entire test program). 210*8e33eff8Schristos */ 211*8e33eff8Schristos 212*8e33eff8Schristos /* malloc(). */ 213*8e33eff8Schristos tick0 = ticker_read(decay_ticker); 214*8e33eff8Schristos p = malloc(large0); 215*8e33eff8Schristos assert_ptr_not_null(p, "Unexpected malloc() failure"); 216*8e33eff8Schristos tick1 = ticker_read(decay_ticker); 217*8e33eff8Schristos assert_u32_ne(tick1, tick0, "Expected ticker to tick during malloc()"); 218*8e33eff8Schristos /* free(). */ 219*8e33eff8Schristos tick0 = ticker_read(decay_ticker); 220*8e33eff8Schristos free(p); 221*8e33eff8Schristos tick1 = ticker_read(decay_ticker); 222*8e33eff8Schristos assert_u32_ne(tick1, tick0, "Expected ticker to tick during free()"); 223*8e33eff8Schristos 224*8e33eff8Schristos /* calloc(). */ 225*8e33eff8Schristos tick0 = ticker_read(decay_ticker); 226*8e33eff8Schristos p = calloc(1, large0); 227*8e33eff8Schristos assert_ptr_not_null(p, "Unexpected calloc() failure"); 228*8e33eff8Schristos tick1 = ticker_read(decay_ticker); 229*8e33eff8Schristos assert_u32_ne(tick1, tick0, "Expected ticker to tick during calloc()"); 230*8e33eff8Schristos free(p); 231*8e33eff8Schristos 232*8e33eff8Schristos /* posix_memalign(). */ 233*8e33eff8Schristos tick0 = ticker_read(decay_ticker); 234*8e33eff8Schristos assert_d_eq(posix_memalign(&p, sizeof(size_t), large0), 0, 235*8e33eff8Schristos "Unexpected posix_memalign() failure"); 236*8e33eff8Schristos tick1 = ticker_read(decay_ticker); 237*8e33eff8Schristos assert_u32_ne(tick1, tick0, 238*8e33eff8Schristos "Expected ticker to tick during posix_memalign()"); 239*8e33eff8Schristos free(p); 240*8e33eff8Schristos 241*8e33eff8Schristos /* aligned_alloc(). */ 242*8e33eff8Schristos tick0 = ticker_read(decay_ticker); 243*8e33eff8Schristos p = aligned_alloc(sizeof(size_t), large0); 244*8e33eff8Schristos assert_ptr_not_null(p, "Unexpected aligned_alloc() failure"); 245*8e33eff8Schristos tick1 = ticker_read(decay_ticker); 246*8e33eff8Schristos assert_u32_ne(tick1, tick0, 247*8e33eff8Schristos "Expected ticker to tick during aligned_alloc()"); 248*8e33eff8Schristos free(p); 249*8e33eff8Schristos 250*8e33eff8Schristos /* realloc(). */ 251*8e33eff8Schristos /* Allocate. */ 252*8e33eff8Schristos tick0 = ticker_read(decay_ticker); 253*8e33eff8Schristos p = realloc(NULL, large0); 254*8e33eff8Schristos assert_ptr_not_null(p, "Unexpected realloc() failure"); 255*8e33eff8Schristos tick1 = ticker_read(decay_ticker); 256*8e33eff8Schristos assert_u32_ne(tick1, tick0, "Expected ticker to tick during realloc()"); 257*8e33eff8Schristos /* Reallocate. */ 258*8e33eff8Schristos tick0 = ticker_read(decay_ticker); 259*8e33eff8Schristos p = realloc(p, large0); 260*8e33eff8Schristos assert_ptr_not_null(p, "Unexpected realloc() failure"); 261*8e33eff8Schristos tick1 = ticker_read(decay_ticker); 262*8e33eff8Schristos assert_u32_ne(tick1, tick0, "Expected ticker to tick during realloc()"); 263*8e33eff8Schristos /* Deallocate. */ 264*8e33eff8Schristos tick0 = ticker_read(decay_ticker); 265*8e33eff8Schristos realloc(p, 0); 266*8e33eff8Schristos tick1 = ticker_read(decay_ticker); 267*8e33eff8Schristos assert_u32_ne(tick1, tick0, "Expected ticker to tick during realloc()"); 268*8e33eff8Schristos 269*8e33eff8Schristos /* 270*8e33eff8Schristos * Test the *allocx() APIs using large and small size classes, with 271*8e33eff8Schristos * tcache explicitly disabled. 272*8e33eff8Schristos */ 273*8e33eff8Schristos { 274*8e33eff8Schristos unsigned i; 275*8e33eff8Schristos size_t allocx_sizes[2]; 276*8e33eff8Schristos allocx_sizes[0] = large0; 277*8e33eff8Schristos allocx_sizes[1] = 1; 278*8e33eff8Schristos 279*8e33eff8Schristos for (i = 0; i < sizeof(allocx_sizes) / sizeof(size_t); i++) { 280*8e33eff8Schristos sz = allocx_sizes[i]; 281*8e33eff8Schristos 282*8e33eff8Schristos /* mallocx(). */ 283*8e33eff8Schristos tick0 = ticker_read(decay_ticker); 284*8e33eff8Schristos p = mallocx(sz, MALLOCX_TCACHE_NONE); 285*8e33eff8Schristos assert_ptr_not_null(p, "Unexpected mallocx() failure"); 286*8e33eff8Schristos tick1 = ticker_read(decay_ticker); 287*8e33eff8Schristos assert_u32_ne(tick1, tick0, 288*8e33eff8Schristos "Expected ticker to tick during mallocx() (sz=%zu)", 289*8e33eff8Schristos sz); 290*8e33eff8Schristos /* rallocx(). */ 291*8e33eff8Schristos tick0 = ticker_read(decay_ticker); 292*8e33eff8Schristos p = rallocx(p, sz, MALLOCX_TCACHE_NONE); 293*8e33eff8Schristos assert_ptr_not_null(p, "Unexpected rallocx() failure"); 294*8e33eff8Schristos tick1 = ticker_read(decay_ticker); 295*8e33eff8Schristos assert_u32_ne(tick1, tick0, 296*8e33eff8Schristos "Expected ticker to tick during rallocx() (sz=%zu)", 297*8e33eff8Schristos sz); 298*8e33eff8Schristos /* xallocx(). */ 299*8e33eff8Schristos tick0 = ticker_read(decay_ticker); 300*8e33eff8Schristos xallocx(p, sz, 0, MALLOCX_TCACHE_NONE); 301*8e33eff8Schristos tick1 = ticker_read(decay_ticker); 302*8e33eff8Schristos assert_u32_ne(tick1, tick0, 303*8e33eff8Schristos "Expected ticker to tick during xallocx() (sz=%zu)", 304*8e33eff8Schristos sz); 305*8e33eff8Schristos /* dallocx(). */ 306*8e33eff8Schristos tick0 = ticker_read(decay_ticker); 307*8e33eff8Schristos dallocx(p, MALLOCX_TCACHE_NONE); 308*8e33eff8Schristos tick1 = ticker_read(decay_ticker); 309*8e33eff8Schristos assert_u32_ne(tick1, tick0, 310*8e33eff8Schristos "Expected ticker to tick during dallocx() (sz=%zu)", 311*8e33eff8Schristos sz); 312*8e33eff8Schristos /* sdallocx(). */ 313*8e33eff8Schristos p = mallocx(sz, MALLOCX_TCACHE_NONE); 314*8e33eff8Schristos assert_ptr_not_null(p, "Unexpected mallocx() failure"); 315*8e33eff8Schristos tick0 = ticker_read(decay_ticker); 316*8e33eff8Schristos sdallocx(p, sz, MALLOCX_TCACHE_NONE); 317*8e33eff8Schristos tick1 = ticker_read(decay_ticker); 318*8e33eff8Schristos assert_u32_ne(tick1, tick0, 319*8e33eff8Schristos "Expected ticker to tick during sdallocx() " 320*8e33eff8Schristos "(sz=%zu)", sz); 321*8e33eff8Schristos } 322*8e33eff8Schristos } 323*8e33eff8Schristos 324*8e33eff8Schristos /* 325*8e33eff8Schristos * Test tcache fill/flush interactions for large and small size classes, 326*8e33eff8Schristos * using an explicit tcache. 327*8e33eff8Schristos */ 328*8e33eff8Schristos unsigned tcache_ind, i; 329*8e33eff8Schristos size_t tcache_sizes[2]; 330*8e33eff8Schristos tcache_sizes[0] = large0; 331*8e33eff8Schristos tcache_sizes[1] = 1; 332*8e33eff8Schristos 333*8e33eff8Schristos size_t tcache_max, sz_tcache_max; 334*8e33eff8Schristos sz_tcache_max = sizeof(tcache_max); 335*8e33eff8Schristos assert_d_eq(mallctl("arenas.tcache_max", (void *)&tcache_max, 336*8e33eff8Schristos &sz_tcache_max, NULL, 0), 0, "Unexpected mallctl() failure"); 337*8e33eff8Schristos 338*8e33eff8Schristos sz = sizeof(unsigned); 339*8e33eff8Schristos assert_d_eq(mallctl("tcache.create", (void *)&tcache_ind, &sz, 340*8e33eff8Schristos NULL, 0), 0, "Unexpected mallctl failure"); 341*8e33eff8Schristos 342*8e33eff8Schristos for (i = 0; i < sizeof(tcache_sizes) / sizeof(size_t); i++) { 343*8e33eff8Schristos sz = tcache_sizes[i]; 344*8e33eff8Schristos 345*8e33eff8Schristos /* tcache fill. */ 346*8e33eff8Schristos tick0 = ticker_read(decay_ticker); 347*8e33eff8Schristos p = mallocx(sz, MALLOCX_TCACHE(tcache_ind)); 348*8e33eff8Schristos assert_ptr_not_null(p, "Unexpected mallocx() failure"); 349*8e33eff8Schristos tick1 = ticker_read(decay_ticker); 350*8e33eff8Schristos assert_u32_ne(tick1, tick0, 351*8e33eff8Schristos "Expected ticker to tick during tcache fill " 352*8e33eff8Schristos "(sz=%zu)", sz); 353*8e33eff8Schristos /* tcache flush. */ 354*8e33eff8Schristos dallocx(p, MALLOCX_TCACHE(tcache_ind)); 355*8e33eff8Schristos tick0 = ticker_read(decay_ticker); 356*8e33eff8Schristos assert_d_eq(mallctl("tcache.flush", NULL, NULL, 357*8e33eff8Schristos (void *)&tcache_ind, sizeof(unsigned)), 0, 358*8e33eff8Schristos "Unexpected mallctl failure"); 359*8e33eff8Schristos tick1 = ticker_read(decay_ticker); 360*8e33eff8Schristos 361*8e33eff8Schristos /* Will only tick if it's in tcache. */ 362*8e33eff8Schristos if (sz <= tcache_max) { 363*8e33eff8Schristos assert_u32_ne(tick1, tick0, 364*8e33eff8Schristos "Expected ticker to tick during tcache " 365*8e33eff8Schristos "flush (sz=%zu)", sz); 366*8e33eff8Schristos } else { 367*8e33eff8Schristos assert_u32_eq(tick1, tick0, 368*8e33eff8Schristos "Unexpected ticker tick during tcache " 369*8e33eff8Schristos "flush (sz=%zu)", sz); 370*8e33eff8Schristos } 371*8e33eff8Schristos } 372*8e33eff8Schristos } 373*8e33eff8Schristos TEST_END 374*8e33eff8Schristos 375*8e33eff8Schristos static void 376*8e33eff8Schristos decay_ticker_helper(unsigned arena_ind, int flags, bool dirty, ssize_t dt, 377*8e33eff8Schristos uint64_t dirty_npurge0, uint64_t muzzy_npurge0, bool terminate_asap) { 378*8e33eff8Schristos #define NINTERVALS 101 379*8e33eff8Schristos nstime_t time, update_interval, decay_ms, deadline; 380*8e33eff8Schristos 381*8e33eff8Schristos nstime_init(&time, 0); 382*8e33eff8Schristos nstime_update(&time); 383*8e33eff8Schristos 384*8e33eff8Schristos nstime_init2(&decay_ms, dt, 0); 385*8e33eff8Schristos nstime_copy(&deadline, &time); 386*8e33eff8Schristos nstime_add(&deadline, &decay_ms); 387*8e33eff8Schristos 388*8e33eff8Schristos nstime_init2(&update_interval, dt, 0); 389*8e33eff8Schristos nstime_idivide(&update_interval, NINTERVALS); 390*8e33eff8Schristos 391*8e33eff8Schristos /* 392*8e33eff8Schristos * Keep q's slab from being deallocated during the looping below. If a 393*8e33eff8Schristos * cached slab were to repeatedly come and go during looping, it could 394*8e33eff8Schristos * prevent the decay backlog ever becoming empty. 395*8e33eff8Schristos */ 396*8e33eff8Schristos void *p = do_mallocx(1, flags); 397*8e33eff8Schristos uint64_t dirty_npurge1, muzzy_npurge1; 398*8e33eff8Schristos do { 399*8e33eff8Schristos for (unsigned i = 0; i < DECAY_NTICKS_PER_UPDATE / 2; 400*8e33eff8Schristos i++) { 401*8e33eff8Schristos void *q = do_mallocx(1, flags); 402*8e33eff8Schristos dallocx(q, flags); 403*8e33eff8Schristos } 404*8e33eff8Schristos dirty_npurge1 = get_arena_dirty_npurge(arena_ind); 405*8e33eff8Schristos muzzy_npurge1 = get_arena_muzzy_npurge(arena_ind); 406*8e33eff8Schristos 407*8e33eff8Schristos nstime_add(&time_mock, &update_interval); 408*8e33eff8Schristos nstime_update(&time); 409*8e33eff8Schristos } while (nstime_compare(&time, &deadline) <= 0 && ((dirty_npurge1 == 410*8e33eff8Schristos dirty_npurge0 && muzzy_npurge1 == muzzy_npurge0) || 411*8e33eff8Schristos !terminate_asap)); 412*8e33eff8Schristos dallocx(p, flags); 413*8e33eff8Schristos 414*8e33eff8Schristos if (config_stats) { 415*8e33eff8Schristos assert_u64_gt(dirty_npurge1 + muzzy_npurge1, dirty_npurge0 + 416*8e33eff8Schristos muzzy_npurge0, "Expected purging to occur"); 417*8e33eff8Schristos } 418*8e33eff8Schristos #undef NINTERVALS 419*8e33eff8Schristos } 420*8e33eff8Schristos 421*8e33eff8Schristos TEST_BEGIN(test_decay_ticker) { 422*8e33eff8Schristos test_skip_if(check_background_thread_enabled()); 423*8e33eff8Schristos #define NPS 2048 424*8e33eff8Schristos ssize_t ddt = opt_dirty_decay_ms; 425*8e33eff8Schristos ssize_t mdt = opt_muzzy_decay_ms; 426*8e33eff8Schristos unsigned arena_ind = do_arena_create(ddt, mdt); 427*8e33eff8Schristos int flags = (MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE); 428*8e33eff8Schristos void *ps[NPS]; 429*8e33eff8Schristos size_t large; 430*8e33eff8Schristos 431*8e33eff8Schristos /* 432*8e33eff8Schristos * Allocate a bunch of large objects, pause the clock, deallocate every 433*8e33eff8Schristos * other object (to fragment virtual memory), restore the clock, then 434*8e33eff8Schristos * [md]allocx() in a tight loop while advancing time rapidly to verify 435*8e33eff8Schristos * the ticker triggers purging. 436*8e33eff8Schristos */ 437*8e33eff8Schristos 438*8e33eff8Schristos size_t tcache_max; 439*8e33eff8Schristos size_t sz = sizeof(size_t); 440*8e33eff8Schristos assert_d_eq(mallctl("arenas.tcache_max", (void *)&tcache_max, &sz, NULL, 441*8e33eff8Schristos 0), 0, "Unexpected mallctl failure"); 442*8e33eff8Schristos large = nallocx(tcache_max + 1, flags); 443*8e33eff8Schristos 444*8e33eff8Schristos do_purge(arena_ind); 445*8e33eff8Schristos uint64_t dirty_npurge0 = get_arena_dirty_npurge(arena_ind); 446*8e33eff8Schristos uint64_t muzzy_npurge0 = get_arena_muzzy_npurge(arena_ind); 447*8e33eff8Schristos 448*8e33eff8Schristos for (unsigned i = 0; i < NPS; i++) { 449*8e33eff8Schristos ps[i] = do_mallocx(large, flags); 450*8e33eff8Schristos } 451*8e33eff8Schristos 452*8e33eff8Schristos nupdates_mock = 0; 453*8e33eff8Schristos nstime_init(&time_mock, 0); 454*8e33eff8Schristos nstime_update(&time_mock); 455*8e33eff8Schristos monotonic_mock = true; 456*8e33eff8Schristos 457*8e33eff8Schristos nstime_monotonic_orig = nstime_monotonic; 458*8e33eff8Schristos nstime_update_orig = nstime_update; 459*8e33eff8Schristos nstime_monotonic = nstime_monotonic_mock; 460*8e33eff8Schristos nstime_update = nstime_update_mock; 461*8e33eff8Schristos 462*8e33eff8Schristos for (unsigned i = 0; i < NPS; i += 2) { 463*8e33eff8Schristos dallocx(ps[i], flags); 464*8e33eff8Schristos unsigned nupdates0 = nupdates_mock; 465*8e33eff8Schristos do_decay(arena_ind); 466*8e33eff8Schristos assert_u_gt(nupdates_mock, nupdates0, 467*8e33eff8Schristos "Expected nstime_update() to be called"); 468*8e33eff8Schristos } 469*8e33eff8Schristos 470*8e33eff8Schristos decay_ticker_helper(arena_ind, flags, true, ddt, dirty_npurge0, 471*8e33eff8Schristos muzzy_npurge0, true); 472*8e33eff8Schristos decay_ticker_helper(arena_ind, flags, false, ddt+mdt, dirty_npurge0, 473*8e33eff8Schristos muzzy_npurge0, false); 474*8e33eff8Schristos 475*8e33eff8Schristos do_arena_destroy(arena_ind); 476*8e33eff8Schristos 477*8e33eff8Schristos nstime_monotonic = nstime_monotonic_orig; 478*8e33eff8Schristos nstime_update = nstime_update_orig; 479*8e33eff8Schristos #undef NPS 480*8e33eff8Schristos } 481*8e33eff8Schristos TEST_END 482*8e33eff8Schristos 483*8e33eff8Schristos TEST_BEGIN(test_decay_nonmonotonic) { 484*8e33eff8Schristos test_skip_if(check_background_thread_enabled()); 485*8e33eff8Schristos #define NPS (SMOOTHSTEP_NSTEPS + 1) 486*8e33eff8Schristos int flags = (MALLOCX_ARENA(0) | MALLOCX_TCACHE_NONE); 487*8e33eff8Schristos void *ps[NPS]; 488*8e33eff8Schristos uint64_t npurge0 = 0; 489*8e33eff8Schristos uint64_t npurge1 = 0; 490*8e33eff8Schristos size_t sz, large0; 491*8e33eff8Schristos unsigned i, nupdates0; 492*8e33eff8Schristos 493*8e33eff8Schristos sz = sizeof(size_t); 494*8e33eff8Schristos assert_d_eq(mallctl("arenas.lextent.0.size", (void *)&large0, &sz, NULL, 495*8e33eff8Schristos 0), 0, "Unexpected mallctl failure"); 496*8e33eff8Schristos 497*8e33eff8Schristos assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0, 498*8e33eff8Schristos "Unexpected mallctl failure"); 499*8e33eff8Schristos do_epoch(); 500*8e33eff8Schristos sz = sizeof(uint64_t); 501*8e33eff8Schristos npurge0 = get_arena_npurge(0); 502*8e33eff8Schristos 503*8e33eff8Schristos nupdates_mock = 0; 504*8e33eff8Schristos nstime_init(&time_mock, 0); 505*8e33eff8Schristos nstime_update(&time_mock); 506*8e33eff8Schristos monotonic_mock = false; 507*8e33eff8Schristos 508*8e33eff8Schristos nstime_monotonic_orig = nstime_monotonic; 509*8e33eff8Schristos nstime_update_orig = nstime_update; 510*8e33eff8Schristos nstime_monotonic = nstime_monotonic_mock; 511*8e33eff8Schristos nstime_update = nstime_update_mock; 512*8e33eff8Schristos 513*8e33eff8Schristos for (i = 0; i < NPS; i++) { 514*8e33eff8Schristos ps[i] = mallocx(large0, flags); 515*8e33eff8Schristos assert_ptr_not_null(ps[i], "Unexpected mallocx() failure"); 516*8e33eff8Schristos } 517*8e33eff8Schristos 518*8e33eff8Schristos for (i = 0; i < NPS; i++) { 519*8e33eff8Schristos dallocx(ps[i], flags); 520*8e33eff8Schristos nupdates0 = nupdates_mock; 521*8e33eff8Schristos assert_d_eq(mallctl("arena.0.decay", NULL, NULL, NULL, 0), 0, 522*8e33eff8Schristos "Unexpected arena.0.decay failure"); 523*8e33eff8Schristos assert_u_gt(nupdates_mock, nupdates0, 524*8e33eff8Schristos "Expected nstime_update() to be called"); 525*8e33eff8Schristos } 526*8e33eff8Schristos 527*8e33eff8Schristos do_epoch(); 528*8e33eff8Schristos sz = sizeof(uint64_t); 529*8e33eff8Schristos npurge1 = get_arena_npurge(0); 530*8e33eff8Schristos 531*8e33eff8Schristos if (config_stats) { 532*8e33eff8Schristos assert_u64_eq(npurge0, npurge1, "Unexpected purging occurred"); 533*8e33eff8Schristos } 534*8e33eff8Schristos 535*8e33eff8Schristos nstime_monotonic = nstime_monotonic_orig; 536*8e33eff8Schristos nstime_update = nstime_update_orig; 537*8e33eff8Schristos #undef NPS 538*8e33eff8Schristos } 539*8e33eff8Schristos TEST_END 540*8e33eff8Schristos 541*8e33eff8Schristos TEST_BEGIN(test_decay_now) { 542*8e33eff8Schristos test_skip_if(check_background_thread_enabled()); 543*8e33eff8Schristos 544*8e33eff8Schristos unsigned arena_ind = do_arena_create(0, 0); 545*8e33eff8Schristos assert_zu_eq(get_arena_pdirty(arena_ind), 0, "Unexpected dirty pages"); 546*8e33eff8Schristos assert_zu_eq(get_arena_pmuzzy(arena_ind), 0, "Unexpected muzzy pages"); 547*8e33eff8Schristos size_t sizes[] = {16, PAGE<<2, HUGEPAGE<<2}; 548*8e33eff8Schristos /* Verify that dirty/muzzy pages never linger after deallocation. */ 549*8e33eff8Schristos for (unsigned i = 0; i < sizeof(sizes)/sizeof(size_t); i++) { 550*8e33eff8Schristos size_t size = sizes[i]; 551*8e33eff8Schristos generate_dirty(arena_ind, size); 552*8e33eff8Schristos assert_zu_eq(get_arena_pdirty(arena_ind), 0, 553*8e33eff8Schristos "Unexpected dirty pages"); 554*8e33eff8Schristos assert_zu_eq(get_arena_pmuzzy(arena_ind), 0, 555*8e33eff8Schristos "Unexpected muzzy pages"); 556*8e33eff8Schristos } 557*8e33eff8Schristos do_arena_destroy(arena_ind); 558*8e33eff8Schristos } 559*8e33eff8Schristos TEST_END 560*8e33eff8Schristos 561*8e33eff8Schristos TEST_BEGIN(test_decay_never) { 562*8e33eff8Schristos test_skip_if(check_background_thread_enabled()); 563*8e33eff8Schristos 564*8e33eff8Schristos unsigned arena_ind = do_arena_create(-1, -1); 565*8e33eff8Schristos int flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE; 566*8e33eff8Schristos assert_zu_eq(get_arena_pdirty(arena_ind), 0, "Unexpected dirty pages"); 567*8e33eff8Schristos assert_zu_eq(get_arena_pmuzzy(arena_ind), 0, "Unexpected muzzy pages"); 568*8e33eff8Schristos size_t sizes[] = {16, PAGE<<2, HUGEPAGE<<2}; 569*8e33eff8Schristos void *ptrs[sizeof(sizes)/sizeof(size_t)]; 570*8e33eff8Schristos for (unsigned i = 0; i < sizeof(sizes)/sizeof(size_t); i++) { 571*8e33eff8Schristos ptrs[i] = do_mallocx(sizes[i], flags); 572*8e33eff8Schristos } 573*8e33eff8Schristos /* Verify that each deallocation generates additional dirty pages. */ 574*8e33eff8Schristos size_t pdirty_prev = get_arena_pdirty(arena_ind); 575*8e33eff8Schristos size_t pmuzzy_prev = get_arena_pmuzzy(arena_ind); 576*8e33eff8Schristos assert_zu_eq(pdirty_prev, 0, "Unexpected dirty pages"); 577*8e33eff8Schristos assert_zu_eq(pmuzzy_prev, 0, "Unexpected muzzy pages"); 578*8e33eff8Schristos for (unsigned i = 0; i < sizeof(sizes)/sizeof(size_t); i++) { 579*8e33eff8Schristos dallocx(ptrs[i], flags); 580*8e33eff8Schristos size_t pdirty = get_arena_pdirty(arena_ind); 581*8e33eff8Schristos size_t pmuzzy = get_arena_pmuzzy(arena_ind); 582*8e33eff8Schristos assert_zu_gt(pdirty, pdirty_prev, 583*8e33eff8Schristos "Expected dirty pages to increase."); 584*8e33eff8Schristos assert_zu_eq(pmuzzy, 0, "Unexpected muzzy pages"); 585*8e33eff8Schristos pdirty_prev = pdirty; 586*8e33eff8Schristos } 587*8e33eff8Schristos do_arena_destroy(arena_ind); 588*8e33eff8Schristos } 589*8e33eff8Schristos TEST_END 590*8e33eff8Schristos 591*8e33eff8Schristos int 592*8e33eff8Schristos main(void) { 593*8e33eff8Schristos return test( 594*8e33eff8Schristos test_decay_ticks, 595*8e33eff8Schristos test_decay_ticker, 596*8e33eff8Schristos test_decay_nonmonotonic, 597*8e33eff8Schristos test_decay_now, 598*8e33eff8Schristos test_decay_never); 599*8e33eff8Schristos } 600