1 #define JEMALLOC_STATS_C_
2 #include "jemalloc/internal/jemalloc_preamble.h"
3 #include "jemalloc/internal/jemalloc_internal_includes.h"
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"
10 const char *global_mutex_names[mutex_prof_num_global_mutexes] = {
12 MUTEX_PROF_GLOBAL_MUTEXES
16 const char *arena_mutex_names[mutex_prof_num_arena_mutexes] = {
18 MUTEX_PROF_ARENA_MUTEXES
22 #define CTL_GET(n, v, t) do { \
23 size_t sz = sizeof(t); \
24 xmallctl(n, (void *)v, &sz, NULL, 0); \
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); \
33 xmallctlbymib(mib, miblen, (void *)v, &sz, NULL, 0); \
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); \
43 xmallctlbymib(mib, miblen, (void *)v, &sz, NULL, 0); \
46 /******************************************************************************/
49 bool opt_stats_print = false;
50 char opt_stats_print_opts[stats_print_tot_num_options+1] = "";
52 /******************************************************************************/
54 /* Calculate x.yyy and output a string (takes a fixed sized char array). */
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. */
62 assert(UINT64_MAX / dividend >= 1000);
65 unsigned n = (unsigned)((dividend * 1000) / divisor);
67 malloc_snprintf(str, 6, "0.00%u", n);
69 malloc_snprintf(str, 6, "0.0%u", n);
70 } else if (n < 1000) {
71 malloc_snprintf(str, 6, "0.%u", n);
73 malloc_snprintf(str, 6, "1");
79 #define MUTEX_CTL_STR_MAX_LENGTH 128
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);
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];
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);
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);
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"};
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) ? "" : ",");
115 malloc_cprintf(write_cb, cbopaque, "%s}%s\n", json_indent,
120 stats_arena_bins_print(void (*write_cb)(void *, const char *), void *cbopaque,
121 bool json, bool large, bool mutex, unsigned i) {
123 bool in_gap, in_gap_prev;
126 CTL_GET("arenas.page", &page, size_t);
128 CTL_GET("arenas.nbins", &nbins, unsigned);
130 malloc_cprintf(write_cb, cbopaque,
131 "\t\t\t\t\"bins\": [\n");
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");
141 for (j = 0, in_gap = false; j < nbins; j++) {
143 size_t reg_size, slab_size, curregs;
146 uint64_t nmalloc, ndalloc, nrequests, nfills, nflushes;
149 CTL_M2_M4_GET("stats.arenas.0.bins.0.nslabs", i, j, &nslabs,
151 in_gap_prev = in_gap;
152 in_gap = (nslabs == 0);
154 if (!json && in_gap_prev && !in_gap) {
155 malloc_cprintf(write_cb, cbopaque,
159 CTL_M2_GET("arenas.bin.0.size", j, ®_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);
163 CTL_M2_M4_GET("stats.arenas.0.bins.0.nmalloc", i, j, &nmalloc,
165 CTL_M2_M4_GET("stats.arenas.0.bins.0.ndalloc", i, j, &ndalloc,
167 CTL_M2_M4_GET("stats.arenas.0.bins.0.curregs", i, j, &curregs,
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,
173 CTL_M2_M4_GET("stats.arenas.0.bins.0.nflushes", i, j, &nflushes,
175 CTL_M2_M4_GET("stats.arenas.0.bins.0.nreslabs", i, j, &nreslabs,
177 CTL_M2_M4_GET("stats.arenas.0.bins.0.curslabs", i, j, &curslabs,
181 malloc_cprintf(write_cb, cbopaque,
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 ? "," : "");
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);
199 malloc_cprintf(write_cb, cbopaque,
201 (j + 1 < nbins) ? "," : "");
202 } else if (!in_gap) {
203 size_t availregs = nregs * curslabs;
205 if (get_rate_str((uint64_t)curregs, (uint64_t)availregs,
207 if (availregs == 0) {
208 malloc_snprintf(util, sizeof(util),
210 } else if (curregs > availregs) {
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.
218 malloc_snprintf(util, sizeof(util),
224 uint64_t mutex_stats[mutex_prof_num_counters];
226 read_arena_bin_mutex_stats(i, j, mutex_stats);
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,
237 /* Output less info for bin mutexes to save space. */
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]);
248 malloc_cprintf(write_cb, cbopaque, "\n");
253 malloc_cprintf(write_cb, cbopaque,
254 "\t\t\t\t]%s\n", large ? "," : "");
257 malloc_cprintf(write_cb, cbopaque,
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;
269 CTL_GET("arenas.nbins", &nbins, unsigned);
270 CTL_GET("arenas.nlextents", &nlextents, unsigned);
272 malloc_cprintf(write_cb, cbopaque,
273 "\t\t\t\t\"lextents\": [\n");
275 malloc_cprintf(write_cb, cbopaque,
276 "large: size ind allocated nmalloc"
277 " ndalloc nrequests curlextents\n");
279 for (j = 0, in_gap = false; j < nlextents; j++) {
280 uint64_t nmalloc, ndalloc, nrequests;
281 size_t lextent_size, curlextents;
283 CTL_M2_M4_GET("stats.arenas.0.lextents.0.nmalloc", i, j,
285 CTL_M2_M4_GET("stats.arenas.0.lextents.0.ndalloc", i, j,
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);
292 if (!json && in_gap_prev && !in_gap) {
293 malloc_cprintf(write_cb, cbopaque,
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);
301 malloc_cprintf(write_cb, cbopaque,
303 "\t\t\t\t\t\t\"curlextents\": %zu\n"
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);
317 malloc_cprintf(write_cb, cbopaque,
321 malloc_cprintf(write_cb, cbopaque,
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];
332 mutex_prof_arena_ind_t i;
333 for (i = 0; i < mutex_prof_num_arena_mutexes; i++) {
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);
345 mutex_stats_output(void (*write_cb)(void *, const char *), void *cbopaque,
346 const char *name, uint64_t stats[mutex_prof_num_counters],
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");
356 malloc_cprintf(write_cb, cbopaque, "%s", name);
357 malloc_cprintf(write_cb, cbopaque, ":%*c",
358 (int)(20 - strlen(name)), ' ');
360 char *fmt_str[2] = {"%12"FMTu32, "%16"FMTu64};
362 malloc_cprintf(write_cb, cbopaque, \
363 fmt_str[sizeof(t) / sizeof(uint32_t) - 1], \
364 (t)stats[mutex_counter_##c]);
367 malloc_cprintf(write_cb, cbopaque, "\n");
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);
376 /* Output mutex stats. */
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));
386 malloc_cprintf(write_cb, cbopaque, "\t\t\t\t}%s\n",
387 json_end ? "" : ",");
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);
398 stats_arena_print(void (*write_cb)(void *, const char *), void *cbopaque,
399 bool json, unsigned i, bool bins, bool large, bool mutex) {
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;
414 CTL_GET("arenas.page", &page, size_t);
416 CTL_M2_GET("stats.arenas.0.nthreads", i, &nthreads, unsigned);
418 malloc_cprintf(write_cb, cbopaque,
419 "\t\t\t\t\"nthreads\": %u,\n", nthreads);
421 malloc_cprintf(write_cb, cbopaque,
422 "assigned threads: %u\n", nthreads);
425 CTL_M2_GET("stats.arenas.0.uptime", i, &uptime, uint64_t);
427 malloc_cprintf(write_cb, cbopaque,
428 "\t\t\t\t\"uptime_ns\": %"FMTu64",\n", uptime);
430 malloc_cprintf(write_cb, cbopaque,
431 "uptime: %"FMTu64"\n", uptime);
434 CTL_M2_GET("stats.arenas.0.dss", i, &dss, const char *);
436 malloc_cprintf(write_cb, cbopaque,
437 "\t\t\t\t\"dss\": \"%s\",\n", dss);
439 malloc_cprintf(write_cb, cbopaque,
440 "dss allocation precedence: %s\n", dss);
443 CTL_M2_GET("stats.arenas.0.dirty_decay_ms", i, &dirty_decay_ms,
445 CTL_M2_GET("stats.arenas.0.muzzy_decay_ms", i, &muzzy_decay_ms,
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,
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,
457 CTL_M2_GET("stats.arenas.0.muzzy_purged", i, &muzzy_purged, uint64_t);
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);
482 malloc_cprintf(write_cb, cbopaque,
483 "decaying: time npages sweeps madvises"
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);
491 malloc_cprintf(write_cb, cbopaque,
492 " dirty: N/A %12zu %12"FMTu64" %12"FMTu64" %12"
493 FMTu64"\n", pdirty, dirty_npurge, dirty_nmadvise,
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);
502 malloc_cprintf(write_cb, cbopaque,
503 " muzzy: N/A %12zu %12"FMTu64" %12"FMTu64" %12"
504 FMTu64"\n", pmuzzy, muzzy_npurge, muzzy_nmadvise,
509 CTL_M2_GET("stats.arenas.0.small.allocated", i, &small_allocated,
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,
516 malloc_cprintf(write_cb, cbopaque,
517 "\t\t\t\t\"small\": {\n");
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);
528 malloc_cprintf(write_cb, cbopaque,
531 malloc_cprintf(write_cb, cbopaque,
533 " ndalloc nrequests\n");
534 malloc_cprintf(write_cb, cbopaque,
535 "small: %12zu %12"FMTu64" %12"FMTu64
537 small_allocated, small_nmalloc, small_ndalloc,
541 CTL_M2_GET("stats.arenas.0.large.allocated", i, &large_allocated,
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,
548 malloc_cprintf(write_cb, cbopaque,
549 "\t\t\t\t\"large\": {\n");
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);
560 malloc_cprintf(write_cb, cbopaque,
563 malloc_cprintf(write_cb, cbopaque,
564 "large: %12zu %12"FMTu64" %12"FMTu64
566 large_allocated, large_nmalloc, large_ndalloc,
568 malloc_cprintf(write_cb, cbopaque,
569 "total: %12zu %12"FMTu64" %12"FMTu64
571 small_allocated + large_allocated, small_nmalloc +
572 large_nmalloc, small_ndalloc + large_ndalloc,
573 small_nrequests + large_nrequests);
576 malloc_cprintf(write_cb, cbopaque,
577 "active: %12zu\n", pactive * page);
580 CTL_M2_GET("stats.arenas.0.mapped", i, &mapped, size_t);
582 malloc_cprintf(write_cb, cbopaque,
583 "\t\t\t\t\"mapped\": %zu,\n", mapped);
585 malloc_cprintf(write_cb, cbopaque,
586 "mapped: %12zu\n", mapped);
589 CTL_M2_GET("stats.arenas.0.retained", i, &retained, size_t);
591 malloc_cprintf(write_cb, cbopaque,
592 "\t\t\t\t\"retained\": %zu,\n", retained);
594 malloc_cprintf(write_cb, cbopaque,
595 "retained: %12zu\n", retained);
598 CTL_M2_GET("stats.arenas.0.base", i, &base, size_t);
600 malloc_cprintf(write_cb, cbopaque,
601 "\t\t\t\t\"base\": %zu,\n", base);
603 malloc_cprintf(write_cb, cbopaque,
604 "base: %12zu\n", base);
607 CTL_M2_GET("stats.arenas.0.internal", i, &internal, size_t);
609 malloc_cprintf(write_cb, cbopaque,
610 "\t\t\t\t\"internal\": %zu,\n", internal);
612 malloc_cprintf(write_cb, cbopaque,
613 "internal: %12zu\n", internal);
616 CTL_M2_GET("stats.arenas.0.tcache_bytes", i, &tcache_bytes, size_t);
618 malloc_cprintf(write_cb, cbopaque,
619 "\t\t\t\t\"tcache\": %zu,\n", tcache_bytes);
621 malloc_cprintf(write_cb, cbopaque,
622 "tcache: %12zu\n", tcache_bytes);
625 CTL_M2_GET("stats.arenas.0.resident", i, &resident, size_t);
627 malloc_cprintf(write_cb, cbopaque,
628 "\t\t\t\t\"resident\": %zu%s\n", resident,
629 (bins || large || mutex) ? "," : "");
631 malloc_cprintf(write_cb, cbopaque,
632 "resident: %12zu\n", resident);
636 stats_arena_mutexes_print(write_cb, cbopaque, json,
637 !(bins || large), i);
640 stats_arena_bins_print(write_cb, cbopaque, json, large, mutex,
644 stats_arena_lextents_print(write_cb, cbopaque, json, i);
649 stats_general_print(void (*write_cb)(void *, const char *), void *cbopaque,
650 bool json, bool more) {
657 size_t sv, bsz, usz, ssz, sssz, cpsz;
660 usz = sizeof(unsigned);
661 ssz = sizeof(size_t);
662 sssz = sizeof(ssize_t);
663 cpsz = sizeof(const char *);
665 CTL_GET("version", &cpv, const char *);
667 malloc_cprintf(write_cb, cbopaque,
668 "\t\t\"version\": \"%s\",\n", cpv);
670 malloc_cprintf(write_cb, cbopaque, "Version: %s\n", cpv);
674 #define CONFIG_WRITE_BOOL_JSON(n, c) \
676 CTL_GET("config."#n, &bv, bool); \
677 malloc_cprintf(write_cb, cbopaque, \
678 "\t\t\t\""#n"\": %s%s\n", bv ? "true" : "false", \
683 malloc_cprintf(write_cb, cbopaque,
684 "\t\t\"config\": {\n");
687 CONFIG_WRITE_BOOL_JSON(cache_oblivious, ",")
689 CTL_GET("config.debug", &bv, bool);
691 malloc_cprintf(write_cb, cbopaque,
692 "\t\t\t\"debug\": %s,\n", bv ? "true" : "false");
694 malloc_cprintf(write_cb, cbopaque, "Assertions %s\n",
695 bv ? "enabled" : "disabled");
698 CONFIG_WRITE_BOOL_JSON(fill, ",")
699 CONFIG_WRITE_BOOL_JSON(lazy_lock, ",")
702 malloc_cprintf(write_cb, cbopaque,
703 "\t\t\t\"malloc_conf\": \"%s\",\n",
706 malloc_cprintf(write_cb, cbopaque,
707 "config.malloc_conf: \"%s\"\n", config_malloc_conf);
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, "")
719 malloc_cprintf(write_cb, cbopaque,
722 #undef CONFIG_WRITE_BOOL_JSON
725 #define OPT_WRITE_BOOL(n, c) \
726 if (je_mallctl("opt."#n, (void *)&bv, &bsz, NULL, 0) == 0) { \
728 malloc_cprintf(write_cb, cbopaque, \
729 "\t\t\t\""#n"\": %s%s\n", bv ? "true" : \
732 malloc_cprintf(write_cb, cbopaque, \
733 " opt."#n": %s\n", bv ? "true" : "false"); \
736 #define OPT_WRITE_BOOL_MUTABLE(n, m, c) { \
738 if (je_mallctl("opt."#n, (void *)&bv, &bsz, NULL, 0) == 0 && \
739 je_mallctl(#m, (void *)&bv2, &bsz, NULL, 0) == 0) { \
741 malloc_cprintf(write_cb, cbopaque, \
742 "\t\t\t\""#n"\": %s%s\n", bv ? "true" : \
745 malloc_cprintf(write_cb, cbopaque, \
746 " opt."#n": %s ("#m": %s)\n", bv ? "true" \
747 : "false", bv2 ? "true" : "false"); \
751 #define OPT_WRITE_UNSIGNED(n, c) \
752 if (je_mallctl("opt."#n, (void *)&uv, &usz, NULL, 0) == 0) { \
754 malloc_cprintf(write_cb, cbopaque, \
755 "\t\t\t\""#n"\": %u%s\n", uv, (c)); \
757 malloc_cprintf(write_cb, cbopaque, \
758 " opt."#n": %u\n", uv); \
761 #define OPT_WRITE_SSIZE_T(n, c) \
762 if (je_mallctl("opt."#n, (void *)&ssv, &sssz, NULL, 0) == 0) { \
764 malloc_cprintf(write_cb, cbopaque, \
765 "\t\t\t\""#n"\": %zd%s\n", ssv, (c)); \
767 malloc_cprintf(write_cb, cbopaque, \
768 " opt."#n": %zd\n", ssv); \
771 #define OPT_WRITE_SSIZE_T_MUTABLE(n, m, c) { \
773 if (je_mallctl("opt."#n, (void *)&ssv, &sssz, NULL, 0) == 0 && \
774 je_mallctl(#m, (void *)&ssv2, &sssz, NULL, 0) == 0) { \
776 malloc_cprintf(write_cb, cbopaque, \
777 "\t\t\t\""#n"\": %zd%s\n", ssv, (c)); \
779 malloc_cprintf(write_cb, cbopaque, \
780 " opt."#n": %zd ("#m": %zd)\n", \
785 #define OPT_WRITE_CHAR_P(n, c) \
786 if (je_mallctl("opt."#n, (void *)&cpv, &cpsz, NULL, 0) == 0) { \
788 malloc_cprintf(write_cb, cbopaque, \
789 "\t\t\t\""#n"\": \"%s\"%s\n", cpv, (c)); \
791 malloc_cprintf(write_cb, cbopaque, \
792 " opt."#n": \"%s\"\n", cpv); \
797 malloc_cprintf(write_cb, cbopaque,
800 malloc_cprintf(write_cb, cbopaque,
801 "Run-time option settings:\n");
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,
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) {
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).
837 OPT_WRITE_CHAR_P(stats_print_opts, "")
840 malloc_cprintf(write_cb, cbopaque,
844 #undef OPT_WRITE_BOOL
845 #undef OPT_WRITE_BOOL_MUTABLE
846 #undef OPT_WRITE_SSIZE_T
847 #undef OPT_WRITE_CHAR_P
851 malloc_cprintf(write_cb, cbopaque,
852 "\t\t\"arenas\": {\n");
855 CTL_GET("arenas.narenas", &uv, unsigned);
857 malloc_cprintf(write_cb, cbopaque,
858 "\t\t\t\"narenas\": %u,\n", uv);
860 malloc_cprintf(write_cb, cbopaque, "Arenas: %u\n", uv);
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);
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);
873 CTL_GET("arenas.quantum", &sv, size_t);
875 malloc_cprintf(write_cb, cbopaque,
876 "\t\t\t\"quantum\": %zu,\n", sv);
878 malloc_cprintf(write_cb, cbopaque, "Quantum size: %zu\n", sv);
881 CTL_GET("arenas.page", &sv, size_t);
883 malloc_cprintf(write_cb, cbopaque,
884 "\t\t\t\"page\": %zu,\n", sv);
886 malloc_cprintf(write_cb, cbopaque, "Page size: %zu\n", sv);
889 if (je_mallctl("arenas.tcache_max", (void *)&sv, &ssz, NULL, 0) == 0) {
891 malloc_cprintf(write_cb, cbopaque,
892 "\t\t\t\"tcache_max\": %zu,\n", sv);
894 malloc_cprintf(write_cb, cbopaque,
895 "Maximum thread-cached size class: %zu\n", sv);
900 unsigned nbins, nlextents, i;
902 CTL_GET("arenas.nbins", &nbins, unsigned);
903 malloc_cprintf(write_cb, cbopaque,
904 "\t\t\t\"nbins\": %u,\n", nbins);
906 CTL_GET("arenas.nhbins", &uv, unsigned);
907 malloc_cprintf(write_cb, cbopaque, "\t\t\t\"nhbins\": %u,\n",
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,
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);
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);
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);
928 malloc_cprintf(write_cb, cbopaque,
929 "\t\t\t\t}%s\n", (i + 1 < nbins) ? "," : "");
931 malloc_cprintf(write_cb, cbopaque,
934 CTL_GET("arenas.nlextents", &nlextents, unsigned);
935 malloc_cprintf(write_cb, cbopaque,
936 "\t\t\t\"nlextents\": %u,\n", nlextents);
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,
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);
948 malloc_cprintf(write_cb, cbopaque,
949 "\t\t\t\t}%s\n", (i + 1 < nlextents) ? "," : "");
951 malloc_cprintf(write_cb, cbopaque,
954 malloc_cprintf(write_cb, cbopaque,
955 "\t\t}%s\n", (config_prof || more) ? "," : "");
959 if (config_prof && json) {
960 malloc_cprintf(write_cb, cbopaque,
961 "\t\t\"prof\": {\n");
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" :
968 CTL_GET("prof.active", &bv, bool);
969 malloc_cprintf(write_cb, cbopaque,
970 "\t\t\t\"active\": %s,\n", bv ? "true" : "false");
972 CTL_GET("prof.gdump", &bv, bool);
973 malloc_cprintf(write_cb, cbopaque,
974 "\t\t\t\"gdump\": %s,\n", bv ? "true" : "false");
976 CTL_GET("prof.interval", &u64v, uint64_t);
977 malloc_cprintf(write_cb, cbopaque,
978 "\t\t\t\"interval\": %"FMTu64",\n", u64v);
980 CTL_GET("prof.lg_sample", &ssv, ssize_t);
981 malloc_cprintf(write_cb, cbopaque,
982 "\t\t\t\"lg_sample\": %zd\n", ssv);
984 malloc_cprintf(write_cb, cbopaque,
985 "\t\t}%s\n", more ? "," : "");
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];
994 mutex_prof_global_ind_t i;
995 for (i = 0; i < mutex_prof_num_global_mutexes; i++) {
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);
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;
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);
1020 uint64_t mutex_stats[mutex_prof_num_global_mutexes][mutex_prof_num_counters];
1022 read_global_mutex_stats(mutex_stats);
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);
1033 num_background_threads = 0;
1034 background_thread_num_runs = 0;
1035 background_thread_run_interval = 0;
1039 malloc_cprintf(write_cb, cbopaque,
1040 "\t\t\"stats\": {\n");
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);
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",
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],
1076 i == mutex_prof_num_global_mutexes - 1);
1078 malloc_cprintf(write_cb, cbopaque, "\t\t\t}\n");
1080 malloc_cprintf(write_cb, cbopaque,
1081 "\t\t}%s\n", (merged || unmerged || destroyed) ? "," : "");
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);
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);
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],
1106 if (merged || destroyed || unmerged) {
1110 malloc_cprintf(write_cb, cbopaque,
1111 "\t\t\"stats.arenas\": {\n");
1114 CTL_GET("arenas.narenas", &narenas, unsigned);
1117 size_t miblen = sizeof(mib) / sizeof(size_t);
1119 VARIABLE_ARRAY(bool, initialized, narenas);
1120 bool destroyed_initialized;
1121 unsigned i, j, ninitialized;
1123 xmallctlnametomib("arena.0.initialized", mib, &miblen);
1124 for (i = ninitialized = 0; i < narenas; i++) {
1127 xmallctlbymib(mib, miblen, &initialized[i], &sz,
1129 if (initialized[i]) {
1133 mib[1] = MALLCTL_ARENAS_DESTROYED;
1135 xmallctlbymib(mib, miblen, &destroyed_initialized, &sz,
1139 if (merged && (ninitialized > 1 || !unmerged)) {
1140 /* Print merged arena stats. */
1142 malloc_cprintf(write_cb, cbopaque,
1143 "\t\t\t\"merged\": {\n");
1145 malloc_cprintf(write_cb, cbopaque,
1146 "\nMerged arenas stats:\n");
1148 stats_arena_print(write_cb, cbopaque, json,
1149 MALLCTL_ARENAS_ALL, bins, large, mutex);
1151 malloc_cprintf(write_cb, cbopaque,
1153 ((destroyed_initialized &&
1154 destroyed) || unmerged) ? "," :
1159 /* Destroyed stats. */
1160 if (destroyed_initialized && destroyed) {
1161 /* Print destroyed arena stats. */
1163 malloc_cprintf(write_cb, cbopaque,
1164 "\t\t\t\"destroyed\": {\n");
1166 malloc_cprintf(write_cb, cbopaque,
1167 "\nDestroyed arenas stats:\n");
1169 stats_arena_print(write_cb, cbopaque, json,
1170 MALLCTL_ARENAS_DESTROYED, bins, large,
1173 malloc_cprintf(write_cb, cbopaque,
1174 "\t\t\t}%s\n", unmerged ? "," :
1179 /* Unmerged stats. */
1181 for (i = j = 0; i < narenas; i++) {
1182 if (initialized[i]) {
1185 malloc_cprintf(write_cb,
1187 "\t\t\t\"%u\": {\n",
1190 malloc_cprintf(write_cb,
1195 stats_arena_print(write_cb,
1196 cbopaque, json, i, bins,
1199 malloc_cprintf(write_cb,
1211 malloc_cprintf(write_cb, cbopaque,
1218 stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
1223 #define OPTION(o, v, d, s) bool v = d;
1228 * Refresh stats, in case mallctl() was called by the application.
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.
1235 u64sz = sizeof(uint64_t);
1236 err = je_mallctl("epoch", (void *)&epoch, &u64sz, (void *)&epoch,
1239 if (err == EAGAIN) {
1240 malloc_write("<jemalloc>: Memory allocation failure in "
1241 "mallctl(\"epoch\", ...)\n");
1244 malloc_write("<jemalloc>: Failure in mallctl(\"epoch\", "
1250 for (unsigned i = 0; opts[i] != '\0'; i++) {
1252 #define OPTION(o, v, d, s) case o: v = s; break;
1261 malloc_cprintf(write_cb, cbopaque,
1263 "\t\"jemalloc\": {\n");
1265 malloc_cprintf(write_cb, cbopaque,
1266 "___ Begin jemalloc statistics ___\n");
1270 stats_general_print(write_cb, cbopaque, json, config_stats);
1273 stats_print_helper(write_cb, cbopaque, json, merged, destroyed,
1274 unmerged, bins, large, mutex);
1278 malloc_cprintf(write_cb, cbopaque,
1282 malloc_cprintf(write_cb, cbopaque,
1283 "--- End jemalloc statistics ---\n");