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