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