• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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