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