Home | History | Annotate | Line # | Download | only in unit
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