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 chunk_hooks_t new_hooks = { 125 chunk_alloc, 126 chunk_dalloc, 127 chunk_commit, 128 chunk_decommit, 129 chunk_purge, 130 chunk_split, 131 chunk_merge 132 }; 133 bool xallocx_success_a, xallocx_success_b, xallocx_success_c; 134 135 /* Install custom chunk hooks. */ 136 old_size = sizeof(chunk_hooks_t); 137 new_size = sizeof(chunk_hooks_t); 138 assert_d_eq(mallctl("arena.0.chunk_hooks", &old_hooks, &old_size, 139 &new_hooks, new_size), 0, "Unexpected chunk_hooks error"); 140 orig_hooks = old_hooks; 141 assert_ptr_ne(old_hooks.alloc, chunk_alloc, "Unexpected alloc error"); 142 assert_ptr_ne(old_hooks.dalloc, chunk_dalloc, 143 "Unexpected dalloc error"); 144 assert_ptr_ne(old_hooks.commit, chunk_commit, 145 "Unexpected commit error"); 146 assert_ptr_ne(old_hooks.decommit, chunk_decommit, 147 "Unexpected decommit error"); 148 assert_ptr_ne(old_hooks.purge, chunk_purge, "Unexpected purge error"); 149 assert_ptr_ne(old_hooks.split, chunk_split, "Unexpected split error"); 150 assert_ptr_ne(old_hooks.merge, chunk_merge, "Unexpected merge error"); 151 152 /* Get large size classes. */ 153 sz = sizeof(size_t); 154 assert_d_eq(mallctl("arenas.lrun.0.size", &large0, &sz, NULL, 0), 0, 155 "Unexpected arenas.lrun.0.size failure"); 156 assert_d_eq(mallctl("arenas.lrun.1.size", &large1, &sz, NULL, 0), 0, 157 "Unexpected arenas.lrun.1.size failure"); 158 159 /* Get huge size classes. */ 160 assert_d_eq(mallctl("arenas.hchunk.0.size", &huge0, &sz, NULL, 0), 0, 161 "Unexpected arenas.hchunk.0.size failure"); 162 assert_d_eq(mallctl("arenas.hchunk.1.size", &huge1, &sz, NULL, 0), 0, 163 "Unexpected arenas.hchunk.1.size failure"); 164 assert_d_eq(mallctl("arenas.hchunk.2.size", &huge2, &sz, NULL, 0), 0, 165 "Unexpected arenas.hchunk.2.size failure"); 166 167 /* Test dalloc/decommit/purge cascade. */ 168 do_dalloc = false; 169 do_decommit = false; 170 p = mallocx(huge0 * 2, 0); 171 assert_ptr_not_null(p, "Unexpected mallocx() error"); 172 did_dalloc = false; 173 did_decommit = false; 174 did_purge = false; 175 did_split = false; 176 xallocx_success_a = (xallocx(p, huge0, 0, 0) == huge0); 177 assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0, 178 "Unexpected arena.0.purge error"); 179 if (xallocx_success_a) { 180 assert_true(did_dalloc, "Expected dalloc"); 181 assert_false(did_decommit, "Unexpected decommit"); 182 assert_true(did_purge, "Expected purge"); 183 } 184 assert_true(did_split, "Expected split"); 185 dallocx(p, 0); 186 do_dalloc = true; 187 188 /* Test decommit/commit and observe split/merge. */ 189 do_dalloc = false; 190 do_decommit = true; 191 p = mallocx(huge0 * 2, 0); 192 assert_ptr_not_null(p, "Unexpected mallocx() error"); 193 did_decommit = false; 194 did_commit = false; 195 did_split = false; 196 did_merge = false; 197 xallocx_success_b = (xallocx(p, huge0, 0, 0) == huge0); 198 assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0, 199 "Unexpected arena.0.purge error"); 200 if (xallocx_success_b) 201 assert_true(did_split, "Expected split"); 202 xallocx_success_c = (xallocx(p, huge0 * 2, 0, 0) == huge0 * 2); 203 assert_b_eq(did_decommit, did_commit, "Expected decommit/commit match"); 204 if (xallocx_success_b && xallocx_success_c) 205 assert_true(did_merge, "Expected merge"); 206 dallocx(p, 0); 207 do_dalloc = true; 208 do_decommit = false; 209 210 /* Test purge for partial-chunk huge allocations. */ 211 if (huge0 * 2 > huge2) { 212 /* 213 * There are at least four size classes per doubling, so a 214 * successful xallocx() from size=huge2 to size=huge1 is 215 * guaranteed to leave trailing purgeable memory. 216 */ 217 p = mallocx(huge2, 0); 218 assert_ptr_not_null(p, "Unexpected mallocx() error"); 219 did_purge = false; 220 assert_zu_eq(xallocx(p, huge1, 0, 0), huge1, 221 "Unexpected xallocx() failure"); 222 assert_true(did_purge, "Expected purge"); 223 dallocx(p, 0); 224 } 225 226 /* Test decommit for large allocations. */ 227 do_decommit = true; 228 p = mallocx(large1, 0); 229 assert_ptr_not_null(p, "Unexpected mallocx() error"); 230 assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0, 231 "Unexpected arena.0.purge error"); 232 did_decommit = false; 233 assert_zu_eq(xallocx(p, large0, 0, 0), large0, 234 "Unexpected xallocx() failure"); 235 assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0, 236 "Unexpected arena.0.purge error"); 237 did_commit = false; 238 assert_zu_eq(xallocx(p, large1, 0, 0), large1, 239 "Unexpected xallocx() failure"); 240 assert_b_eq(did_decommit, did_commit, "Expected decommit/commit match"); 241 dallocx(p, 0); 242 do_decommit = false; 243 244 /* Make sure non-huge allocation succeeds. */ 245 p = mallocx(42, 0); 246 assert_ptr_not_null(p, "Unexpected mallocx() error"); 247 dallocx(p, 0); 248 249 /* Restore chunk hooks. */ 250 assert_d_eq(mallctl("arena.0.chunk_hooks", NULL, NULL, &old_hooks, 251 new_size), 0, "Unexpected chunk_hooks error"); 252 assert_d_eq(mallctl("arena.0.chunk_hooks", &old_hooks, &old_size, 253 NULL, 0), 0, "Unexpected chunk_hooks error"); 254 assert_ptr_eq(old_hooks.alloc, orig_hooks.alloc, 255 "Unexpected alloc error"); 256 assert_ptr_eq(old_hooks.dalloc, orig_hooks.dalloc, 257 "Unexpected dalloc error"); 258 assert_ptr_eq(old_hooks.commit, orig_hooks.commit, 259 "Unexpected commit error"); 260 assert_ptr_eq(old_hooks.decommit, orig_hooks.decommit, 261 "Unexpected decommit error"); 262 assert_ptr_eq(old_hooks.purge, orig_hooks.purge, 263 "Unexpected purge error"); 264 assert_ptr_eq(old_hooks.split, orig_hooks.split, 265 "Unexpected split error"); 266 assert_ptr_eq(old_hooks.merge, orig_hooks.merge, 267 "Unexpected merge error"); 268 } 269 TEST_END 270 271 int 272 main(void) 273 { 274 275 return (test(test_chunk)); 276 } 277