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