1a0698ed9Schristos #include "test/jemalloc_test.h" 2a0698ed9Schristos 3a0698ed9Schristos #include "test/extent_hooks.h" 4a0698ed9Schristos 5*7bdf38e5Schristos #include "jemalloc/internal/arena_types.h" 6a0698ed9Schristos 7a0698ed9Schristos static void 8a0698ed9Schristos test_extent_body(unsigned arena_ind) { 9a0698ed9Schristos void *p; 10a0698ed9Schristos size_t large0, large1, large2, sz; 11a0698ed9Schristos size_t purge_mib[3]; 12a0698ed9Schristos size_t purge_miblen; 13a0698ed9Schristos int flags; 14a0698ed9Schristos bool xallocx_success_a, xallocx_success_b, xallocx_success_c; 15a0698ed9Schristos 16a0698ed9Schristos flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE; 17a0698ed9Schristos 18a0698ed9Schristos /* Get large size classes. */ 19a0698ed9Schristos sz = sizeof(size_t); 20*7bdf38e5Schristos expect_d_eq(mallctl("arenas.lextent.0.size", (void *)&large0, &sz, NULL, 21a0698ed9Schristos 0), 0, "Unexpected arenas.lextent.0.size failure"); 22*7bdf38e5Schristos expect_d_eq(mallctl("arenas.lextent.1.size", (void *)&large1, &sz, NULL, 23a0698ed9Schristos 0), 0, "Unexpected arenas.lextent.1.size failure"); 24*7bdf38e5Schristos expect_d_eq(mallctl("arenas.lextent.2.size", (void *)&large2, &sz, NULL, 25a0698ed9Schristos 0), 0, "Unexpected arenas.lextent.2.size failure"); 26a0698ed9Schristos 27a0698ed9Schristos /* Test dalloc/decommit/purge cascade. */ 28a0698ed9Schristos purge_miblen = sizeof(purge_mib)/sizeof(size_t); 29*7bdf38e5Schristos expect_d_eq(mallctlnametomib("arena.0.purge", purge_mib, &purge_miblen), 30a0698ed9Schristos 0, "Unexpected mallctlnametomib() failure"); 31a0698ed9Schristos purge_mib[1] = (size_t)arena_ind; 32a0698ed9Schristos called_alloc = false; 33a0698ed9Schristos try_alloc = true; 34a0698ed9Schristos try_dalloc = false; 35a0698ed9Schristos try_decommit = false; 36a0698ed9Schristos p = mallocx(large0 * 2, flags); 37*7bdf38e5Schristos expect_ptr_not_null(p, "Unexpected mallocx() error"); 38*7bdf38e5Schristos expect_true(called_alloc, "Expected alloc call"); 39a0698ed9Schristos called_dalloc = false; 40a0698ed9Schristos called_decommit = false; 41a0698ed9Schristos did_purge_lazy = false; 42a0698ed9Schristos did_purge_forced = false; 43a0698ed9Schristos called_split = false; 44a0698ed9Schristos xallocx_success_a = (xallocx(p, large0, 0, flags) == large0); 45*7bdf38e5Schristos expect_d_eq(mallctlbymib(purge_mib, purge_miblen, NULL, NULL, NULL, 0), 46a0698ed9Schristos 0, "Unexpected arena.%u.purge error", arena_ind); 47a0698ed9Schristos if (xallocx_success_a) { 48*7bdf38e5Schristos expect_true(called_dalloc, "Expected dalloc call"); 49*7bdf38e5Schristos expect_true(called_decommit, "Expected decommit call"); 50*7bdf38e5Schristos expect_true(did_purge_lazy || did_purge_forced, 51a0698ed9Schristos "Expected purge"); 52*7bdf38e5Schristos expect_true(called_split, "Expected split call"); 53a0698ed9Schristos } 54a0698ed9Schristos dallocx(p, flags); 55a0698ed9Schristos try_dalloc = true; 56a0698ed9Schristos 57a0698ed9Schristos /* Test decommit/commit and observe split/merge. */ 58a0698ed9Schristos try_dalloc = false; 59a0698ed9Schristos try_decommit = true; 60a0698ed9Schristos p = mallocx(large0 * 2, flags); 61*7bdf38e5Schristos expect_ptr_not_null(p, "Unexpected mallocx() error"); 62a0698ed9Schristos did_decommit = false; 63a0698ed9Schristos did_commit = false; 64a0698ed9Schristos called_split = false; 65a0698ed9Schristos did_split = false; 66a0698ed9Schristos did_merge = false; 67a0698ed9Schristos xallocx_success_b = (xallocx(p, large0, 0, flags) == large0); 68*7bdf38e5Schristos expect_d_eq(mallctlbymib(purge_mib, purge_miblen, NULL, NULL, NULL, 0), 69a0698ed9Schristos 0, "Unexpected arena.%u.purge error", arena_ind); 70a0698ed9Schristos if (xallocx_success_b) { 71*7bdf38e5Schristos expect_true(did_split, "Expected split"); 72a0698ed9Schristos } 73a0698ed9Schristos xallocx_success_c = (xallocx(p, large0 * 2, 0, flags) == large0 * 2); 74a0698ed9Schristos if (did_split) { 75*7bdf38e5Schristos expect_b_eq(did_decommit, did_commit, 76a0698ed9Schristos "Expected decommit/commit match"); 77a0698ed9Schristos } 78a0698ed9Schristos if (xallocx_success_b && xallocx_success_c) { 79*7bdf38e5Schristos expect_true(did_merge, "Expected merge"); 80a0698ed9Schristos } 81a0698ed9Schristos dallocx(p, flags); 82a0698ed9Schristos try_dalloc = true; 83a0698ed9Schristos try_decommit = false; 84a0698ed9Schristos 85a0698ed9Schristos /* Make sure non-large allocation succeeds. */ 86a0698ed9Schristos p = mallocx(42, flags); 87*7bdf38e5Schristos expect_ptr_not_null(p, "Unexpected mallocx() error"); 88a0698ed9Schristos dallocx(p, flags); 89a0698ed9Schristos } 90a0698ed9Schristos 91a0698ed9Schristos static void 92a0698ed9Schristos test_manual_hook_auto_arena(void) { 93a0698ed9Schristos unsigned narenas; 94a0698ed9Schristos size_t old_size, new_size, sz; 95a0698ed9Schristos size_t hooks_mib[3]; 96a0698ed9Schristos size_t hooks_miblen; 97a0698ed9Schristos extent_hooks_t *new_hooks, *old_hooks; 98a0698ed9Schristos 99a0698ed9Schristos extent_hooks_prep(); 100a0698ed9Schristos 101a0698ed9Schristos sz = sizeof(unsigned); 102a0698ed9Schristos /* Get number of auto arenas. */ 103*7bdf38e5Schristos expect_d_eq(mallctl("opt.narenas", (void *)&narenas, &sz, NULL, 0), 104a0698ed9Schristos 0, "Unexpected mallctl() failure"); 105a0698ed9Schristos if (narenas == 1) { 106a0698ed9Schristos return; 107a0698ed9Schristos } 108a0698ed9Schristos 109a0698ed9Schristos /* Install custom extent hooks on arena 1 (might not be initialized). */ 110a0698ed9Schristos hooks_miblen = sizeof(hooks_mib)/sizeof(size_t); 111*7bdf38e5Schristos expect_d_eq(mallctlnametomib("arena.0.extent_hooks", hooks_mib, 112a0698ed9Schristos &hooks_miblen), 0, "Unexpected mallctlnametomib() failure"); 113a0698ed9Schristos hooks_mib[1] = 1; 114a0698ed9Schristos old_size = sizeof(extent_hooks_t *); 115a0698ed9Schristos new_hooks = &hooks; 116a0698ed9Schristos new_size = sizeof(extent_hooks_t *); 117*7bdf38e5Schristos expect_d_eq(mallctlbymib(hooks_mib, hooks_miblen, (void *)&old_hooks, 118a0698ed9Schristos &old_size, (void *)&new_hooks, new_size), 0, 119a0698ed9Schristos "Unexpected extent_hooks error"); 120a0698ed9Schristos static bool auto_arena_created = false; 121a0698ed9Schristos if (old_hooks != &hooks) { 122*7bdf38e5Schristos expect_b_eq(auto_arena_created, false, 123a0698ed9Schristos "Expected auto arena 1 created only once."); 124a0698ed9Schristos auto_arena_created = true; 125a0698ed9Schristos } 126a0698ed9Schristos } 127a0698ed9Schristos 128a0698ed9Schristos static void 129a0698ed9Schristos test_manual_hook_body(void) { 130a0698ed9Schristos unsigned arena_ind; 131a0698ed9Schristos size_t old_size, new_size, sz; 132a0698ed9Schristos size_t hooks_mib[3]; 133a0698ed9Schristos size_t hooks_miblen; 134a0698ed9Schristos extent_hooks_t *new_hooks, *old_hooks; 135a0698ed9Schristos 136a0698ed9Schristos extent_hooks_prep(); 137a0698ed9Schristos 138a0698ed9Schristos sz = sizeof(unsigned); 139*7bdf38e5Schristos expect_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz, NULL, 0), 140a0698ed9Schristos 0, "Unexpected mallctl() failure"); 141a0698ed9Schristos 142a0698ed9Schristos /* Install custom extent hooks. */ 143a0698ed9Schristos hooks_miblen = sizeof(hooks_mib)/sizeof(size_t); 144*7bdf38e5Schristos expect_d_eq(mallctlnametomib("arena.0.extent_hooks", hooks_mib, 145a0698ed9Schristos &hooks_miblen), 0, "Unexpected mallctlnametomib() failure"); 146a0698ed9Schristos hooks_mib[1] = (size_t)arena_ind; 147a0698ed9Schristos old_size = sizeof(extent_hooks_t *); 148a0698ed9Schristos new_hooks = &hooks; 149a0698ed9Schristos new_size = sizeof(extent_hooks_t *); 150*7bdf38e5Schristos expect_d_eq(mallctlbymib(hooks_mib, hooks_miblen, (void *)&old_hooks, 151a0698ed9Schristos &old_size, (void *)&new_hooks, new_size), 0, 152a0698ed9Schristos "Unexpected extent_hooks error"); 153*7bdf38e5Schristos expect_ptr_ne(old_hooks->alloc, extent_alloc_hook, 154a0698ed9Schristos "Unexpected extent_hooks error"); 155*7bdf38e5Schristos expect_ptr_ne(old_hooks->dalloc, extent_dalloc_hook, 156a0698ed9Schristos "Unexpected extent_hooks error"); 157*7bdf38e5Schristos expect_ptr_ne(old_hooks->commit, extent_commit_hook, 158a0698ed9Schristos "Unexpected extent_hooks error"); 159*7bdf38e5Schristos expect_ptr_ne(old_hooks->decommit, extent_decommit_hook, 160a0698ed9Schristos "Unexpected extent_hooks error"); 161*7bdf38e5Schristos expect_ptr_ne(old_hooks->purge_lazy, extent_purge_lazy_hook, 162a0698ed9Schristos "Unexpected extent_hooks error"); 163*7bdf38e5Schristos expect_ptr_ne(old_hooks->purge_forced, extent_purge_forced_hook, 164a0698ed9Schristos "Unexpected extent_hooks error"); 165*7bdf38e5Schristos expect_ptr_ne(old_hooks->split, extent_split_hook, 166a0698ed9Schristos "Unexpected extent_hooks error"); 167*7bdf38e5Schristos expect_ptr_ne(old_hooks->merge, extent_merge_hook, 168a0698ed9Schristos "Unexpected extent_hooks error"); 169a0698ed9Schristos 170*7bdf38e5Schristos if (!is_background_thread_enabled()) { 171a0698ed9Schristos test_extent_body(arena_ind); 172a0698ed9Schristos } 173a0698ed9Schristos 174a0698ed9Schristos /* Restore extent hooks. */ 175*7bdf38e5Schristos expect_d_eq(mallctlbymib(hooks_mib, hooks_miblen, NULL, NULL, 176a0698ed9Schristos (void *)&old_hooks, new_size), 0, "Unexpected extent_hooks error"); 177*7bdf38e5Schristos expect_d_eq(mallctlbymib(hooks_mib, hooks_miblen, (void *)&old_hooks, 178a0698ed9Schristos &old_size, NULL, 0), 0, "Unexpected extent_hooks error"); 179*7bdf38e5Schristos expect_ptr_eq(old_hooks, default_hooks, "Unexpected extent_hooks error"); 180*7bdf38e5Schristos expect_ptr_eq(old_hooks->alloc, default_hooks->alloc, 181a0698ed9Schristos "Unexpected extent_hooks error"); 182*7bdf38e5Schristos expect_ptr_eq(old_hooks->dalloc, default_hooks->dalloc, 183a0698ed9Schristos "Unexpected extent_hooks error"); 184*7bdf38e5Schristos expect_ptr_eq(old_hooks->commit, default_hooks->commit, 185a0698ed9Schristos "Unexpected extent_hooks error"); 186*7bdf38e5Schristos expect_ptr_eq(old_hooks->decommit, default_hooks->decommit, 187a0698ed9Schristos "Unexpected extent_hooks error"); 188*7bdf38e5Schristos expect_ptr_eq(old_hooks->purge_lazy, default_hooks->purge_lazy, 189a0698ed9Schristos "Unexpected extent_hooks error"); 190*7bdf38e5Schristos expect_ptr_eq(old_hooks->purge_forced, default_hooks->purge_forced, 191a0698ed9Schristos "Unexpected extent_hooks error"); 192*7bdf38e5Schristos expect_ptr_eq(old_hooks->split, default_hooks->split, 193a0698ed9Schristos "Unexpected extent_hooks error"); 194*7bdf38e5Schristos expect_ptr_eq(old_hooks->merge, default_hooks->merge, 195a0698ed9Schristos "Unexpected extent_hooks error"); 196a0698ed9Schristos } 197a0698ed9Schristos 198a0698ed9Schristos TEST_BEGIN(test_extent_manual_hook) { 199a0698ed9Schristos test_manual_hook_auto_arena(); 200a0698ed9Schristos test_manual_hook_body(); 201a0698ed9Schristos 202a0698ed9Schristos /* Test failure paths. */ 203a0698ed9Schristos try_split = false; 204a0698ed9Schristos test_manual_hook_body(); 205a0698ed9Schristos try_merge = false; 206a0698ed9Schristos test_manual_hook_body(); 207a0698ed9Schristos try_purge_lazy = false; 208a0698ed9Schristos try_purge_forced = false; 209a0698ed9Schristos test_manual_hook_body(); 210a0698ed9Schristos 211a0698ed9Schristos try_split = try_merge = try_purge_lazy = try_purge_forced = true; 212a0698ed9Schristos } 213a0698ed9Schristos TEST_END 214a0698ed9Schristos 215a0698ed9Schristos TEST_BEGIN(test_extent_auto_hook) { 216a0698ed9Schristos unsigned arena_ind; 217a0698ed9Schristos size_t new_size, sz; 218a0698ed9Schristos extent_hooks_t *new_hooks; 219a0698ed9Schristos 220a0698ed9Schristos extent_hooks_prep(); 221a0698ed9Schristos 222a0698ed9Schristos sz = sizeof(unsigned); 223a0698ed9Schristos new_hooks = &hooks; 224a0698ed9Schristos new_size = sizeof(extent_hooks_t *); 225*7bdf38e5Schristos expect_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz, 226a0698ed9Schristos (void *)&new_hooks, new_size), 0, "Unexpected mallctl() failure"); 227a0698ed9Schristos 228*7bdf38e5Schristos test_skip_if(is_background_thread_enabled()); 229a0698ed9Schristos test_extent_body(arena_ind); 230a0698ed9Schristos } 231a0698ed9Schristos TEST_END 232a0698ed9Schristos 233*7bdf38e5Schristos static void 234*7bdf38e5Schristos test_arenas_create_ext_base(arena_config_t config, 235*7bdf38e5Schristos bool expect_hook_data, bool expect_hook_metadata) 236*7bdf38e5Schristos { 237*7bdf38e5Schristos unsigned arena, arena1; 238*7bdf38e5Schristos void *ptr; 239*7bdf38e5Schristos size_t sz = sizeof(unsigned); 240*7bdf38e5Schristos 241*7bdf38e5Schristos extent_hooks_prep(); 242*7bdf38e5Schristos 243*7bdf38e5Schristos called_alloc = false; 244*7bdf38e5Schristos expect_d_eq(mallctl("experimental.arenas_create_ext", 245*7bdf38e5Schristos (void *)&arena, &sz, &config, sizeof(arena_config_t)), 0, 246*7bdf38e5Schristos "Unexpected mallctl() failure"); 247*7bdf38e5Schristos expect_b_eq(called_alloc, expect_hook_metadata, 248*7bdf38e5Schristos "expected hook metadata alloc mismatch"); 249*7bdf38e5Schristos 250*7bdf38e5Schristos called_alloc = false; 251*7bdf38e5Schristos ptr = mallocx(42, MALLOCX_ARENA(arena) | MALLOCX_TCACHE_NONE); 252*7bdf38e5Schristos expect_b_eq(called_alloc, expect_hook_data, 253*7bdf38e5Schristos "expected hook data alloc mismatch"); 254*7bdf38e5Schristos 255*7bdf38e5Schristos expect_ptr_not_null(ptr, "Unexpected mallocx() failure"); 256*7bdf38e5Schristos expect_d_eq(mallctl("arenas.lookup", &arena1, &sz, &ptr, sizeof(ptr)), 257*7bdf38e5Schristos 0, "Unexpected mallctl() failure"); 258*7bdf38e5Schristos expect_u_eq(arena, arena1, "Unexpected arena index"); 259*7bdf38e5Schristos dallocx(ptr, 0); 260*7bdf38e5Schristos } 261*7bdf38e5Schristos 262*7bdf38e5Schristos TEST_BEGIN(test_arenas_create_ext_with_ehooks_no_metadata) { 263*7bdf38e5Schristos arena_config_t config; 264*7bdf38e5Schristos config.extent_hooks = &hooks; 265*7bdf38e5Schristos config.metadata_use_hooks = false; 266*7bdf38e5Schristos 267*7bdf38e5Schristos test_arenas_create_ext_base(config, true, false); 268*7bdf38e5Schristos } 269*7bdf38e5Schristos TEST_END 270*7bdf38e5Schristos 271*7bdf38e5Schristos TEST_BEGIN(test_arenas_create_ext_with_ehooks_with_metadata) { 272*7bdf38e5Schristos arena_config_t config; 273*7bdf38e5Schristos config.extent_hooks = &hooks; 274*7bdf38e5Schristos config.metadata_use_hooks = true; 275*7bdf38e5Schristos 276*7bdf38e5Schristos test_arenas_create_ext_base(config, true, true); 277*7bdf38e5Schristos } 278*7bdf38e5Schristos TEST_END 279*7bdf38e5Schristos 280a0698ed9Schristos int 281a0698ed9Schristos main(void) { 282a0698ed9Schristos return test( 283a0698ed9Schristos test_extent_manual_hook, 284*7bdf38e5Schristos test_extent_auto_hook, 285*7bdf38e5Schristos test_arenas_create_ext_with_ehooks_no_metadata, 286*7bdf38e5Schristos test_arenas_create_ext_with_ehooks_with_metadata); 287a0698ed9Schristos } 288