1 #include "test/jemalloc_test.h"
2 
3 const char *malloc_conf = "decay_time:1,lg_tcache_max:0";
4 
5 static nstime_monotonic_t *nstime_monotonic_orig;
6 static nstime_update_t *nstime_update_orig;
7 
8 static unsigned nupdates_mock;
9 static nstime_t time_mock;
10 static bool monotonic_mock;
11 
12 static bool
nstime_monotonic_mock(void)13 nstime_monotonic_mock(void)
14 {
15 	return (monotonic_mock);
16 }
17 
18 static bool
nstime_update_mock(nstime_t * time)19 nstime_update_mock(nstime_t *time)
20 {
21 	nupdates_mock++;
22 	if (monotonic_mock)
23 		nstime_copy(time, &time_mock);
24 	return (!monotonic_mock);
25 }
26 
TEST_BEGIN(test_decay_ticks)27 TEST_BEGIN(test_decay_ticks)
28 {
29 	ticker_t *decay_ticker;
30 	unsigned tick0, tick1;
31 	size_t sz, large0;
32 	void *p;
33 
34 	decay_ticker = decay_ticker_get(tsd_fetch(), 0);
35 	assert_ptr_not_null(decay_ticker,
36 	    "Unexpected failure getting decay ticker");
37 
38 	sz = sizeof(size_t);
39 	assert_d_eq(mallctl("arenas.lextent.0.size", (void *)&large0, &sz, NULL,
40 	    0), 0, "Unexpected mallctl failure");
41 
42 	/*
43 	 * Test the standard APIs using a large size class, since we can't
44 	 * control tcache interactions for small size classes (except by
45 	 * completely disabling tcache for the entire test program).
46 	 */
47 
48 	/* malloc(). */
49 	tick0 = ticker_read(decay_ticker);
50 	p = malloc(large0);
51 	assert_ptr_not_null(p, "Unexpected malloc() failure");
52 	tick1 = ticker_read(decay_ticker);
53 	assert_u32_ne(tick1, tick0, "Expected ticker to tick during malloc()");
54 	/* free(). */
55 	tick0 = ticker_read(decay_ticker);
56 	free(p);
57 	tick1 = ticker_read(decay_ticker);
58 	assert_u32_ne(tick1, tick0, "Expected ticker to tick during free()");
59 
60 	/* calloc(). */
61 	tick0 = ticker_read(decay_ticker);
62 	p = calloc(1, large0);
63 	assert_ptr_not_null(p, "Unexpected calloc() failure");
64 	tick1 = ticker_read(decay_ticker);
65 	assert_u32_ne(tick1, tick0, "Expected ticker to tick during calloc()");
66 	free(p);
67 
68 	/* posix_memalign(). */
69 	tick0 = ticker_read(decay_ticker);
70 	assert_d_eq(posix_memalign(&p, sizeof(size_t), large0), 0,
71 	    "Unexpected posix_memalign() failure");
72 	tick1 = ticker_read(decay_ticker);
73 	assert_u32_ne(tick1, tick0,
74 	    "Expected ticker to tick during posix_memalign()");
75 	free(p);
76 
77 	/* aligned_alloc(). */
78 	tick0 = ticker_read(decay_ticker);
79 	p = aligned_alloc(sizeof(size_t), large0);
80 	assert_ptr_not_null(p, "Unexpected aligned_alloc() failure");
81 	tick1 = ticker_read(decay_ticker);
82 	assert_u32_ne(tick1, tick0,
83 	    "Expected ticker to tick during aligned_alloc()");
84 	free(p);
85 
86 	/* realloc(). */
87 	/* Allocate. */
88 	tick0 = ticker_read(decay_ticker);
89 	p = realloc(NULL, large0);
90 	assert_ptr_not_null(p, "Unexpected realloc() failure");
91 	tick1 = ticker_read(decay_ticker);
92 	assert_u32_ne(tick1, tick0, "Expected ticker to tick during realloc()");
93 	/* Reallocate. */
94 	tick0 = ticker_read(decay_ticker);
95 	p = realloc(p, large0);
96 	assert_ptr_not_null(p, "Unexpected realloc() failure");
97 	tick1 = ticker_read(decay_ticker);
98 	assert_u32_ne(tick1, tick0, "Expected ticker to tick during realloc()");
99 	/* Deallocate. */
100 	tick0 = ticker_read(decay_ticker);
101 	realloc(p, 0);
102 	tick1 = ticker_read(decay_ticker);
103 	assert_u32_ne(tick1, tick0, "Expected ticker to tick during realloc()");
104 
105 	/*
106 	 * Test the *allocx() APIs using large and small size classes, with
107 	 * tcache explicitly disabled.
108 	 */
109 	{
110 		unsigned i;
111 		size_t allocx_sizes[2];
112 		allocx_sizes[0] = large0;
113 		allocx_sizes[1] = 1;
114 
115 		for (i = 0; i < sizeof(allocx_sizes) / sizeof(size_t); i++) {
116 			sz = allocx_sizes[i];
117 
118 			/* mallocx(). */
119 			tick0 = ticker_read(decay_ticker);
120 			p = mallocx(sz, MALLOCX_TCACHE_NONE);
121 			assert_ptr_not_null(p, "Unexpected mallocx() failure");
122 			tick1 = ticker_read(decay_ticker);
123 			assert_u32_ne(tick1, tick0,
124 			    "Expected ticker to tick during mallocx() (sz=%zu)",
125 			    sz);
126 			/* rallocx(). */
127 			tick0 = ticker_read(decay_ticker);
128 			p = rallocx(p, sz, MALLOCX_TCACHE_NONE);
129 			assert_ptr_not_null(p, "Unexpected rallocx() failure");
130 			tick1 = ticker_read(decay_ticker);
131 			assert_u32_ne(tick1, tick0,
132 			    "Expected ticker to tick during rallocx() (sz=%zu)",
133 			    sz);
134 			/* xallocx(). */
135 			tick0 = ticker_read(decay_ticker);
136 			xallocx(p, sz, 0, MALLOCX_TCACHE_NONE);
137 			tick1 = ticker_read(decay_ticker);
138 			assert_u32_ne(tick1, tick0,
139 			    "Expected ticker to tick during xallocx() (sz=%zu)",
140 			    sz);
141 			/* dallocx(). */
142 			tick0 = ticker_read(decay_ticker);
143 			dallocx(p, MALLOCX_TCACHE_NONE);
144 			tick1 = ticker_read(decay_ticker);
145 			assert_u32_ne(tick1, tick0,
146 			    "Expected ticker to tick during dallocx() (sz=%zu)",
147 			    sz);
148 			/* sdallocx(). */
149 			p = mallocx(sz, MALLOCX_TCACHE_NONE);
150 			assert_ptr_not_null(p, "Unexpected mallocx() failure");
151 			tick0 = ticker_read(decay_ticker);
152 			sdallocx(p, sz, MALLOCX_TCACHE_NONE);
153 			tick1 = ticker_read(decay_ticker);
154 			assert_u32_ne(tick1, tick0,
155 			    "Expected ticker to tick during sdallocx() "
156 			    "(sz=%zu)", sz);
157 		}
158 	}
159 
160 	/*
161 	 * Test tcache fill/flush interactions for large and small size classes,
162 	 * using an explicit tcache.
163 	 */
164 	if (config_tcache) {
165 		unsigned tcache_ind, i;
166 		size_t tcache_sizes[2];
167 		tcache_sizes[0] = large0;
168 		tcache_sizes[1] = 1;
169 
170 		sz = sizeof(unsigned);
171 		assert_d_eq(mallctl("tcache.create", (void *)&tcache_ind, &sz,
172 		    NULL, 0), 0, "Unexpected mallctl failure");
173 
174 		for (i = 0; i < sizeof(tcache_sizes) / sizeof(size_t); i++) {
175 			sz = tcache_sizes[i];
176 
177 			/* tcache fill. */
178 			tick0 = ticker_read(decay_ticker);
179 			p = mallocx(sz, MALLOCX_TCACHE(tcache_ind));
180 			assert_ptr_not_null(p, "Unexpected mallocx() failure");
181 			tick1 = ticker_read(decay_ticker);
182 			assert_u32_ne(tick1, tick0,
183 			    "Expected ticker to tick during tcache fill "
184 			    "(sz=%zu)", sz);
185 			/* tcache flush. */
186 			dallocx(p, MALLOCX_TCACHE(tcache_ind));
187 			tick0 = ticker_read(decay_ticker);
188 			assert_d_eq(mallctl("tcache.flush", NULL, NULL,
189 			    (void *)&tcache_ind, sizeof(unsigned)), 0,
190 			    "Unexpected mallctl failure");
191 			tick1 = ticker_read(decay_ticker);
192 			assert_u32_ne(tick1, tick0,
193 			    "Expected ticker to tick during tcache flush "
194 			    "(sz=%zu)", sz);
195 		}
196 	}
197 }
198 TEST_END
199 
TEST_BEGIN(test_decay_ticker)200 TEST_BEGIN(test_decay_ticker)
201 {
202 #define	NPS 1024
203 	int flags = (MALLOCX_ARENA(0) | MALLOCX_TCACHE_NONE);
204 	void *ps[NPS];
205 	uint64_t epoch;
206 	uint64_t npurge0 = 0;
207 	uint64_t npurge1 = 0;
208 	size_t sz, large;
209 	unsigned i, nupdates0;
210 	nstime_t time, decay_time, deadline;
211 
212 	/*
213 	 * Allocate a bunch of large objects, pause the clock, deallocate the
214 	 * objects, restore the clock, then [md]allocx() in a tight loop to
215 	 * verify the ticker triggers purging.
216 	 */
217 
218 	if (config_tcache) {
219 		size_t tcache_max;
220 
221 		sz = sizeof(size_t);
222 		assert_d_eq(mallctl("arenas.tcache_max", (void *)&tcache_max,
223 		    &sz, NULL, 0), 0, "Unexpected mallctl failure");
224 		large = nallocx(tcache_max + 1, flags);
225 	}  else {
226 		sz = sizeof(size_t);
227 		assert_d_eq(mallctl("arenas.lextent.0.size", &large, &sz, NULL,
228 		    0), 0, "Unexpected mallctl failure");
229 	}
230 
231 	assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
232 	    "Unexpected mallctl failure");
233 	assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch,
234 	    sizeof(uint64_t)), 0, "Unexpected mallctl failure");
235 	sz = sizeof(uint64_t);
236 	assert_d_eq(mallctl("stats.arenas.0.npurge", (void *)&npurge0, &sz,
237 	    NULL, 0), config_stats ? 0 : ENOENT, "Unexpected mallctl result");
238 
239 	for (i = 0; i < NPS; i++) {
240 		ps[i] = mallocx(large, flags);
241 		assert_ptr_not_null(ps[i], "Unexpected mallocx() failure");
242 	}
243 
244 	nupdates_mock = 0;
245 	nstime_init(&time_mock, 0);
246 	nstime_update(&time_mock);
247 	monotonic_mock = true;
248 
249 	nstime_monotonic_orig = nstime_monotonic;
250 	nstime_update_orig = nstime_update;
251 	nstime_monotonic = nstime_monotonic_mock;
252 	nstime_update = nstime_update_mock;
253 
254 	for (i = 0; i < NPS; i++) {
255 		dallocx(ps[i], flags);
256 		nupdates0 = nupdates_mock;
257 		assert_d_eq(mallctl("arena.0.decay", NULL, NULL, NULL, 0), 0,
258 		    "Unexpected arena.0.decay failure");
259 		assert_u_gt(nupdates_mock, nupdates0,
260 		    "Expected nstime_update() to be called");
261 	}
262 
263 	nstime_monotonic = nstime_monotonic_orig;
264 	nstime_update = nstime_update_orig;
265 
266 	nstime_init(&time, 0);
267 	nstime_update(&time);
268 	nstime_init2(&decay_time, opt_decay_time, 0);
269 	nstime_copy(&deadline, &time);
270 	nstime_add(&deadline, &decay_time);
271 	do {
272 		for (i = 0; i < DECAY_NTICKS_PER_UPDATE / 2; i++) {
273 			void *p = mallocx(1, flags);
274 			assert_ptr_not_null(p, "Unexpected mallocx() failure");
275 			dallocx(p, flags);
276 		}
277 		assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch,
278 		    sizeof(uint64_t)), 0, "Unexpected mallctl failure");
279 		sz = sizeof(uint64_t);
280 		assert_d_eq(mallctl("stats.arenas.0.npurge", (void *)&npurge1,
281 		    &sz, NULL, 0), config_stats ? 0 : ENOENT,
282 		    "Unexpected mallctl result");
283 
284 		nstime_update(&time);
285 	} while (nstime_compare(&time, &deadline) <= 0 && npurge1 == npurge0);
286 
287 	if (config_stats)
288 		assert_u64_gt(npurge1, npurge0, "Expected purging to occur");
289 #undef NPS
290 }
291 TEST_END
292 
TEST_BEGIN(test_decay_nonmonotonic)293 TEST_BEGIN(test_decay_nonmonotonic)
294 {
295 #define	NPS (SMOOTHSTEP_NSTEPS + 1)
296 	int flags = (MALLOCX_ARENA(0) | MALLOCX_TCACHE_NONE);
297 	void *ps[NPS];
298 	uint64_t epoch;
299 	uint64_t npurge0 = 0;
300 	uint64_t npurge1 = 0;
301 	size_t sz, large0;
302 	unsigned i, nupdates0;
303 
304 	sz = sizeof(size_t);
305 	assert_d_eq(mallctl("arenas.lextent.0.size", (void *)&large0, &sz, NULL,
306 	    0), 0, "Unexpected mallctl failure");
307 
308 	assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
309 	    "Unexpected mallctl failure");
310 	assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch,
311 	    sizeof(uint64_t)), 0, "Unexpected mallctl failure");
312 	sz = sizeof(uint64_t);
313 	assert_d_eq(mallctl("stats.arenas.0.npurge", (void *)&npurge0, &sz,
314 	    NULL, 0), config_stats ? 0 : ENOENT, "Unexpected mallctl result");
315 
316 	nupdates_mock = 0;
317 	nstime_init(&time_mock, 0);
318 	nstime_update(&time_mock);
319 	monotonic_mock = false;
320 
321 	nstime_monotonic_orig = nstime_monotonic;
322 	nstime_update_orig = nstime_update;
323 	nstime_monotonic = nstime_monotonic_mock;
324 	nstime_update = nstime_update_mock;
325 
326 	for (i = 0; i < NPS; i++) {
327 		ps[i] = mallocx(large0, flags);
328 		assert_ptr_not_null(ps[i], "Unexpected mallocx() failure");
329 	}
330 
331 	for (i = 0; i < NPS; i++) {
332 		dallocx(ps[i], flags);
333 		nupdates0 = nupdates_mock;
334 		assert_d_eq(mallctl("arena.0.decay", NULL, NULL, NULL, 0), 0,
335 		    "Unexpected arena.0.decay failure");
336 		assert_u_gt(nupdates_mock, nupdates0,
337 		    "Expected nstime_update() to be called");
338 	}
339 
340 	assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch,
341 	    sizeof(uint64_t)), 0, "Unexpected mallctl failure");
342 	sz = sizeof(uint64_t);
343 	assert_d_eq(mallctl("stats.arenas.0.npurge", (void *)&npurge1, &sz,
344 	    NULL, 0), config_stats ? 0 : ENOENT, "Unexpected mallctl result");
345 
346 	if (config_stats)
347 		assert_u64_eq(npurge0, npurge1, "Unexpected purging occurred");
348 
349 	nstime_monotonic = nstime_monotonic_orig;
350 	nstime_update = nstime_update_orig;
351 #undef NPS
352 }
353 TEST_END
354 
355 int
main(void)356 main(void)
357 {
358 	return (test(
359 	    test_decay_ticks,
360 	    test_decay_ticker,
361 	    test_decay_nonmonotonic));
362 }
363