mallctl.c revision 1.1.1.1.14.1 1 #include "test/jemalloc_test.h"
2
3 #include "jemalloc/internal/ctl.h"
4 #include "jemalloc/internal/hook.h"
5 #include "jemalloc/internal/util.h"
6
7 TEST_BEGIN(test_mallctl_errors) {
8 uint64_t epoch;
9 size_t sz;
10
11 expect_d_eq(mallctl("no_such_name", NULL, NULL, NULL, 0), ENOENT,
12 "mallctl() should return ENOENT for non-existent names");
13
14 expect_d_eq(mallctl("version", NULL, NULL, "0.0.0", strlen("0.0.0")),
15 EPERM, "mallctl() should return EPERM on attempt to write "
16 "read-only value");
17
18 expect_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch,
19 sizeof(epoch)-1), EINVAL,
20 "mallctl() should return EINVAL for input size mismatch");
21 expect_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch,
22 sizeof(epoch)+1), EINVAL,
23 "mallctl() should return EINVAL for input size mismatch");
24
25 sz = sizeof(epoch)-1;
26 expect_d_eq(mallctl("epoch", (void *)&epoch, &sz, NULL, 0), EINVAL,
27 "mallctl() should return EINVAL for output size mismatch");
28 sz = sizeof(epoch)+1;
29 expect_d_eq(mallctl("epoch", (void *)&epoch, &sz, NULL, 0), EINVAL,
30 "mallctl() should return EINVAL for output size mismatch");
31 }
32 TEST_END
33
34 TEST_BEGIN(test_mallctlnametomib_errors) {
35 size_t mib[1];
36 size_t miblen;
37
38 miblen = sizeof(mib)/sizeof(size_t);
39 expect_d_eq(mallctlnametomib("no_such_name", mib, &miblen), ENOENT,
40 "mallctlnametomib() should return ENOENT for non-existent names");
41 }
42 TEST_END
43
44 TEST_BEGIN(test_mallctlbymib_errors) {
45 uint64_t epoch;
46 size_t sz;
47 size_t mib[1];
48 size_t miblen;
49
50 miblen = sizeof(mib)/sizeof(size_t);
51 expect_d_eq(mallctlnametomib("version", mib, &miblen), 0,
52 "Unexpected mallctlnametomib() failure");
53
54 expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, "0.0.0",
55 strlen("0.0.0")), EPERM, "mallctl() should return EPERM on "
56 "attempt to write read-only value");
57
58 miblen = sizeof(mib)/sizeof(size_t);
59 expect_d_eq(mallctlnametomib("epoch", mib, &miblen), 0,
60 "Unexpected mallctlnametomib() failure");
61
62 expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, (void *)&epoch,
63 sizeof(epoch)-1), EINVAL,
64 "mallctlbymib() should return EINVAL for input size mismatch");
65 expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, (void *)&epoch,
66 sizeof(epoch)+1), EINVAL,
67 "mallctlbymib() should return EINVAL for input size mismatch");
68
69 sz = sizeof(epoch)-1;
70 expect_d_eq(mallctlbymib(mib, miblen, (void *)&epoch, &sz, NULL, 0),
71 EINVAL,
72 "mallctlbymib() should return EINVAL for output size mismatch");
73 sz = sizeof(epoch)+1;
74 expect_d_eq(mallctlbymib(mib, miblen, (void *)&epoch, &sz, NULL, 0),
75 EINVAL,
76 "mallctlbymib() should return EINVAL for output size mismatch");
77 }
78 TEST_END
79
80 TEST_BEGIN(test_mallctl_read_write) {
81 uint64_t old_epoch, new_epoch;
82 size_t sz = sizeof(old_epoch);
83
84 /* Blind. */
85 expect_d_eq(mallctl("epoch", NULL, NULL, NULL, 0), 0,
86 "Unexpected mallctl() failure");
87 expect_zu_eq(sz, sizeof(old_epoch), "Unexpected output size");
88
89 /* Read. */
90 expect_d_eq(mallctl("epoch", (void *)&old_epoch, &sz, NULL, 0), 0,
91 "Unexpected mallctl() failure");
92 expect_zu_eq(sz, sizeof(old_epoch), "Unexpected output size");
93
94 /* Write. */
95 expect_d_eq(mallctl("epoch", NULL, NULL, (void *)&new_epoch,
96 sizeof(new_epoch)), 0, "Unexpected mallctl() failure");
97 expect_zu_eq(sz, sizeof(old_epoch), "Unexpected output size");
98
99 /* Read+write. */
100 expect_d_eq(mallctl("epoch", (void *)&old_epoch, &sz,
101 (void *)&new_epoch, sizeof(new_epoch)), 0,
102 "Unexpected mallctl() failure");
103 expect_zu_eq(sz, sizeof(old_epoch), "Unexpected output size");
104 }
105 TEST_END
106
107 TEST_BEGIN(test_mallctlnametomib_short_mib) {
108 size_t mib[4];
109 size_t miblen;
110
111 miblen = 3;
112 mib[3] = 42;
113 expect_d_eq(mallctlnametomib("arenas.bin.0.nregs", mib, &miblen), 0,
114 "Unexpected mallctlnametomib() failure");
115 expect_zu_eq(miblen, 3, "Unexpected mib output length");
116 expect_zu_eq(mib[3], 42,
117 "mallctlnametomib() wrote past the end of the input mib");
118 }
119 TEST_END
120
121 TEST_BEGIN(test_mallctlnametomib_short_name) {
122 size_t mib[4];
123 size_t miblen;
124
125 miblen = 4;
126 mib[3] = 42;
127 expect_d_eq(mallctlnametomib("arenas.bin.0", mib, &miblen), 0,
128 "Unexpected mallctlnametomib() failure");
129 expect_zu_eq(miblen, 3, "Unexpected mib output length");
130 expect_zu_eq(mib[3], 42,
131 "mallctlnametomib() wrote past the end of the input mib");
132 }
133 TEST_END
134
135 TEST_BEGIN(test_mallctlmibnametomib) {
136 size_t mib[4];
137 size_t miblen = 4;
138 uint32_t result, result_ref;
139 size_t len_result = sizeof(uint32_t);
140
141 tsd_t *tsd = tsd_fetch();
142
143 /* Error cases */
144 assert_d_eq(ctl_mibnametomib(tsd, mib, 0, "bob", &miblen), ENOENT, "");
145 assert_zu_eq(miblen, 4, "");
146 assert_d_eq(ctl_mibnametomib(tsd, mib, 0, "9999", &miblen), ENOENT, "");
147 assert_zu_eq(miblen, 4, "");
148
149 /* Valid case. */
150 assert_d_eq(ctl_mibnametomib(tsd, mib, 0, "arenas", &miblen), 0, "");
151 assert_zu_eq(miblen, 1, "");
152 miblen = 4;
153 assert_d_eq(ctl_mibnametomib(tsd, mib, 1, "bin", &miblen), 0, "");
154 assert_zu_eq(miblen, 2, "");
155 expect_d_eq(mallctlbymib(mib, miblen, &result, &len_result, NULL, 0),
156 ENOENT, "mallctlbymib() should fail on partial path");
157
158 /* Error cases. */
159 miblen = 4;
160 assert_d_eq(ctl_mibnametomib(tsd, mib, 2, "bob", &miblen), ENOENT, "");
161 assert_zu_eq(miblen, 4, "");
162 assert_d_eq(ctl_mibnametomib(tsd, mib, 2, "9999", &miblen), ENOENT, "");
163 assert_zu_eq(miblen, 4, "");
164
165 /* Valid case. */
166 assert_d_eq(ctl_mibnametomib(tsd, mib, 2, "0", &miblen), 0, "");
167 assert_zu_eq(miblen, 3, "");
168 expect_d_eq(mallctlbymib(mib, miblen, &result, &len_result, NULL, 0),
169 ENOENT, "mallctlbymib() should fail on partial path");
170
171 /* Error cases. */
172 miblen = 4;
173 assert_d_eq(ctl_mibnametomib(tsd, mib, 3, "bob", &miblen), ENOENT, "");
174 assert_zu_eq(miblen, 4, "");
175 assert_d_eq(ctl_mibnametomib(tsd, mib, 3, "9999", &miblen), ENOENT, "");
176 assert_zu_eq(miblen, 4, "");
177
178 /* Valid case. */
179 assert_d_eq(ctl_mibnametomib(tsd, mib, 3, "nregs", &miblen), 0, "");
180 assert_zu_eq(miblen, 4, "");
181 assert_d_eq(mallctlbymib(mib, miblen, &result, &len_result, NULL, 0),
182 0, "Unexpected mallctlbymib() failure");
183 assert_d_eq(mallctl("arenas.bin.0.nregs", &result_ref, &len_result,
184 NULL, 0), 0, "Unexpected mallctl() failure");
185 expect_zu_eq(result, result_ref,
186 "mallctlbymib() and mallctl() returned different result");
187 }
188 TEST_END
189
190 TEST_BEGIN(test_mallctlbymibname) {
191 size_t mib[4];
192 size_t miblen = 4;
193 uint32_t result, result_ref;
194 size_t len_result = sizeof(uint32_t);
195
196 tsd_t *tsd = tsd_fetch();
197
198 /* Error cases. */
199
200 assert_d_eq(mallctlnametomib("arenas", mib, &miblen), 0,
201 "Unexpected mallctlnametomib() failure");
202 assert_zu_eq(miblen, 1, "");
203
204 miblen = 4;
205 assert_d_eq(ctl_bymibname(tsd, mib, 1, "bin.0", &miblen,
206 &result, &len_result, NULL, 0), ENOENT, "");
207 miblen = 4;
208 assert_d_eq(ctl_bymibname(tsd, mib, 1, "bin.0.bob", &miblen,
209 &result, &len_result, NULL, 0), ENOENT, "");
210 assert_zu_eq(miblen, 4, "");
211
212 /* Valid cases. */
213
214 assert_d_eq(mallctl("arenas.bin.0.nregs", &result_ref, &len_result,
215 NULL, 0), 0, "Unexpected mallctl() failure");
216 miblen = 4;
217
218 assert_d_eq(ctl_bymibname(tsd, mib, 0, "arenas.bin.0.nregs", &miblen,
219 &result, &len_result, NULL, 0), 0, "");
220 assert_zu_eq(miblen, 4, "");
221 expect_zu_eq(result, result_ref, "Unexpected result");
222
223 assert_d_eq(ctl_bymibname(tsd, mib, 1, "bin.0.nregs", &miblen, &result,
224 &len_result, NULL, 0), 0, "");
225 assert_zu_eq(miblen, 4, "");
226 expect_zu_eq(result, result_ref, "Unexpected result");
227
228 assert_d_eq(ctl_bymibname(tsd, mib, 2, "0.nregs", &miblen, &result,
229 &len_result, NULL, 0), 0, "");
230 assert_zu_eq(miblen, 4, "");
231 expect_zu_eq(result, result_ref, "Unexpected result");
232
233 assert_d_eq(ctl_bymibname(tsd, mib, 3, "nregs", &miblen, &result,
234 &len_result, NULL, 0), 0, "");
235 assert_zu_eq(miblen, 4, "");
236 expect_zu_eq(result, result_ref, "Unexpected result");
237 }
238 TEST_END
239
240 TEST_BEGIN(test_mallctl_config) {
241 #define TEST_MALLCTL_CONFIG(config, t) do { \
242 t oldval; \
243 size_t sz = sizeof(oldval); \
244 expect_d_eq(mallctl("config."#config, (void *)&oldval, &sz, \
245 NULL, 0), 0, "Unexpected mallctl() failure"); \
246 expect_b_eq(oldval, config_##config, "Incorrect config value"); \
247 expect_zu_eq(sz, sizeof(oldval), "Unexpected output size"); \
248 } while (0)
249
250 TEST_MALLCTL_CONFIG(cache_oblivious, bool);
251 TEST_MALLCTL_CONFIG(debug, bool);
252 TEST_MALLCTL_CONFIG(fill, bool);
253 TEST_MALLCTL_CONFIG(lazy_lock, bool);
254 TEST_MALLCTL_CONFIG(malloc_conf, const char *);
255 TEST_MALLCTL_CONFIG(prof, bool);
256 TEST_MALLCTL_CONFIG(prof_libgcc, bool);
257 TEST_MALLCTL_CONFIG(prof_libunwind, bool);
258 TEST_MALLCTL_CONFIG(stats, bool);
259 TEST_MALLCTL_CONFIG(utrace, bool);
260 TEST_MALLCTL_CONFIG(xmalloc, bool);
261
262 #undef TEST_MALLCTL_CONFIG
263 }
264 TEST_END
265
266 TEST_BEGIN(test_mallctl_opt) {
267 bool config_always = true;
268
269 #define TEST_MALLCTL_OPT(t, opt, config) do { \
270 t oldval; \
271 size_t sz = sizeof(oldval); \
272 int expected = config_##config ? 0 : ENOENT; \
273 int result = mallctl("opt."#opt, (void *)&oldval, &sz, NULL, \
274 0); \
275 expect_d_eq(result, expected, \
276 "Unexpected mallctl() result for opt."#opt); \
277 expect_zu_eq(sz, sizeof(oldval), "Unexpected output size"); \
278 } while (0)
279
280 TEST_MALLCTL_OPT(bool, abort, always);
281 TEST_MALLCTL_OPT(bool, abort_conf, always);
282 TEST_MALLCTL_OPT(bool, cache_oblivious, always);
283 TEST_MALLCTL_OPT(bool, trust_madvise, always);
284 TEST_MALLCTL_OPT(bool, confirm_conf, always);
285 TEST_MALLCTL_OPT(const char *, metadata_thp, always);
286 TEST_MALLCTL_OPT(bool, retain, always);
287 TEST_MALLCTL_OPT(const char *, dss, always);
288 TEST_MALLCTL_OPT(bool, hpa, always);
289 TEST_MALLCTL_OPT(size_t, hpa_slab_max_alloc, always);
290 TEST_MALLCTL_OPT(size_t, hpa_sec_nshards, always);
291 TEST_MALLCTL_OPT(size_t, hpa_sec_max_alloc, always);
292 TEST_MALLCTL_OPT(size_t, hpa_sec_max_bytes, always);
293 TEST_MALLCTL_OPT(size_t, hpa_sec_bytes_after_flush, always);
294 TEST_MALLCTL_OPT(size_t, hpa_sec_batch_fill_extra, always);
295 TEST_MALLCTL_OPT(unsigned, narenas, always);
296 TEST_MALLCTL_OPT(const char *, percpu_arena, always);
297 TEST_MALLCTL_OPT(size_t, oversize_threshold, always);
298 TEST_MALLCTL_OPT(bool, background_thread, always);
299 TEST_MALLCTL_OPT(ssize_t, dirty_decay_ms, always);
300 TEST_MALLCTL_OPT(ssize_t, muzzy_decay_ms, always);
301 TEST_MALLCTL_OPT(bool, stats_print, always);
302 TEST_MALLCTL_OPT(const char *, stats_print_opts, always);
303 TEST_MALLCTL_OPT(int64_t, stats_interval, always);
304 TEST_MALLCTL_OPT(const char *, stats_interval_opts, always);
305 TEST_MALLCTL_OPT(const char *, junk, fill);
306 TEST_MALLCTL_OPT(bool, zero, fill);
307 TEST_MALLCTL_OPT(bool, utrace, utrace);
308 TEST_MALLCTL_OPT(bool, xmalloc, xmalloc);
309 TEST_MALLCTL_OPT(bool, tcache, always);
310 TEST_MALLCTL_OPT(size_t, lg_extent_max_active_fit, always);
311 TEST_MALLCTL_OPT(size_t, tcache_max, always);
312 TEST_MALLCTL_OPT(const char *, thp, always);
313 TEST_MALLCTL_OPT(const char *, zero_realloc, always);
314 TEST_MALLCTL_OPT(bool, prof, prof);
315 TEST_MALLCTL_OPT(const char *, prof_prefix, prof);
316 TEST_MALLCTL_OPT(bool, prof_active, prof);
317 TEST_MALLCTL_OPT(ssize_t, lg_prof_sample, prof);
318 TEST_MALLCTL_OPT(bool, prof_accum, prof);
319 TEST_MALLCTL_OPT(ssize_t, lg_prof_interval, prof);
320 TEST_MALLCTL_OPT(bool, prof_gdump, prof);
321 TEST_MALLCTL_OPT(bool, prof_final, prof);
322 TEST_MALLCTL_OPT(bool, prof_leak, prof);
323 TEST_MALLCTL_OPT(bool, prof_leak_error, prof);
324 TEST_MALLCTL_OPT(ssize_t, prof_recent_alloc_max, prof);
325 TEST_MALLCTL_OPT(bool, prof_stats, prof);
326 TEST_MALLCTL_OPT(bool, prof_sys_thread_name, prof);
327 TEST_MALLCTL_OPT(ssize_t, lg_san_uaf_align, uaf_detection);
328
329 #undef TEST_MALLCTL_OPT
330 }
331 TEST_END
332
333 TEST_BEGIN(test_manpage_example) {
334 unsigned nbins, i;
335 size_t mib[4];
336 size_t len, miblen;
337
338 len = sizeof(nbins);
339 expect_d_eq(mallctl("arenas.nbins", (void *)&nbins, &len, NULL, 0), 0,
340 "Unexpected mallctl() failure");
341
342 miblen = 4;
343 expect_d_eq(mallctlnametomib("arenas.bin.0.size", mib, &miblen), 0,
344 "Unexpected mallctlnametomib() failure");
345 for (i = 0; i < nbins; i++) {
346 size_t bin_size;
347
348 mib[2] = i;
349 len = sizeof(bin_size);
350 expect_d_eq(mallctlbymib(mib, miblen, (void *)&bin_size, &len,
351 NULL, 0), 0, "Unexpected mallctlbymib() failure");
352 /* Do something with bin_size... */
353 }
354 }
355 TEST_END
356
357 TEST_BEGIN(test_tcache_none) {
358 test_skip_if(!opt_tcache);
359
360 /* Allocate p and q. */
361 void *p0 = mallocx(42, 0);
362 expect_ptr_not_null(p0, "Unexpected mallocx() failure");
363 void *q = mallocx(42, 0);
364 expect_ptr_not_null(q, "Unexpected mallocx() failure");
365
366 /* Deallocate p and q, but bypass the tcache for q. */
367 dallocx(p0, 0);
368 dallocx(q, MALLOCX_TCACHE_NONE);
369
370 /* Make sure that tcache-based allocation returns p, not q. */
371 void *p1 = mallocx(42, 0);
372 expect_ptr_not_null(p1, "Unexpected mallocx() failure");
373 if (!opt_prof && !san_uaf_detection_enabled()) {
374 expect_ptr_eq(p0, p1,
375 "Expected tcache to allocate cached region");
376 }
377
378 /* Clean up. */
379 dallocx(p1, MALLOCX_TCACHE_NONE);
380 }
381 TEST_END
382
383 TEST_BEGIN(test_tcache) {
384 #define NTCACHES 10
385 unsigned tis[NTCACHES];
386 void *ps[NTCACHES];
387 void *qs[NTCACHES];
388 unsigned i;
389 size_t sz, psz, qsz;
390
391 psz = 42;
392 qsz = nallocx(psz, 0) + 1;
393
394 /* Create tcaches. */
395 for (i = 0; i < NTCACHES; i++) {
396 sz = sizeof(unsigned);
397 expect_d_eq(mallctl("tcache.create", (void *)&tis[i], &sz, NULL,
398 0), 0, "Unexpected mallctl() failure, i=%u", i);
399 }
400
401 /* Exercise tcache ID recycling. */
402 for (i = 0; i < NTCACHES; i++) {
403 expect_d_eq(mallctl("tcache.destroy", NULL, NULL,
404 (void *)&tis[i], sizeof(unsigned)), 0,
405 "Unexpected mallctl() failure, i=%u", i);
406 }
407 for (i = 0; i < NTCACHES; i++) {
408 sz = sizeof(unsigned);
409 expect_d_eq(mallctl("tcache.create", (void *)&tis[i], &sz, NULL,
410 0), 0, "Unexpected mallctl() failure, i=%u", i);
411 }
412
413 /* Flush empty tcaches. */
414 for (i = 0; i < NTCACHES; i++) {
415 expect_d_eq(mallctl("tcache.flush", NULL, NULL, (void *)&tis[i],
416 sizeof(unsigned)), 0, "Unexpected mallctl() failure, i=%u",
417 i);
418 }
419
420 /* Cache some allocations. */
421 for (i = 0; i < NTCACHES; i++) {
422 ps[i] = mallocx(psz, MALLOCX_TCACHE(tis[i]));
423 expect_ptr_not_null(ps[i], "Unexpected mallocx() failure, i=%u",
424 i);
425 dallocx(ps[i], MALLOCX_TCACHE(tis[i]));
426
427 qs[i] = mallocx(qsz, MALLOCX_TCACHE(tis[i]));
428 expect_ptr_not_null(qs[i], "Unexpected mallocx() failure, i=%u",
429 i);
430 dallocx(qs[i], MALLOCX_TCACHE(tis[i]));
431 }
432
433 /* Verify that tcaches allocate cached regions. */
434 for (i = 0; i < NTCACHES; i++) {
435 void *p0 = ps[i];
436 ps[i] = mallocx(psz, MALLOCX_TCACHE(tis[i]));
437 expect_ptr_not_null(ps[i], "Unexpected mallocx() failure, i=%u",
438 i);
439 if (!san_uaf_detection_enabled()) {
440 expect_ptr_eq(ps[i], p0, "Expected mallocx() to "
441 "allocate cached region, i=%u", i);
442 }
443 }
444
445 /* Verify that reallocation uses cached regions. */
446 for (i = 0; i < NTCACHES; i++) {
447 void *q0 = qs[i];
448 qs[i] = rallocx(ps[i], qsz, MALLOCX_TCACHE(tis[i]));
449 expect_ptr_not_null(qs[i], "Unexpected rallocx() failure, i=%u",
450 i);
451 if (!san_uaf_detection_enabled()) {
452 expect_ptr_eq(qs[i], q0, "Expected rallocx() to "
453 "allocate cached region, i=%u", i);
454 }
455 /* Avoid undefined behavior in case of test failure. */
456 if (qs[i] == NULL) {
457 qs[i] = ps[i];
458 }
459 }
460 for (i = 0; i < NTCACHES; i++) {
461 dallocx(qs[i], MALLOCX_TCACHE(tis[i]));
462 }
463
464 /* Flush some non-empty tcaches. */
465 for (i = 0; i < NTCACHES/2; i++) {
466 expect_d_eq(mallctl("tcache.flush", NULL, NULL, (void *)&tis[i],
467 sizeof(unsigned)), 0, "Unexpected mallctl() failure, i=%u",
468 i);
469 }
470
471 /* Destroy tcaches. */
472 for (i = 0; i < NTCACHES; i++) {
473 expect_d_eq(mallctl("tcache.destroy", NULL, NULL,
474 (void *)&tis[i], sizeof(unsigned)), 0,
475 "Unexpected mallctl() failure, i=%u", i);
476 }
477 }
478 TEST_END
479
480 TEST_BEGIN(test_thread_arena) {
481 unsigned old_arena_ind, new_arena_ind, narenas;
482
483 const char *opa;
484 size_t sz = sizeof(opa);
485 expect_d_eq(mallctl("opt.percpu_arena", (void *)&opa, &sz, NULL, 0), 0,
486 "Unexpected mallctl() failure");
487
488 sz = sizeof(unsigned);
489 expect_d_eq(mallctl("arenas.narenas", (void *)&narenas, &sz, NULL, 0),
490 0, "Unexpected mallctl() failure");
491 if (opt_oversize_threshold != 0) {
492 narenas--;
493 }
494 expect_u_eq(narenas, opt_narenas, "Number of arenas incorrect");
495
496 if (strcmp(opa, "disabled") == 0) {
497 new_arena_ind = narenas - 1;
498 expect_d_eq(mallctl("thread.arena", (void *)&old_arena_ind, &sz,
499 (void *)&new_arena_ind, sizeof(unsigned)), 0,
500 "Unexpected mallctl() failure");
501 new_arena_ind = 0;
502 expect_d_eq(mallctl("thread.arena", (void *)&old_arena_ind, &sz,
503 (void *)&new_arena_ind, sizeof(unsigned)), 0,
504 "Unexpected mallctl() failure");
505 } else {
506 expect_d_eq(mallctl("thread.arena", (void *)&old_arena_ind, &sz,
507 NULL, 0), 0, "Unexpected mallctl() failure");
508 new_arena_ind = percpu_arena_ind_limit(opt_percpu_arena) - 1;
509 if (old_arena_ind != new_arena_ind) {
510 expect_d_eq(mallctl("thread.arena",
511 (void *)&old_arena_ind, &sz, (void *)&new_arena_ind,
512 sizeof(unsigned)), EPERM, "thread.arena ctl "
513 "should not be allowed with percpu arena");
514 }
515 }
516 }
517 TEST_END
518
519 TEST_BEGIN(test_arena_i_initialized) {
520 unsigned narenas, i;
521 size_t sz;
522 size_t mib[3];
523 size_t miblen = sizeof(mib) / sizeof(size_t);
524 bool initialized;
525
526 sz = sizeof(narenas);
527 expect_d_eq(mallctl("arenas.narenas", (void *)&narenas, &sz, NULL, 0),
528 0, "Unexpected mallctl() failure");
529
530 expect_d_eq(mallctlnametomib("arena.0.initialized", mib, &miblen), 0,
531 "Unexpected mallctlnametomib() failure");
532 for (i = 0; i < narenas; i++) {
533 mib[1] = i;
534 sz = sizeof(initialized);
535 expect_d_eq(mallctlbymib(mib, miblen, &initialized, &sz, NULL,
536 0), 0, "Unexpected mallctl() failure");
537 }
538
539 mib[1] = MALLCTL_ARENAS_ALL;
540 sz = sizeof(initialized);
541 expect_d_eq(mallctlbymib(mib, miblen, &initialized, &sz, NULL, 0), 0,
542 "Unexpected mallctl() failure");
543 expect_true(initialized,
544 "Merged arena statistics should always be initialized");
545
546 /* Equivalent to the above but using mallctl() directly. */
547 sz = sizeof(initialized);
548 expect_d_eq(mallctl(
549 "arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".initialized",
550 (void *)&initialized, &sz, NULL, 0), 0,
551 "Unexpected mallctl() failure");
552 expect_true(initialized,
553 "Merged arena statistics should always be initialized");
554 }
555 TEST_END
556
557 TEST_BEGIN(test_arena_i_dirty_decay_ms) {
558 ssize_t dirty_decay_ms, orig_dirty_decay_ms, prev_dirty_decay_ms;
559 size_t sz = sizeof(ssize_t);
560
561 expect_d_eq(mallctl("arena.0.dirty_decay_ms",
562 (void *)&orig_dirty_decay_ms, &sz, NULL, 0), 0,
563 "Unexpected mallctl() failure");
564
565 dirty_decay_ms = -2;
566 expect_d_eq(mallctl("arena.0.dirty_decay_ms", NULL, NULL,
567 (void *)&dirty_decay_ms, sizeof(ssize_t)), EFAULT,
568 "Unexpected mallctl() success");
569
570 dirty_decay_ms = 0x7fffffff;
571 expect_d_eq(mallctl("arena.0.dirty_decay_ms", NULL, NULL,
572 (void *)&dirty_decay_ms, sizeof(ssize_t)), 0,
573 "Unexpected mallctl() failure");
574
575 for (prev_dirty_decay_ms = dirty_decay_ms, dirty_decay_ms = -1;
576 dirty_decay_ms < 20; prev_dirty_decay_ms = dirty_decay_ms,
577 dirty_decay_ms++) {
578 ssize_t old_dirty_decay_ms;
579
580 expect_d_eq(mallctl("arena.0.dirty_decay_ms",
581 (void *)&old_dirty_decay_ms, &sz, (void *)&dirty_decay_ms,
582 sizeof(ssize_t)), 0, "Unexpected mallctl() failure");
583 expect_zd_eq(old_dirty_decay_ms, prev_dirty_decay_ms,
584 "Unexpected old arena.0.dirty_decay_ms");
585 }
586 }
587 TEST_END
588
589 TEST_BEGIN(test_arena_i_muzzy_decay_ms) {
590 ssize_t muzzy_decay_ms, orig_muzzy_decay_ms, prev_muzzy_decay_ms;
591 size_t sz = sizeof(ssize_t);
592
593 expect_d_eq(mallctl("arena.0.muzzy_decay_ms",
594 (void *)&orig_muzzy_decay_ms, &sz, NULL, 0), 0,
595 "Unexpected mallctl() failure");
596
597 muzzy_decay_ms = -2;
598 expect_d_eq(mallctl("arena.0.muzzy_decay_ms", NULL, NULL,
599 (void *)&muzzy_decay_ms, sizeof(ssize_t)), EFAULT,
600 "Unexpected mallctl() success");
601
602 muzzy_decay_ms = 0x7fffffff;
603 expect_d_eq(mallctl("arena.0.muzzy_decay_ms", NULL, NULL,
604 (void *)&muzzy_decay_ms, sizeof(ssize_t)), 0,
605 "Unexpected mallctl() failure");
606
607 for (prev_muzzy_decay_ms = muzzy_decay_ms, muzzy_decay_ms = -1;
608 muzzy_decay_ms < 20; prev_muzzy_decay_ms = muzzy_decay_ms,
609 muzzy_decay_ms++) {
610 ssize_t old_muzzy_decay_ms;
611
612 expect_d_eq(mallctl("arena.0.muzzy_decay_ms",
613 (void *)&old_muzzy_decay_ms, &sz, (void *)&muzzy_decay_ms,
614 sizeof(ssize_t)), 0, "Unexpected mallctl() failure");
615 expect_zd_eq(old_muzzy_decay_ms, prev_muzzy_decay_ms,
616 "Unexpected old arena.0.muzzy_decay_ms");
617 }
618 }
619 TEST_END
620
621 TEST_BEGIN(test_arena_i_purge) {
622 unsigned narenas;
623 size_t sz = sizeof(unsigned);
624 size_t mib[3];
625 size_t miblen = 3;
626
627 expect_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
628 "Unexpected mallctl() failure");
629
630 expect_d_eq(mallctl("arenas.narenas", (void *)&narenas, &sz, NULL, 0),
631 0, "Unexpected mallctl() failure");
632 expect_d_eq(mallctlnametomib("arena.0.purge", mib, &miblen), 0,
633 "Unexpected mallctlnametomib() failure");
634 mib[1] = narenas;
635 expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
636 "Unexpected mallctlbymib() failure");
637
638 mib[1] = MALLCTL_ARENAS_ALL;
639 expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
640 "Unexpected mallctlbymib() failure");
641 }
642 TEST_END
643
644 TEST_BEGIN(test_arena_i_decay) {
645 unsigned narenas;
646 size_t sz = sizeof(unsigned);
647 size_t mib[3];
648 size_t miblen = 3;
649
650 expect_d_eq(mallctl("arena.0.decay", NULL, NULL, NULL, 0), 0,
651 "Unexpected mallctl() failure");
652
653 expect_d_eq(mallctl("arenas.narenas", (void *)&narenas, &sz, NULL, 0),
654 0, "Unexpected mallctl() failure");
655 expect_d_eq(mallctlnametomib("arena.0.decay", mib, &miblen), 0,
656 "Unexpected mallctlnametomib() failure");
657 mib[1] = narenas;
658 expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
659 "Unexpected mallctlbymib() failure");
660
661 mib[1] = MALLCTL_ARENAS_ALL;
662 expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
663 "Unexpected mallctlbymib() failure");
664 }
665 TEST_END
666
667 TEST_BEGIN(test_arena_i_dss) {
668 const char *dss_prec_old, *dss_prec_new;
669 size_t sz = sizeof(dss_prec_old);
670 size_t mib[3];
671 size_t miblen;
672
673 miblen = sizeof(mib)/sizeof(size_t);
674 expect_d_eq(mallctlnametomib("arena.0.dss", mib, &miblen), 0,
675 "Unexpected mallctlnametomib() error");
676
677 dss_prec_new = "disabled";
678 expect_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_old, &sz,
679 (void *)&dss_prec_new, sizeof(dss_prec_new)), 0,
680 "Unexpected mallctl() failure");
681 expect_str_ne(dss_prec_old, "primary",
682 "Unexpected default for dss precedence");
683
684 expect_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_new, &sz,
685 (void *)&dss_prec_old, sizeof(dss_prec_old)), 0,
686 "Unexpected mallctl() failure");
687
688 expect_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_old, &sz, NULL,
689 0), 0, "Unexpected mallctl() failure");
690 expect_str_ne(dss_prec_old, "primary",
691 "Unexpected value for dss precedence");
692
693 mib[1] = narenas_total_get();
694 dss_prec_new = "disabled";
695 expect_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_old, &sz,
696 (void *)&dss_prec_new, sizeof(dss_prec_new)), 0,
697 "Unexpected mallctl() failure");
698 expect_str_ne(dss_prec_old, "primary",
699 "Unexpected default for dss precedence");
700
701 expect_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_new, &sz,
702 (void *)&dss_prec_old, sizeof(dss_prec_new)), 0,
703 "Unexpected mallctl() failure");
704
705 expect_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_old, &sz, NULL,
706 0), 0, "Unexpected mallctl() failure");
707 expect_str_ne(dss_prec_old, "primary",
708 "Unexpected value for dss precedence");
709 }
710 TEST_END
711
712 TEST_BEGIN(test_arena_i_retain_grow_limit) {
713 size_t old_limit, new_limit, default_limit;
714 size_t mib[3];
715 size_t miblen;
716
717 bool retain_enabled;
718 size_t sz = sizeof(retain_enabled);
719 expect_d_eq(mallctl("opt.retain", &retain_enabled, &sz, NULL, 0),
720 0, "Unexpected mallctl() failure");
721 test_skip_if(!retain_enabled);
722
723 sz = sizeof(default_limit);
724 miblen = sizeof(mib)/sizeof(size_t);
725 expect_d_eq(mallctlnametomib("arena.0.retain_grow_limit", mib, &miblen),
726 0, "Unexpected mallctlnametomib() error");
727
728 expect_d_eq(mallctlbymib(mib, miblen, &default_limit, &sz, NULL, 0), 0,
729 "Unexpected mallctl() failure");
730 expect_zu_eq(default_limit, SC_LARGE_MAXCLASS,
731 "Unexpected default for retain_grow_limit");
732
733 new_limit = PAGE - 1;
734 expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, &new_limit,
735 sizeof(new_limit)), EFAULT, "Unexpected mallctl() success");
736
737 new_limit = PAGE + 1;
738 expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, &new_limit,
739 sizeof(new_limit)), 0, "Unexpected mallctl() failure");
740 expect_d_eq(mallctlbymib(mib, miblen, &old_limit, &sz, NULL, 0), 0,
741 "Unexpected mallctl() failure");
742 expect_zu_eq(old_limit, PAGE,
743 "Unexpected value for retain_grow_limit");
744
745 /* Expect grow less than psize class 10. */
746 new_limit = sz_pind2sz(10) - 1;
747 expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, &new_limit,
748 sizeof(new_limit)), 0, "Unexpected mallctl() failure");
749 expect_d_eq(mallctlbymib(mib, miblen, &old_limit, &sz, NULL, 0), 0,
750 "Unexpected mallctl() failure");
751 expect_zu_eq(old_limit, sz_pind2sz(9),
752 "Unexpected value for retain_grow_limit");
753
754 /* Restore to default. */
755 expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, &default_limit,
756 sizeof(default_limit)), 0, "Unexpected mallctl() failure");
757 }
758 TEST_END
759
760 TEST_BEGIN(test_arenas_dirty_decay_ms) {
761 ssize_t dirty_decay_ms, orig_dirty_decay_ms, prev_dirty_decay_ms;
762 size_t sz = sizeof(ssize_t);
763
764 expect_d_eq(mallctl("arenas.dirty_decay_ms",
765 (void *)&orig_dirty_decay_ms, &sz, NULL, 0), 0,
766 "Unexpected mallctl() failure");
767
768 dirty_decay_ms = -2;
769 expect_d_eq(mallctl("arenas.dirty_decay_ms", NULL, NULL,
770 (void *)&dirty_decay_ms, sizeof(ssize_t)), EFAULT,
771 "Unexpected mallctl() success");
772
773 dirty_decay_ms = 0x7fffffff;
774 expect_d_eq(mallctl("arenas.dirty_decay_ms", NULL, NULL,
775 (void *)&dirty_decay_ms, sizeof(ssize_t)), 0,
776 "Expected mallctl() failure");
777
778 for (prev_dirty_decay_ms = dirty_decay_ms, dirty_decay_ms = -1;
779 dirty_decay_ms < 20; prev_dirty_decay_ms = dirty_decay_ms,
780 dirty_decay_ms++) {
781 ssize_t old_dirty_decay_ms;
782
783 expect_d_eq(mallctl("arenas.dirty_decay_ms",
784 (void *)&old_dirty_decay_ms, &sz, (void *)&dirty_decay_ms,
785 sizeof(ssize_t)), 0, "Unexpected mallctl() failure");
786 expect_zd_eq(old_dirty_decay_ms, prev_dirty_decay_ms,
787 "Unexpected old arenas.dirty_decay_ms");
788 }
789 }
790 TEST_END
791
792 TEST_BEGIN(test_arenas_muzzy_decay_ms) {
793 ssize_t muzzy_decay_ms, orig_muzzy_decay_ms, prev_muzzy_decay_ms;
794 size_t sz = sizeof(ssize_t);
795
796 expect_d_eq(mallctl("arenas.muzzy_decay_ms",
797 (void *)&orig_muzzy_decay_ms, &sz, NULL, 0), 0,
798 "Unexpected mallctl() failure");
799
800 muzzy_decay_ms = -2;
801 expect_d_eq(mallctl("arenas.muzzy_decay_ms", NULL, NULL,
802 (void *)&muzzy_decay_ms, sizeof(ssize_t)), EFAULT,
803 "Unexpected mallctl() success");
804
805 muzzy_decay_ms = 0x7fffffff;
806 expect_d_eq(mallctl("arenas.muzzy_decay_ms", NULL, NULL,
807 (void *)&muzzy_decay_ms, sizeof(ssize_t)), 0,
808 "Expected mallctl() failure");
809
810 for (prev_muzzy_decay_ms = muzzy_decay_ms, muzzy_decay_ms = -1;
811 muzzy_decay_ms < 20; prev_muzzy_decay_ms = muzzy_decay_ms,
812 muzzy_decay_ms++) {
813 ssize_t old_muzzy_decay_ms;
814
815 expect_d_eq(mallctl("arenas.muzzy_decay_ms",
816 (void *)&old_muzzy_decay_ms, &sz, (void *)&muzzy_decay_ms,
817 sizeof(ssize_t)), 0, "Unexpected mallctl() failure");
818 expect_zd_eq(old_muzzy_decay_ms, prev_muzzy_decay_ms,
819 "Unexpected old arenas.muzzy_decay_ms");
820 }
821 }
822 TEST_END
823
824 TEST_BEGIN(test_arenas_constants) {
825 #define TEST_ARENAS_CONSTANT(t, name, expected) do { \
826 t name; \
827 size_t sz = sizeof(t); \
828 expect_d_eq(mallctl("arenas."#name, (void *)&name, &sz, NULL, \
829 0), 0, "Unexpected mallctl() failure"); \
830 expect_zu_eq(name, expected, "Incorrect "#name" size"); \
831 } while (0)
832
833 TEST_ARENAS_CONSTANT(size_t, quantum, QUANTUM);
834 TEST_ARENAS_CONSTANT(size_t, page, PAGE);
835 TEST_ARENAS_CONSTANT(unsigned, nbins, SC_NBINS);
836 TEST_ARENAS_CONSTANT(unsigned, nlextents, SC_NSIZES - SC_NBINS);
837
838 #undef TEST_ARENAS_CONSTANT
839 }
840 TEST_END
841
842 TEST_BEGIN(test_arenas_bin_constants) {
843 #define TEST_ARENAS_BIN_CONSTANT(t, name, expected) do { \
844 t name; \
845 size_t sz = sizeof(t); \
846 expect_d_eq(mallctl("arenas.bin.0."#name, (void *)&name, &sz, \
847 NULL, 0), 0, "Unexpected mallctl() failure"); \
848 expect_zu_eq(name, expected, "Incorrect "#name" size"); \
849 } while (0)
850
851 TEST_ARENAS_BIN_CONSTANT(size_t, size, bin_infos[0].reg_size);
852 TEST_ARENAS_BIN_CONSTANT(uint32_t, nregs, bin_infos[0].nregs);
853 TEST_ARENAS_BIN_CONSTANT(size_t, slab_size,
854 bin_infos[0].slab_size);
855 TEST_ARENAS_BIN_CONSTANT(uint32_t, nshards, bin_infos[0].n_shards);
856
857 #undef TEST_ARENAS_BIN_CONSTANT
858 }
859 TEST_END
860
861 TEST_BEGIN(test_arenas_lextent_constants) {
862 #define TEST_ARENAS_LEXTENT_CONSTANT(t, name, expected) do { \
863 t name; \
864 size_t sz = sizeof(t); \
865 expect_d_eq(mallctl("arenas.lextent.0."#name, (void *)&name, \
866 &sz, NULL, 0), 0, "Unexpected mallctl() failure"); \
867 expect_zu_eq(name, expected, "Incorrect "#name" size"); \
868 } while (0)
869
870 TEST_ARENAS_LEXTENT_CONSTANT(size_t, size,
871 SC_LARGE_MINCLASS);
872
873 #undef TEST_ARENAS_LEXTENT_CONSTANT
874 }
875 TEST_END
876
877 TEST_BEGIN(test_arenas_create) {
878 unsigned narenas_before, arena, narenas_after;
879 size_t sz = sizeof(unsigned);
880
881 expect_d_eq(mallctl("arenas.narenas", (void *)&narenas_before, &sz,
882 NULL, 0), 0, "Unexpected mallctl() failure");
883 expect_d_eq(mallctl("arenas.create", (void *)&arena, &sz, NULL, 0), 0,
884 "Unexpected mallctl() failure");
885 expect_d_eq(mallctl("arenas.narenas", (void *)&narenas_after, &sz, NULL,
886 0), 0, "Unexpected mallctl() failure");
887
888 expect_u_eq(narenas_before+1, narenas_after,
889 "Unexpected number of arenas before versus after extension");
890 expect_u_eq(arena, narenas_after-1, "Unexpected arena index");
891 }
892 TEST_END
893
894 TEST_BEGIN(test_arenas_lookup) {
895 unsigned arena, arena1;
896 void *ptr;
897 size_t sz = sizeof(unsigned);
898
899 expect_d_eq(mallctl("arenas.create", (void *)&arena, &sz, NULL, 0), 0,
900 "Unexpected mallctl() failure");
901 ptr = mallocx(42, MALLOCX_ARENA(arena) | MALLOCX_TCACHE_NONE);
902 expect_ptr_not_null(ptr, "Unexpected mallocx() failure");
903 expect_d_eq(mallctl("arenas.lookup", &arena1, &sz, &ptr, sizeof(ptr)),
904 0, "Unexpected mallctl() failure");
905 expect_u_eq(arena, arena1, "Unexpected arena index");
906 dallocx(ptr, 0);
907 }
908 TEST_END
909
910 TEST_BEGIN(test_prof_active) {
911 /*
912 * If config_prof is off, then the test for prof_active in
913 * test_mallctl_opt was already enough.
914 */
915 test_skip_if(!config_prof);
916 test_skip_if(opt_prof);
917
918 bool active, old;
919 size_t len = sizeof(bool);
920
921 active = true;
922 expect_d_eq(mallctl("prof.active", NULL, NULL, &active, len), ENOENT,
923 "Setting prof_active to true should fail when opt_prof is off");
924 old = true;
925 expect_d_eq(mallctl("prof.active", &old, &len, &active, len), ENOENT,
926 "Setting prof_active to true should fail when opt_prof is off");
927 expect_true(old, "old value should not be touched when mallctl fails");
928 active = false;
929 expect_d_eq(mallctl("prof.active", NULL, NULL, &active, len), 0,
930 "Setting prof_active to false should succeed when opt_prof is off");
931 expect_d_eq(mallctl("prof.active", &old, &len, &active, len), 0,
932 "Setting prof_active to false should succeed when opt_prof is off");
933 expect_false(old, "prof_active should be false when opt_prof is off");
934 }
935 TEST_END
936
937 TEST_BEGIN(test_stats_arenas) {
938 #define TEST_STATS_ARENAS(t, name) do { \
939 t name; \
940 size_t sz = sizeof(t); \
941 expect_d_eq(mallctl("stats.arenas.0."#name, (void *)&name, &sz, \
942 NULL, 0), 0, "Unexpected mallctl() failure"); \
943 } while (0)
944
945 TEST_STATS_ARENAS(unsigned, nthreads);
946 TEST_STATS_ARENAS(const char *, dss);
947 TEST_STATS_ARENAS(ssize_t, dirty_decay_ms);
948 TEST_STATS_ARENAS(ssize_t, muzzy_decay_ms);
949 TEST_STATS_ARENAS(size_t, pactive);
950 TEST_STATS_ARENAS(size_t, pdirty);
951
952 #undef TEST_STATS_ARENAS
953 }
954 TEST_END
955
956 static void
957 alloc_hook(void *extra, UNUSED hook_alloc_t type, UNUSED void *result,
958 UNUSED uintptr_t result_raw, UNUSED uintptr_t args_raw[3]) {
959 *(bool *)extra = true;
960 }
961
962 static void
963 dalloc_hook(void *extra, UNUSED hook_dalloc_t type,
964 UNUSED void *address, UNUSED uintptr_t args_raw[3]) {
965 *(bool *)extra = true;
966 }
967
968 TEST_BEGIN(test_hooks) {
969 bool hook_called = false;
970 hooks_t hooks = {&alloc_hook, &dalloc_hook, NULL, &hook_called};
971 void *handle = NULL;
972 size_t sz = sizeof(handle);
973 int err = mallctl("experimental.hooks.install", &handle, &sz, &hooks,
974 sizeof(hooks));
975 expect_d_eq(err, 0, "Hook installation failed");
976 expect_ptr_ne(handle, NULL, "Hook installation gave null handle");
977 void *ptr = mallocx(1, 0);
978 expect_true(hook_called, "Alloc hook not called");
979 hook_called = false;
980 free(ptr);
981 expect_true(hook_called, "Free hook not called");
982
983 err = mallctl("experimental.hooks.remove", NULL, NULL, &handle,
984 sizeof(handle));
985 expect_d_eq(err, 0, "Hook removal failed");
986 hook_called = false;
987 ptr = mallocx(1, 0);
988 free(ptr);
989 expect_false(hook_called, "Hook called after removal");
990 }
991 TEST_END
992
993 TEST_BEGIN(test_hooks_exhaustion) {
994 bool hook_called = false;
995 hooks_t hooks = {&alloc_hook, &dalloc_hook, NULL, &hook_called};
996
997 void *handle;
998 void *handles[HOOK_MAX];
999 size_t sz = sizeof(handle);
1000 int err;
1001 for (int i = 0; i < HOOK_MAX; i++) {
1002 handle = NULL;
1003 err = mallctl("experimental.hooks.install", &handle, &sz,
1004 &hooks, sizeof(hooks));
1005 expect_d_eq(err, 0, "Error installation hooks");
1006 expect_ptr_ne(handle, NULL, "Got NULL handle");
1007 handles[i] = handle;
1008 }
1009 err = mallctl("experimental.hooks.install", &handle, &sz, &hooks,
1010 sizeof(hooks));
1011 expect_d_eq(err, EAGAIN, "Should have failed hook installation");
1012 for (int i = 0; i < HOOK_MAX; i++) {
1013 err = mallctl("experimental.hooks.remove", NULL, NULL,
1014 &handles[i], sizeof(handles[i]));
1015 expect_d_eq(err, 0, "Hook removal failed");
1016 }
1017 /* Insertion failed, but then we removed some; it should work now. */
1018 handle = NULL;
1019 err = mallctl("experimental.hooks.install", &handle, &sz, &hooks,
1020 sizeof(hooks));
1021 expect_d_eq(err, 0, "Hook insertion failed");
1022 expect_ptr_ne(handle, NULL, "Got NULL handle");
1023 err = mallctl("experimental.hooks.remove", NULL, NULL, &handle,
1024 sizeof(handle));
1025 expect_d_eq(err, 0, "Hook removal failed");
1026 }
1027 TEST_END
1028
1029 TEST_BEGIN(test_thread_idle) {
1030 /*
1031 * We're cheating a little bit in this test, and inferring things about
1032 * implementation internals (like tcache details). We have to;
1033 * thread.idle has no guaranteed effects. We need stats to make these
1034 * inferences.
1035 */
1036 test_skip_if(!config_stats);
1037
1038 int err;
1039 size_t sz;
1040 size_t miblen;
1041
1042 bool tcache_enabled = false;
1043 sz = sizeof(tcache_enabled);
1044 err = mallctl("thread.tcache.enabled", &tcache_enabled, &sz, NULL, 0);
1045 expect_d_eq(err, 0, "");
1046 test_skip_if(!tcache_enabled);
1047
1048 size_t tcache_max;
1049 sz = sizeof(tcache_max);
1050 err = mallctl("arenas.tcache_max", &tcache_max, &sz, NULL, 0);
1051 expect_d_eq(err, 0, "");
1052 test_skip_if(tcache_max == 0);
1053
1054 unsigned arena_ind;
1055 sz = sizeof(arena_ind);
1056 err = mallctl("thread.arena", &arena_ind, &sz, NULL, 0);
1057 expect_d_eq(err, 0, "");
1058
1059 /* We're going to do an allocation of size 1, which we know is small. */
1060 size_t mib[5];
1061 miblen = sizeof(mib)/sizeof(mib[0]);
1062 err = mallctlnametomib("stats.arenas.0.small.ndalloc", mib, &miblen);
1063 expect_d_eq(err, 0, "");
1064 mib[2] = arena_ind;
1065
1066 /*
1067 * This alloc and dalloc should leave something in the tcache, in a
1068 * small size's cache bin.
1069 */
1070 void *ptr = mallocx(1, 0);
1071 dallocx(ptr, 0);
1072
1073 uint64_t epoch;
1074 err = mallctl("epoch", NULL, NULL, &epoch, sizeof(epoch));
1075 expect_d_eq(err, 0, "");
1076
1077 uint64_t small_dalloc_pre_idle;
1078 sz = sizeof(small_dalloc_pre_idle);
1079 err = mallctlbymib(mib, miblen, &small_dalloc_pre_idle, &sz, NULL, 0);
1080 expect_d_eq(err, 0, "");
1081
1082 err = mallctl("thread.idle", NULL, NULL, NULL, 0);
1083 expect_d_eq(err, 0, "");
1084
1085 err = mallctl("epoch", NULL, NULL, &epoch, sizeof(epoch));
1086 expect_d_eq(err, 0, "");
1087
1088 uint64_t small_dalloc_post_idle;
1089 sz = sizeof(small_dalloc_post_idle);
1090 err = mallctlbymib(mib, miblen, &small_dalloc_post_idle, &sz, NULL, 0);
1091 expect_d_eq(err, 0, "");
1092
1093 expect_u64_lt(small_dalloc_pre_idle, small_dalloc_post_idle,
1094 "Purge didn't flush the tcache");
1095 }
1096 TEST_END
1097
1098 TEST_BEGIN(test_thread_peak) {
1099 test_skip_if(!config_stats);
1100
1101 /*
1102 * We don't commit to any stable amount of accuracy for peak tracking
1103 * (in practice, when this test was written, we made sure to be within
1104 * 100k). But 10MB is big for more or less any definition of big.
1105 */
1106 size_t big_size = 10 * 1024 * 1024;
1107 size_t small_size = 256;
1108
1109 void *ptr;
1110 int err;
1111 size_t sz;
1112 uint64_t peak;
1113 sz = sizeof(uint64_t);
1114
1115 err = mallctl("thread.peak.reset", NULL, NULL, NULL, 0);
1116 expect_d_eq(err, 0, "");
1117 ptr = mallocx(SC_SMALL_MAXCLASS, 0);
1118 err = mallctl("thread.peak.read", &peak, &sz, NULL, 0);
1119 expect_d_eq(err, 0, "");
1120 expect_u64_eq(peak, SC_SMALL_MAXCLASS, "Missed an update");
1121 free(ptr);
1122 err = mallctl("thread.peak.read", &peak, &sz, NULL, 0);
1123 expect_d_eq(err, 0, "");
1124 expect_u64_eq(peak, SC_SMALL_MAXCLASS, "Freeing changed peak");
1125 ptr = mallocx(big_size, 0);
1126 free(ptr);
1127 /*
1128 * The peak should have hit big_size in the last two lines, even though
1129 * the net allocated bytes has since dropped back down to zero. We
1130 * should have noticed the peak change without having down any mallctl
1131 * calls while net allocated bytes was high.
1132 */
1133 err = mallctl("thread.peak.read", &peak, &sz, NULL, 0);
1134 expect_d_eq(err, 0, "");
1135 expect_u64_ge(peak, big_size, "Missed a peak change.");
1136
1137 /* Allocate big_size, but using small allocations. */
1138 size_t nallocs = big_size / small_size;
1139 void **ptrs = calloc(nallocs, sizeof(void *));
1140 err = mallctl("thread.peak.reset", NULL, NULL, NULL, 0);
1141 expect_d_eq(err, 0, "");
1142 err = mallctl("thread.peak.read", &peak, &sz, NULL, 0);
1143 expect_d_eq(err, 0, "");
1144 expect_u64_eq(0, peak, "Missed a reset.");
1145 for (size_t i = 0; i < nallocs; i++) {
1146 ptrs[i] = mallocx(small_size, 0);
1147 }
1148 for (size_t i = 0; i < nallocs; i++) {
1149 free(ptrs[i]);
1150 }
1151 err = mallctl("thread.peak.read", &peak, &sz, NULL, 0);
1152 expect_d_eq(err, 0, "");
1153 /*
1154 * We don't guarantee exactness; make sure we're within 10% of the peak,
1155 * though.
1156 */
1157 expect_u64_ge(peak, nallocx(small_size, 0) * nallocs * 9 / 10,
1158 "Missed some peak changes.");
1159 expect_u64_le(peak, nallocx(small_size, 0) * nallocs * 11 / 10,
1160 "Overcounted peak changes.");
1161 free(ptrs);
1162 }
1163 TEST_END
1164
1165 typedef struct activity_test_data_s activity_test_data_t;
1166 struct activity_test_data_s {
1167 uint64_t obtained_alloc;
1168 uint64_t obtained_dalloc;
1169 };
1170
1171 static void
1172 activity_test_callback(void *uctx, uint64_t alloc, uint64_t dalloc) {
1173 activity_test_data_t *test_data = (activity_test_data_t *)uctx;
1174 test_data->obtained_alloc = alloc;
1175 test_data->obtained_dalloc = dalloc;
1176 }
1177
1178 TEST_BEGIN(test_thread_activity_callback) {
1179 test_skip_if(!config_stats);
1180
1181 const size_t big_size = 10 * 1024 * 1024;
1182 void *ptr;
1183 int err;
1184 size_t sz;
1185
1186 uint64_t *allocatedp;
1187 uint64_t *deallocatedp;
1188 sz = sizeof(allocatedp);
1189 err = mallctl("thread.allocatedp", &allocatedp, &sz, NULL, 0);
1190 assert_d_eq(0, err, "");
1191 err = mallctl("thread.deallocatedp", &deallocatedp, &sz, NULL, 0);
1192 assert_d_eq(0, err, "");
1193
1194 activity_callback_thunk_t old_thunk = {(activity_callback_t)111,
1195 (void *)222};
1196
1197 activity_test_data_t test_data = {333, 444};
1198 activity_callback_thunk_t new_thunk =
1199 {&activity_test_callback, &test_data};
1200
1201 sz = sizeof(old_thunk);
1202 err = mallctl("experimental.thread.activity_callback", &old_thunk, &sz,
1203 &new_thunk, sizeof(new_thunk));
1204 assert_d_eq(0, err, "");
1205
1206 expect_true(old_thunk.callback == NULL, "Callback already installed");
1207 expect_true(old_thunk.uctx == NULL, "Callback data already installed");
1208
1209 ptr = mallocx(big_size, 0);
1210 expect_u64_eq(test_data.obtained_alloc, *allocatedp, "");
1211 expect_u64_eq(test_data.obtained_dalloc, *deallocatedp, "");
1212
1213 free(ptr);
1214 expect_u64_eq(test_data.obtained_alloc, *allocatedp, "");
1215 expect_u64_eq(test_data.obtained_dalloc, *deallocatedp, "");
1216
1217 sz = sizeof(old_thunk);
1218 new_thunk = (activity_callback_thunk_t){ NULL, NULL };
1219 err = mallctl("experimental.thread.activity_callback", &old_thunk, &sz,
1220 &new_thunk, sizeof(new_thunk));
1221 assert_d_eq(0, err, "");
1222
1223 expect_true(old_thunk.callback == &activity_test_callback, "");
1224 expect_true(old_thunk.uctx == &test_data, "");
1225
1226 /* Inserting NULL should have turned off tracking. */
1227 test_data.obtained_alloc = 333;
1228 test_data.obtained_dalloc = 444;
1229 ptr = mallocx(big_size, 0);
1230 free(ptr);
1231 expect_u64_eq(333, test_data.obtained_alloc, "");
1232 expect_u64_eq(444, test_data.obtained_dalloc, "");
1233 }
1234 TEST_END
1235
1236 int
1237 main(void) {
1238 return test(
1239 test_mallctl_errors,
1240 test_mallctlnametomib_errors,
1241 test_mallctlbymib_errors,
1242 test_mallctl_read_write,
1243 test_mallctlnametomib_short_mib,
1244 test_mallctlnametomib_short_name,
1245 test_mallctlmibnametomib,
1246 test_mallctlbymibname,
1247 test_mallctl_config,
1248 test_mallctl_opt,
1249 test_manpage_example,
1250 test_tcache_none,
1251 test_tcache,
1252 test_thread_arena,
1253 test_arena_i_initialized,
1254 test_arena_i_dirty_decay_ms,
1255 test_arena_i_muzzy_decay_ms,
1256 test_arena_i_purge,
1257 test_arena_i_decay,
1258 test_arena_i_dss,
1259 test_arena_i_retain_grow_limit,
1260 test_arenas_dirty_decay_ms,
1261 test_arenas_muzzy_decay_ms,
1262 test_arenas_constants,
1263 test_arenas_bin_constants,
1264 test_arenas_lextent_constants,
1265 test_arenas_create,
1266 test_arenas_lookup,
1267 test_prof_active,
1268 test_stats_arenas,
1269 test_hooks,
1270 test_hooks_exhaustion,
1271 test_thread_idle,
1272 test_thread_peak,
1273 test_thread_activity_callback);
1274 }
1275