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