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