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