1 #ifndef ARENA_RESET_PROF_C_
2 #include "test/jemalloc_test.h"
3 #endif
4
5 #include "jemalloc/internal/extent_mmap.h"
6 #include "jemalloc/internal/rtree.h"
7
8 #include "test/extent_hooks.h"
9
10 static unsigned
get_nsizes_impl(const char * cmd)11 get_nsizes_impl(const char *cmd) {
12 unsigned ret;
13 size_t z;
14
15 z = sizeof(unsigned);
16 assert_d_eq(mallctl(cmd, (void *)&ret, &z, NULL, 0), 0,
17 "Unexpected mallctl(\"%s\", ...) failure", cmd);
18
19 return ret;
20 }
21
22 static unsigned
get_nsmall(void)23 get_nsmall(void) {
24 return get_nsizes_impl("arenas.nbins");
25 }
26
27 static unsigned
get_nlarge(void)28 get_nlarge(void) {
29 return get_nsizes_impl("arenas.nlextents");
30 }
31
32 static size_t
get_size_impl(const char * cmd,size_t ind)33 get_size_impl(const char *cmd, size_t ind) {
34 size_t ret;
35 size_t z;
36 size_t mib[4];
37 size_t miblen = 4;
38
39 z = sizeof(size_t);
40 assert_d_eq(mallctlnametomib(cmd, mib, &miblen),
41 0, "Unexpected mallctlnametomib(\"%s\", ...) failure", cmd);
42 mib[2] = ind;
43 z = sizeof(size_t);
44 assert_d_eq(mallctlbymib(mib, miblen, (void *)&ret, &z, NULL, 0),
45 0, "Unexpected mallctlbymib([\"%s\", %zu], ...) failure", cmd, ind);
46
47 return ret;
48 }
49
50 static size_t
get_small_size(size_t ind)51 get_small_size(size_t ind) {
52 return get_size_impl("arenas.bin.0.size", ind);
53 }
54
55 static size_t
get_large_size(size_t ind)56 get_large_size(size_t ind) {
57 return get_size_impl("arenas.lextent.0.size", ind);
58 }
59
60 /* Like ivsalloc(), but safe to call on discarded allocations. */
61 static size_t
vsalloc(tsdn_t * tsdn,const void * ptr)62 vsalloc(tsdn_t *tsdn, const void *ptr) {
63 rtree_ctx_t rtree_ctx_fallback;
64 rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
65
66 extent_t *extent;
67 szind_t szind;
68 if (rtree_extent_szind_read(tsdn, &extents_rtree, rtree_ctx,
69 (uintptr_t)ptr, false, &extent, &szind)) {
70 return 0;
71 }
72
73 if (extent == NULL) {
74 return 0;
75 }
76 if (extent_state_get(extent) != extent_state_active) {
77 return 0;
78 }
79
80 if (szind == NSIZES) {
81 return 0;
82 }
83
84 return sz_index2size(szind);
85 }
86
87 static unsigned
do_arena_create(extent_hooks_t * h)88 do_arena_create(extent_hooks_t *h) {
89 unsigned arena_ind;
90 size_t sz = sizeof(unsigned);
91 assert_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz,
92 (void *)(h != NULL ? &h : NULL), (h != NULL ? sizeof(h) : 0)), 0,
93 "Unexpected mallctl() failure");
94 return arena_ind;
95 }
96
97 static void
do_arena_reset_pre(unsigned arena_ind,void *** ptrs,unsigned * nptrs)98 do_arena_reset_pre(unsigned arena_ind, void ***ptrs, unsigned *nptrs) {
99 #define NLARGE 32
100 unsigned nsmall, nlarge, i;
101 size_t sz;
102 int flags;
103 tsdn_t *tsdn;
104
105 flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;
106
107 nsmall = get_nsmall();
108 nlarge = get_nlarge() > NLARGE ? NLARGE : get_nlarge();
109 *nptrs = nsmall + nlarge;
110 *ptrs = (void **)malloc(*nptrs * sizeof(void *));
111 assert_ptr_not_null(*ptrs, "Unexpected malloc() failure");
112
113 /* Allocate objects with a wide range of sizes. */
114 for (i = 0; i < nsmall; i++) {
115 sz = get_small_size(i);
116 (*ptrs)[i] = mallocx(sz, flags);
117 assert_ptr_not_null((*ptrs)[i],
118 "Unexpected mallocx(%zu, %#x) failure", sz, flags);
119 }
120 for (i = 0; i < nlarge; i++) {
121 sz = get_large_size(i);
122 (*ptrs)[nsmall + i] = mallocx(sz, flags);
123 assert_ptr_not_null((*ptrs)[i],
124 "Unexpected mallocx(%zu, %#x) failure", sz, flags);
125 }
126
127 tsdn = tsdn_fetch();
128
129 /* Verify allocations. */
130 for (i = 0; i < *nptrs; i++) {
131 assert_zu_gt(ivsalloc(tsdn, (*ptrs)[i]), 0,
132 "Allocation should have queryable size");
133 }
134 }
135
136 static void
do_arena_reset_post(void ** ptrs,unsigned nptrs,unsigned arena_ind)137 do_arena_reset_post(void **ptrs, unsigned nptrs, unsigned arena_ind) {
138 tsdn_t *tsdn;
139 unsigned i;
140
141 tsdn = tsdn_fetch();
142
143 if (have_background_thread) {
144 malloc_mutex_lock(tsdn,
145 &background_thread_info[arena_ind % ncpus].mtx);
146 }
147 /* Verify allocations no longer exist. */
148 for (i = 0; i < nptrs; i++) {
149 assert_zu_eq(vsalloc(tsdn, ptrs[i]), 0,
150 "Allocation should no longer exist");
151 }
152 if (have_background_thread) {
153 malloc_mutex_unlock(tsdn,
154 &background_thread_info[arena_ind % ncpus].mtx);
155 }
156
157 free(ptrs);
158 }
159
160 static void
do_arena_reset_destroy(const char * name,unsigned arena_ind)161 do_arena_reset_destroy(const char *name, unsigned arena_ind) {
162 size_t mib[3];
163 size_t miblen;
164
165 miblen = sizeof(mib)/sizeof(size_t);
166 assert_d_eq(mallctlnametomib(name, mib, &miblen), 0,
167 "Unexpected mallctlnametomib() failure");
168 mib[1] = (size_t)arena_ind;
169 assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
170 "Unexpected mallctlbymib() failure");
171 }
172
173 static void
do_arena_reset(unsigned arena_ind)174 do_arena_reset(unsigned arena_ind) {
175 do_arena_reset_destroy("arena.0.reset", arena_ind);
176 }
177
178 static void
do_arena_destroy(unsigned arena_ind)179 do_arena_destroy(unsigned arena_ind) {
180 do_arena_reset_destroy("arena.0.destroy", arena_ind);
181 }
182
TEST_BEGIN(test_arena_reset)183 TEST_BEGIN(test_arena_reset) {
184 unsigned arena_ind;
185 void **ptrs;
186 unsigned nptrs;
187
188 arena_ind = do_arena_create(NULL);
189 do_arena_reset_pre(arena_ind, &ptrs, &nptrs);
190 do_arena_reset(arena_ind);
191 do_arena_reset_post(ptrs, nptrs, arena_ind);
192 }
193 TEST_END
194
195 static bool
arena_i_initialized(unsigned arena_ind,bool refresh)196 arena_i_initialized(unsigned arena_ind, bool refresh) {
197 bool initialized;
198 size_t mib[3];
199 size_t miblen, sz;
200
201 if (refresh) {
202 uint64_t epoch = 1;
203 assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch,
204 sizeof(epoch)), 0, "Unexpected mallctl() failure");
205 }
206
207 miblen = sizeof(mib)/sizeof(size_t);
208 assert_d_eq(mallctlnametomib("arena.0.initialized", mib, &miblen), 0,
209 "Unexpected mallctlnametomib() failure");
210 mib[1] = (size_t)arena_ind;
211 sz = sizeof(initialized);
212 assert_d_eq(mallctlbymib(mib, miblen, (void *)&initialized, &sz, NULL,
213 0), 0, "Unexpected mallctlbymib() failure");
214
215 return initialized;
216 }
217
TEST_BEGIN(test_arena_destroy_initial)218 TEST_BEGIN(test_arena_destroy_initial) {
219 assert_false(arena_i_initialized(MALLCTL_ARENAS_DESTROYED, false),
220 "Destroyed arena stats should not be initialized");
221 }
222 TEST_END
223
TEST_BEGIN(test_arena_destroy_hooks_default)224 TEST_BEGIN(test_arena_destroy_hooks_default) {
225 unsigned arena_ind, arena_ind_another, arena_ind_prev;
226 void **ptrs;
227 unsigned nptrs;
228
229 arena_ind = do_arena_create(NULL);
230 do_arena_reset_pre(arena_ind, &ptrs, &nptrs);
231
232 assert_false(arena_i_initialized(arena_ind, false),
233 "Arena stats should not be initialized");
234 assert_true(arena_i_initialized(arena_ind, true),
235 "Arena stats should be initialized");
236
237 /*
238 * Create another arena before destroying one, to better verify arena
239 * index reuse.
240 */
241 arena_ind_another = do_arena_create(NULL);
242
243 do_arena_destroy(arena_ind);
244
245 assert_false(arena_i_initialized(arena_ind, true),
246 "Arena stats should not be initialized");
247 assert_true(arena_i_initialized(MALLCTL_ARENAS_DESTROYED, false),
248 "Destroyed arena stats should be initialized");
249
250 do_arena_reset_post(ptrs, nptrs, arena_ind);
251
252 arena_ind_prev = arena_ind;
253 arena_ind = do_arena_create(NULL);
254 do_arena_reset_pre(arena_ind, &ptrs, &nptrs);
255 assert_u_eq(arena_ind, arena_ind_prev,
256 "Arena index should have been recycled");
257 do_arena_destroy(arena_ind);
258 do_arena_reset_post(ptrs, nptrs, arena_ind);
259
260 do_arena_destroy(arena_ind_another);
261 }
262 TEST_END
263
264 /*
265 * Actually unmap extents, regardless of opt_retain, so that attempts to access
266 * a destroyed arena's memory will segfault.
267 */
268 static bool
extent_dalloc_unmap(extent_hooks_t * extent_hooks,void * addr,size_t size,bool committed,unsigned arena_ind)269 extent_dalloc_unmap(extent_hooks_t *extent_hooks, void *addr, size_t size,
270 bool committed, unsigned arena_ind) {
271 TRACE_HOOK("%s(extent_hooks=%p, addr=%p, size=%zu, committed=%s, "
272 "arena_ind=%u)\n", __func__, extent_hooks, addr, size, committed ?
273 "true" : "false", arena_ind);
274 assert_ptr_eq(extent_hooks, &hooks,
275 "extent_hooks should be same as pointer used to set hooks");
276 assert_ptr_eq(extent_hooks->dalloc, extent_dalloc_unmap,
277 "Wrong hook function");
278 called_dalloc = true;
279 if (!try_dalloc) {
280 return true;
281 }
282 pages_unmap(addr, size);
283 did_dalloc = true;
284 return false;
285 }
286
287 static extent_hooks_t hooks_orig;
288
289 static extent_hooks_t hooks_unmap = {
290 extent_alloc_hook,
291 extent_dalloc_unmap, /* dalloc */
292 extent_destroy_hook,
293 extent_commit_hook,
294 extent_decommit_hook,
295 extent_purge_lazy_hook,
296 extent_purge_forced_hook,
297 extent_split_hook,
298 extent_merge_hook
299 };
300
TEST_BEGIN(test_arena_destroy_hooks_unmap)301 TEST_BEGIN(test_arena_destroy_hooks_unmap) {
302 unsigned arena_ind;
303 void **ptrs;
304 unsigned nptrs;
305
306 extent_hooks_prep();
307 try_decommit = false;
308 memcpy(&hooks_orig, &hooks, sizeof(extent_hooks_t));
309 memcpy(&hooks, &hooks_unmap, sizeof(extent_hooks_t));
310
311 did_alloc = false;
312 arena_ind = do_arena_create(&hooks);
313 do_arena_reset_pre(arena_ind, &ptrs, &nptrs);
314
315 assert_true(did_alloc, "Expected alloc");
316
317 assert_false(arena_i_initialized(arena_ind, false),
318 "Arena stats should not be initialized");
319 assert_true(arena_i_initialized(arena_ind, true),
320 "Arena stats should be initialized");
321
322 did_dalloc = false;
323 do_arena_destroy(arena_ind);
324 assert_true(did_dalloc, "Expected dalloc");
325
326 assert_false(arena_i_initialized(arena_ind, true),
327 "Arena stats should not be initialized");
328 assert_true(arena_i_initialized(MALLCTL_ARENAS_DESTROYED, false),
329 "Destroyed arena stats should be initialized");
330
331 do_arena_reset_post(ptrs, nptrs, arena_ind);
332
333 memcpy(&hooks, &hooks_orig, sizeof(extent_hooks_t));
334 }
335 TEST_END
336
337 int
main(void)338 main(void) {
339 return test(
340 test_arena_reset,
341 test_arena_destroy_initial,
342 test_arena_destroy_hooks_default,
343 test_arena_destroy_hooks_unmap);
344 }
345