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