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