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