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