Home | History | Annotate | Download | only in src
      1 #define	JEMALLOC_CTL_C_
      2 #include "jemalloc/internal/jemalloc_internal.h"
      3 
      4 /******************************************************************************/
      5 /* Data. */
      6 
      7 /*
      8  * ctl_mtx protects the following:
      9  * - ctl_stats.*
     10  */
     11 static malloc_mutex_t	ctl_mtx;
     12 static bool		ctl_initialized;
     13 static uint64_t		ctl_epoch;
     14 static ctl_stats_t	ctl_stats;
     15 
     16 /******************************************************************************/
     17 /* Helpers for named and indexed nodes. */
     18 
     19 JEMALLOC_INLINE_C const ctl_named_node_t *
     20 ctl_named_node(const ctl_node_t *node)
     21 {
     22 
     23 	return ((node->named) ? (const ctl_named_node_t *)node : NULL);
     24 }
     25 
     26 JEMALLOC_INLINE_C const ctl_named_node_t *
     27 ctl_named_children(const ctl_named_node_t *node, int index)
     28 {
     29 	const ctl_named_node_t *children = ctl_named_node(node->children);
     30 
     31 	return (children ? &children[index] : NULL);
     32 }
     33 
     34 JEMALLOC_INLINE_C const ctl_indexed_node_t *
     35 ctl_indexed_node(const ctl_node_t *node)
     36 {
     37 
     38 	return (!node->named ? (const ctl_indexed_node_t *)node : NULL);
     39 }
     40 
     41 /******************************************************************************/
     42 /* Function prototypes for non-inline static functions. */
     43 
     44 #define	CTL_PROTO(n)							\
     45 static int	n##_ctl(const size_t *mib, size_t miblen, void *oldp,	\
     46     size_t *oldlenp, void *newp, size_t newlen);
     47 
     48 #define	INDEX_PROTO(n)							\
     49 static const ctl_named_node_t	*n##_index(const size_t *mib,		\
     50     size_t miblen, size_t i);
     51 
     52 static bool	ctl_arena_init(ctl_arena_stats_t *astats);
     53 static void	ctl_arena_clear(ctl_arena_stats_t *astats);
     54 static void	ctl_arena_stats_amerge(ctl_arena_stats_t *cstats,
     55     arena_t *arena);
     56 static void	ctl_arena_stats_smerge(ctl_arena_stats_t *sstats,
     57     ctl_arena_stats_t *astats);
     58 static void	ctl_arena_refresh(arena_t *arena, unsigned i);
     59 static bool	ctl_grow(void);
     60 static void	ctl_refresh(void);
     61 static bool	ctl_init(void);
     62 static int	ctl_lookup(const char *name, ctl_node_t const **nodesp,
     63     size_t *mibp, size_t *depthp);
     64 
     65 CTL_PROTO(version)
     66 CTL_PROTO(epoch)
     67 CTL_PROTO(thread_tcache_enabled)
     68 CTL_PROTO(thread_tcache_flush)
     69 CTL_PROTO(thread_prof_name)
     70 CTL_PROTO(thread_prof_active)
     71 CTL_PROTO(thread_arena)
     72 CTL_PROTO(thread_allocated)
     73 CTL_PROTO(thread_allocatedp)
     74 CTL_PROTO(thread_deallocated)
     75 CTL_PROTO(thread_deallocatedp)
     76 CTL_PROTO(config_debug)
     77 CTL_PROTO(config_fill)
     78 CTL_PROTO(config_lazy_lock)
     79 CTL_PROTO(config_munmap)
     80 CTL_PROTO(config_prof)
     81 CTL_PROTO(config_prof_libgcc)
     82 CTL_PROTO(config_prof_libunwind)
     83 CTL_PROTO(config_stats)
     84 CTL_PROTO(config_tcache)
     85 CTL_PROTO(config_tls)
     86 CTL_PROTO(config_utrace)
     87 CTL_PROTO(config_valgrind)
     88 CTL_PROTO(config_xmalloc)
     89 CTL_PROTO(opt_abort)
     90 CTL_PROTO(opt_dss)
     91 CTL_PROTO(opt_lg_chunk)
     92 CTL_PROTO(opt_narenas)
     93 CTL_PROTO(opt_lg_dirty_mult)
     94 CTL_PROTO(opt_stats_print)
     95 CTL_PROTO(opt_junk)
     96 CTL_PROTO(opt_zero)
     97 CTL_PROTO(opt_quarantine)
     98 CTL_PROTO(opt_redzone)
     99 CTL_PROTO(opt_utrace)
    100 CTL_PROTO(opt_xmalloc)
    101 CTL_PROTO(opt_tcache)
    102 CTL_PROTO(opt_lg_tcache_max)
    103 CTL_PROTO(opt_prof)
    104 CTL_PROTO(opt_prof_prefix)
    105 CTL_PROTO(opt_prof_active)
    106 CTL_PROTO(opt_prof_thread_active_init)
    107 CTL_PROTO(opt_lg_prof_sample)
    108 CTL_PROTO(opt_lg_prof_interval)
    109 CTL_PROTO(opt_prof_gdump)
    110 CTL_PROTO(opt_prof_final)
    111 CTL_PROTO(opt_prof_leak)
    112 CTL_PROTO(opt_prof_accum)
    113 CTL_PROTO(tcache_create)
    114 CTL_PROTO(tcache_flush)
    115 CTL_PROTO(tcache_destroy)
    116 CTL_PROTO(arena_i_purge)
    117 static void	arena_purge(unsigned arena_ind);
    118 CTL_PROTO(arena_i_dss)
    119 CTL_PROTO(arena_i_lg_dirty_mult)
    120 CTL_PROTO(arena_i_chunk_alloc)
    121 CTL_PROTO(arena_i_chunk_dalloc)
    122 CTL_PROTO(arena_i_chunk_purge)
    123 INDEX_PROTO(arena_i)
    124 CTL_PROTO(arenas_bin_i_size)
    125 CTL_PROTO(arenas_bin_i_nregs)
    126 CTL_PROTO(arenas_bin_i_run_size)
    127 INDEX_PROTO(arenas_bin_i)
    128 CTL_PROTO(arenas_lrun_i_size)
    129 INDEX_PROTO(arenas_lrun_i)
    130 CTL_PROTO(arenas_hchunk_i_size)
    131 INDEX_PROTO(arenas_hchunk_i)
    132 CTL_PROTO(arenas_narenas)
    133 CTL_PROTO(arenas_initialized)
    134 CTL_PROTO(arenas_lg_dirty_mult)
    135 CTL_PROTO(arenas_quantum)
    136 CTL_PROTO(arenas_page)
    137 CTL_PROTO(arenas_tcache_max)
    138 CTL_PROTO(arenas_nbins)
    139 CTL_PROTO(arenas_nhbins)
    140 CTL_PROTO(arenas_nlruns)
    141 CTL_PROTO(arenas_nhchunks)
    142 CTL_PROTO(arenas_extend)
    143 CTL_PROTO(prof_thread_active_init)
    144 CTL_PROTO(prof_active)
    145 CTL_PROTO(prof_dump)
    146 CTL_PROTO(prof_gdump)
    147 CTL_PROTO(prof_reset)
    148 CTL_PROTO(prof_interval)
    149 CTL_PROTO(lg_prof_sample)
    150 CTL_PROTO(stats_arenas_i_small_allocated)
    151 CTL_PROTO(stats_arenas_i_small_nmalloc)
    152 CTL_PROTO(stats_arenas_i_small_ndalloc)
    153 CTL_PROTO(stats_arenas_i_small_nrequests)
    154 CTL_PROTO(stats_arenas_i_large_allocated)
    155 CTL_PROTO(stats_arenas_i_large_nmalloc)
    156 CTL_PROTO(stats_arenas_i_large_ndalloc)
    157 CTL_PROTO(stats_arenas_i_large_nrequests)
    158 CTL_PROTO(stats_arenas_i_huge_allocated)
    159 CTL_PROTO(stats_arenas_i_huge_nmalloc)
    160 CTL_PROTO(stats_arenas_i_huge_ndalloc)
    161 CTL_PROTO(stats_arenas_i_huge_nrequests)
    162 CTL_PROTO(stats_arenas_i_bins_j_nmalloc)
    163 CTL_PROTO(stats_arenas_i_bins_j_ndalloc)
    164 CTL_PROTO(stats_arenas_i_bins_j_nrequests)
    165 CTL_PROTO(stats_arenas_i_bins_j_curregs)
    166 CTL_PROTO(stats_arenas_i_bins_j_nfills)
    167 CTL_PROTO(stats_arenas_i_bins_j_nflushes)
    168 CTL_PROTO(stats_arenas_i_bins_j_nruns)
    169 CTL_PROTO(stats_arenas_i_bins_j_nreruns)
    170 CTL_PROTO(stats_arenas_i_bins_j_curruns)
    171 INDEX_PROTO(stats_arenas_i_bins_j)
    172 CTL_PROTO(stats_arenas_i_lruns_j_nmalloc)
    173 CTL_PROTO(stats_arenas_i_lruns_j_ndalloc)
    174 CTL_PROTO(stats_arenas_i_lruns_j_nrequests)
    175 CTL_PROTO(stats_arenas_i_lruns_j_curruns)
    176 INDEX_PROTO(stats_arenas_i_lruns_j)
    177 CTL_PROTO(stats_arenas_i_hchunks_j_nmalloc)
    178 CTL_PROTO(stats_arenas_i_hchunks_j_ndalloc)
    179 CTL_PROTO(stats_arenas_i_hchunks_j_nrequests)
    180 CTL_PROTO(stats_arenas_i_hchunks_j_curhchunks)
    181 INDEX_PROTO(stats_arenas_i_hchunks_j)
    182 CTL_PROTO(stats_arenas_i_nthreads)
    183 CTL_PROTO(stats_arenas_i_dss)
    184 CTL_PROTO(stats_arenas_i_lg_dirty_mult)
    185 CTL_PROTO(stats_arenas_i_pactive)
    186 CTL_PROTO(stats_arenas_i_pdirty)
    187 CTL_PROTO(stats_arenas_i_mapped)
    188 CTL_PROTO(stats_arenas_i_npurge)
    189 CTL_PROTO(stats_arenas_i_nmadvise)
    190 CTL_PROTO(stats_arenas_i_purged)
    191 CTL_PROTO(stats_arenas_i_metadata_mapped)
    192 CTL_PROTO(stats_arenas_i_metadata_allocated)
    193 INDEX_PROTO(stats_arenas_i)
    194 CTL_PROTO(stats_cactive)
    195 CTL_PROTO(stats_allocated)
    196 CTL_PROTO(stats_active)
    197 CTL_PROTO(stats_metadata)
    198 CTL_PROTO(stats_resident)
    199 CTL_PROTO(stats_mapped)
    200 
    201 /******************************************************************************/
    202 /* mallctl tree. */
    203 
    204 /* Maximum tree depth. */
    205 #define	CTL_MAX_DEPTH	6
    206 
    207 #define	NAME(n)	{true},	n
    208 #define	CHILD(t, c)							\
    209 	sizeof(c##_node) / sizeof(ctl_##t##_node_t),			\
    210 	(ctl_node_t *)c##_node,						\
    211 	NULL
    212 #define	CTL(c)	0, NULL, c##_ctl
    213 
    214 /*
    215  * Only handles internal indexed nodes, since there are currently no external
    216  * ones.
    217  */
    218 #define	INDEX(i)	{false},	i##_index
    219 
    220 static const ctl_named_node_t	thread_tcache_node[] = {
    221 	{NAME("enabled"),	CTL(thread_tcache_enabled)},
    222 	{NAME("flush"),		CTL(thread_tcache_flush)}
    223 };
    224 
    225 static const ctl_named_node_t	thread_prof_node[] = {
    226 	{NAME("name"),		CTL(thread_prof_name)},
    227 	{NAME("active"),	CTL(thread_prof_active)}
    228 };
    229 
    230 static const ctl_named_node_t	thread_node[] = {
    231 	{NAME("arena"),		CTL(thread_arena)},
    232 	{NAME("allocated"),	CTL(thread_allocated)},
    233 	{NAME("allocatedp"),	CTL(thread_allocatedp)},
    234 	{NAME("deallocated"),	CTL(thread_deallocated)},
    235 	{NAME("deallocatedp"),	CTL(thread_deallocatedp)},
    236 	{NAME("tcache"),	CHILD(named, thread_tcache)},
    237 	{NAME("prof"),		CHILD(named, thread_prof)}
    238 };
    239 
    240 static const ctl_named_node_t	config_node[] = {
    241 	{NAME("debug"),		CTL(config_debug)},
    242 	{NAME("fill"),		CTL(config_fill)},
    243 	{NAME("lazy_lock"),	CTL(config_lazy_lock)},
    244 	{NAME("munmap"),	CTL(config_munmap)},
    245 	{NAME("prof"),		CTL(config_prof)},
    246 	{NAME("prof_libgcc"),	CTL(config_prof_libgcc)},
    247 	{NAME("prof_libunwind"), CTL(config_prof_libunwind)},
    248 	{NAME("stats"),		CTL(config_stats)},
    249 	{NAME("tcache"),	CTL(config_tcache)},
    250 	{NAME("tls"),		CTL(config_tls)},
    251 	{NAME("utrace"),	CTL(config_utrace)},
    252 	{NAME("valgrind"),	CTL(config_valgrind)},
    253 	{NAME("xmalloc"),	CTL(config_xmalloc)}
    254 };
    255 
    256 static const ctl_named_node_t opt_node[] = {
    257 	{NAME("abort"),		CTL(opt_abort)},
    258 	{NAME("dss"),		CTL(opt_dss)},
    259 	{NAME("lg_chunk"),	CTL(opt_lg_chunk)},
    260 	{NAME("narenas"),	CTL(opt_narenas)},
    261 	{NAME("lg_dirty_mult"),	CTL(opt_lg_dirty_mult)},
    262 	{NAME("stats_print"),	CTL(opt_stats_print)},
    263 	{NAME("junk"),		CTL(opt_junk)},
    264 	{NAME("zero"),		CTL(opt_zero)},
    265 	{NAME("quarantine"),	CTL(opt_quarantine)},
    266 	{NAME("redzone"),	CTL(opt_redzone)},
    267 	{NAME("utrace"),	CTL(opt_utrace)},
    268 	{NAME("xmalloc"),	CTL(opt_xmalloc)},
    269 	{NAME("tcache"),	CTL(opt_tcache)},
    270 	{NAME("lg_tcache_max"),	CTL(opt_lg_tcache_max)},
    271 	{NAME("prof"),		CTL(opt_prof)},
    272 	{NAME("prof_prefix"),	CTL(opt_prof_prefix)},
    273 	{NAME("prof_active"),	CTL(opt_prof_active)},
    274 	{NAME("prof_thread_active_init"), CTL(opt_prof_thread_active_init)},
    275 	{NAME("lg_prof_sample"), CTL(opt_lg_prof_sample)},
    276 	{NAME("lg_prof_interval"), CTL(opt_lg_prof_interval)},
    277 	{NAME("prof_gdump"),	CTL(opt_prof_gdump)},
    278 	{NAME("prof_final"),	CTL(opt_prof_final)},
    279 	{NAME("prof_leak"),	CTL(opt_prof_leak)},
    280 	{NAME("prof_accum"),	CTL(opt_prof_accum)}
    281 };
    282 
    283 static const ctl_named_node_t	tcache_node[] = {
    284 	{NAME("create"),	CTL(tcache_create)},
    285 	{NAME("flush"),		CTL(tcache_flush)},
    286 	{NAME("destroy"),	CTL(tcache_destroy)}
    287 };
    288 
    289 static const ctl_named_node_t chunk_node[] = {
    290 	{NAME("alloc"),		CTL(arena_i_chunk_alloc)},
    291 	{NAME("dalloc"),	CTL(arena_i_chunk_dalloc)},
    292 	{NAME("purge"),		CTL(arena_i_chunk_purge)}
    293 };
    294 
    295 static const ctl_named_node_t arena_i_node[] = {
    296 	{NAME("purge"),		CTL(arena_i_purge)},
    297 	{NAME("dss"),		CTL(arena_i_dss)},
    298 	{NAME("lg_dirty_mult"),	CTL(arena_i_lg_dirty_mult)},
    299 	{NAME("chunk"),		CHILD(named, chunk)},
    300 };
    301 static const ctl_named_node_t super_arena_i_node[] = {
    302 	{NAME(""),		CHILD(named, arena_i)}
    303 };
    304 
    305 static const ctl_indexed_node_t arena_node[] = {
    306 	{INDEX(arena_i)}
    307 };
    308 
    309 static const ctl_named_node_t arenas_bin_i_node[] = {
    310 	{NAME("size"),		CTL(arenas_bin_i_size)},
    311 	{NAME("nregs"),		CTL(arenas_bin_i_nregs)},
    312 	{NAME("run_size"),	CTL(arenas_bin_i_run_size)}
    313 };
    314 static const ctl_named_node_t super_arenas_bin_i_node[] = {
    315 	{NAME(""),		CHILD(named, arenas_bin_i)}
    316 };
    317 
    318 static const ctl_indexed_node_t arenas_bin_node[] = {
    319 	{INDEX(arenas_bin_i)}
    320 };
    321 
    322 static const ctl_named_node_t arenas_lrun_i_node[] = {
    323 	{NAME("size"),		CTL(arenas_lrun_i_size)}
    324 };
    325 static const ctl_named_node_t super_arenas_lrun_i_node[] = {
    326 	{NAME(""),		CHILD(named, arenas_lrun_i)}
    327 };
    328 
    329 static const ctl_indexed_node_t arenas_lrun_node[] = {
    330 	{INDEX(arenas_lrun_i)}
    331 };
    332 
    333 static const ctl_named_node_t arenas_hchunk_i_node[] = {
    334 	{NAME("size"),		CTL(arenas_hchunk_i_size)}
    335 };
    336 static const ctl_named_node_t super_arenas_hchunk_i_node[] = {
    337 	{NAME(""),		CHILD(named, arenas_hchunk_i)}
    338 };
    339 
    340 static const ctl_indexed_node_t arenas_hchunk_node[] = {
    341 	{INDEX(arenas_hchunk_i)}
    342 };
    343 
    344 static const ctl_named_node_t arenas_node[] = {
    345 	{NAME("narenas"),	CTL(arenas_narenas)},
    346 	{NAME("initialized"),	CTL(arenas_initialized)},
    347 	{NAME("lg_dirty_mult"),	CTL(arenas_lg_dirty_mult)},
    348 	{NAME("quantum"),	CTL(arenas_quantum)},
    349 	{NAME("page"),		CTL(arenas_page)},
    350 	{NAME("tcache_max"),	CTL(arenas_tcache_max)},
    351 	{NAME("nbins"),		CTL(arenas_nbins)},
    352 	{NAME("nhbins"),	CTL(arenas_nhbins)},
    353 	{NAME("bin"),		CHILD(indexed, arenas_bin)},
    354 	{NAME("nlruns"),	CTL(arenas_nlruns)},
    355 	{NAME("lrun"),		CHILD(indexed, arenas_lrun)},
    356 	{NAME("nhchunks"),	CTL(arenas_nhchunks)},
    357 	{NAME("hchunk"),	CHILD(indexed, arenas_hchunk)},
    358 	{NAME("extend"),	CTL(arenas_extend)}
    359 };
    360 
    361 static const ctl_named_node_t	prof_node[] = {
    362 	{NAME("thread_active_init"), CTL(prof_thread_active_init)},
    363 	{NAME("active"),	CTL(prof_active)},
    364 	{NAME("dump"),		CTL(prof_dump)},
    365 	{NAME("gdump"),		CTL(prof_gdump)},
    366 	{NAME("reset"),		CTL(prof_reset)},
    367 	{NAME("interval"),	CTL(prof_interval)},
    368 	{NAME("lg_sample"),	CTL(lg_prof_sample)}
    369 };
    370 
    371 static const ctl_named_node_t stats_arenas_i_metadata_node[] = {
    372 	{NAME("mapped"),	CTL(stats_arenas_i_metadata_mapped)},
    373 	{NAME("allocated"),	CTL(stats_arenas_i_metadata_allocated)}
    374 };
    375 
    376 static const ctl_named_node_t stats_arenas_i_small_node[] = {
    377 	{NAME("allocated"),	CTL(stats_arenas_i_small_allocated)},
    378 	{NAME("nmalloc"),	CTL(stats_arenas_i_small_nmalloc)},
    379 	{NAME("ndalloc"),	CTL(stats_arenas_i_small_ndalloc)},
    380 	{NAME("nrequests"),	CTL(stats_arenas_i_small_nrequests)}
    381 };
    382 
    383 static const ctl_named_node_t stats_arenas_i_large_node[] = {
    384 	{NAME("allocated"),	CTL(stats_arenas_i_large_allocated)},
    385 	{NAME("nmalloc"),	CTL(stats_arenas_i_large_nmalloc)},
    386 	{NAME("ndalloc"),	CTL(stats_arenas_i_large_ndalloc)},
    387 	{NAME("nrequests"),	CTL(stats_arenas_i_large_nrequests)}
    388 };
    389 
    390 static const ctl_named_node_t stats_arenas_i_huge_node[] = {
    391 	{NAME("allocated"),	CTL(stats_arenas_i_huge_allocated)},
    392 	{NAME("nmalloc"),	CTL(stats_arenas_i_huge_nmalloc)},
    393 	{NAME("ndalloc"),	CTL(stats_arenas_i_huge_ndalloc)},
    394 	{NAME("nrequests"),	CTL(stats_arenas_i_huge_nrequests)}
    395 };
    396 
    397 static const ctl_named_node_t stats_arenas_i_bins_j_node[] = {
    398 	{NAME("nmalloc"),	CTL(stats_arenas_i_bins_j_nmalloc)},
    399 	{NAME("ndalloc"),	CTL(stats_arenas_i_bins_j_ndalloc)},
    400 	{NAME("nrequests"),	CTL(stats_arenas_i_bins_j_nrequests)},
    401 	{NAME("curregs"),	CTL(stats_arenas_i_bins_j_curregs)},
    402 	{NAME("nfills"),	CTL(stats_arenas_i_bins_j_nfills)},
    403 	{NAME("nflushes"),	CTL(stats_arenas_i_bins_j_nflushes)},
    404 	{NAME("nruns"),		CTL(stats_arenas_i_bins_j_nruns)},
    405 	{NAME("nreruns"),	CTL(stats_arenas_i_bins_j_nreruns)},
    406 	{NAME("curruns"),	CTL(stats_arenas_i_bins_j_curruns)}
    407 };
    408 static const ctl_named_node_t super_stats_arenas_i_bins_j_node[] = {
    409 	{NAME(""),		CHILD(named, stats_arenas_i_bins_j)}
    410 };
    411 
    412 static const ctl_indexed_node_t stats_arenas_i_bins_node[] = {
    413 	{INDEX(stats_arenas_i_bins_j)}
    414 };
    415 
    416 static const ctl_named_node_t stats_arenas_i_lruns_j_node[] = {
    417 	{NAME("nmalloc"),	CTL(stats_arenas_i_lruns_j_nmalloc)},
    418 	{NAME("ndalloc"),	CTL(stats_arenas_i_lruns_j_ndalloc)},
    419 	{NAME("nrequests"),	CTL(stats_arenas_i_lruns_j_nrequests)},
    420 	{NAME("curruns"),	CTL(stats_arenas_i_lruns_j_curruns)}
    421 };
    422 static const ctl_named_node_t super_stats_arenas_i_lruns_j_node[] = {
    423 	{NAME(""),		CHILD(named, stats_arenas_i_lruns_j)}
    424 };
    425 
    426 static const ctl_indexed_node_t stats_arenas_i_lruns_node[] = {
    427 	{INDEX(stats_arenas_i_lruns_j)}
    428 };
    429 
    430 static const ctl_named_node_t stats_arenas_i_hchunks_j_node[] = {
    431 	{NAME("nmalloc"),	CTL(stats_arenas_i_hchunks_j_nmalloc)},
    432 	{NAME("ndalloc"),	CTL(stats_arenas_i_hchunks_j_ndalloc)},
    433 	{NAME("nrequests"),	CTL(stats_arenas_i_hchunks_j_nrequests)},
    434 	{NAME("curhchunks"),	CTL(stats_arenas_i_hchunks_j_curhchunks)}
    435 };
    436 static const ctl_named_node_t super_stats_arenas_i_hchunks_j_node[] = {
    437 	{NAME(""),		CHILD(named, stats_arenas_i_hchunks_j)}
    438 };
    439 
    440 static const ctl_indexed_node_t stats_arenas_i_hchunks_node[] = {
    441 	{INDEX(stats_arenas_i_hchunks_j)}
    442 };
    443 
    444 static const ctl_named_node_t stats_arenas_i_node[] = {
    445 	{NAME("nthreads"),	CTL(stats_arenas_i_nthreads)},
    446 	{NAME("dss"),		CTL(stats_arenas_i_dss)},
    447 	{NAME("lg_dirty_mult"),	CTL(stats_arenas_i_lg_dirty_mult)},
    448 	{NAME("pactive"),	CTL(stats_arenas_i_pactive)},
    449 	{NAME("pdirty"),	CTL(stats_arenas_i_pdirty)},
    450 	{NAME("mapped"),	CTL(stats_arenas_i_mapped)},
    451 	{NAME("npurge"),	CTL(stats_arenas_i_npurge)},
    452 	{NAME("nmadvise"),	CTL(stats_arenas_i_nmadvise)},
    453 	{NAME("purged"),	CTL(stats_arenas_i_purged)},
    454 	{NAME("metadata"),	CHILD(named, stats_arenas_i_metadata)},
    455 	{NAME("small"),		CHILD(named, stats_arenas_i_small)},
    456 	{NAME("large"),		CHILD(named, stats_arenas_i_large)},
    457 	{NAME("huge"),		CHILD(named, stats_arenas_i_huge)},
    458 	{NAME("bins"),		CHILD(indexed, stats_arenas_i_bins)},
    459 	{NAME("lruns"),		CHILD(indexed, stats_arenas_i_lruns)},
    460 	{NAME("hchunks"),	CHILD(indexed, stats_arenas_i_hchunks)}
    461 };
    462 static const ctl_named_node_t super_stats_arenas_i_node[] = {
    463 	{NAME(""),		CHILD(named, stats_arenas_i)}
    464 };
    465 
    466 static const ctl_indexed_node_t stats_arenas_node[] = {
    467 	{INDEX(stats_arenas_i)}
    468 };
    469 
    470 static const ctl_named_node_t stats_node[] = {
    471 	{NAME("cactive"),	CTL(stats_cactive)},
    472 	{NAME("allocated"),	CTL(stats_allocated)},
    473 	{NAME("active"),	CTL(stats_active)},
    474 	{NAME("metadata"),	CTL(stats_metadata)},
    475 	{NAME("resident"),	CTL(stats_resident)},
    476 	{NAME("mapped"),	CTL(stats_mapped)},
    477 	{NAME("arenas"),	CHILD(indexed, stats_arenas)}
    478 };
    479 
    480 static const ctl_named_node_t	root_node[] = {
    481 	{NAME("version"),	CTL(version)},
    482 	{NAME("epoch"),		CTL(epoch)},
    483 	{NAME("thread"),	CHILD(named, thread)},
    484 	{NAME("config"),	CHILD(named, config)},
    485 	{NAME("opt"),		CHILD(named, opt)},
    486 	{NAME("tcache"),	CHILD(named, tcache)},
    487 	{NAME("arena"),		CHILD(indexed, arena)},
    488 	{NAME("arenas"),	CHILD(named, arenas)},
    489 	{NAME("prof"),		CHILD(named, prof)},
    490 	{NAME("stats"),		CHILD(named, stats)}
    491 };
    492 static const ctl_named_node_t super_root_node[] = {
    493 	{NAME(""),		CHILD(named, root)}
    494 };
    495 
    496 #undef NAME
    497 #undef CHILD
    498 #undef CTL
    499 #undef INDEX
    500 
    501 /******************************************************************************/
    502 
    503 static bool
    504 ctl_arena_init(ctl_arena_stats_t *astats)
    505 {
    506 
    507 	if (astats->lstats == NULL) {
    508 		astats->lstats = (malloc_large_stats_t *)a0malloc(nlclasses *
    509 		    sizeof(malloc_large_stats_t));
    510 		if (astats->lstats == NULL)
    511 			return (true);
    512 	}
    513 
    514 	if (astats->hstats == NULL) {
    515 		astats->hstats = (malloc_huge_stats_t *)a0malloc(nhclasses *
    516 		    sizeof(malloc_huge_stats_t));
    517 		if (astats->hstats == NULL)
    518 			return (true);
    519 	}
    520 
    521 	return (false);
    522 }
    523 
    524 static void
    525 ctl_arena_clear(ctl_arena_stats_t *astats)
    526 {
    527 
    528 	astats->dss = dss_prec_names[dss_prec_limit];
    529 	astats->lg_dirty_mult = -1;
    530 	astats->pactive = 0;
    531 	astats->pdirty = 0;
    532 	if (config_stats) {
    533 		memset(&astats->astats, 0, sizeof(arena_stats_t));
    534 		astats->allocated_small = 0;
    535 		astats->nmalloc_small = 0;
    536 		astats->ndalloc_small = 0;
    537 		astats->nrequests_small = 0;
    538 		memset(astats->bstats, 0, NBINS * sizeof(malloc_bin_stats_t));
    539 		memset(astats->lstats, 0, nlclasses *
    540 		    sizeof(malloc_large_stats_t));
    541 		memset(astats->hstats, 0, nhclasses *
    542 		    sizeof(malloc_huge_stats_t));
    543 	}
    544 }
    545 
    546 static void
    547 ctl_arena_stats_amerge(ctl_arena_stats_t *cstats, arena_t *arena)
    548 {
    549 	unsigned i;
    550 
    551 	arena_stats_merge(arena, &cstats->dss, &cstats->lg_dirty_mult,
    552 	    &cstats->pactive, &cstats->pdirty, &cstats->astats, cstats->bstats,
    553 	    cstats->lstats, cstats->hstats);
    554 
    555 	for (i = 0; i < NBINS; i++) {
    556 		cstats->allocated_small += cstats->bstats[i].curregs *
    557 		    index2size(i);
    558 		cstats->nmalloc_small += cstats->bstats[i].nmalloc;
    559 		cstats->ndalloc_small += cstats->bstats[i].ndalloc;
    560 		cstats->nrequests_small += cstats->bstats[i].nrequests;
    561 	}
    562 }
    563 
    564 static void
    565 ctl_arena_stats_smerge(ctl_arena_stats_t *sstats, ctl_arena_stats_t *astats)
    566 {
    567 	unsigned i;
    568 
    569 	sstats->pactive += astats->pactive;
    570 	sstats->pdirty += astats->pdirty;
    571 
    572 	sstats->astats.mapped += astats->astats.mapped;
    573 	sstats->astats.npurge += astats->astats.npurge;
    574 	sstats->astats.nmadvise += astats->astats.nmadvise;
    575 	sstats->astats.purged += astats->astats.purged;
    576 
    577 	sstats->astats.metadata_mapped += astats->astats.metadata_mapped;
    578 	sstats->astats.metadata_allocated += astats->astats.metadata_allocated;
    579 
    580 	sstats->allocated_small += astats->allocated_small;
    581 	sstats->nmalloc_small += astats->nmalloc_small;
    582 	sstats->ndalloc_small += astats->ndalloc_small;
    583 	sstats->nrequests_small += astats->nrequests_small;
    584 
    585 	sstats->astats.allocated_large += astats->astats.allocated_large;
    586 	sstats->astats.nmalloc_large += astats->astats.nmalloc_large;
    587 	sstats->astats.ndalloc_large += astats->astats.ndalloc_large;
    588 	sstats->astats.nrequests_large += astats->astats.nrequests_large;
    589 
    590 	sstats->astats.allocated_huge += astats->astats.allocated_huge;
    591 	sstats->astats.nmalloc_huge += astats->astats.nmalloc_huge;
    592 	sstats->astats.ndalloc_huge += astats->astats.ndalloc_huge;
    593 
    594 	for (i = 0; i < NBINS; i++) {
    595 		sstats->bstats[i].nmalloc += astats->bstats[i].nmalloc;
    596 		sstats->bstats[i].ndalloc += astats->bstats[i].ndalloc;
    597 		sstats->bstats[i].nrequests += astats->bstats[i].nrequests;
    598 		sstats->bstats[i].curregs += astats->bstats[i].curregs;
    599 		if (config_tcache) {
    600 			sstats->bstats[i].nfills += astats->bstats[i].nfills;
    601 			sstats->bstats[i].nflushes +=
    602 			    astats->bstats[i].nflushes;
    603 		}
    604 		sstats->bstats[i].nruns += astats->bstats[i].nruns;
    605 		sstats->bstats[i].reruns += astats->bstats[i].reruns;
    606 		sstats->bstats[i].curruns += astats->bstats[i].curruns;
    607 	}
    608 
    609 	for (i = 0; i < nlclasses; i++) {
    610 		sstats->lstats[i].nmalloc += astats->lstats[i].nmalloc;
    611 		sstats->lstats[i].ndalloc += astats->lstats[i].ndalloc;
    612 		sstats->lstats[i].nrequests += astats->lstats[i].nrequests;
    613 		sstats->lstats[i].curruns += astats->lstats[i].curruns;
    614 	}
    615 
    616 	for (i = 0; i < nhclasses; i++) {
    617 		sstats->hstats[i].nmalloc += astats->hstats[i].nmalloc;
    618 		sstats->hstats[i].ndalloc += astats->hstats[i].ndalloc;
    619 		sstats->hstats[i].curhchunks += astats->hstats[i].curhchunks;
    620 	}
    621 }
    622 
    623 static void
    624 ctl_arena_refresh(arena_t *arena, unsigned i)
    625 {
    626 	ctl_arena_stats_t *astats = &ctl_stats.arenas[i];
    627 	ctl_arena_stats_t *sstats = &ctl_stats.arenas[ctl_stats.narenas];
    628 
    629 	ctl_arena_clear(astats);
    630 
    631 	sstats->nthreads += astats->nthreads;
    632 	if (config_stats) {
    633 		ctl_arena_stats_amerge(astats, arena);
    634 		/* Merge into sum stats as well. */
    635 		ctl_arena_stats_smerge(sstats, astats);
    636 	} else {
    637 		astats->pactive += arena->nactive;
    638 		astats->pdirty += arena->ndirty;
    639 		/* Merge into sum stats as well. */
    640 		sstats->pactive += arena->nactive;
    641 		sstats->pdirty += arena->ndirty;
    642 	}
    643 }
    644 
    645 static bool
    646 ctl_grow(void)
    647 {
    648 	ctl_arena_stats_t *astats;
    649 
    650 	/* Initialize new arena. */
    651 	if (arena_init(ctl_stats.narenas) == NULL)
    652 		return (true);
    653 
    654 	/* Allocate extended arena stats. */
    655 	astats = (ctl_arena_stats_t *)a0malloc((ctl_stats.narenas + 2) *
    656 	    sizeof(ctl_arena_stats_t));
    657 	if (astats == NULL)
    658 		return (true);
    659 
    660 	/* Initialize the new astats element. */
    661 	memcpy(astats, ctl_stats.arenas, (ctl_stats.narenas + 1) *
    662 	    sizeof(ctl_arena_stats_t));
    663 	memset(&astats[ctl_stats.narenas + 1], 0, sizeof(ctl_arena_stats_t));
    664 	if (ctl_arena_init(&astats[ctl_stats.narenas + 1])) {
    665 		a0dalloc(astats);
    666 		return (true);
    667 	}
    668 	/* Swap merged stats to their new location. */
    669 	{
    670 		ctl_arena_stats_t tstats;
    671 		memcpy(&tstats, &astats[ctl_stats.narenas],
    672 		    sizeof(ctl_arena_stats_t));
    673 		memcpy(&astats[ctl_stats.narenas],
    674 		    &astats[ctl_stats.narenas + 1], sizeof(ctl_arena_stats_t));
    675 		memcpy(&astats[ctl_stats.narenas + 1], &tstats,
    676 		    sizeof(ctl_arena_stats_t));
    677 	}
    678 	a0dalloc(ctl_stats.arenas);
    679 	ctl_stats.arenas = astats;
    680 	ctl_stats.narenas++;
    681 
    682 	return (false);
    683 }
    684 
    685 static void
    686 ctl_refresh(void)
    687 {
    688 	tsd_t *tsd;
    689 	unsigned i;
    690 	bool refreshed;
    691 	VARIABLE_ARRAY(arena_t *, tarenas, ctl_stats.narenas);
    692 
    693 	/*
    694 	 * Clear sum stats, since they will be merged into by
    695 	 * ctl_arena_refresh().
    696 	 */
    697 	ctl_stats.arenas[ctl_stats.narenas].nthreads = 0;
    698 	ctl_arena_clear(&ctl_stats.arenas[ctl_stats.narenas]);
    699 
    700 	tsd = tsd_fetch();
    701 	for (i = 0, refreshed = false; i < ctl_stats.narenas; i++) {
    702 		tarenas[i] = arena_get(tsd, i, false, false);
    703 		if (tarenas[i] == NULL && !refreshed) {
    704 			tarenas[i] = arena_get(tsd, i, false, true);
    705 			refreshed = true;
    706 		}
    707 	}
    708 
    709 	for (i = 0; i < ctl_stats.narenas; i++) {
    710 		if (tarenas[i] != NULL)
    711 			ctl_stats.arenas[i].nthreads = arena_nbound(i);
    712 		else
    713 			ctl_stats.arenas[i].nthreads = 0;
    714 	}
    715 
    716 	for (i = 0; i < ctl_stats.narenas; i++) {
    717 		bool initialized = (tarenas[i] != NULL);
    718 
    719 		ctl_stats.arenas[i].initialized = initialized;
    720 		if (initialized)
    721 			ctl_arena_refresh(tarenas[i], i);
    722 	}
    723 
    724 	if (config_stats) {
    725 		size_t base_allocated, base_resident, base_mapped;
    726 		base_stats_get(&base_allocated, &base_resident, &base_mapped);
    727 		ctl_stats.allocated =
    728 		    ctl_stats.arenas[ctl_stats.narenas].allocated_small +
    729 		    ctl_stats.arenas[ctl_stats.narenas].astats.allocated_large +
    730 		    ctl_stats.arenas[ctl_stats.narenas].astats.allocated_huge;
    731 		ctl_stats.active =
    732 		    (ctl_stats.arenas[ctl_stats.narenas].pactive << LG_PAGE);
    733 		ctl_stats.metadata = base_allocated +
    734 		    ctl_stats.arenas[ctl_stats.narenas].astats.metadata_mapped +
    735 		    ctl_stats.arenas[ctl_stats.narenas].astats
    736 		    .metadata_allocated;
    737 		ctl_stats.resident = base_resident +
    738 		    ctl_stats.arenas[ctl_stats.narenas].astats.metadata_mapped +
    739 		    ((ctl_stats.arenas[ctl_stats.narenas].pactive +
    740 		    ctl_stats.arenas[ctl_stats.narenas].pdirty) << LG_PAGE);
    741 		ctl_stats.mapped = base_mapped +
    742 		    ctl_stats.arenas[ctl_stats.narenas].astats.mapped;
    743 	}
    744 
    745 	ctl_epoch++;
    746 }
    747 
    748 static bool
    749 ctl_init(void)
    750 {
    751 	bool ret;
    752 
    753 	malloc_mutex_lock(&ctl_mtx);
    754 	if (!ctl_initialized) {
    755 		/*
    756 		 * Allocate space for one extra arena stats element, which
    757 		 * contains summed stats across all arenas.
    758 		 */
    759 		ctl_stats.narenas = narenas_total_get();
    760 		ctl_stats.arenas = (ctl_arena_stats_t *)a0malloc(
    761 		    (ctl_stats.narenas + 1) * sizeof(ctl_arena_stats_t));
    762 		if (ctl_stats.arenas == NULL) {
    763 			ret = true;
    764 			goto label_return;
    765 		}
    766 		memset(ctl_stats.arenas, 0, (ctl_stats.narenas + 1) *
    767 		    sizeof(ctl_arena_stats_t));
    768 
    769 		/*
    770 		 * Initialize all stats structures, regardless of whether they
    771 		 * ever get used.  Lazy initialization would allow errors to
    772 		 * cause inconsistent state to be viewable by the application.
    773 		 */
    774 		if (config_stats) {
    775 			unsigned i;
    776 			for (i = 0; i <= ctl_stats.narenas; i++) {
    777 				if (ctl_arena_init(&ctl_stats.arenas[i])) {
    778 					unsigned j;
    779 					for (j = 0; j < i; j++) {
    780 						a0dalloc(
    781 						    ctl_stats.arenas[j].lstats);
    782 						a0dalloc(
    783 						    ctl_stats.arenas[j].hstats);
    784 					}
    785 					a0dalloc(ctl_stats.arenas);
    786 					ctl_stats.arenas = NULL;
    787 					ret = true;
    788 					goto label_return;
    789 				}
    790 			}
    791 		}
    792 		ctl_stats.arenas[ctl_stats.narenas].initialized = true;
    793 
    794 		ctl_epoch = 0;
    795 		ctl_refresh();
    796 		ctl_initialized = true;
    797 	}
    798 
    799 	ret = false;
    800 label_return:
    801 	malloc_mutex_unlock(&ctl_mtx);
    802 	return (ret);
    803 }
    804 
    805 static int
    806 ctl_lookup(const char *name, ctl_node_t const **nodesp, size_t *mibp,
    807     size_t *depthp)
    808 {
    809 	int ret;
    810 	const char *elm, *tdot, *dot;
    811 	size_t elen, i, j;
    812 	const ctl_named_node_t *node;
    813 
    814 	elm = name;
    815 	/* Equivalent to strchrnul(). */
    816 	dot = ((tdot = strchr(elm, '.')) != NULL) ? tdot : strchr(elm, '\0');
    817 	elen = (size_t)((uintptr_t)dot - (uintptr_t)elm);
    818 	if (elen == 0) {
    819 		ret = ENOENT;
    820 		goto label_return;
    821 	}
    822 	node = super_root_node;
    823 	for (i = 0; i < *depthp; i++) {
    824 		assert(node);
    825 		assert(node->nchildren > 0);
    826 		if (ctl_named_node(node->children) != NULL) {
    827 			const ctl_named_node_t *pnode = node;
    828 
    829 			/* Children are named. */
    830 			for (j = 0; j < node->nchildren; j++) {
    831 				const ctl_named_node_t *child =
    832 				    ctl_named_children(node, j);
    833 				if (strlen(child->name) == elen &&
    834 				    strncmp(elm, child->name, elen) == 0) {
    835 					node = child;
    836 					if (nodesp != NULL)
    837 						nodesp[i] =
    838 						    (const ctl_node_t *)node;
    839 					mibp[i] = j;
    840 					break;
    841 				}
    842 			}
    843 			if (node == pnode) {
    844 				ret = ENOENT;
    845 				goto label_return;
    846 			}
    847 		} else {
    848 			uintmax_t index;
    849 			const ctl_indexed_node_t *inode;
    850 
    851 			/* Children are indexed. */
    852 			index = malloc_strtoumax(elm, NULL, 10);
    853 			if (index == UINTMAX_MAX || index > SIZE_T_MAX) {
    854 				ret = ENOENT;
    855 				goto label_return;
    856 			}
    857 
    858 			inode = ctl_indexed_node(node->children);
    859 			node = inode->index(mibp, *depthp, (size_t)index);
    860 			if (node == NULL) {
    861 				ret = ENOENT;
    862 				goto label_return;
    863 			}
    864 
    865 			if (nodesp != NULL)
    866 				nodesp[i] = (const ctl_node_t *)node;
    867 			mibp[i] = (size_t)index;
    868 		}
    869 
    870 		if (node->ctl != NULL) {
    871 			/* Terminal node. */
    872 			if (*dot != '\0') {
    873 				/*
    874 				 * The name contains more elements than are
    875 				 * in this path through the tree.
    876 				 */
    877 				ret = ENOENT;
    878 				goto label_return;
    879 			}
    880 			/* Complete lookup successful. */
    881 			*depthp = i + 1;
    882 			break;
    883 		}
    884 
    885 		/* Update elm. */
    886 		if (*dot == '\0') {
    887 			/* No more elements. */
    888 			ret = ENOENT;
    889 			goto label_return;
    890 		}
    891 		elm = &dot[1];
    892 		dot = ((tdot = strchr(elm, '.')) != NULL) ? tdot :
    893 		    strchr(elm, '\0');
    894 		elen = (size_t)((uintptr_t)dot - (uintptr_t)elm);
    895 	}
    896 
    897 	ret = 0;
    898 label_return:
    899 	return (ret);
    900 }
    901 
    902 int
    903 ctl_byname(const char *name, void *oldp, size_t *oldlenp, void *newp,
    904     size_t newlen)
    905 {
    906 	int ret;
    907 	size_t depth;
    908 	ctl_node_t const *nodes[CTL_MAX_DEPTH];
    909 	size_t mib[CTL_MAX_DEPTH];
    910 	const ctl_named_node_t *node;
    911 
    912 	if (!ctl_initialized && ctl_init()) {
    913 		ret = EAGAIN;
    914 		goto label_return;
    915 	}
    916 
    917 	depth = CTL_MAX_DEPTH;
    918 	ret = ctl_lookup(name, nodes, mib, &depth);
    919 	if (ret != 0)
    920 		goto label_return;
    921 
    922 	node = ctl_named_node(nodes[depth-1]);
    923 	if (node != NULL && node->ctl)
    924 		ret = node->ctl(mib, depth, oldp, oldlenp, newp, newlen);
    925 	else {
    926 		/* The name refers to a partial path through the ctl tree. */
    927 		ret = ENOENT;
    928 	}
    929 
    930 label_return:
    931 	return(ret);
    932 }
    933 
    934 int
    935 ctl_nametomib(const char *name, size_t *mibp, size_t *miblenp)
    936 {
    937 	int ret;
    938 
    939 	if (!ctl_initialized && ctl_init()) {
    940 		ret = EAGAIN;
    941 		goto label_return;
    942 	}
    943 
    944 	ret = ctl_lookup(name, NULL, mibp, miblenp);
    945 label_return:
    946 	return(ret);
    947 }
    948 
    949 int
    950 ctl_bymib(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
    951     void *newp, size_t newlen)
    952 {
    953 	int ret;
    954 	const ctl_named_node_t *node;
    955 	size_t i;
    956 
    957 	if (!ctl_initialized && ctl_init()) {
    958 		ret = EAGAIN;
    959 		goto label_return;
    960 	}
    961 
    962 	/* Iterate down the tree. */
    963 	node = super_root_node;
    964 	for (i = 0; i < miblen; i++) {
    965 		assert(node);
    966 		assert(node->nchildren > 0);
    967 		if (ctl_named_node(node->children) != NULL) {
    968 			/* Children are named. */
    969 			if (node->nchildren <= mib[i]) {
    970 				ret = ENOENT;
    971 				goto label_return;
    972 			}
    973 			node = ctl_named_children(node, mib[i]);
    974 		} else {
    975 			const ctl_indexed_node_t *inode;
    976 
    977 			/* Indexed element. */
    978 			inode = ctl_indexed_node(node->children);
    979 			node = inode->index(mib, miblen, mib[i]);
    980 			if (node == NULL) {
    981 				ret = ENOENT;
    982 				goto label_return;
    983 			}
    984 		}
    985 	}
    986 
    987 	/* Call the ctl function. */
    988 	if (node && node->ctl)
    989 		ret = node->ctl(mib, miblen, oldp, oldlenp, newp, newlen);
    990 	else {
    991 		/* Partial MIB. */
    992 		ret = ENOENT;
    993 	}
    994 
    995 label_return:
    996 	return(ret);
    997 }
    998 
    999 bool
   1000 ctl_boot(void)
   1001 {
   1002 
   1003 	if (malloc_mutex_init(&ctl_mtx))
   1004 		return (true);
   1005 
   1006 	ctl_initialized = false;
   1007 
   1008 	return (false);
   1009 }
   1010 
   1011 void
   1012 ctl_prefork(void)
   1013 {
   1014 
   1015 	malloc_mutex_prefork(&ctl_mtx);
   1016 }
   1017 
   1018 void
   1019 ctl_postfork_parent(void)
   1020 {
   1021 
   1022 	malloc_mutex_postfork_parent(&ctl_mtx);
   1023 }
   1024 
   1025 void
   1026 ctl_postfork_child(void)
   1027 {
   1028 
   1029 	malloc_mutex_postfork_child(&ctl_mtx);
   1030 }
   1031 
   1032 /******************************************************************************/
   1033 /* *_ctl() functions. */
   1034 
   1035 #define	READONLY()	do {						\
   1036 	if (newp != NULL || newlen != 0) {				\
   1037 		ret = EPERM;						\
   1038 		goto label_return;					\
   1039 	}								\
   1040 } while (0)
   1041 
   1042 #define	WRITEONLY()	do {						\
   1043 	if (oldp != NULL || oldlenp != NULL) {				\
   1044 		ret = EPERM;						\
   1045 		goto label_return;					\
   1046 	}								\
   1047 } while (0)
   1048 
   1049 #define	READ_XOR_WRITE()	do {					\
   1050 	if ((oldp != NULL && oldlenp != NULL) && (newp != NULL ||	\
   1051 	    newlen != 0)) {						\
   1052 		ret = EPERM;						\
   1053 		goto label_return;					\
   1054 	}								\
   1055 } while (0)
   1056 
   1057 #define	READ(v, t)	do {						\
   1058 	if (oldp != NULL && oldlenp != NULL) {				\
   1059 		if (*oldlenp != sizeof(t)) {				\
   1060 			size_t	copylen = (sizeof(t) <= *oldlenp)	\
   1061 			    ? sizeof(t) : *oldlenp;			\
   1062 			memcpy(oldp, (void *)&(v), copylen);		\
   1063 			ret = EINVAL;					\
   1064 			goto label_return;				\
   1065 		} else							\
   1066 			*(t *)oldp = (v);				\
   1067 	}								\
   1068 } while (0)
   1069 
   1070 #define	WRITE(v, t)	do {						\
   1071 	if (newp != NULL) {						\
   1072 		if (newlen != sizeof(t)) {				\
   1073 			ret = EINVAL;					\
   1074 			goto label_return;				\
   1075 		}							\
   1076 		(v) = *(t *)newp;					\
   1077 	}								\
   1078 } while (0)
   1079 
   1080 /*
   1081  * There's a lot of code duplication in the following macros due to limitations
   1082  * in how nested cpp macros are expanded.
   1083  */
   1084 #define	CTL_RO_CLGEN(c, l, n, v, t)					\
   1085 static int								\
   1086 n##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,	\
   1087     void *newp, size_t newlen)						\
   1088 {									\
   1089 	int ret;							\
   1090 	t oldval;							\
   1091 									\
   1092 	if (!(c))							\
   1093 		return (ENOENT);					\
   1094 	if (l)								\
   1095 		malloc_mutex_lock(&ctl_mtx);				\
   1096 	READONLY();							\
   1097 	oldval = (v);							\
   1098 	READ(oldval, t);						\
   1099 									\
   1100 	ret = 0;							\
   1101 label_return:								\
   1102 	if (l)								\
   1103 		malloc_mutex_unlock(&ctl_mtx);				\
   1104 	return (ret);							\
   1105 }
   1106 
   1107 #define	CTL_RO_CGEN(c, n, v, t)						\
   1108 static int								\
   1109 n##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,	\
   1110     void *newp, size_t newlen)						\
   1111 {									\
   1112 	int ret;							\
   1113 	t oldval;							\
   1114 									\
   1115 	if (!(c))							\
   1116 		return (ENOENT);					\
   1117 	malloc_mutex_lock(&ctl_mtx);					\
   1118 	READONLY();							\
   1119 	oldval = (v);							\
   1120 	READ(oldval, t);						\
   1121 									\
   1122 	ret = 0;							\
   1123 label_return:								\
   1124 	malloc_mutex_unlock(&ctl_mtx);					\
   1125 	return (ret);							\
   1126 }
   1127 
   1128 #define	CTL_RO_GEN(n, v, t)						\
   1129 static int								\
   1130 n##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,	\
   1131     void *newp, size_t newlen)						\
   1132 {									\
   1133 	int ret;							\
   1134 	t oldval;							\
   1135 									\
   1136 	malloc_mutex_lock(&ctl_mtx);					\
   1137 	READONLY();							\
   1138 	oldval = (v);							\
   1139 	READ(oldval, t);						\
   1140 									\
   1141 	ret = 0;							\
   1142 label_return:								\
   1143 	malloc_mutex_unlock(&ctl_mtx);					\
   1144 	return (ret);							\
   1145 }
   1146 
   1147 /*
   1148  * ctl_mtx is not acquired, under the assumption that no pertinent data will
   1149  * mutate during the call.
   1150  */
   1151 #define	CTL_RO_NL_CGEN(c, n, v, t)					\
   1152 static int								\
   1153 n##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,	\
   1154     void *newp, size_t newlen)						\
   1155 {									\
   1156 	int ret;							\
   1157 	t oldval;							\
   1158 									\
   1159 	if (!(c))							\
   1160 		return (ENOENT);					\
   1161 	READONLY();							\
   1162 	oldval = (v);							\
   1163 	READ(oldval, t);						\
   1164 									\
   1165 	ret = 0;							\
   1166 label_return:								\
   1167 	return (ret);							\
   1168 }
   1169 
   1170 #define	CTL_RO_NL_GEN(n, v, t)						\
   1171 static int								\
   1172 n##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,	\
   1173     void *newp, size_t newlen)						\
   1174 {									\
   1175 	int ret;							\
   1176 	t oldval;							\
   1177 									\
   1178 	READONLY();							\
   1179 	oldval = (v);							\
   1180 	READ(oldval, t);						\
   1181 									\
   1182 	ret = 0;							\
   1183 label_return:								\
   1184 	return (ret);							\
   1185 }
   1186 
   1187 #define	CTL_TSD_RO_NL_CGEN(c, n, m, t)					\
   1188 static int								\
   1189 n##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,	\
   1190     void *newp, size_t newlen)						\
   1191 {									\
   1192 	int ret;							\
   1193 	t oldval;							\
   1194 	tsd_t *tsd;							\
   1195 									\
   1196 	if (!(c))							\
   1197 		return (ENOENT);					\
   1198 	READONLY();							\
   1199 	tsd = tsd_fetch();						\
   1200 	oldval = (m(tsd));						\
   1201 	READ(oldval, t);						\
   1202 									\
   1203 	ret = 0;							\
   1204 label_return:								\
   1205 	return (ret);							\
   1206 }
   1207 
   1208 #define	CTL_RO_BOOL_CONFIG_GEN(n)					\
   1209 static int								\
   1210 n##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,	\
   1211     void *newp, size_t newlen)						\
   1212 {									\
   1213 	int ret;							\
   1214 	bool oldval;							\
   1215 									\
   1216 	READONLY();							\
   1217 	oldval = n;							\
   1218 	READ(oldval, bool);						\
   1219 									\
   1220 	ret = 0;							\
   1221 label_return:								\
   1222 	return (ret);							\
   1223 }
   1224 
   1225 /******************************************************************************/
   1226 
   1227 CTL_RO_NL_GEN(version, JEMALLOC_VERSION, const char *)
   1228 
   1229 static int
   1230 epoch_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
   1231     void *newp, size_t newlen)
   1232 {
   1233 	int ret;
   1234 	UNUSED uint64_t newval;
   1235 
   1236 	malloc_mutex_lock(&ctl_mtx);
   1237 	WRITE(newval, uint64_t);
   1238 	if (newp != NULL)
   1239 		ctl_refresh();
   1240 	READ(ctl_epoch, uint64_t);
   1241 
   1242 	ret = 0;
   1243 label_return:
   1244 	malloc_mutex_unlock(&ctl_mtx);
   1245 	return (ret);
   1246 }
   1247 
   1248 /******************************************************************************/
   1249 
   1250 CTL_RO_BOOL_CONFIG_GEN(config_debug)
   1251 CTL_RO_BOOL_CONFIG_GEN(config_fill)
   1252 CTL_RO_BOOL_CONFIG_GEN(config_lazy_lock)
   1253 CTL_RO_BOOL_CONFIG_GEN(config_munmap)
   1254 CTL_RO_BOOL_CONFIG_GEN(config_prof)
   1255 CTL_RO_BOOL_CONFIG_GEN(config_prof_libgcc)
   1256 CTL_RO_BOOL_CONFIG_GEN(config_prof_libunwind)
   1257 CTL_RO_BOOL_CONFIG_GEN(config_stats)
   1258 CTL_RO_BOOL_CONFIG_GEN(config_tcache)
   1259 CTL_RO_BOOL_CONFIG_GEN(config_tls)
   1260 CTL_RO_BOOL_CONFIG_GEN(config_utrace)
   1261 CTL_RO_BOOL_CONFIG_GEN(config_valgrind)
   1262 CTL_RO_BOOL_CONFIG_GEN(config_xmalloc)
   1263 
   1264 /******************************************************************************/
   1265 
   1266 CTL_RO_NL_GEN(opt_abort, opt_abort, bool)
   1267 CTL_RO_NL_GEN(opt_dss, opt_dss, const char *)
   1268 CTL_RO_NL_GEN(opt_lg_chunk, opt_lg_chunk, size_t)
   1269 CTL_RO_NL_GEN(opt_narenas, opt_narenas, size_t)
   1270 CTL_RO_NL_GEN(opt_lg_dirty_mult, opt_lg_dirty_mult, ssize_t)
   1271 CTL_RO_NL_GEN(opt_stats_print, opt_stats_print, bool)
   1272 CTL_RO_NL_CGEN(config_fill, opt_junk, opt_junk, const char *)
   1273 CTL_RO_NL_CGEN(config_fill, opt_quarantine, opt_quarantine, size_t)
   1274 CTL_RO_NL_CGEN(config_fill, opt_redzone, opt_redzone, bool)
   1275 CTL_RO_NL_CGEN(config_fill, opt_zero, opt_zero, bool)
   1276 CTL_RO_NL_CGEN(config_utrace, opt_utrace, opt_utrace, bool)
   1277 CTL_RO_NL_CGEN(config_xmalloc, opt_xmalloc, opt_xmalloc, bool)
   1278 CTL_RO_NL_CGEN(config_tcache, opt_tcache, opt_tcache, bool)
   1279 CTL_RO_NL_CGEN(config_tcache, opt_lg_tcache_max, opt_lg_tcache_max, ssize_t)
   1280 CTL_RO_NL_CGEN(config_prof, opt_prof, opt_prof, bool)
   1281 CTL_RO_NL_CGEN(config_prof, opt_prof_prefix, opt_prof_prefix, const char *)
   1282 CTL_RO_NL_CGEN(config_prof, opt_prof_active, opt_prof_active, bool)
   1283 CTL_RO_NL_CGEN(config_prof, opt_prof_thread_active_init,
   1284     opt_prof_thread_active_init, bool)
   1285 CTL_RO_NL_CGEN(config_prof, opt_lg_prof_sample, opt_lg_prof_sample, size_t)
   1286 CTL_RO_NL_CGEN(config_prof, opt_prof_accum, opt_prof_accum, bool)
   1287 CTL_RO_NL_CGEN(config_prof, opt_lg_prof_interval, opt_lg_prof_interval, ssize_t)
   1288 CTL_RO_NL_CGEN(config_prof, opt_prof_gdump, opt_prof_gdump, bool)
   1289 CTL_RO_NL_CGEN(config_prof, opt_prof_final, opt_prof_final, bool)
   1290 CTL_RO_NL_CGEN(config_prof, opt_prof_leak, opt_prof_leak, bool)
   1291 
   1292 /******************************************************************************/
   1293 
   1294 static int
   1295 thread_arena_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
   1296     void *newp, size_t newlen)
   1297 {
   1298 	int ret;
   1299 	tsd_t *tsd;
   1300 	arena_t *oldarena;
   1301 	unsigned newind, oldind;
   1302 
   1303 	tsd = tsd_fetch();
   1304 	oldarena = arena_choose(tsd, NULL);
   1305 	if (oldarena == NULL)
   1306 		return (EAGAIN);
   1307 
   1308 	malloc_mutex_lock(&ctl_mtx);
   1309 	newind = oldind = oldarena->ind;
   1310 	WRITE(newind, unsigned);
   1311 	READ(oldind, unsigned);
   1312 	if (newind != oldind) {
   1313 		arena_t *newarena;
   1314 
   1315 		if (newind >= ctl_stats.narenas) {
   1316 			/* New arena index is out of range. */
   1317 			ret = EFAULT;
   1318 			goto label_return;
   1319 		}
   1320 
   1321 		/* Initialize arena if necessary. */
   1322 		newarena = arena_get(tsd, newind, true, true);
   1323 		if (newarena == NULL) {
   1324 			ret = EAGAIN;
   1325 			goto label_return;
   1326 		}
   1327 		/* Set new arena/tcache associations. */
   1328 		arena_migrate(tsd, oldind, newind);
   1329 		if (config_tcache) {
   1330 			tcache_t *tcache = tsd_tcache_get(tsd);
   1331 			if (tcache != NULL) {
   1332 				tcache_arena_reassociate(tcache, oldarena,
   1333 				    newarena);
   1334 			}
   1335 		}
   1336 	}
   1337 
   1338 	ret = 0;
   1339 label_return:
   1340 	malloc_mutex_unlock(&ctl_mtx);
   1341 	return (ret);
   1342 }
   1343 
   1344 CTL_TSD_RO_NL_CGEN(config_stats, thread_allocated, tsd_thread_allocated_get,
   1345     uint64_t)
   1346 CTL_TSD_RO_NL_CGEN(config_stats, thread_allocatedp, tsd_thread_allocatedp_get,
   1347     uint64_t *)
   1348 CTL_TSD_RO_NL_CGEN(config_stats, thread_deallocated, tsd_thread_deallocated_get,
   1349     uint64_t)
   1350 CTL_TSD_RO_NL_CGEN(config_stats, thread_deallocatedp,
   1351     tsd_thread_deallocatedp_get, uint64_t *)
   1352 
   1353 static int
   1354 thread_tcache_enabled_ctl(const size_t *mib, size_t miblen, void *oldp,
   1355     size_t *oldlenp, void *newp, size_t newlen)
   1356 {
   1357 	int ret;
   1358 	bool oldval;
   1359 
   1360 	if (!config_tcache)
   1361 		return (ENOENT);
   1362 
   1363 	oldval = tcache_enabled_get();
   1364 	if (newp != NULL) {
   1365 		if (newlen != sizeof(bool)) {
   1366 			ret = EINVAL;
   1367 			goto label_return;
   1368 		}
   1369 		tcache_enabled_set(*(bool *)newp);
   1370 	}
   1371 	READ(oldval, bool);
   1372 
   1373 	ret = 0;
   1374 label_return:
   1375 	return (ret);
   1376 }
   1377 
   1378 static int
   1379 thread_tcache_flush_ctl(const size_t *mib, size_t miblen, void *oldp,
   1380     size_t *oldlenp, void *newp, size_t newlen)
   1381 {
   1382 	int ret;
   1383 
   1384 	if (!config_tcache)
   1385 		return (ENOENT);
   1386 
   1387 	READONLY();
   1388 	WRITEONLY();
   1389 
   1390 	tcache_flush();
   1391 
   1392 	ret = 0;
   1393 label_return:
   1394 	return (ret);
   1395 }
   1396 
   1397 static int
   1398 thread_prof_name_ctl(const size_t *mib, size_t miblen, void *oldp,
   1399     size_t *oldlenp, void *newp, size_t newlen)
   1400 {
   1401 	int ret;
   1402 
   1403 	if (!config_prof)
   1404 		return (ENOENT);
   1405 
   1406 	READ_XOR_WRITE();
   1407 
   1408 	if (newp != NULL) {
   1409 		tsd_t *tsd;
   1410 
   1411 		if (newlen != sizeof(const char *)) {
   1412 			ret = EINVAL;
   1413 			goto label_return;
   1414 		}
   1415 
   1416 		tsd = tsd_fetch();
   1417 
   1418 		if ((ret = prof_thread_name_set(tsd, *(const char **)newp)) !=
   1419 		    0)
   1420 			goto label_return;
   1421 	} else {
   1422 		const char *oldname = prof_thread_name_get();
   1423 		READ(oldname, const char *);
   1424 	}
   1425 
   1426 	ret = 0;
   1427 label_return:
   1428 	return (ret);
   1429 }
   1430 
   1431 static int
   1432 thread_prof_active_ctl(const size_t *mib, size_t miblen, void *oldp,
   1433     size_t *oldlenp, void *newp, size_t newlen)
   1434 {
   1435 	int ret;
   1436 	bool oldval;
   1437 
   1438 	if (!config_prof)
   1439 		return (ENOENT);
   1440 
   1441 	oldval = prof_thread_active_get();
   1442 	if (newp != NULL) {
   1443 		if (newlen != sizeof(bool)) {
   1444 			ret = EINVAL;
   1445 			goto label_return;
   1446 		}
   1447 		if (prof_thread_active_set(*(bool *)newp)) {
   1448 			ret = EAGAIN;
   1449 			goto label_return;
   1450 		}
   1451 	}
   1452 	READ(oldval, bool);
   1453 
   1454 	ret = 0;
   1455 label_return:
   1456 	return (ret);
   1457 }
   1458 
   1459 /******************************************************************************/
   1460 
   1461 static int
   1462 tcache_create_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
   1463     void *newp, size_t newlen)
   1464 {
   1465 	int ret;
   1466 	tsd_t *tsd;
   1467 	unsigned tcache_ind;
   1468 
   1469 	if (!config_tcache)
   1470 		return (ENOENT);
   1471 
   1472 	tsd = tsd_fetch();
   1473 
   1474 	malloc_mutex_lock(&ctl_mtx);
   1475 	READONLY();
   1476 	if (tcaches_create(tsd, &tcache_ind)) {
   1477 		ret = EFAULT;
   1478 		goto label_return;
   1479 	}
   1480 	READ(tcache_ind, unsigned);
   1481 
   1482 	ret = 0;
   1483 label_return:
   1484 	malloc_mutex_unlock(&ctl_mtx);
   1485 	return (ret);
   1486 }
   1487 
   1488 static int
   1489 tcache_flush_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
   1490     void *newp, size_t newlen)
   1491 {
   1492 	int ret;
   1493 	tsd_t *tsd;
   1494 	unsigned tcache_ind;
   1495 
   1496 	if (!config_tcache)
   1497 		return (ENOENT);
   1498 
   1499 	tsd = tsd_fetch();
   1500 
   1501 	WRITEONLY();
   1502 	tcache_ind = UINT_MAX;
   1503 	WRITE(tcache_ind, unsigned);
   1504 	if (tcache_ind == UINT_MAX) {
   1505 		ret = EFAULT;
   1506 		goto label_return;
   1507 	}
   1508 	tcaches_flush(tsd, tcache_ind);
   1509 
   1510 	ret = 0;
   1511 label_return:
   1512 	return (ret);
   1513 }
   1514 
   1515 static int
   1516 tcache_destroy_ctl(const size_t *mib, size_t miblen, void *oldp,
   1517     size_t *oldlenp, void *newp, size_t newlen)
   1518 {
   1519 	int ret;
   1520 	tsd_t *tsd;
   1521 	unsigned tcache_ind;
   1522 
   1523 	if (!config_tcache)
   1524 		return (ENOENT);
   1525 
   1526 	tsd = tsd_fetch();
   1527 
   1528 	WRITEONLY();
   1529 	tcache_ind = UINT_MAX;
   1530 	WRITE(tcache_ind, unsigned);
   1531 	if (tcache_ind == UINT_MAX) {
   1532 		ret = EFAULT;
   1533 		goto label_return;
   1534 	}
   1535 	tcaches_destroy(tsd, tcache_ind);
   1536 
   1537 	ret = 0;
   1538 label_return:
   1539 	return (ret);
   1540 }
   1541 
   1542 /******************************************************************************/
   1543 
   1544 /* ctl_mutex must be held during execution of this function. */
   1545 static void
   1546 arena_purge(unsigned arena_ind)
   1547 {
   1548 	tsd_t *tsd;
   1549 	unsigned i;
   1550 	bool refreshed;
   1551 	VARIABLE_ARRAY(arena_t *, tarenas, ctl_stats.narenas);
   1552 
   1553 	tsd = tsd_fetch();
   1554 	for (i = 0, refreshed = false; i < ctl_stats.narenas; i++) {
   1555 		tarenas[i] = arena_get(tsd, i, false, false);
   1556 		if (tarenas[i] == NULL && !refreshed) {
   1557 			tarenas[i] = arena_get(tsd, i, false, true);
   1558 			refreshed = true;
   1559 		}
   1560 	}
   1561 
   1562 	if (arena_ind == ctl_stats.narenas) {
   1563 		unsigned i;
   1564 		for (i = 0; i < ctl_stats.narenas; i++) {
   1565 			if (tarenas[i] != NULL)
   1566 				arena_purge_all(tarenas[i]);
   1567 		}
   1568 	} else {
   1569 		assert(arena_ind < ctl_stats.narenas);
   1570 		if (tarenas[arena_ind] != NULL)
   1571 			arena_purge_all(tarenas[arena_ind]);
   1572 	}
   1573 }
   1574 
   1575 static int
   1576 arena_i_purge_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
   1577     void *newp, size_t newlen)
   1578 {
   1579 	int ret;
   1580 
   1581 	READONLY();
   1582 	WRITEONLY();
   1583 	malloc_mutex_lock(&ctl_mtx);
   1584 	arena_purge(mib[1]);
   1585 	malloc_mutex_unlock(&ctl_mtx);
   1586 
   1587 	ret = 0;
   1588 label_return:
   1589 	return (ret);
   1590 }
   1591 
   1592 static int
   1593 arena_i_dss_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
   1594     void *newp, size_t newlen)
   1595 {
   1596 	int ret;
   1597 	const char *dss = NULL;
   1598 	unsigned arena_ind = mib[1];
   1599 	dss_prec_t dss_prec_old = dss_prec_limit;
   1600 	dss_prec_t dss_prec = dss_prec_limit;
   1601 
   1602 	malloc_mutex_lock(&ctl_mtx);
   1603 	WRITE(dss, const char *);
   1604 	if (dss != NULL) {
   1605 		int i;
   1606 		bool match = false;
   1607 
   1608 		for (i = 0; i < dss_prec_limit; i++) {
   1609 			if (strcmp(dss_prec_names[i], dss) == 0) {
   1610 				dss_prec = i;
   1611 				match = true;
   1612 				break;
   1613 			}
   1614 		}
   1615 
   1616 		if (!match) {
   1617 			ret = EINVAL;
   1618 			goto label_return;
   1619 		}
   1620 	}
   1621 
   1622 	if (arena_ind < ctl_stats.narenas) {
   1623 		arena_t *arena = arena_get(tsd_fetch(), arena_ind, false, true);
   1624 		if (arena == NULL || (dss_prec != dss_prec_limit &&
   1625 		    arena_dss_prec_set(arena, dss_prec))) {
   1626 			ret = EFAULT;
   1627 			goto label_return;
   1628 		}
   1629 		dss_prec_old = arena_dss_prec_get(arena);
   1630 	} else {
   1631 		if (dss_prec != dss_prec_limit &&
   1632 		    chunk_dss_prec_set(dss_prec)) {
   1633 			ret = EFAULT;
   1634 			goto label_return;
   1635 		}
   1636 		dss_prec_old = chunk_dss_prec_get();
   1637 	}
   1638 
   1639 	dss = dss_prec_names[dss_prec_old];
   1640 	READ(dss, const char *);
   1641 
   1642 	ret = 0;
   1643 label_return:
   1644 	malloc_mutex_unlock(&ctl_mtx);
   1645 	return (ret);
   1646 }
   1647 
   1648 static int
   1649 arena_i_lg_dirty_mult_ctl(const size_t *mib, size_t miblen, void *oldp,
   1650     size_t *oldlenp, void *newp, size_t newlen)
   1651 {
   1652 	int ret;
   1653 	unsigned arena_ind = mib[1];
   1654 	arena_t *arena;
   1655 
   1656 	arena = arena_get(tsd_fetch(), arena_ind, false, true);
   1657 	if (arena == NULL) {
   1658 		ret = EFAULT;
   1659 		goto label_return;
   1660 	}
   1661 
   1662 	if (oldp != NULL && oldlenp != NULL) {
   1663 		size_t oldval = arena_lg_dirty_mult_get(arena);
   1664 		READ(oldval, ssize_t);
   1665 	}
   1666 	if (newp != NULL) {
   1667 		if (newlen != sizeof(ssize_t)) {
   1668 			ret = EINVAL;
   1669 			goto label_return;
   1670 		}
   1671 		if (arena_lg_dirty_mult_set(arena, *(ssize_t *)newp)) {
   1672 			ret = EFAULT;
   1673 			goto label_return;
   1674 		}
   1675 	}
   1676 
   1677 	ret = 0;
   1678 label_return:
   1679 	return (ret);
   1680 }
   1681 
   1682 #define	CHUNK_FUNC(n)							\
   1683 static int								\
   1684 arena_i_chunk_##n##_ctl(const size_t *mib, size_t miblen, void *oldp,	\
   1685     size_t *oldlenp, void *newp, size_t newlen)				\
   1686 {									\
   1687 									\
   1688 	int ret;							\
   1689 	unsigned arena_ind = mib[1];					\
   1690 	arena_t *arena;							\
   1691 									\
   1692 	malloc_mutex_lock(&ctl_mtx);					\
   1693 	if (arena_ind < narenas_total_get() && (arena =			\
   1694 	    arena_get(tsd_fetch(), arena_ind, false, true)) != NULL) {	\
   1695 		malloc_mutex_lock(&arena->lock);			\
   1696 		READ(arena->chunk_##n, chunk_##n##_t *);		\
   1697 		WRITE(arena->chunk_##n, chunk_##n##_t *);		\
   1698 	} else {							\
   1699 		ret = EFAULT;						\
   1700 		goto label_outer_return;				\
   1701 	}								\
   1702 	ret = 0;							\
   1703 label_return:								\
   1704 	malloc_mutex_unlock(&arena->lock);				\
   1705 label_outer_return:							\
   1706 	malloc_mutex_unlock(&ctl_mtx);					\
   1707 	return (ret);							\
   1708 }
   1709 CHUNK_FUNC(alloc)
   1710 CHUNK_FUNC(dalloc)
   1711 CHUNK_FUNC(purge)
   1712 #undef CHUNK_FUNC
   1713 
   1714 static const ctl_named_node_t *
   1715 arena_i_index(const size_t *mib, size_t miblen, size_t i)
   1716 {
   1717 	const ctl_named_node_t * ret;
   1718 
   1719 	malloc_mutex_lock(&ctl_mtx);
   1720 	if (i > ctl_stats.narenas) {
   1721 		ret = NULL;
   1722 		goto label_return;
   1723 	}
   1724 
   1725 	ret = super_arena_i_node;
   1726 label_return:
   1727 	malloc_mutex_unlock(&ctl_mtx);
   1728 	return (ret);
   1729 }
   1730 
   1731 /******************************************************************************/
   1732 
   1733 static int
   1734 arenas_narenas_ctl(const size_t *mib, size_t miblen, void *oldp,
   1735     size_t *oldlenp, void *newp, size_t newlen)
   1736 {
   1737 	int ret;
   1738 	unsigned narenas;
   1739 
   1740 	malloc_mutex_lock(&ctl_mtx);
   1741 	READONLY();
   1742 	if (*oldlenp != sizeof(unsigned)) {
   1743 		ret = EINVAL;
   1744 		goto label_return;
   1745 	}
   1746 	narenas = ctl_stats.narenas;
   1747 	READ(narenas, unsigned);
   1748 
   1749 	ret = 0;
   1750 label_return:
   1751 	malloc_mutex_unlock(&ctl_mtx);
   1752 	return (ret);
   1753 }
   1754 
   1755 static int
   1756 arenas_initialized_ctl(const size_t *mib, size_t miblen, void *oldp,
   1757     size_t *oldlenp, void *newp, size_t newlen)
   1758 {
   1759 	int ret;
   1760 	unsigned nread, i;
   1761 
   1762 	malloc_mutex_lock(&ctl_mtx);
   1763 	READONLY();
   1764 	if (*oldlenp != ctl_stats.narenas * sizeof(bool)) {
   1765 		ret = EINVAL;
   1766 		nread = (*oldlenp < ctl_stats.narenas * sizeof(bool))
   1767 		    ? (*oldlenp / sizeof(bool)) : ctl_stats.narenas;
   1768 	} else {
   1769 		ret = 0;
   1770 		nread = ctl_stats.narenas;
   1771 	}
   1772 
   1773 	for (i = 0; i < nread; i++)
   1774 		((bool *)oldp)[i] = ctl_stats.arenas[i].initialized;
   1775 
   1776 label_return:
   1777 	malloc_mutex_unlock(&ctl_mtx);
   1778 	return (ret);
   1779 }
   1780 
   1781 static int
   1782 arenas_lg_dirty_mult_ctl(const size_t *mib, size_t miblen, void *oldp,
   1783     size_t *oldlenp, void *newp, size_t newlen)
   1784 {
   1785 	int ret;
   1786 
   1787 	if (oldp != NULL && oldlenp != NULL) {
   1788 		size_t oldval = arena_lg_dirty_mult_default_get();
   1789 		READ(oldval, ssize_t);
   1790 	}
   1791 	if (newp != NULL) {
   1792 		if (newlen != sizeof(ssize_t)) {
   1793 			ret = EINVAL;
   1794 			goto label_return;
   1795 		}
   1796 		if (arena_lg_dirty_mult_default_set(*(ssize_t *)newp)) {
   1797 			ret = EFAULT;
   1798 			goto label_return;
   1799 		}
   1800 	}
   1801 
   1802 	ret = 0;
   1803 label_return:
   1804 	return (ret);
   1805 }
   1806 
   1807 CTL_RO_NL_GEN(arenas_quantum, QUANTUM, size_t)
   1808 CTL_RO_NL_GEN(arenas_page, PAGE, size_t)
   1809 CTL_RO_NL_CGEN(config_tcache, arenas_tcache_max, tcache_maxclass, size_t)
   1810 CTL_RO_NL_GEN(arenas_nbins, NBINS, unsigned)
   1811 CTL_RO_NL_CGEN(config_tcache, arenas_nhbins, nhbins, unsigned)
   1812 CTL_RO_NL_GEN(arenas_bin_i_size, arena_bin_info[mib[2]].reg_size, size_t)
   1813 CTL_RO_NL_GEN(arenas_bin_i_nregs, arena_bin_info[mib[2]].nregs, uint32_t)
   1814 CTL_RO_NL_GEN(arenas_bin_i_run_size, arena_bin_info[mib[2]].run_size, size_t)
   1815 static const ctl_named_node_t *
   1816 arenas_bin_i_index(const size_t *mib, size_t miblen, size_t i)
   1817 {
   1818 
   1819 	if (i > NBINS)
   1820 		return (NULL);
   1821 	return (super_arenas_bin_i_node);
   1822 }
   1823 
   1824 CTL_RO_NL_GEN(arenas_nlruns, nlclasses, unsigned)
   1825 CTL_RO_NL_GEN(arenas_lrun_i_size, index2size(NBINS+mib[2]), size_t)
   1826 static const ctl_named_node_t *
   1827 arenas_lrun_i_index(const size_t *mib, size_t miblen, size_t i)
   1828 {
   1829 
   1830 	if (i > nlclasses)
   1831 		return (NULL);
   1832 	return (super_arenas_lrun_i_node);
   1833 }
   1834 
   1835 CTL_RO_NL_GEN(arenas_nhchunks, nhclasses, unsigned)
   1836 CTL_RO_NL_GEN(arenas_hchunk_i_size, index2size(NBINS+nlclasses+mib[2]), size_t)
   1837 static const ctl_named_node_t *
   1838 arenas_hchunk_i_index(const size_t *mib, size_t miblen, size_t i)
   1839 {
   1840 
   1841 	if (i > nhclasses)
   1842 		return (NULL);
   1843 	return (super_arenas_hchunk_i_node);
   1844 }
   1845 
   1846 static int
   1847 arenas_extend_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
   1848     void *newp, size_t newlen)
   1849 {
   1850 	int ret;
   1851 	unsigned narenas;
   1852 
   1853 	malloc_mutex_lock(&ctl_mtx);
   1854 	READONLY();
   1855 	if (ctl_grow()) {
   1856 		ret = EAGAIN;
   1857 		goto label_return;
   1858 	}
   1859 	narenas = ctl_stats.narenas - 1;
   1860 	READ(narenas, unsigned);
   1861 
   1862 	ret = 0;
   1863 label_return:
   1864 	malloc_mutex_unlock(&ctl_mtx);
   1865 	return (ret);
   1866 }
   1867 
   1868 /******************************************************************************/
   1869 
   1870 static int
   1871 prof_thread_active_init_ctl(const size_t *mib, size_t miblen, void *oldp,
   1872     size_t *oldlenp, void *newp, size_t newlen)
   1873 {
   1874 	int ret;
   1875 	bool oldval;
   1876 
   1877 	if (!config_prof)
   1878 		return (ENOENT);
   1879 
   1880 	if (newp != NULL) {
   1881 		if (newlen != sizeof(bool)) {
   1882 			ret = EINVAL;
   1883 			goto label_return;
   1884 		}
   1885 		oldval = prof_thread_active_init_set(*(bool *)newp);
   1886 	} else
   1887 		oldval = prof_thread_active_init_get();
   1888 	READ(oldval, bool);
   1889 
   1890 	ret = 0;
   1891 label_return:
   1892 	return (ret);
   1893 }
   1894 
   1895 static int
   1896 prof_active_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
   1897     void *newp, size_t newlen)
   1898 {
   1899 	int ret;
   1900 	bool oldval;
   1901 
   1902 	if (!config_prof)
   1903 		return (ENOENT);
   1904 
   1905 	if (newp != NULL) {
   1906 		if (newlen != sizeof(bool)) {
   1907 			ret = EINVAL;
   1908 			goto label_return;
   1909 		}
   1910 		oldval = prof_active_set(*(bool *)newp);
   1911 	} else
   1912 		oldval = prof_active_get();
   1913 	READ(oldval, bool);
   1914 
   1915 	ret = 0;
   1916 label_return:
   1917 	return (ret);
   1918 }
   1919 
   1920 static int
   1921 prof_dump_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
   1922     void *newp, size_t newlen)
   1923 {
   1924 	int ret;
   1925 	const char *filename = NULL;
   1926 
   1927 	if (!config_prof)
   1928 		return (ENOENT);
   1929 
   1930 	WRITEONLY();
   1931 	WRITE(filename, const char *);
   1932 
   1933 	if (prof_mdump(filename)) {
   1934 		ret = EFAULT;
   1935 		goto label_return;
   1936 	}
   1937 
   1938 	ret = 0;
   1939 label_return:
   1940 	return (ret);
   1941 }
   1942 
   1943 static int
   1944 prof_gdump_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
   1945     void *newp, size_t newlen)
   1946 {
   1947 	int ret;
   1948 	bool oldval;
   1949 
   1950 	if (!config_prof)
   1951 		return (ENOENT);
   1952 
   1953 	if (newp != NULL) {
   1954 		if (newlen != sizeof(bool)) {
   1955 			ret = EINVAL;
   1956 			goto label_return;
   1957 		}
   1958 		oldval = prof_gdump_set(*(bool *)newp);
   1959 	} else
   1960 		oldval = prof_gdump_get();
   1961 	READ(oldval, bool);
   1962 
   1963 	ret = 0;
   1964 label_return:
   1965 	return (ret);
   1966 }
   1967 
   1968 static int
   1969 prof_reset_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
   1970     void *newp, size_t newlen)
   1971 {
   1972 	int ret;
   1973 	size_t lg_sample = lg_prof_sample;
   1974 	tsd_t *tsd;
   1975 
   1976 	if (!config_prof)
   1977 		return (ENOENT);
   1978 
   1979 	WRITEONLY();
   1980 	WRITE(lg_sample, size_t);
   1981 	if (lg_sample >= (sizeof(uint64_t) << 3))
   1982 		lg_sample = (sizeof(uint64_t) << 3) - 1;
   1983 
   1984 	tsd = tsd_fetch();
   1985 
   1986 	prof_reset(tsd, lg_sample);
   1987 
   1988 	ret = 0;
   1989 label_return:
   1990 	return (ret);
   1991 }
   1992 
   1993 CTL_RO_NL_CGEN(config_prof, prof_interval, prof_interval, uint64_t)
   1994 CTL_RO_NL_CGEN(config_prof, lg_prof_sample, lg_prof_sample, size_t)
   1995 
   1996 /******************************************************************************/
   1997 
   1998 CTL_RO_CGEN(config_stats, stats_cactive, &stats_cactive, size_t *)
   1999 CTL_RO_CGEN(config_stats, stats_allocated, ctl_stats.allocated, size_t)
   2000 CTL_RO_CGEN(config_stats, stats_active, ctl_stats.active, size_t)
   2001 CTL_RO_CGEN(config_stats, stats_metadata, ctl_stats.metadata, size_t)
   2002 CTL_RO_CGEN(config_stats, stats_resident, ctl_stats.resident, size_t)
   2003 CTL_RO_CGEN(config_stats, stats_mapped, ctl_stats.mapped, size_t)
   2004 
   2005 CTL_RO_GEN(stats_arenas_i_dss, ctl_stats.arenas[mib[2]].dss, const char *)
   2006 CTL_RO_GEN(stats_arenas_i_lg_dirty_mult, ctl_stats.arenas[mib[2]].lg_dirty_mult,
   2007     ssize_t)
   2008 CTL_RO_GEN(stats_arenas_i_nthreads, ctl_stats.arenas[mib[2]].nthreads, unsigned)
   2009 CTL_RO_GEN(stats_arenas_i_pactive, ctl_stats.arenas[mib[2]].pactive, size_t)
   2010 CTL_RO_GEN(stats_arenas_i_pdirty, ctl_stats.arenas[mib[2]].pdirty, size_t)
   2011 CTL_RO_CGEN(config_stats, stats_arenas_i_mapped,
   2012     ctl_stats.arenas[mib[2]].astats.mapped, size_t)
   2013 CTL_RO_CGEN(config_stats, stats_arenas_i_npurge,
   2014     ctl_stats.arenas[mib[2]].astats.npurge, uint64_t)
   2015 CTL_RO_CGEN(config_stats, stats_arenas_i_nmadvise,
   2016     ctl_stats.arenas[mib[2]].astats.nmadvise, uint64_t)
   2017 CTL_RO_CGEN(config_stats, stats_arenas_i_purged,
   2018     ctl_stats.arenas[mib[2]].astats.purged, uint64_t)
   2019 CTL_RO_CGEN(config_stats, stats_arenas_i_metadata_mapped,
   2020     ctl_stats.arenas[mib[2]].astats.metadata_mapped, size_t)
   2021 CTL_RO_CGEN(config_stats, stats_arenas_i_metadata_allocated,
   2022     ctl_stats.arenas[mib[2]].astats.metadata_allocated, size_t)
   2023 
   2024 CTL_RO_CGEN(config_stats, stats_arenas_i_small_allocated,
   2025     ctl_stats.arenas[mib[2]].allocated_small, size_t)
   2026 CTL_RO_CGEN(config_stats, stats_arenas_i_small_nmalloc,
   2027     ctl_stats.arenas[mib[2]].nmalloc_small, uint64_t)
   2028 CTL_RO_CGEN(config_stats, stats_arenas_i_small_ndalloc,
   2029     ctl_stats.arenas[mib[2]].ndalloc_small, uint64_t)
   2030 CTL_RO_CGEN(config_stats, stats_arenas_i_small_nrequests,
   2031     ctl_stats.arenas[mib[2]].nrequests_small, uint64_t)
   2032 CTL_RO_CGEN(config_stats, stats_arenas_i_large_allocated,
   2033     ctl_stats.arenas[mib[2]].astats.allocated_large, size_t)
   2034 CTL_RO_CGEN(config_stats, stats_arenas_i_large_nmalloc,
   2035     ctl_stats.arenas[mib[2]].astats.nmalloc_large, uint64_t)
   2036 CTL_RO_CGEN(config_stats, stats_arenas_i_large_ndalloc,
   2037     ctl_stats.arenas[mib[2]].astats.ndalloc_large, uint64_t)
   2038 CTL_RO_CGEN(config_stats, stats_arenas_i_large_nrequests,
   2039     ctl_stats.arenas[mib[2]].astats.nrequests_large, uint64_t)
   2040 CTL_RO_CGEN(config_stats, stats_arenas_i_huge_allocated,
   2041     ctl_stats.arenas[mib[2]].astats.allocated_huge, size_t)
   2042 CTL_RO_CGEN(config_stats, stats_arenas_i_huge_nmalloc,
   2043     ctl_stats.arenas[mib[2]].astats.nmalloc_huge, uint64_t)
   2044 CTL_RO_CGEN(config_stats, stats_arenas_i_huge_ndalloc,
   2045     ctl_stats.arenas[mib[2]].astats.ndalloc_huge, uint64_t)
   2046 CTL_RO_CGEN(config_stats, stats_arenas_i_huge_nrequests,
   2047     ctl_stats.arenas[mib[2]].astats.nmalloc_huge, uint64_t) /* Intentional. */
   2048 
   2049 CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nmalloc,
   2050     ctl_stats.arenas[mib[2]].bstats[mib[4]].nmalloc, uint64_t)
   2051 CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_ndalloc,
   2052     ctl_stats.arenas[mib[2]].bstats[mib[4]].ndalloc, uint64_t)
   2053 CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nrequests,
   2054     ctl_stats.arenas[mib[2]].bstats[mib[4]].nrequests, uint64_t)
   2055 CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_curregs,
   2056     ctl_stats.arenas[mib[2]].bstats[mib[4]].curregs, size_t)
   2057 CTL_RO_CGEN(config_stats && config_tcache, stats_arenas_i_bins_j_nfills,
   2058     ctl_stats.arenas[mib[2]].bstats[mib[4]].nfills, uint64_t)
   2059 CTL_RO_CGEN(config_stats && config_tcache, stats_arenas_i_bins_j_nflushes,
   2060     ctl_stats.arenas[mib[2]].bstats[mib[4]].nflushes, uint64_t)
   2061 CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nruns,
   2062     ctl_stats.arenas[mib[2]].bstats[mib[4]].nruns, uint64_t)
   2063 CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nreruns,
   2064     ctl_stats.arenas[mib[2]].bstats[mib[4]].reruns, uint64_t)
   2065 CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_curruns,
   2066     ctl_stats.arenas[mib[2]].bstats[mib[4]].curruns, size_t)
   2067 
   2068 static const ctl_named_node_t *
   2069 stats_arenas_i_bins_j_index(const size_t *mib, size_t miblen, size_t j)
   2070 {
   2071 
   2072 	if (j > NBINS)
   2073 		return (NULL);
   2074 	return (super_stats_arenas_i_bins_j_node);
   2075 }
   2076 
   2077 CTL_RO_CGEN(config_stats, stats_arenas_i_lruns_j_nmalloc,
   2078     ctl_stats.arenas[mib[2]].lstats[mib[4]].nmalloc, uint64_t)
   2079 CTL_RO_CGEN(config_stats, stats_arenas_i_lruns_j_ndalloc,
   2080     ctl_stats.arenas[mib[2]].lstats[mib[4]].ndalloc, uint64_t)
   2081 CTL_RO_CGEN(config_stats, stats_arenas_i_lruns_j_nrequests,
   2082     ctl_stats.arenas[mib[2]].lstats[mib[4]].nrequests, uint64_t)
   2083 CTL_RO_CGEN(config_stats, stats_arenas_i_lruns_j_curruns,
   2084     ctl_stats.arenas[mib[2]].lstats[mib[4]].curruns, size_t)
   2085 
   2086 static const ctl_named_node_t *
   2087 stats_arenas_i_lruns_j_index(const size_t *mib, size_t miblen, size_t j)
   2088 {
   2089 
   2090 	if (j > nlclasses)
   2091 		return (NULL);
   2092 	return (super_stats_arenas_i_lruns_j_node);
   2093 }
   2094 
   2095 CTL_RO_CGEN(config_stats, stats_arenas_i_hchunks_j_nmalloc,
   2096     ctl_stats.arenas[mib[2]].hstats[mib[4]].nmalloc, uint64_t)
   2097 CTL_RO_CGEN(config_stats, stats_arenas_i_hchunks_j_ndalloc,
   2098     ctl_stats.arenas[mib[2]].hstats[mib[4]].ndalloc, uint64_t)
   2099 CTL_RO_CGEN(config_stats, stats_arenas_i_hchunks_j_nrequests,
   2100     ctl_stats.arenas[mib[2]].hstats[mib[4]].nmalloc, /* Intentional. */
   2101     uint64_t)
   2102 CTL_RO_CGEN(config_stats, stats_arenas_i_hchunks_j_curhchunks,
   2103     ctl_stats.arenas[mib[2]].hstats[mib[4]].curhchunks, size_t)
   2104 
   2105 static const ctl_named_node_t *
   2106 stats_arenas_i_hchunks_j_index(const size_t *mib, size_t miblen, size_t j)
   2107 {
   2108 
   2109 	if (j > nhclasses)
   2110 		return (NULL);
   2111 	return (super_stats_arenas_i_hchunks_j_node);
   2112 }
   2113 
   2114 static const ctl_named_node_t *
   2115 stats_arenas_i_index(const size_t *mib, size_t miblen, size_t i)
   2116 {
   2117 	const ctl_named_node_t * ret;
   2118 
   2119 	malloc_mutex_lock(&ctl_mtx);
   2120 	if (i > ctl_stats.narenas || !ctl_stats.arenas[i].initialized) {
   2121 		ret = NULL;
   2122 		goto label_return;
   2123 	}
   2124 
   2125 	ret = super_stats_arenas_i_node;
   2126 label_return:
   2127 	malloc_mutex_unlock(&ctl_mtx);
   2128 	return (ret);
   2129 }
   2130