Add confirm_conf option

If the confirm_conf option is set, when the program starts, each of
the four malloc_conf strings will be printed, and each option will
be printed when being set.
This commit is contained in:
Yinan Zhang 2019-04-30 13:54:00 -07:00 committed by Qi Wang
parent 4c63b0e76a
commit c92ac30601
6 changed files with 204 additions and 115 deletions

View File

@ -904,6 +904,23 @@ mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".decay",
</para></listitem> </para></listitem>
</varlistentry> </varlistentry>
<varlistentry id="opt.confirm_conf">
<term>
<mallctl>opt.confirm_conf</mallctl>
(<type>bool</type>)
<literal>r-</literal>
</term>
<listitem><para>Confirm-runtime-options-when-program-starts
enabled/disabled. If true, the string specified via
<option>--with-malloc-conf</option>, the string pointed to by the
global variable <varname>malloc_conf</varname>, the <quote>name</quote>
of the file referenced by the symbolic link named
<filename class="symlink">/etc/malloc.conf</filename>, and the value of
the environment variable <envar>MALLOC_CONF</envar>, will be printed in
order. Then, each option being set will be individually printed. This
option is disabled by default.</para></listitem>
</varlistentry>
<varlistentry id="opt.abort_conf"> <varlistentry id="opt.abort_conf">
<term> <term>
<mallctl>opt.abort_conf</mallctl> <mallctl>opt.abort_conf</mallctl>

View File

@ -10,6 +10,7 @@ extern bool malloc_slow;
/* Run-time options. */ /* Run-time options. */
extern bool opt_abort; extern bool opt_abort;
extern bool opt_abort_conf; extern bool opt_abort_conf;
extern bool opt_confirm_conf;
extern const char *opt_junk; extern const char *opt_junk;
extern bool opt_junk_alloc; extern bool opt_junk_alloc;
extern bool opt_junk_free; extern bool opt_junk_free;

View File

@ -81,6 +81,7 @@ CTL_PROTO(config_utrace)
CTL_PROTO(config_xmalloc) CTL_PROTO(config_xmalloc)
CTL_PROTO(opt_abort) CTL_PROTO(opt_abort)
CTL_PROTO(opt_abort_conf) CTL_PROTO(opt_abort_conf)
CTL_PROTO(opt_confirm_conf)
CTL_PROTO(opt_metadata_thp) CTL_PROTO(opt_metadata_thp)
CTL_PROTO(opt_retain) CTL_PROTO(opt_retain)
CTL_PROTO(opt_dss) CTL_PROTO(opt_dss)
@ -304,6 +305,7 @@ static const ctl_named_node_t config_node[] = {
static const ctl_named_node_t opt_node[] = { static const ctl_named_node_t opt_node[] = {
{NAME("abort"), CTL(opt_abort)}, {NAME("abort"), CTL(opt_abort)},
{NAME("abort_conf"), CTL(opt_abort_conf)}, {NAME("abort_conf"), CTL(opt_abort_conf)},
{NAME("confirm_conf"), CTL(opt_confirm_conf)},
{NAME("metadata_thp"), CTL(opt_metadata_thp)}, {NAME("metadata_thp"), CTL(opt_metadata_thp)},
{NAME("retain"), CTL(opt_retain)}, {NAME("retain"), CTL(opt_retain)},
{NAME("dss"), CTL(opt_dss)}, {NAME("dss"), CTL(opt_dss)},
@ -1741,6 +1743,7 @@ CTL_RO_CONFIG_GEN(config_xmalloc, bool)
CTL_RO_NL_GEN(opt_abort, opt_abort, bool) CTL_RO_NL_GEN(opt_abort, opt_abort, bool)
CTL_RO_NL_GEN(opt_abort_conf, opt_abort_conf, bool) CTL_RO_NL_GEN(opt_abort_conf, opt_abort_conf, bool)
CTL_RO_NL_GEN(opt_confirm_conf, opt_confirm_conf, bool)
CTL_RO_NL_GEN(opt_metadata_thp, metadata_thp_mode_names[opt_metadata_thp], CTL_RO_NL_GEN(opt_metadata_thp, metadata_thp_mode_names[opt_metadata_thp],
const char *) const char *)
CTL_RO_NL_GEN(opt_retain, opt_retain, bool) CTL_RO_NL_GEN(opt_retain, opt_retain, bool)

View File

@ -43,6 +43,8 @@ bool opt_abort_conf =
false false
#endif #endif
; ;
/* Intentionally default off, even with debug builds. */
bool opt_confirm_conf = false;
const char *opt_junk = const char *opt_junk =
#if (defined(JEMALLOC_DEBUG) && defined(JEMALLOC_FILL)) #if (defined(JEMALLOC_DEBUG) && defined(JEMALLOC_FILL))
"true" "true"
@ -929,30 +931,33 @@ malloc_slow_flag_init(void) {
malloc_slow = (malloc_slow_flags != 0); malloc_slow = (malloc_slow_flags != 0);
} }
static void /* Number of sources for initializing malloc_conf */
malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) { #define MALLOC_CONF_NSOURCES 4
unsigned i;
char buf[PATH_MAX + 1];
const char *opts, *k, *v;
size_t klen, vlen;
for (i = 0; i < 4; i++) { static const char *
/* Get runtime configuration. */ obtain_malloc_conf(unsigned which_source, char buf[PATH_MAX + 1]) {
switch (i) { if (config_debug) {
static unsigned read_source = 0;
/*
* Each source should only be read once, to minimize # of
* syscalls on init.
*/
assert(read_source++ == which_source);
}
assert(which_source < MALLOC_CONF_NSOURCES);
const char *ret;
switch (which_source) {
case 0: case 0:
opts = config_malloc_conf; ret = config_malloc_conf;
break; break;
case 1: case 1:
if (je_malloc_conf != NULL) { if (je_malloc_conf != NULL) {
/* /* Use options that were compiled into the program. */
* Use options that were compiled into the ret = je_malloc_conf;
* program.
*/
opts = je_malloc_conf;
} else { } else {
/* No configuration specified. */ /* No configuration specified. */
buf[0] = '\0'; ret = NULL;
opts = buf;
} }
break; break;
case 2: { case 2: {
@ -968,14 +973,13 @@ malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
; ;
/* /*
* Try to use the contents of the "/etc/malloc.conf" * Try to use the contents of the "/etc/malloc.conf" symbolic
* symbolic link's name. * link's name.
*/ */
#ifndef JEMALLOC_READLINKAT #ifndef JEMALLOC_READLINKAT
linklen = readlink(linkname, buf, sizeof(buf) - 1); linklen = readlink(linkname, buf, PATH_MAX);
#else #else
linklen = readlinkat(AT_FDCWD, linkname, buf, linklen = readlinkat(AT_FDCWD, linkname, buf, PATH_MAX);
sizeof(buf) - 1);
#endif #endif
if (linklen == -1) { if (linklen == -1) {
/* No configuration specified. */ /* No configuration specified. */
@ -985,7 +989,7 @@ malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
} }
#endif #endif
buf[linklen] = '\0'; buf[linklen] = '\0';
opts = buf; ret = buf;
break; break;
} case 3: { } case 3: {
const char *envname = const char *envname =
@ -996,26 +1000,71 @@ malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
#endif #endif
; ;
if ((opts = jemalloc_secure_getenv(envname)) != NULL) { if ((ret = jemalloc_secure_getenv(envname)) != NULL) {
/* /*
* Do nothing; opts is already initialized to * Do nothing; opts is already initialized to the value
* the value of the MALLOC_CONF environment * of the MALLOC_CONF environment variable.
* variable.
*/ */
} else { } else {
/* No configuration specified. */ /* No configuration specified. */
buf[0] = '\0'; ret = NULL;
opts = buf;
} }
break; break;
} default: } default:
not_reached(); not_reached();
buf[0] = '\0'; ret = NULL;
opts = buf; }
return ret;
}
static void
malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
bool initial_call, const char *opts_cache[MALLOC_CONF_NSOURCES],
char buf[PATH_MAX + 1]) {
static const char *opts_explain[MALLOC_CONF_NSOURCES] = {
"string specified via --with-malloc-conf",
"string pointed to by the global variable malloc_conf",
"\"name\" of the file referenced by the symbolic link named "
"/etc/malloc.conf",
"value of the environment variable MALLOC_CONF"
};
unsigned i;
const char *opts, *k, *v;
size_t klen, vlen;
for (i = 0; i < MALLOC_CONF_NSOURCES; i++) {
/* Get runtime configuration. */
if (initial_call) {
opts_cache[i] = obtain_malloc_conf(i, buf);
}
opts = opts_cache[i];
if (!initial_call && opt_confirm_conf) {
malloc_printf(
"<jemalloc>: malloc_conf #%u (%s): \"%s\"\n",
i + 1, opts_explain[i], opts != NULL ? opts : "");
}
if (opts == NULL) {
continue;
} }
while (*opts != '\0' && !malloc_conf_next(&opts, &k, &klen, &v, while (*opts != '\0' && !malloc_conf_next(&opts, &k, &klen, &v,
&vlen)) { &vlen)) {
#define CONF_ERROR(msg, k, klen, v, vlen) \
if (!initial_call) { \
malloc_conf_error( \
msg, k, klen, v, vlen); \
cur_opt_valid = false; \
}
#define CONF_CONTINUE { \
if (!initial_call && opt_confirm_conf \
&& cur_opt_valid) { \
malloc_printf("<jemalloc>: Set "\
"conf value: %.*s:%.*s\n", \
(int)klen, k, (int)vlen, v);\
} \
continue; \
}
#define CONF_MATCH(n) \ #define CONF_MATCH(n) \
(sizeof(n)-1 == klen && strncmp(n, k, klen) == 0) (sizeof(n)-1 == klen && strncmp(n, k, klen) == 0)
#define CONF_MATCH_VALUE(n) \ #define CONF_MATCH_VALUE(n) \
@ -1027,11 +1076,10 @@ malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
} else if (CONF_MATCH_VALUE("false")) { \ } else if (CONF_MATCH_VALUE("false")) { \
o = false; \ o = false; \
} else { \ } else { \
malloc_conf_error( \ CONF_ERROR("Invalid conf value",\
"Invalid conf value", \
k, klen, v, vlen); \ k, klen, v, vlen); \
} \ } \
continue; \ CONF_CONTINUE; \
} }
/* /*
* One of the CONF_MIN macros below expands, in one of the use points, * One of the CONF_MIN macros below expands, in one of the use points,
@ -1054,8 +1102,7 @@ malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
um = malloc_strtoumax(v, &end, 0); \ um = malloc_strtoumax(v, &end, 0); \
if (get_errno() != 0 || (uintptr_t)end -\ if (get_errno() != 0 || (uintptr_t)end -\
(uintptr_t)v != vlen) { \ (uintptr_t)v != vlen) { \
malloc_conf_error( \ CONF_ERROR("Invalid conf value",\
"Invalid conf value", \
k, klen, v, vlen); \ k, klen, v, vlen); \
} else if (clip) { \ } else if (clip) { \
if (check_min(um, (t)(min))) { \ if (check_min(um, (t)(min))) { \
@ -1069,7 +1116,7 @@ malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
} else { \ } else { \
if (check_min(um, (t)(min)) || \ if (check_min(um, (t)(min)) || \
check_max(um, (t)(max))) { \ check_max(um, (t)(max))) { \
malloc_conf_error( \ CONF_ERROR( \
"Out-of-range " \ "Out-of-range " \
"conf value", \ "conf value", \
k, klen, v, vlen); \ k, klen, v, vlen); \
@ -1077,7 +1124,7 @@ malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
o = (t)um; \ o = (t)um; \
} \ } \
} \ } \
continue; \ CONF_CONTINUE; \
} }
#define CONF_HANDLE_UNSIGNED(o, n, min, max, check_min, check_max, \ #define CONF_HANDLE_UNSIGNED(o, n, min, max, check_min, check_max, \
clip) \ clip) \
@ -1095,18 +1142,17 @@ malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
l = strtol(v, &end, 0); \ l = strtol(v, &end, 0); \
if (get_errno() != 0 || (uintptr_t)end -\ if (get_errno() != 0 || (uintptr_t)end -\
(uintptr_t)v != vlen) { \ (uintptr_t)v != vlen) { \
malloc_conf_error( \ CONF_ERROR("Invalid conf value",\
"Invalid conf value", \
k, klen, v, vlen); \ k, klen, v, vlen); \
} else if (l < (ssize_t)(min) || l > \ } else if (l < (ssize_t)(min) || l > \
(ssize_t)(max)) { \ (ssize_t)(max)) { \
malloc_conf_error( \ CONF_ERROR( \
"Out-of-range conf value", \ "Out-of-range conf value", \
k, klen, v, vlen); \ k, klen, v, vlen); \
} else { \ } else { \
o = l; \ o = l; \
} \ } \
continue; \ CONF_CONTINUE; \
} }
#define CONF_HANDLE_CHAR_P(o, n, d) \ #define CONF_HANDLE_CHAR_P(o, n, d) \
if (CONF_MATCH(n)) { \ if (CONF_MATCH(n)) { \
@ -1115,7 +1161,14 @@ malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
sizeof(o)-1; \ sizeof(o)-1; \
strncpy(o, v, cpylen); \ strncpy(o, v, cpylen); \
o[cpylen] = '\0'; \ o[cpylen] = '\0'; \
continue; \ CONF_CONTINUE; \
}
bool cur_opt_valid = true;
CONF_HANDLE_BOOL(opt_confirm_conf, "confirm_conf")
if (initial_call) {
continue;
} }
CONF_HANDLE_BOOL(opt_abort, "abort") CONF_HANDLE_BOOL(opt_abort, "abort")
@ -1132,10 +1185,10 @@ malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
} }
} }
if (!match) { if (!match) {
malloc_conf_error("Invalid conf value", CONF_ERROR("Invalid conf value",
k, klen, v, vlen); k, klen, v, vlen);
} }
continue; CONF_CONTINUE;
} }
CONF_HANDLE_BOOL(opt_retain, "retain") CONF_HANDLE_BOOL(opt_retain, "retain")
if (strncmp("dss", k, klen) == 0) { if (strncmp("dss", k, klen) == 0) {
@ -1145,7 +1198,7 @@ malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
if (strncmp(dss_prec_names[i], v, vlen) if (strncmp(dss_prec_names[i], v, vlen)
== 0) { == 0) {
if (extent_dss_prec_set(i)) { if (extent_dss_prec_set(i)) {
malloc_conf_error( CONF_ERROR(
"Error setting dss", "Error setting dss",
k, klen, v, vlen); k, klen, v, vlen);
} else { } else {
@ -1157,10 +1210,10 @@ malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
} }
} }
if (!match) { if (!match) {
malloc_conf_error("Invalid conf value", CONF_ERROR("Invalid conf value",
k, klen, v, vlen); k, klen, v, vlen);
} }
continue; CONF_CONTINUE;
} }
CONF_HANDLE_UNSIGNED(opt_narenas, "narenas", 1, CONF_HANDLE_UNSIGNED(opt_narenas, "narenas", 1,
UINT_MAX, CONF_CHECK_MIN, CONF_DONT_CHECK_MAX, UINT_MAX, CONF_CHECK_MIN, CONF_DONT_CHECK_MAX,
@ -1178,14 +1231,14 @@ malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
if (err || bin_update_shard_size( if (err || bin_update_shard_size(
bin_shard_sizes, size_start, bin_shard_sizes, size_start,
size_end, nshards)) { size_end, nshards)) {
malloc_conf_error( CONF_ERROR(
"Invalid settings for " "Invalid settings for "
"bin_shards", k, klen, v, "bin_shards", k, klen, v,
vlen); vlen);
break; break;
} }
} while (vlen_left > 0); } while (vlen_left > 0);
continue; CONF_CONTINUE;
} }
CONF_HANDLE_SSIZE_T(opt_dirty_decay_ms, CONF_HANDLE_SSIZE_T(opt_dirty_decay_ms,
"dirty_decay_ms", -1, NSTIME_SEC_MAX * KQU(1000) < "dirty_decay_ms", -1, NSTIME_SEC_MAX * KQU(1000) <
@ -1198,7 +1251,7 @@ malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
CONF_HANDLE_BOOL(opt_stats_print, "stats_print") CONF_HANDLE_BOOL(opt_stats_print, "stats_print")
if (CONF_MATCH("stats_print_opts")) { if (CONF_MATCH("stats_print_opts")) {
init_opt_stats_print_opts(v, vlen); init_opt_stats_print_opts(v, vlen);
continue; CONF_CONTINUE;
} }
if (config_fill) { if (config_fill) {
if (CONF_MATCH("junk")) { if (CONF_MATCH("junk")) {
@ -1219,11 +1272,11 @@ malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
opt_junk_alloc = false; opt_junk_alloc = false;
opt_junk_free = true; opt_junk_free = true;
} else { } else {
malloc_conf_error( CONF_ERROR(
"Invalid conf value", k, "Invalid conf value",
klen, v, vlen); k, klen, v, vlen);
} }
continue; CONF_CONTINUE;
} }
CONF_HANDLE_BOOL(opt_zero, "zero") CONF_HANDLE_BOOL(opt_zero, "zero")
} }
@ -1260,7 +1313,7 @@ malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
if (strncmp(percpu_arena_mode_names[i], if (strncmp(percpu_arena_mode_names[i],
v, vlen) == 0) { v, vlen) == 0) {
if (!have_percpu_arena) { if (!have_percpu_arena) {
malloc_conf_error( CONF_ERROR(
"No getcpu support", "No getcpu support",
k, klen, v, vlen); k, klen, v, vlen);
} }
@ -1270,10 +1323,10 @@ malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
} }
} }
if (!match) { if (!match) {
malloc_conf_error("Invalid conf value", CONF_ERROR("Invalid conf value",
k, klen, v, vlen); k, klen, v, vlen);
} }
continue; CONF_CONTINUE;
} }
CONF_HANDLE_BOOL(opt_background_thread, CONF_HANDLE_BOOL(opt_background_thread,
"background_thread"); "background_thread");
@ -1299,13 +1352,12 @@ malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
sc_data, slab_start, sc_data, slab_start,
slab_end, (int)pgs); slab_end, (int)pgs);
} else { } else {
malloc_conf_error( CONF_ERROR("Invalid settings "
"Invalid settings for " "for slab_sizes",
"slab_sizes", k, klen, v, k, klen, v, vlen);
vlen);
} }
} while (!err && vlen_left > 0); } while (!err && vlen_left > 0);
continue; CONF_CONTINUE;
} }
if (config_prof) { if (config_prof) {
CONF_HANDLE_BOOL(opt_prof, "prof") CONF_HANDLE_BOOL(opt_prof, "prof")
@ -1334,7 +1386,7 @@ malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
vlen : sizeof(log_var_names) - 1); vlen : sizeof(log_var_names) - 1);
strncpy(log_var_names, v, cpylen); strncpy(log_var_names, v, cpylen);
log_var_names[cpylen] = '\0'; log_var_names[cpylen] = '\0';
continue; CONF_CONTINUE;
} }
} }
if (CONF_MATCH("thp")) { if (CONF_MATCH("thp")) {
@ -1343,7 +1395,7 @@ malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
if (strncmp(thp_mode_names[i],v, vlen) if (strncmp(thp_mode_names[i],v, vlen)
== 0) { == 0) {
if (!have_madvise_huge) { if (!have_madvise_huge) {
malloc_conf_error( CONF_ERROR(
"No THP support", "No THP support",
k, klen, v, vlen); k, klen, v, vlen);
} }
@ -1353,13 +1405,14 @@ malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
} }
} }
if (!match) { if (!match) {
malloc_conf_error("Invalid conf value", CONF_ERROR("Invalid conf value",
k, klen, v, vlen); k, klen, v, vlen);
} }
continue; CONF_CONTINUE;
} }
malloc_conf_error("Invalid conf pair", k, klen, v, CONF_ERROR("Invalid conf pair", k, klen, v, vlen);
vlen); #undef CONF_ERROR
#undef CONF_CONTINUE
#undef CONF_MATCH #undef CONF_MATCH
#undef CONF_MATCH_VALUE #undef CONF_MATCH_VALUE
#undef CONF_HANDLE_BOOL #undef CONF_HANDLE_BOOL
@ -1382,6 +1435,19 @@ malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
atomic_store_b(&log_init_done, true, ATOMIC_RELEASE); atomic_store_b(&log_init_done, true, ATOMIC_RELEASE);
} }
static void
malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
const char *opts_cache[MALLOC_CONF_NSOURCES] = {NULL, NULL, NULL, NULL};
char buf[PATH_MAX + 1];
/* The first call only set the confirm_conf option and opts_cache */
malloc_conf_init_helper(NULL, NULL, true, opts_cache, buf);
malloc_conf_init_helper(sc_data, bin_shard_sizes, false, opts_cache,
NULL);
}
#undef MALLOC_CONF_NSOURCES
static bool static bool
malloc_init_hard_needed(void) { malloc_init_hard_needed(void) {
if (malloc_initialized() || (IS_INITIALIZER && malloc_init_state == if (malloc_initialized() || (IS_INITIALIZER && malloc_init_state ==

View File

@ -1065,6 +1065,7 @@ stats_general_print(emitter_t *emitter) {
OPT_WRITE_BOOL("abort") OPT_WRITE_BOOL("abort")
OPT_WRITE_BOOL("abort_conf") OPT_WRITE_BOOL("abort_conf")
OPT_WRITE_BOOL("confirm_conf")
OPT_WRITE_BOOL("retain") OPT_WRITE_BOOL("retain")
OPT_WRITE_CHAR_P("dss") OPT_WRITE_CHAR_P("dss")
OPT_WRITE_UNSIGNED("narenas") OPT_WRITE_UNSIGNED("narenas")

View File

@ -159,6 +159,7 @@ TEST_BEGIN(test_mallctl_opt) {
TEST_MALLCTL_OPT(bool, abort, always); TEST_MALLCTL_OPT(bool, abort, always);
TEST_MALLCTL_OPT(bool, abort_conf, always); TEST_MALLCTL_OPT(bool, abort_conf, always);
TEST_MALLCTL_OPT(bool, confirm_conf, always);
TEST_MALLCTL_OPT(const char *, metadata_thp, always); TEST_MALLCTL_OPT(const char *, metadata_thp, always);
TEST_MALLCTL_OPT(bool, retain, always); TEST_MALLCTL_OPT(bool, retain, always);
TEST_MALLCTL_OPT(const char *, dss, always); TEST_MALLCTL_OPT(const char *, dss, always);