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