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, ®_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 } 1286