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