1 #include "test/jemalloc_test.h" 2 3 #ifdef JEMALLOC_FILL 4 const char *malloc_conf = "junk:false"; 5 #endif 6 7 static chunk_hooks_t orig_hooks; 8 static chunk_hooks_t old_hooks; 9 10 static bool do_dalloc = true; 11 static bool do_decommit; 12 13 static bool did_alloc; 14 static bool did_dalloc; 15 static bool did_commit; 16 static bool did_decommit; 17 static bool did_purge; 18 static bool did_split; 19 static bool did_merge; 20 21 #if 0 22 # define TRACE_HOOK(fmt, ...) malloc_printf(fmt, __VA_ARGS__) 23 #else 24 # define TRACE_HOOK(fmt, ...) 25 #endif 26 27 void * 28 chunk_alloc(void *new_addr, size_t size, size_t alignment, bool *zero, 29 bool *commit, unsigned arena_ind) 30 { 31 32 TRACE_HOOK("%s(new_addr=%p, size=%zu, alignment=%zu, *zero=%s, " 33 "*commit=%s, arena_ind=%u)\n", __func__, new_addr, size, alignment, 34 *zero ? "true" : "false", *commit ? "true" : "false", arena_ind); 35 did_alloc = true; 36 return (old_hooks.alloc(new_addr, size, alignment, zero, commit, 37 arena_ind)); 38 } 39 40 bool 41 chunk_dalloc(void *chunk, size_t size, bool committed, unsigned arena_ind) 42 { 43 44 TRACE_HOOK("%s(chunk=%p, size=%zu, committed=%s, arena_ind=%u)\n", 45 __func__, chunk, size, committed ? "true" : "false", arena_ind); 46 did_dalloc = true; 47 if (!do_dalloc) 48 return (true); 49 return (old_hooks.dalloc(chunk, size, committed, arena_ind)); 50 } 51 52 bool 53 chunk_commit(void *chunk, size_t size, size_t offset, size_t length, 54 unsigned arena_ind) 55 { 56 bool err; 57 58 TRACE_HOOK("%s(chunk=%p, size=%zu, offset=%zu, length=%zu, " 59 "arena_ind=%u)\n", __func__, chunk, size, offset, length, 60 arena_ind); 61 err = old_hooks.commit(chunk, size, offset, length, arena_ind); 62 did_commit = !err; 63 return (err); 64 } 65 66 bool 67 chunk_decommit(void *chunk, size_t size, size_t offset, size_t length, 68 unsigned arena_ind) 69 { 70 bool err; 71 72 TRACE_HOOK("%s(chunk=%p, size=%zu, offset=%zu, length=%zu, " 73 "arena_ind=%u)\n", __func__, chunk, size, offset, length, 74 arena_ind); 75 if (!do_decommit) 76 return (true); 77 err = old_hooks.decommit(chunk, size, offset, length, arena_ind); 78 did_decommit = !err; 79 return (err); 80 } 81 82 bool 83 chunk_purge(void *chunk, size_t size, size_t offset, size_t length, 84 unsigned arena_ind) 85 { 86 87 TRACE_HOOK("%s(chunk=%p, size=%zu, offset=%zu, length=%zu " 88 "arena_ind=%u)\n", __func__, chunk, size, offset, length, 89 arena_ind); 90 did_purge = true; 91 return (old_hooks.purge(chunk, size, offset, length, arena_ind)); 92 } 93 94 bool 95 chunk_split(void *chunk, size_t size, size_t size_a, size_t size_b, 96 bool committed, unsigned arena_ind) 97 { 98 99 TRACE_HOOK("%s(chunk=%p, size=%zu, size_a=%zu, size_b=%zu, " 100 "committed=%s, arena_ind=%u)\n", __func__, chunk, size, size_a, 101 size_b, committed ? "true" : "false", arena_ind); 102 did_split = true; 103 return (old_hooks.split(chunk, size, size_a, size_b, committed, 104 arena_ind)); 105 } 106 107 bool 108 chunk_merge(void *chunk_a, size_t size_a, void *chunk_b, size_t size_b, 109 bool committed, unsigned arena_ind) 110 { 111 112 TRACE_HOOK("%s(chunk_a=%p, size_a=%zu, chunk_b=%p size_b=%zu, " 113 "committed=%s, arena_ind=%u)\n", __func__, chunk_a, size_a, chunk_b, 114 size_b, committed ? "true" : "false", arena_ind); 115 did_merge = true; 116 return (old_hooks.merge(chunk_a, size_a, chunk_b, size_b, 117 committed, arena_ind)); 118 } 119 120 TEST_BEGIN(test_chunk) 121 { 122 void *p; 123 size_t old_size, new_size, large0, large1, huge0, huge1, huge2, sz; 124 unsigned arena_ind; 125 int flags; 126 size_t hooks_mib[3], purge_mib[3]; 127 size_t hooks_miblen, purge_miblen; 128 chunk_hooks_t new_hooks = { 129 chunk_alloc, 130 chunk_dalloc, 131 chunk_commit, 132 chunk_decommit, 133 chunk_purge, 134 chunk_split, 135 chunk_merge 136 }; 137 bool xallocx_success_a, xallocx_success_b, xallocx_success_c; 138 139 sz = sizeof(unsigned); 140 assert_d_eq(mallctl("arenas.extend", (void *)&arena_ind, &sz, NULL, 0), 141 0, "Unexpected mallctl() failure"); 142 flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE; 143 144 /* Install custom chunk hooks. */ 145 hooks_miblen = sizeof(hooks_mib)/sizeof(size_t); 146 assert_d_eq(mallctlnametomib("arena.0.chunk_hooks", hooks_mib, 147 &hooks_miblen), 0, "Unexpected mallctlnametomib() failure"); 148 hooks_mib[1] = (size_t)arena_ind; 149 old_size = sizeof(chunk_hooks_t); 150 new_size = sizeof(chunk_hooks_t); 151 assert_d_eq(mallctlbymib(hooks_mib, hooks_miblen, (void *)&old_hooks, 152 &old_size, (void *)&new_hooks, new_size), 0, 153 "Unexpected chunk_hooks error"); 154 orig_hooks = old_hooks; 155 assert_ptr_ne(old_hooks.alloc, chunk_alloc, "Unexpected alloc error"); 156 assert_ptr_ne(old_hooks.dalloc, chunk_dalloc, 157 "Unexpected dalloc error"); 158 assert_ptr_ne(old_hooks.commit, chunk_commit, 159 "Unexpected commit error"); 160 assert_ptr_ne(old_hooks.decommit, chunk_decommit, 161 "Unexpected decommit error"); 162 assert_ptr_ne(old_hooks.purge, chunk_purge, "Unexpected purge error"); 163 assert_ptr_ne(old_hooks.split, chunk_split, "Unexpected split error"); 164 assert_ptr_ne(old_hooks.merge, chunk_merge, "Unexpected merge error"); 165 166 /* Get large size classes. */ 167 sz = sizeof(size_t); 168 assert_d_eq(mallctl("arenas.lrun.0.size", (void *)&large0, &sz, NULL, 169 0), 0, "Unexpected arenas.lrun.0.size failure"); 170 assert_d_eq(mallctl("arenas.lrun.1.size", (void *)&large1, &sz, NULL, 171 0), 0, "Unexpected arenas.lrun.1.size failure"); 172 173 /* Get huge size classes. */ 174 assert_d_eq(mallctl("arenas.hchunk.0.size", (void *)&huge0, &sz, NULL, 175 0), 0, "Unexpected arenas.hchunk.0.size failure"); 176 assert_d_eq(mallctl("arenas.hchunk.1.size", (void *)&huge1, &sz, NULL, 177 0), 0, "Unexpected arenas.hchunk.1.size failure"); 178 assert_d_eq(mallctl("arenas.hchunk.2.size", (void *)&huge2, &sz, NULL, 179 0), 0, "Unexpected arenas.hchunk.2.size failure"); 180 181 /* Test dalloc/decommit/purge cascade. */ 182 purge_miblen = sizeof(purge_mib)/sizeof(size_t); 183 assert_d_eq(mallctlnametomib("arena.0.purge", purge_mib, &purge_miblen), 184 0, "Unexpected mallctlnametomib() failure"); 185 purge_mib[1] = (size_t)arena_ind; 186 do_dalloc = false; 187 do_decommit = false; 188 p = mallocx(huge0 * 2, flags); 189 assert_ptr_not_null(p, "Unexpected mallocx() error"); 190 did_dalloc = false; 191 did_decommit = false; 192 did_purge = false; 193 did_split = false; 194 xallocx_success_a = (xallocx(p, huge0, 0, flags) == huge0); 195 assert_d_eq(mallctlbymib(purge_mib, purge_miblen, NULL, NULL, NULL, 0), 196 0, "Unexpected arena.%u.purge error", arena_ind); 197 if (xallocx_success_a) { 198 assert_true(did_dalloc, "Expected dalloc"); 199 assert_false(did_decommit, "Unexpected decommit"); 200 assert_true(did_purge, "Expected purge"); 201 } 202 assert_true(did_split, "Expected split"); 203 dallocx(p, flags); 204 do_dalloc = true; 205 206 /* Test decommit/commit and observe split/merge. */ 207 do_dalloc = false; 208 do_decommit = true; 209 p = mallocx(huge0 * 2, flags); 210 assert_ptr_not_null(p, "Unexpected mallocx() error"); 211 did_decommit = false; 212 did_commit = false; 213 did_split = false; 214 did_merge = false; 215 xallocx_success_b = (xallocx(p, huge0, 0, flags) == huge0); 216 assert_d_eq(mallctlbymib(purge_mib, purge_miblen, NULL, NULL, NULL, 0), 217 0, "Unexpected arena.%u.purge error", arena_ind); 218 if (xallocx_success_b) 219 assert_true(did_split, "Expected split"); 220 xallocx_success_c = (xallocx(p, huge0 * 2, 0, flags) == huge0 * 2); 221 assert_b_eq(did_decommit, did_commit, "Expected decommit/commit match"); 222 if (xallocx_success_b && xallocx_success_c) 223 assert_true(did_merge, "Expected merge"); 224 dallocx(p, flags); 225 do_dalloc = true; 226 do_decommit = false; 227 228 /* Test purge for partial-chunk huge allocations. */ 229 if (huge0 * 2 > huge2) { 230 /* 231 * There are at least four size classes per doubling, so a 232 * successful xallocx() from size=huge2 to size=huge1 is 233 * guaranteed to leave trailing purgeable memory. 234 */ 235 p = mallocx(huge2, flags); 236 assert_ptr_not_null(p, "Unexpected mallocx() error"); 237 did_purge = false; 238 assert_zu_eq(xallocx(p, huge1, 0, flags), huge1, 239 "Unexpected xallocx() failure"); 240 assert_true(did_purge, "Expected purge"); 241 dallocx(p, flags); 242 } 243 244 /* Test decommit for large allocations. */ 245 do_decommit = true; 246 p = mallocx(large1, flags); 247 assert_ptr_not_null(p, "Unexpected mallocx() error"); 248 assert_d_eq(mallctlbymib(purge_mib, purge_miblen, NULL, NULL, NULL, 0), 249 0, "Unexpected arena.%u.purge error", arena_ind); 250 did_decommit = false; 251 assert_zu_eq(xallocx(p, large0, 0, flags), large0, 252 "Unexpected xallocx() failure"); 253 assert_d_eq(mallctlbymib(purge_mib, purge_miblen, NULL, NULL, NULL, 0), 254 0, "Unexpected arena.%u.purge error", arena_ind); 255 did_commit = false; 256 assert_zu_eq(xallocx(p, large1, 0, flags), large1, 257 "Unexpected xallocx() failure"); 258 assert_b_eq(did_decommit, did_commit, "Expected decommit/commit match"); 259 dallocx(p, flags); 260 do_decommit = false; 261 262 /* Make sure non-huge allocation succeeds. */ 263 p = mallocx(42, flags); 264 assert_ptr_not_null(p, "Unexpected mallocx() error"); 265 dallocx(p, flags); 266 267 /* Restore chunk hooks. */ 268 assert_d_eq(mallctlbymib(hooks_mib, hooks_miblen, NULL, NULL, 269 (void *)&old_hooks, new_size), 0, "Unexpected chunk_hooks error"); 270 assert_d_eq(mallctlbymib(hooks_mib, hooks_miblen, (void *)&old_hooks, 271 &old_size, NULL, 0), 0, "Unexpected chunk_hooks error"); 272 assert_ptr_eq(old_hooks.alloc, orig_hooks.alloc, 273 "Unexpected alloc error"); 274 assert_ptr_eq(old_hooks.dalloc, orig_hooks.dalloc, 275 "Unexpected dalloc error"); 276 assert_ptr_eq(old_hooks.commit, orig_hooks.commit, 277 "Unexpected commit error"); 278 assert_ptr_eq(old_hooks.decommit, orig_hooks.decommit, 279 "Unexpected decommit error"); 280 assert_ptr_eq(old_hooks.purge, orig_hooks.purge, 281 "Unexpected purge error"); 282 assert_ptr_eq(old_hooks.split, orig_hooks.split, 283 "Unexpected split error"); 284 assert_ptr_eq(old_hooks.merge, orig_hooks.merge, 285 "Unexpected merge error"); 286 } 287 TEST_END 288 289 int 290 main(void) 291 { 292 293 return (test(test_chunk)); 294 } 295