xref: /freebsd/contrib/jemalloc/src/stats.c (revision 0957b409)
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 
11 const char *global_mutex_names[mutex_prof_num_global_mutexes] = {
12 #define OP(mtx) #mtx,
13 	MUTEX_PROF_GLOBAL_MUTEXES
14 #undef OP
15 };
16 
17 const 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 
50 bool opt_stats_print = false;
51 char 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). */
56 static bool
57 get_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
81 static void
82 gen_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 
87 static void
88 mutex_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 
121 static void
122 mutex_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 
144 static void
145 mutex_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 
169 static void
170 mutex_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. */
192 static void
193 mutex_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 
218 static void
219 stats_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 
410 static void
411 stats_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 
497 static void
498 stats_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 
522 static void
523 stats_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 
825 static void
826 stats_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 nhbins;
1012 	CTL_GET("arenas.nhbins", &nhbins, unsigned);
1013 	emitter_kv(emitter, "nhbins", "Number of thread-cache bin size classes",
1014 	    emitter_type_unsigned, &nhbins);
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 
1064 static void
1065 stats_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 
1224 void
1225 stats_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