1#define JEMALLOC_STATS_C_
2#include "jemalloc/internal/jemalloc_preamble.h"
3#include "jemalloc/internal/jemalloc_internal_includes.h"
4
5#include "jemalloc/internal/assert.h"
6#include "jemalloc/internal/ctl.h"
7#include "jemalloc/internal/emitter.h"
8#include "jemalloc/internal/mutex.h"
9#include "jemalloc/internal/mutex_prof.h"
10
11const char *global_mutex_names[mutex_prof_num_global_mutexes] = {
12#define OP(mtx) #mtx,
13	MUTEX_PROF_GLOBAL_MUTEXES
14#undef OP
15};
16
17const char *arena_mutex_names[mutex_prof_num_arena_mutexes] = {
18#define OP(mtx) #mtx,
19	MUTEX_PROF_ARENA_MUTEXES
20#undef OP
21};
22
23#define CTL_GET(n, v, t) do {						\
24	size_t sz = sizeof(t);						\
25	xmallctl(n, (void *)v, &sz, NULL, 0);				\
26} while (0)
27
28#define CTL_M2_GET(n, i, v, t) do {					\
29	size_t mib[CTL_MAX_DEPTH];					\
30	size_t miblen = sizeof(mib) / sizeof(size_t);			\
31	size_t sz = sizeof(t);						\
32	xmallctlnametomib(n, mib, &miblen);				\
33	mib[2] = (i);							\
34	xmallctlbymib(mib, miblen, (void *)v, &sz, NULL, 0);		\
35} while (0)
36
37#define CTL_M2_M4_GET(n, i, j, v, t) do {				\
38	size_t mib[CTL_MAX_DEPTH];					\
39	size_t miblen = sizeof(mib) / sizeof(size_t);			\
40	size_t sz = sizeof(t);						\
41	xmallctlnametomib(n, mib, &miblen);				\
42	mib[2] = (i);							\
43	mib[4] = (j);							\
44	xmallctlbymib(mib, miblen, (void *)v, &sz, NULL, 0);		\
45} while (0)
46
47/******************************************************************************/
48/* Data. */
49
50bool opt_stats_print = false;
51char opt_stats_print_opts[stats_print_tot_num_options+1] = "";
52
53/******************************************************************************/
54
55/* Calculate x.yyy and output a string (takes a fixed sized char array). */
56static bool
57get_rate_str(uint64_t dividend, uint64_t divisor, char str[6]) {
58	if (divisor == 0 || dividend > divisor) {
59		/* The rate is not supposed to be greater than 1. */
60		return true;
61	}
62	if (dividend > 0) {
63		assert(UINT64_MAX / dividend >= 1000);
64	}
65
66	unsigned n = (unsigned)((dividend * 1000) / divisor);
67	if (n < 10) {
68		malloc_snprintf(str, 6, "0.00%u", n);
69	} else if (n < 100) {
70		malloc_snprintf(str, 6, "0.0%u", n);
71	} else if (n < 1000) {
72		malloc_snprintf(str, 6, "0.%u", n);
73	} else {
74		malloc_snprintf(str, 6, "1");
75	}
76
77	return false;
78}
79
80#define MUTEX_CTL_STR_MAX_LENGTH 128
81static void
82gen_mutex_ctl_str(char *str, size_t buf_len, const char *prefix,
83    const char *mutex, const char *counter) {
84	malloc_snprintf(str, buf_len, "stats.%s.%s.%s", prefix, mutex, counter);
85}
86
87static void
88mutex_stats_init_cols(emitter_row_t *row, const char *table_name,
89    emitter_col_t *name,
90    emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],
91    emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters]) {
92	mutex_prof_uint64_t_counter_ind_t k_uint64_t = 0;
93	mutex_prof_uint32_t_counter_ind_t k_uint32_t = 0;
94
95	emitter_col_t *col;
96
97	if (name != NULL) {
98		emitter_col_init(name, row);
99		name->justify = emitter_justify_left;
100		name->width = 21;
101		name->type = emitter_type_title;
102		name->str_val = table_name;
103	}
104
105#define WIDTH_uint32_t 12
106#define WIDTH_uint64_t 16
107#define OP(counter, counter_type, human)				\
108	col = &col_##counter_type[k_##counter_type];			\
109	++k_##counter_type;						\
110	emitter_col_init(col, row);					\
111	col->justify = emitter_justify_right;				\
112	col->width = WIDTH_##counter_type;				\
113	col->type = emitter_type_title;					\
114	col->str_val = human;
115	MUTEX_PROF_COUNTERS
116#undef OP
117#undef WIDTH_uint32_t
118#undef WIDTH_uint64_t
119}
120
121static void
122mutex_stats_read_global(const char *name, emitter_col_t *col_name,
123    emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],
124    emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters]) {
125	char cmd[MUTEX_CTL_STR_MAX_LENGTH];
126
127	col_name->str_val = name;
128
129	emitter_col_t *dst;
130#define EMITTER_TYPE_uint32_t emitter_type_uint32
131#define EMITTER_TYPE_uint64_t emitter_type_uint64
132#define OP(counter, counter_type, human)				\
133	dst = &col_##counter_type[mutex_counter_##counter];		\
134	dst->type = EMITTER_TYPE_##counter_type;			\
135	gen_mutex_ctl_str(cmd, MUTEX_CTL_STR_MAX_LENGTH,		\
136	    "mutexes", name, #counter);					\
137	CTL_GET(cmd, (counter_type *)&dst->bool_val, counter_type);
138	MUTEX_PROF_COUNTERS
139#undef OP
140#undef EMITTER_TYPE_uint32_t
141#undef EMITTER_TYPE_uint64_t
142}
143
144static void
145mutex_stats_read_arena(unsigned arena_ind, mutex_prof_arena_ind_t mutex_ind,
146    const char *name, emitter_col_t *col_name,
147    emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],
148    emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters]) {
149	char cmd[MUTEX_CTL_STR_MAX_LENGTH];
150
151	col_name->str_val = name;
152
153	emitter_col_t *dst;
154#define EMITTER_TYPE_uint32_t emitter_type_uint32
155#define EMITTER_TYPE_uint64_t emitter_type_uint64
156#define OP(counter, counter_type, human)				\
157	dst = &col_##counter_type[mutex_counter_##counter];		\
158	dst->type = EMITTER_TYPE_##counter_type;			\
159	gen_mutex_ctl_str(cmd, MUTEX_CTL_STR_MAX_LENGTH,		\
160	    "arenas.0.mutexes",	arena_mutex_names[mutex_ind], #counter);\
161	CTL_M2_GET(cmd, arena_ind,					\
162	    (counter_type *)&dst->bool_val, counter_type);
163	MUTEX_PROF_COUNTERS
164#undef OP
165#undef EMITTER_TYPE_uint32_t
166#undef EMITTER_TYPE_uint64_t
167}
168
169static void
170mutex_stats_read_arena_bin(unsigned arena_ind, unsigned bin_ind,
171    emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],
172    emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters]) {
173	char cmd[MUTEX_CTL_STR_MAX_LENGTH];
174	emitter_col_t *dst;
175
176#define EMITTER_TYPE_uint32_t emitter_type_uint32
177#define EMITTER_TYPE_uint64_t emitter_type_uint64
178#define OP(counter, counter_type, human)				\
179	dst = &col_##counter_type[mutex_counter_##counter];		\
180	dst->type = EMITTER_TYPE_##counter_type;			\
181	gen_mutex_ctl_str(cmd, MUTEX_CTL_STR_MAX_LENGTH,		\
182	    "arenas.0.bins.0","mutex", #counter);			\
183	CTL_M2_M4_GET(cmd, arena_ind, bin_ind,				\
184	    (counter_type *)&dst->bool_val, counter_type);
185	MUTEX_PROF_COUNTERS
186#undef OP
187#undef EMITTER_TYPE_uint32_t
188#undef EMITTER_TYPE_uint64_t
189}
190
191/* "row" can be NULL to avoid emitting in table mode. */
192static void
193mutex_stats_emit(emitter_t *emitter, emitter_row_t *row,
194    emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],
195    emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters]) {
196	if (row != NULL) {
197		emitter_table_row(emitter, row);
198	}
199
200	mutex_prof_uint64_t_counter_ind_t k_uint64_t = 0;
201	mutex_prof_uint32_t_counter_ind_t k_uint32_t = 0;
202
203	emitter_col_t *col;
204
205#define EMITTER_TYPE_uint32_t emitter_type_uint32
206#define EMITTER_TYPE_uint64_t emitter_type_uint64
207#define OP(counter, type, human)					\
208	col = &col_##type[k_##type];						\
209	++k_##type;							\
210	emitter_json_kv(emitter, #counter, EMITTER_TYPE_##type,		\
211	    (const void *)&col->bool_val);
212	MUTEX_PROF_COUNTERS;
213#undef OP
214#undef EMITTER_TYPE_uint32_t
215#undef EMITTER_TYPE_uint64_t
216}
217
218static void
219stats_arena_bins_print(emitter_t *emitter, bool mutex, unsigned i) {
220	size_t page;
221	bool in_gap, in_gap_prev;
222	unsigned nbins, j;
223
224	CTL_GET("arenas.page", &page, size_t);
225
226	CTL_GET("arenas.nbins", &nbins, unsigned);
227
228	emitter_row_t header_row;
229	emitter_row_init(&header_row);
230
231	emitter_row_t row;
232	emitter_row_init(&row);
233#define COL(name, left_or_right, col_width, etype)			\
234	emitter_col_t col_##name;					\
235	emitter_col_init(&col_##name, &row);				\
236	col_##name.justify = emitter_justify_##left_or_right;		\
237	col_##name.width = col_width;					\
238	col_##name.type = emitter_type_##etype;				\
239	emitter_col_t header_col_##name;				\
240	emitter_col_init(&header_col_##name, &header_row);		\
241	header_col_##name.justify = emitter_justify_##left_or_right;	\
242	header_col_##name.width = col_width;				\
243	header_col_##name.type = emitter_type_title;			\
244	header_col_##name.str_val = #name;
245
246	COL(size, right, 20, size)
247	COL(ind, right, 4, unsigned)
248	COL(allocated, right, 13, uint64)
249	COL(nmalloc, right, 13, uint64)
250	COL(ndalloc, right, 13, uint64)
251	COL(nrequests, right, 13, uint64)
252	COL(curregs, right, 13, size)
253	COL(curslabs, right, 13, size)
254	COL(regs, right, 5, unsigned)
255	COL(pgs, right, 4, size)
256	/* To buffer a right- and left-justified column. */
257	COL(justify_spacer, right, 1, title)
258	COL(util, right, 6, title)
259	COL(nfills, right, 13, uint64)
260	COL(nflushes, right, 13, uint64)
261	COL(nslabs, right, 13, uint64)
262	COL(nreslabs, right, 13, uint64)
263#undef COL
264
265	/* Don't want to actually print the name. */
266	header_col_justify_spacer.str_val = " ";
267	col_justify_spacer.str_val = " ";
268
269
270	emitter_col_t col_mutex64[mutex_prof_num_uint64_t_counters];
271	emitter_col_t col_mutex32[mutex_prof_num_uint32_t_counters];
272
273	emitter_col_t header_mutex64[mutex_prof_num_uint64_t_counters];
274	emitter_col_t header_mutex32[mutex_prof_num_uint32_t_counters];
275
276	if (mutex) {
277		mutex_stats_init_cols(&row, NULL, NULL, col_mutex64,
278		    col_mutex32);
279		mutex_stats_init_cols(&header_row, NULL, NULL, header_mutex64,
280		    header_mutex32);
281	}
282
283	/*
284	 * We print a "bins:" header as part of the table row; we need to adjust
285	 * the header size column to compensate.
286	 */
287	header_col_size.width -=5;
288	emitter_table_printf(emitter, "bins:");
289	emitter_table_row(emitter, &header_row);
290	emitter_json_arr_begin(emitter, "bins");
291
292	for (j = 0, in_gap = false; j < nbins; j++) {
293		uint64_t nslabs;
294		size_t reg_size, slab_size, curregs;
295		size_t curslabs;
296		uint32_t nregs;
297		uint64_t nmalloc, ndalloc, nrequests, nfills, nflushes;
298		uint64_t nreslabs;
299
300		CTL_M2_M4_GET("stats.arenas.0.bins.0.nslabs", i, j, &nslabs,
301		    uint64_t);
302		in_gap_prev = in_gap;
303		in_gap = (nslabs == 0);
304
305		if (in_gap_prev && !in_gap) {
306			emitter_table_printf(emitter,
307			    "                     ---\n");
308		}
309
310		CTL_M2_GET("arenas.bin.0.size", j, &reg_size, size_t);
311		CTL_M2_GET("arenas.bin.0.nregs", j, &nregs, uint32_t);
312		CTL_M2_GET("arenas.bin.0.slab_size", j, &slab_size, size_t);
313
314		CTL_M2_M4_GET("stats.arenas.0.bins.0.nmalloc", i, j, &nmalloc,
315		    uint64_t);
316		CTL_M2_M4_GET("stats.arenas.0.bins.0.ndalloc", i, j, &ndalloc,
317		    uint64_t);
318		CTL_M2_M4_GET("stats.arenas.0.bins.0.curregs", i, j, &curregs,
319		    size_t);
320		CTL_M2_M4_GET("stats.arenas.0.bins.0.nrequests", i, j,
321		    &nrequests, uint64_t);
322		CTL_M2_M4_GET("stats.arenas.0.bins.0.nfills", i, j, &nfills,
323		    uint64_t);
324		CTL_M2_M4_GET("stats.arenas.0.bins.0.nflushes", i, j, &nflushes,
325		    uint64_t);
326		CTL_M2_M4_GET("stats.arenas.0.bins.0.nreslabs", i, j, &nreslabs,
327		    uint64_t);
328		CTL_M2_M4_GET("stats.arenas.0.bins.0.curslabs", i, j, &curslabs,
329		    size_t);
330
331		if (mutex) {
332			mutex_stats_read_arena_bin(i, j, col_mutex64,
333			    col_mutex32);
334		}
335
336		emitter_json_arr_obj_begin(emitter);
337		emitter_json_kv(emitter, "nmalloc", emitter_type_uint64,
338		    &nmalloc);
339		emitter_json_kv(emitter, "ndalloc", emitter_type_uint64,
340		    &ndalloc);
341		emitter_json_kv(emitter, "curregs", emitter_type_size,
342		    &curregs);
343		emitter_json_kv(emitter, "nrequests", emitter_type_uint64,
344		    &nrequests);
345		emitter_json_kv(emitter, "nfills", emitter_type_uint64,
346		    &nfills);
347		emitter_json_kv(emitter, "nflushes", emitter_type_uint64,
348		    &nflushes);
349		emitter_json_kv(emitter, "nreslabs", emitter_type_uint64,
350		    &nreslabs);
351		emitter_json_kv(emitter, "curslabs", emitter_type_size,
352		    &curslabs);
353		if (mutex) {
354			emitter_json_dict_begin(emitter, "mutex");
355			mutex_stats_emit(emitter, NULL, col_mutex64,
356			    col_mutex32);
357			emitter_json_dict_end(emitter);
358		}
359		emitter_json_arr_obj_end(emitter);
360
361		size_t availregs = nregs * curslabs;
362		char util[6];
363		if (get_rate_str((uint64_t)curregs, (uint64_t)availregs, util))
364		{
365			if (availregs == 0) {
366				malloc_snprintf(util, sizeof(util), "1");
367			} else if (curregs > availregs) {
368				/*
369				 * Race detected: the counters were read in
370				 * separate mallctl calls and concurrent
371				 * operations happened in between.  In this case
372				 * no meaningful utilization can be computed.
373				 */
374				malloc_snprintf(util, sizeof(util), " race");
375			} else {
376				not_reached();
377			}
378		}
379
380		col_size.size_val = reg_size;
381		col_ind.unsigned_val = j;
382		col_allocated.size_val = curregs * reg_size;
383		col_nmalloc.uint64_val = nmalloc;
384		col_ndalloc.uint64_val = ndalloc;
385		col_nrequests.uint64_val = nrequests;
386		col_curregs.size_val = curregs;
387		col_curslabs.size_val = curslabs;
388		col_regs.unsigned_val = nregs;
389		col_pgs.size_val = slab_size / page;
390		col_util.str_val = util;
391		col_nfills.uint64_val = nfills;
392		col_nflushes.uint64_val = nflushes;
393		col_nslabs.uint64_val = nslabs;
394		col_nreslabs.uint64_val = nreslabs;
395
396		/*
397		 * Note that mutex columns were initialized above, if mutex ==
398		 * true.
399		 */
400
401		emitter_table_row(emitter, &row);
402	}
403	emitter_json_arr_end(emitter); /* Close "bins". */
404
405	if (in_gap) {
406		emitter_table_printf(emitter, "                     ---\n");
407	}
408}
409
410static void
411stats_arena_lextents_print(emitter_t *emitter, unsigned i) {
412	unsigned nbins, nlextents, j;
413	bool in_gap, in_gap_prev;
414
415	CTL_GET("arenas.nbins", &nbins, unsigned);
416	CTL_GET("arenas.nlextents", &nlextents, unsigned);
417
418	emitter_row_t header_row;
419	emitter_row_init(&header_row);
420	emitter_row_t row;
421	emitter_row_init(&row);
422
423#define COL(name, left_or_right, col_width, etype)			\
424	emitter_col_t header_##name;					\
425	emitter_col_init(&header_##name, &header_row);			\
426	header_##name.justify = emitter_justify_##left_or_right;	\
427	header_##name.width = col_width;				\
428	header_##name.type = emitter_type_title;			\
429	header_##name.str_val = #name;					\
430									\
431	emitter_col_t col_##name;					\
432	emitter_col_init(&col_##name, &row);				\
433	col_##name.justify = emitter_justify_##left_or_right;		\
434	col_##name.width = col_width;					\
435	col_##name.type = emitter_type_##etype;
436
437	COL(size, right, 20, size)
438	COL(ind, right, 4, unsigned)
439	COL(allocated, right, 13, size)
440	COL(nmalloc, right, 13, uint64)
441	COL(ndalloc, right, 13, uint64)
442	COL(nrequests, right, 13, uint64)
443	COL(curlextents, right, 13, size)
444#undef COL
445
446	/* As with bins, we label the large extents table. */
447	header_size.width -= 6;
448	emitter_table_printf(emitter, "large:");
449	emitter_table_row(emitter, &header_row);
450	emitter_json_arr_begin(emitter, "lextents");
451
452	for (j = 0, in_gap = false; j < nlextents; j++) {
453		uint64_t nmalloc, ndalloc, nrequests;
454		size_t lextent_size, curlextents;
455
456		CTL_M2_M4_GET("stats.arenas.0.lextents.0.nmalloc", i, j,
457		    &nmalloc, uint64_t);
458		CTL_M2_M4_GET("stats.arenas.0.lextents.0.ndalloc", i, j,
459		    &ndalloc, uint64_t);
460		CTL_M2_M4_GET("stats.arenas.0.lextents.0.nrequests", i, j,
461		    &nrequests, uint64_t);
462		in_gap_prev = in_gap;
463		in_gap = (nrequests == 0);
464
465		if (in_gap_prev && !in_gap) {
466			emitter_table_printf(emitter,
467			    "                     ---\n");
468		}
469
470		CTL_M2_GET("arenas.lextent.0.size", j, &lextent_size, size_t);
471		CTL_M2_M4_GET("stats.arenas.0.lextents.0.curlextents", i, j,
472		    &curlextents, size_t);
473
474		emitter_json_arr_obj_begin(emitter);
475		emitter_json_kv(emitter, "curlextents", emitter_type_size,
476		    &curlextents);
477		emitter_json_arr_obj_end(emitter);
478
479		col_size.size_val = lextent_size;
480		col_ind.unsigned_val = nbins + j;
481		col_allocated.size_val = curlextents * lextent_size;
482		col_nmalloc.uint64_val = nmalloc;
483		col_ndalloc.uint64_val = ndalloc;
484		col_nrequests.uint64_val = nrequests;
485		col_curlextents.size_val = curlextents;
486
487		if (!in_gap) {
488			emitter_table_row(emitter, &row);
489		}
490	}
491	emitter_json_arr_end(emitter); /* Close "lextents". */
492	if (in_gap) {
493		emitter_table_printf(emitter, "                     ---\n");
494	}
495}
496
497static void
498stats_arena_mutexes_print(emitter_t *emitter, unsigned arena_ind) {
499	emitter_row_t row;
500	emitter_col_t col_name;
501	emitter_col_t col64[mutex_prof_num_uint64_t_counters];
502	emitter_col_t col32[mutex_prof_num_uint32_t_counters];
503
504	emitter_row_init(&row);
505	mutex_stats_init_cols(&row, "", &col_name, col64, col32);
506
507	emitter_json_dict_begin(emitter, "mutexes");
508	emitter_table_row(emitter, &row);
509
510	for (mutex_prof_arena_ind_t i = 0; i < mutex_prof_num_arena_mutexes;
511	    i++) {
512		const char *name = arena_mutex_names[i];
513		emitter_json_dict_begin(emitter, name);
514		mutex_stats_read_arena(arena_ind, i, name, &col_name, col64,
515		    col32);
516		mutex_stats_emit(emitter, &row, col64, col32);
517		emitter_json_dict_end(emitter); /* Close the mutex dict. */
518	}
519	emitter_json_dict_end(emitter); /* End "mutexes". */
520}
521
522static void
523stats_arena_print(emitter_t *emitter, unsigned i, bool bins, bool large,
524    bool mutex) {
525	unsigned nthreads;
526	const char *dss;
527	ssize_t dirty_decay_ms, muzzy_decay_ms;
528	size_t page, pactive, pdirty, pmuzzy, mapped, retained;
529	size_t base, internal, resident, metadata_thp;
530	uint64_t dirty_npurge, dirty_nmadvise, dirty_purged;
531	uint64_t muzzy_npurge, muzzy_nmadvise, muzzy_purged;
532	size_t small_allocated;
533	uint64_t small_nmalloc, small_ndalloc, small_nrequests;
534	size_t large_allocated;
535	uint64_t large_nmalloc, large_ndalloc, large_nrequests;
536	size_t tcache_bytes;
537	uint64_t uptime;
538
539	CTL_GET("arenas.page", &page, size_t);
540
541	CTL_M2_GET("stats.arenas.0.nthreads", i, &nthreads, unsigned);
542	emitter_kv(emitter, "nthreads", "assigned threads",
543	    emitter_type_unsigned, &nthreads);
544
545	CTL_M2_GET("stats.arenas.0.uptime", i, &uptime, uint64_t);
546	emitter_kv(emitter, "uptime_ns", "uptime", emitter_type_uint64,
547	    &uptime);
548
549	CTL_M2_GET("stats.arenas.0.dss", i, &dss, const char *);
550	emitter_kv(emitter, "dss", "dss allocation precedence",
551	    emitter_type_string, &dss);
552
553	CTL_M2_GET("stats.arenas.0.dirty_decay_ms", i, &dirty_decay_ms,
554	    ssize_t);
555	CTL_M2_GET("stats.arenas.0.muzzy_decay_ms", i, &muzzy_decay_ms,
556	    ssize_t);
557	CTL_M2_GET("stats.arenas.0.pactive", i, &pactive, size_t);
558	CTL_M2_GET("stats.arenas.0.pdirty", i, &pdirty, size_t);
559	CTL_M2_GET("stats.arenas.0.pmuzzy", i, &pmuzzy, size_t);
560	CTL_M2_GET("stats.arenas.0.dirty_npurge", i, &dirty_npurge, uint64_t);
561	CTL_M2_GET("stats.arenas.0.dirty_nmadvise", i, &dirty_nmadvise,
562	    uint64_t);
563	CTL_M2_GET("stats.arenas.0.dirty_purged", i, &dirty_purged, uint64_t);
564	CTL_M2_GET("stats.arenas.0.muzzy_npurge", i, &muzzy_npurge, uint64_t);
565	CTL_M2_GET("stats.arenas.0.muzzy_nmadvise", i, &muzzy_nmadvise,
566	    uint64_t);
567	CTL_M2_GET("stats.arenas.0.muzzy_purged", i, &muzzy_purged, uint64_t);
568
569	emitter_row_t decay_row;
570	emitter_row_init(&decay_row);
571
572	/* JSON-style emission. */
573	emitter_json_kv(emitter, "dirty_decay_ms", emitter_type_ssize,
574	    &dirty_decay_ms);
575	emitter_json_kv(emitter, "muzzy_decay_ms", emitter_type_ssize,
576	    &muzzy_decay_ms);
577
578	emitter_json_kv(emitter, "pactive", emitter_type_size, &pactive);
579	emitter_json_kv(emitter, "pdirty", emitter_type_size, &pdirty);
580	emitter_json_kv(emitter, "pmuzzy", emitter_type_size, &pmuzzy);
581
582	emitter_json_kv(emitter, "dirty_npurge", emitter_type_uint64,
583	    &dirty_npurge);
584	emitter_json_kv(emitter, "dirty_nmadvise", emitter_type_uint64,
585	    &dirty_nmadvise);
586	emitter_json_kv(emitter, "dirty_purged", emitter_type_uint64,
587	    &dirty_purged);
588
589	emitter_json_kv(emitter, "muzzy_npurge", emitter_type_uint64,
590	    &muzzy_npurge);
591	emitter_json_kv(emitter, "muzzy_nmadvise", emitter_type_uint64,
592	    &muzzy_nmadvise);
593	emitter_json_kv(emitter, "muzzy_purged", emitter_type_uint64,
594	    &muzzy_purged);
595
596	/* Table-style emission. */
597	emitter_col_t decay_type;
598	emitter_col_init(&decay_type, &decay_row);
599	decay_type.justify = emitter_justify_right;
600	decay_type.width = 9;
601	decay_type.type = emitter_type_title;
602	decay_type.str_val = "decaying:";
603
604	emitter_col_t decay_time;
605	emitter_col_init(&decay_time, &decay_row);
606	decay_time.justify = emitter_justify_right;
607	decay_time.width = 6;
608	decay_time.type = emitter_type_title;
609	decay_time.str_val = "time";
610
611	emitter_col_t decay_npages;
612	emitter_col_init(&decay_npages, &decay_row);
613	decay_npages.justify = emitter_justify_right;
614	decay_npages.width = 13;
615	decay_npages.type = emitter_type_title;
616	decay_npages.str_val = "npages";
617
618	emitter_col_t decay_sweeps;
619	emitter_col_init(&decay_sweeps, &decay_row);
620	decay_sweeps.justify = emitter_justify_right;
621	decay_sweeps.width = 13;
622	decay_sweeps.type = emitter_type_title;
623	decay_sweeps.str_val = "sweeps";
624
625	emitter_col_t decay_madvises;
626	emitter_col_init(&decay_madvises, &decay_row);
627	decay_madvises.justify = emitter_justify_right;
628	decay_madvises.width = 13;
629	decay_madvises.type = emitter_type_title;
630	decay_madvises.str_val = "madvises";
631
632	emitter_col_t decay_purged;
633	emitter_col_init(&decay_purged, &decay_row);
634	decay_purged.justify = emitter_justify_right;
635	decay_purged.width = 13;
636	decay_purged.type = emitter_type_title;
637	decay_purged.str_val = "purged";
638
639	/* Title row. */
640	emitter_table_row(emitter, &decay_row);
641
642	/* Dirty row. */
643	decay_type.str_val = "dirty:";
644
645	if (dirty_decay_ms >= 0) {
646		decay_time.type = emitter_type_ssize;
647		decay_time.ssize_val = dirty_decay_ms;
648	} else {
649		decay_time.type = emitter_type_title;
650		decay_time.str_val = "N/A";
651	}
652
653	decay_npages.type = emitter_type_size;
654	decay_npages.size_val = pdirty;
655
656	decay_sweeps.type = emitter_type_uint64;
657	decay_sweeps.uint64_val = dirty_npurge;
658
659	decay_madvises.type = emitter_type_uint64;
660	decay_madvises.uint64_val = dirty_nmadvise;
661
662	decay_purged.type = emitter_type_uint64;
663	decay_purged.uint64_val = dirty_purged;
664
665	emitter_table_row(emitter, &decay_row);
666
667	/* Muzzy row. */
668	decay_type.str_val = "muzzy:";
669
670	if (muzzy_decay_ms >= 0) {
671		decay_time.type = emitter_type_ssize;
672		decay_time.ssize_val = muzzy_decay_ms;
673	} else {
674		decay_time.type = emitter_type_title;
675		decay_time.str_val = "N/A";
676	}
677
678	decay_npages.type = emitter_type_size;
679	decay_npages.size_val = pmuzzy;
680
681	decay_sweeps.type = emitter_type_uint64;
682	decay_sweeps.uint64_val = muzzy_npurge;
683
684	decay_madvises.type = emitter_type_uint64;
685	decay_madvises.uint64_val = muzzy_nmadvise;
686
687	decay_purged.type = emitter_type_uint64;
688	decay_purged.uint64_val = muzzy_purged;
689
690	emitter_table_row(emitter, &decay_row);
691
692	/* Small / large / total allocation counts. */
693	emitter_row_t alloc_count_row;
694	emitter_row_init(&alloc_count_row);
695
696	emitter_col_t alloc_count_title;
697	emitter_col_init(&alloc_count_title, &alloc_count_row);
698	alloc_count_title.justify = emitter_justify_left;
699	alloc_count_title.width = 25;
700	alloc_count_title.type = emitter_type_title;
701	alloc_count_title.str_val = "";
702
703	emitter_col_t alloc_count_allocated;
704	emitter_col_init(&alloc_count_allocated, &alloc_count_row);
705	alloc_count_allocated.justify = emitter_justify_right;
706	alloc_count_allocated.width = 12;
707	alloc_count_allocated.type = emitter_type_title;
708	alloc_count_allocated.str_val = "allocated";
709
710	emitter_col_t alloc_count_nmalloc;
711	emitter_col_init(&alloc_count_nmalloc, &alloc_count_row);
712	alloc_count_nmalloc.justify = emitter_justify_right;
713	alloc_count_nmalloc.width = 12;
714	alloc_count_nmalloc.type = emitter_type_title;
715	alloc_count_nmalloc.str_val = "nmalloc";
716
717	emitter_col_t alloc_count_ndalloc;
718	emitter_col_init(&alloc_count_ndalloc, &alloc_count_row);
719	alloc_count_ndalloc.justify = emitter_justify_right;
720	alloc_count_ndalloc.width = 12;
721	alloc_count_ndalloc.type = emitter_type_title;
722	alloc_count_ndalloc.str_val = "ndalloc";
723
724	emitter_col_t alloc_count_nrequests;
725	emitter_col_init(&alloc_count_nrequests, &alloc_count_row);
726	alloc_count_nrequests.justify = emitter_justify_right;
727	alloc_count_nrequests.width = 12;
728	alloc_count_nrequests.type = emitter_type_title;
729	alloc_count_nrequests.str_val = "nrequests";
730
731	emitter_table_row(emitter, &alloc_count_row);
732
733#define GET_AND_EMIT_ALLOC_STAT(small_or_large, name, valtype)		\
734	CTL_M2_GET("stats.arenas.0." #small_or_large "." #name, i,	\
735	    &small_or_large##_##name, valtype##_t);			\
736	emitter_json_kv(emitter, #name, emitter_type_##valtype,		\
737	    &small_or_large##_##name);					\
738	alloc_count_##name.type = emitter_type_##valtype;		\
739	alloc_count_##name.valtype##_val = small_or_large##_##name;
740
741	emitter_json_dict_begin(emitter, "small");
742	alloc_count_title.str_val = "small:";
743
744	GET_AND_EMIT_ALLOC_STAT(small, allocated, size)
745	GET_AND_EMIT_ALLOC_STAT(small, nmalloc, uint64)
746	GET_AND_EMIT_ALLOC_STAT(small, ndalloc, uint64)
747	GET_AND_EMIT_ALLOC_STAT(small, nrequests, uint64)
748
749	emitter_table_row(emitter, &alloc_count_row);
750	emitter_json_dict_end(emitter); /* Close "small". */
751
752	emitter_json_dict_begin(emitter, "large");
753	alloc_count_title.str_val = "large:";
754
755	GET_AND_EMIT_ALLOC_STAT(large, allocated, size)
756	GET_AND_EMIT_ALLOC_STAT(large, nmalloc, uint64)
757	GET_AND_EMIT_ALLOC_STAT(large, ndalloc, uint64)
758	GET_AND_EMIT_ALLOC_STAT(large, nrequests, uint64)
759
760	emitter_table_row(emitter, &alloc_count_row);
761	emitter_json_dict_end(emitter); /* Close "large". */
762
763#undef GET_AND_EMIT_ALLOC_STAT
764
765	/* Aggregated small + large stats are emitter only in table mode. */
766	alloc_count_title.str_val = "total:";
767	alloc_count_allocated.size_val = small_allocated + large_allocated;
768	alloc_count_nmalloc.uint64_val = small_nmalloc + large_nmalloc;
769	alloc_count_ndalloc.uint64_val = small_ndalloc + large_ndalloc;
770	alloc_count_nrequests.uint64_val = small_nrequests + large_nrequests;
771	emitter_table_row(emitter, &alloc_count_row);
772
773	emitter_row_t mem_count_row;
774	emitter_row_init(&mem_count_row);
775
776	emitter_col_t mem_count_title;
777	emitter_col_init(&mem_count_title, &mem_count_row);
778	mem_count_title.justify = emitter_justify_left;
779	mem_count_title.width = 25;
780	mem_count_title.type = emitter_type_title;
781	mem_count_title.str_val = "";
782
783	emitter_col_t mem_count_val;
784	emitter_col_init(&mem_count_val, &mem_count_row);
785	mem_count_val.justify = emitter_justify_right;
786	mem_count_val.width = 12;
787	mem_count_val.type = emitter_type_title;
788	mem_count_val.str_val = "";
789
790	emitter_table_row(emitter, &mem_count_row);
791	mem_count_val.type = emitter_type_size;
792
793	/* Active count in bytes is emitted only in table mode. */
794	mem_count_title.str_val = "active:";
795	mem_count_val.size_val = pactive * page;
796	emitter_table_row(emitter, &mem_count_row);
797
798#define GET_AND_EMIT_MEM_STAT(stat)					\
799	CTL_M2_GET("stats.arenas.0."#stat, i, &stat, size_t);		\
800	emitter_json_kv(emitter, #stat, emitter_type_size, &stat);	\
801	mem_count_title.str_val = #stat":";				\
802	mem_count_val.size_val = stat;					\
803	emitter_table_row(emitter, &mem_count_row);
804
805	GET_AND_EMIT_MEM_STAT(mapped)
806	GET_AND_EMIT_MEM_STAT(retained)
807	GET_AND_EMIT_MEM_STAT(base)
808	GET_AND_EMIT_MEM_STAT(internal)
809	GET_AND_EMIT_MEM_STAT(metadata_thp)
810	GET_AND_EMIT_MEM_STAT(tcache_bytes)
811	GET_AND_EMIT_MEM_STAT(resident)
812#undef GET_AND_EMIT_MEM_STAT
813
814	if (mutex) {
815		stats_arena_mutexes_print(emitter, i);
816	}
817	if (bins) {
818		stats_arena_bins_print(emitter, mutex, i);
819	}
820	if (large) {
821		stats_arena_lextents_print(emitter, i);
822	}
823}
824
825static void
826stats_general_print(emitter_t *emitter) {
827	const char *cpv;
828	bool bv, bv2;
829	unsigned uv;
830	uint32_t u32v;
831	uint64_t u64v;
832	ssize_t ssv, ssv2;
833	size_t sv, bsz, usz, ssz, sssz, cpsz;
834
835	bsz = sizeof(bool);
836	usz = sizeof(unsigned);
837	ssz = sizeof(size_t);
838	sssz = sizeof(ssize_t);
839	cpsz = sizeof(const char *);
840
841	CTL_GET("version", &cpv, const char *);
842	emitter_kv(emitter, "version", "Version", emitter_type_string, &cpv);
843
844	/* config. */
845	emitter_dict_begin(emitter, "config", "Build-time option settings");
846#define CONFIG_WRITE_BOOL(name)						\
847	do {								\
848		CTL_GET("config."#name, &bv, bool);			\
849		emitter_kv(emitter, #name, "config."#name,		\
850		    emitter_type_bool, &bv);				\
851	} while (0)
852
853	CONFIG_WRITE_BOOL(cache_oblivious);
854	CONFIG_WRITE_BOOL(debug);
855	CONFIG_WRITE_BOOL(fill);
856	CONFIG_WRITE_BOOL(lazy_lock);
857	emitter_kv(emitter, "malloc_conf", "config.malloc_conf",
858	    emitter_type_string, &config_malloc_conf);
859
860	CONFIG_WRITE_BOOL(prof);
861	CONFIG_WRITE_BOOL(prof_libgcc);
862	CONFIG_WRITE_BOOL(prof_libunwind);
863	CONFIG_WRITE_BOOL(stats);
864	CONFIG_WRITE_BOOL(utrace);
865	CONFIG_WRITE_BOOL(xmalloc);
866#undef CONFIG_WRITE_BOOL
867	emitter_dict_end(emitter); /* Close "config" dict. */
868
869	/* opt. */
870#define OPT_WRITE(name, var, size, emitter_type)			\
871	if (je_mallctl("opt."name, (void *)&var, &size, NULL, 0) ==	\
872	    0) {							\
873		emitter_kv(emitter, name, "opt."name, emitter_type,	\
874		    &var);						\
875	}
876
877#define OPT_WRITE_MUTABLE(name, var1, var2, size, emitter_type,		\
878    altname)								\
879	if (je_mallctl("opt."name, (void *)&var1, &size, NULL, 0) ==	\
880	    0 && je_mallctl(altname, (void *)&var2, &size, NULL, 0)	\
881	    == 0) {							\
882		emitter_kv_note(emitter, name, "opt."name,		\
883		    emitter_type, &var1, altname, emitter_type,		\
884		    &var2);						\
885	}
886
887#define OPT_WRITE_BOOL(name) OPT_WRITE(name, bv, bsz, emitter_type_bool)
888#define OPT_WRITE_BOOL_MUTABLE(name, altname)				\
889	OPT_WRITE_MUTABLE(name, bv, bv2, bsz, emitter_type_bool, altname)
890
891#define OPT_WRITE_UNSIGNED(name)					\
892	OPT_WRITE(name, uv, usz, emitter_type_unsigned)
893
894#define OPT_WRITE_SSIZE_T(name)						\
895	OPT_WRITE(name, ssv, sssz, emitter_type_ssize)
896#define OPT_WRITE_SSIZE_T_MUTABLE(name, altname)			\
897	OPT_WRITE_MUTABLE(name, ssv, ssv2, sssz, emitter_type_ssize,	\
898	    altname)
899
900#define OPT_WRITE_CHAR_P(name)						\
901	OPT_WRITE(name, cpv, cpsz, emitter_type_string)
902
903	emitter_dict_begin(emitter, "opt", "Run-time option settings");
904
905	OPT_WRITE_BOOL("abort")
906	OPT_WRITE_BOOL("abort_conf")
907	OPT_WRITE_BOOL("retain")
908	OPT_WRITE_CHAR_P("dss")
909	OPT_WRITE_UNSIGNED("narenas")
910	OPT_WRITE_CHAR_P("percpu_arena")
911	OPT_WRITE_CHAR_P("metadata_thp")
912	OPT_WRITE_BOOL_MUTABLE("background_thread", "background_thread")
913	OPT_WRITE_SSIZE_T_MUTABLE("dirty_decay_ms", "arenas.dirty_decay_ms")
914	OPT_WRITE_SSIZE_T_MUTABLE("muzzy_decay_ms", "arenas.muzzy_decay_ms")
915	OPT_WRITE_UNSIGNED("lg_extent_max_active_fit")
916	OPT_WRITE_CHAR_P("junk")
917	OPT_WRITE_BOOL("zero")
918	OPT_WRITE_BOOL("utrace")
919	OPT_WRITE_BOOL("xmalloc")
920	OPT_WRITE_BOOL("tcache")
921	OPT_WRITE_SSIZE_T("lg_tcache_max")
922	OPT_WRITE_CHAR_P("thp")
923	OPT_WRITE_BOOL("prof")
924	OPT_WRITE_CHAR_P("prof_prefix")
925	OPT_WRITE_BOOL_MUTABLE("prof_active", "prof.active")
926	OPT_WRITE_BOOL_MUTABLE("prof_thread_active_init",
927	    "prof.thread_active_init")
928	OPT_WRITE_SSIZE_T_MUTABLE("lg_prof_sample", "prof.lg_sample")
929	OPT_WRITE_BOOL("prof_accum")
930	OPT_WRITE_SSIZE_T("lg_prof_interval")
931	OPT_WRITE_BOOL("prof_gdump")
932	OPT_WRITE_BOOL("prof_final")
933	OPT_WRITE_BOOL("prof_leak")
934	OPT_WRITE_BOOL("stats_print")
935	OPT_WRITE_CHAR_P("stats_print_opts")
936
937	emitter_dict_end(emitter);
938
939#undef OPT_WRITE
940#undef OPT_WRITE_MUTABLE
941#undef OPT_WRITE_BOOL
942#undef OPT_WRITE_BOOL_MUTABLE
943#undef OPT_WRITE_UNSIGNED
944#undef OPT_WRITE_SSIZE_T
945#undef OPT_WRITE_SSIZE_T_MUTABLE
946#undef OPT_WRITE_CHAR_P
947
948	/* prof. */
949	if (config_prof) {
950		emitter_dict_begin(emitter, "prof", "Profiling settings");
951
952		CTL_GET("prof.thread_active_init", &bv, bool);
953		emitter_kv(emitter, "thread_active_init",
954		    "prof.thread_active_init", emitter_type_bool, &bv);
955
956		CTL_GET("prof.active", &bv, bool);
957		emitter_kv(emitter, "active", "prof.active", emitter_type_bool,
958		    &bv);
959
960		CTL_GET("prof.gdump", &bv, bool);
961		emitter_kv(emitter, "gdump", "prof.gdump", emitter_type_bool,
962		    &bv);
963
964		CTL_GET("prof.interval", &u64v, uint64_t);
965		emitter_kv(emitter, "interval", "prof.interval",
966		    emitter_type_uint64, &u64v);
967
968		CTL_GET("prof.lg_sample", &ssv, ssize_t);
969		emitter_kv(emitter, "lg_sample", "prof.lg_sample",
970		    emitter_type_ssize, &ssv);
971
972		emitter_dict_end(emitter); /* Close "prof". */
973	}
974
975	/* arenas. */
976	/*
977	 * The json output sticks arena info into an "arenas" dict; the table
978	 * output puts them at the top-level.
979	 */
980	emitter_json_dict_begin(emitter, "arenas");
981
982	CTL_GET("arenas.narenas", &uv, unsigned);
983	emitter_kv(emitter, "narenas", "Arenas", emitter_type_unsigned, &uv);
984
985	/*
986	 * Decay settings are emitted only in json mode; in table mode, they're
987	 * emitted as notes with the opt output, above.
988	 */
989	CTL_GET("arenas.dirty_decay_ms", &ssv, ssize_t);
990	emitter_json_kv(emitter, "dirty_decay_ms", emitter_type_ssize, &ssv);
991
992	CTL_GET("arenas.muzzy_decay_ms", &ssv, ssize_t);
993	emitter_json_kv(emitter, "muzzy_decay_ms", emitter_type_ssize, &ssv);
994
995	CTL_GET("arenas.quantum", &sv, size_t);
996	emitter_kv(emitter, "quantum", "Quantum size", emitter_type_size, &sv);
997
998	CTL_GET("arenas.page", &sv, size_t);
999	emitter_kv(emitter, "page", "Page size", emitter_type_size, &sv);
1000
1001	if (je_mallctl("arenas.tcache_max", (void *)&sv, &ssz, NULL, 0) == 0) {
1002		emitter_kv(emitter, "tcache_max",
1003		    "Maximum thread-cached size class", emitter_type_size, &sv);
1004	}
1005
1006	unsigned nbins;
1007	CTL_GET("arenas.nbins", &nbins, unsigned);
1008	emitter_kv(emitter, "nbins", "Number of bin size classes",
1009	    emitter_type_unsigned, &nbins);
1010
1011	unsigned nh_bins;
1012	CTL_GET("arenas.nhbins", &nh_bins, unsigned);
1013	emitter_kv(emitter, "nhbins", "Number of thread-cache bin size classes",
1014	    emitter_type_unsigned, &nh_bins);
1015
1016	/*
1017	 * We do enough mallctls in a loop that we actually want to omit them
1018	 * (not just omit the printing).
1019	 */
1020	if (emitter->output == emitter_output_json) {
1021		emitter_json_arr_begin(emitter, "bin");
1022		for (unsigned i = 0; i < nbins; i++) {
1023			emitter_json_arr_obj_begin(emitter);
1024
1025			CTL_M2_GET("arenas.bin.0.size", i, &sv, size_t);
1026			emitter_json_kv(emitter, "size", emitter_type_size,
1027			    &sv);
1028
1029			CTL_M2_GET("arenas.bin.0.nregs", i, &u32v, uint32_t);
1030			emitter_json_kv(emitter, "nregs", emitter_type_uint32,
1031			    &u32v);
1032
1033			CTL_M2_GET("arenas.bin.0.slab_size", i, &sv, size_t);
1034			emitter_json_kv(emitter, "slab_size", emitter_type_size,
1035			    &sv);
1036
1037			emitter_json_arr_obj_end(emitter);
1038		}
1039		emitter_json_arr_end(emitter); /* Close "bin". */
1040	}
1041
1042	unsigned nlextents;
1043	CTL_GET("arenas.nlextents", &nlextents, unsigned);
1044	emitter_kv(emitter, "nlextents", "Number of large size classes",
1045	    emitter_type_unsigned, &nlextents);
1046
1047	if (emitter->output == emitter_output_json) {
1048		emitter_json_arr_begin(emitter, "lextent");
1049		for (unsigned i = 0; i < nlextents; i++) {
1050			emitter_json_arr_obj_begin(emitter);
1051
1052			CTL_M2_GET("arenas.lextent.0.size", i, &sv, size_t);
1053			emitter_json_kv(emitter, "size", emitter_type_size,
1054			    &sv);
1055
1056			emitter_json_arr_obj_end(emitter);
1057		}
1058		emitter_json_arr_end(emitter); /* Close "lextent". */
1059	}
1060
1061	emitter_json_dict_end(emitter); /* Close "arenas" */
1062}
1063
1064static void
1065stats_print_helper(emitter_t *emitter, bool merged, bool destroyed,
1066    bool unmerged, bool bins, bool large, bool mutex) {
1067	/*
1068	 * These should be deleted.  We keep them around for a while, to aid in
1069	 * the transition to the emitter code.
1070	 */
1071	size_t allocated, active, metadata, metadata_thp, resident, mapped,
1072	    retained;
1073	size_t num_background_threads;
1074	uint64_t background_thread_num_runs, background_thread_run_interval;
1075
1076	CTL_GET("stats.allocated", &allocated, size_t);
1077	CTL_GET("stats.active", &active, size_t);
1078	CTL_GET("stats.metadata", &metadata, size_t);
1079	CTL_GET("stats.metadata_thp", &metadata_thp, size_t);
1080	CTL_GET("stats.resident", &resident, size_t);
1081	CTL_GET("stats.mapped", &mapped, size_t);
1082	CTL_GET("stats.retained", &retained, size_t);
1083
1084	if (have_background_thread) {
1085		CTL_GET("stats.background_thread.num_threads",
1086		    &num_background_threads, size_t);
1087		CTL_GET("stats.background_thread.num_runs",
1088		    &background_thread_num_runs, uint64_t);
1089		CTL_GET("stats.background_thread.run_interval",
1090		    &background_thread_run_interval, uint64_t);
1091	} else {
1092		num_background_threads = 0;
1093		background_thread_num_runs = 0;
1094		background_thread_run_interval = 0;
1095	}
1096
1097	/* Generic global stats. */
1098	emitter_json_dict_begin(emitter, "stats");
1099	emitter_json_kv(emitter, "allocated", emitter_type_size, &allocated);
1100	emitter_json_kv(emitter, "active", emitter_type_size, &active);
1101	emitter_json_kv(emitter, "metadata", emitter_type_size, &metadata);
1102	emitter_json_kv(emitter, "metadata_thp", emitter_type_size,
1103	    &metadata_thp);
1104	emitter_json_kv(emitter, "resident", emitter_type_size, &resident);
1105	emitter_json_kv(emitter, "mapped", emitter_type_size, &mapped);
1106	emitter_json_kv(emitter, "retained", emitter_type_size, &retained);
1107
1108	emitter_table_printf(emitter, "Allocated: %zu, active: %zu, "
1109	    "metadata: %zu (n_thp %zu), resident: %zu, mapped: %zu, "
1110	    "retained: %zu\n", allocated, active, metadata, metadata_thp,
1111	    resident, mapped, retained);
1112
1113	/* Background thread stats. */
1114	emitter_json_dict_begin(emitter, "background_thread");
1115	emitter_json_kv(emitter, "num_threads", emitter_type_size,
1116	    &num_background_threads);
1117	emitter_json_kv(emitter, "num_runs", emitter_type_uint64,
1118	    &background_thread_num_runs);
1119	emitter_json_kv(emitter, "run_interval", emitter_type_uint64,
1120	    &background_thread_run_interval);
1121	emitter_json_dict_end(emitter); /* Close "background_thread". */
1122
1123	emitter_table_printf(emitter, "Background threads: %zu, "
1124	    "num_runs: %"FMTu64", run_interval: %"FMTu64" ns\n",
1125	    num_background_threads, background_thread_num_runs,
1126	    background_thread_run_interval);
1127
1128	if (mutex) {
1129		emitter_row_t row;
1130		emitter_col_t name;
1131		emitter_col_t col64[mutex_prof_num_uint64_t_counters];
1132		emitter_col_t col32[mutex_prof_num_uint32_t_counters];
1133
1134		emitter_row_init(&row);
1135		mutex_stats_init_cols(&row, "", &name, col64, col32);
1136
1137		emitter_table_row(emitter, &row);
1138		emitter_json_dict_begin(emitter, "mutexes");
1139
1140		for (int i = 0; i < mutex_prof_num_global_mutexes; i++) {
1141			mutex_stats_read_global(global_mutex_names[i], &name,
1142			    col64, col32);
1143			emitter_json_dict_begin(emitter, global_mutex_names[i]);
1144			mutex_stats_emit(emitter, &row, col64, col32);
1145			emitter_json_dict_end(emitter);
1146		}
1147
1148		emitter_json_dict_end(emitter); /* Close "mutexes". */
1149	}
1150
1151	emitter_json_dict_end(emitter); /* Close "stats". */
1152
1153	if (merged || destroyed || unmerged) {
1154		unsigned narenas;
1155
1156		emitter_json_dict_begin(emitter, "stats.arenas");
1157
1158		CTL_GET("arenas.narenas", &narenas, unsigned);
1159		size_t mib[3];
1160		size_t miblen = sizeof(mib) / sizeof(size_t);
1161		size_t sz;
1162		VARIABLE_ARRAY(bool, initialized, narenas);
1163		bool destroyed_initialized;
1164		unsigned i, j, ninitialized;
1165
1166		xmallctlnametomib("arena.0.initialized", mib, &miblen);
1167		for (i = ninitialized = 0; i < narenas; i++) {
1168			mib[1] = i;
1169			sz = sizeof(bool);
1170			xmallctlbymib(mib, miblen, &initialized[i], &sz,
1171			    NULL, 0);
1172			if (initialized[i]) {
1173				ninitialized++;
1174			}
1175		}
1176		mib[1] = MALLCTL_ARENAS_DESTROYED;
1177		sz = sizeof(bool);
1178		xmallctlbymib(mib, miblen, &destroyed_initialized, &sz,
1179		    NULL, 0);
1180
1181		/* Merged stats. */
1182		if (merged && (ninitialized > 1 || !unmerged)) {
1183			/* Print merged arena stats. */
1184			emitter_table_printf(emitter, "Merged arenas stats:\n");
1185			emitter_json_dict_begin(emitter, "merged");
1186			stats_arena_print(emitter, MALLCTL_ARENAS_ALL, bins,
1187			    large, mutex);
1188			emitter_json_dict_end(emitter); /* Close "merged". */
1189		}
1190
1191		/* Destroyed stats. */
1192		if (destroyed_initialized && destroyed) {
1193			/* Print destroyed arena stats. */
1194			emitter_table_printf(emitter,
1195			    "Destroyed arenas stats:\n");
1196			emitter_json_dict_begin(emitter, "destroyed");
1197			stats_arena_print(emitter, MALLCTL_ARENAS_DESTROYED,
1198			    bins, large, mutex);
1199			emitter_json_dict_end(emitter); /* Close "destroyed". */
1200		}
1201
1202		/* Unmerged stats. */
1203		if (unmerged) {
1204			for (i = j = 0; i < narenas; i++) {
1205				if (initialized[i]) {
1206					char arena_ind_str[20];
1207					malloc_snprintf(arena_ind_str,
1208					    sizeof(arena_ind_str), "%u", i);
1209					emitter_json_dict_begin(emitter,
1210					    arena_ind_str);
1211					emitter_table_printf(emitter,
1212					    "arenas[%s]:\n", arena_ind_str);
1213					stats_arena_print(emitter, i, bins,
1214					    large, mutex);
1215					/* Close "<arena-ind>". */
1216					emitter_json_dict_end(emitter);
1217				}
1218			}
1219		}
1220		emitter_json_dict_end(emitter); /* Close "stats.arenas". */
1221	}
1222}
1223
1224void
1225stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
1226    const char *opts) {
1227	int err;
1228	uint64_t epoch;
1229	size_t u64sz;
1230#define OPTION(o, v, d, s) bool v = d;
1231	STATS_PRINT_OPTIONS
1232#undef OPTION
1233
1234	/*
1235	 * Refresh stats, in case mallctl() was called by the application.
1236	 *
1237	 * Check for OOM here, since refreshing the ctl cache can trigger
1238	 * allocation.  In practice, none of the subsequent mallctl()-related
1239	 * calls in this function will cause OOM if this one succeeds.
1240	 * */
1241	epoch = 1;
1242	u64sz = sizeof(uint64_t);
1243	err = je_mallctl("epoch", (void *)&epoch, &u64sz, (void *)&epoch,
1244	    sizeof(uint64_t));
1245	if (err != 0) {
1246		if (err == EAGAIN) {
1247			malloc_write("<jemalloc>: Memory allocation failure in "
1248			    "mallctl(\"epoch\", ...)\n");
1249			return;
1250		}
1251		malloc_write("<jemalloc>: Failure in mallctl(\"epoch\", "
1252		    "...)\n");
1253		abort();
1254	}
1255
1256	if (opts != NULL) {
1257		for (unsigned i = 0; opts[i] != '\0'; i++) {
1258			switch (opts[i]) {
1259#define OPTION(o, v, d, s) case o: v = s; break;
1260				STATS_PRINT_OPTIONS
1261#undef OPTION
1262			default:;
1263			}
1264		}
1265	}
1266
1267	emitter_t emitter;
1268	emitter_init(&emitter,
1269	    json ? emitter_output_json : emitter_output_table, write_cb,
1270	    cbopaque);
1271	emitter_begin(&emitter);
1272	emitter_table_printf(&emitter, "___ Begin jemalloc statistics ___\n");
1273	emitter_json_dict_begin(&emitter, "jemalloc");
1274
1275	if (general) {
1276		stats_general_print(&emitter);
1277	}
1278	if (config_stats) {
1279		stats_print_helper(&emitter, merged, destroyed, unmerged,
1280		    bins, large, mutex);
1281	}
1282
1283	emitter_json_dict_end(&emitter); /* Closes the "jemalloc" dict. */
1284	emitter_table_printf(&emitter, "--- End jemalloc statistics ---\n");
1285	emitter_end(&emitter);
1286}
1287