1 #include <stdlib.h> 2 #include <stdio.h> 3 #include <inttypes.h> 4 #include <linux/string.h> 5 #include <linux/time64.h> 6 #include <math.h> 7 #include <perf/cpumap.h> 8 #include "color.h" 9 #include "counts.h" 10 #include "evlist.h" 11 #include "evsel.h" 12 #include "stat.h" 13 #include "top.h" 14 #include "thread_map.h" 15 #include "cpumap.h" 16 #include "string2.h" 17 #include <linux/ctype.h> 18 #include "cgroup.h" 19 #include <api/fs/fs.h> 20 #include "util.h" 21 #include "iostat.h" 22 #include "pmu.h" 23 #include "pmus.h" 24 25 #define CNTR_NOT_SUPPORTED "<not supported>" 26 #define CNTR_NOT_COUNTED "<not counted>" 27 28 #define METRIC_LEN 38 29 #define EVNAME_LEN 32 30 #define COUNTS_LEN 18 31 #define INTERVAL_LEN 16 32 #define CGROUP_LEN 16 33 #define COMM_LEN 16 34 #define PID_LEN 7 35 #define CPUS_LEN 4 36 37 static int aggr_header_lens[] = { 38 [AGGR_CORE] = 18, 39 [AGGR_CACHE] = 22, 40 [AGGR_DIE] = 12, 41 [AGGR_SOCKET] = 6, 42 [AGGR_NODE] = 6, 43 [AGGR_NONE] = 6, 44 [AGGR_THREAD] = 16, 45 [AGGR_GLOBAL] = 0, 46 }; 47 48 static const char *aggr_header_csv[] = { 49 [AGGR_CORE] = "core,cpus,", 50 [AGGR_CACHE] = "cache,cpus,", 51 [AGGR_DIE] = "die,cpus,", 52 [AGGR_SOCKET] = "socket,cpus,", 53 [AGGR_NONE] = "cpu,", 54 [AGGR_THREAD] = "comm-pid,", 55 [AGGR_NODE] = "node,", 56 [AGGR_GLOBAL] = "" 57 }; 58 59 static const char *aggr_header_std[] = { 60 [AGGR_CORE] = "core", 61 [AGGR_CACHE] = "cache", 62 [AGGR_DIE] = "die", 63 [AGGR_SOCKET] = "socket", 64 [AGGR_NONE] = "cpu", 65 [AGGR_THREAD] = "comm-pid", 66 [AGGR_NODE] = "node", 67 [AGGR_GLOBAL] = "" 68 }; 69 70 static void print_running_std(struct perf_stat_config *config, u64 run, u64 ena) 71 { 72 if (run != ena) 73 fprintf(config->output, " (%.2f%%)", 100.0 * run / ena); 74 } 75 76 static void print_running_csv(struct perf_stat_config *config, u64 run, u64 ena) 77 { 78 double enabled_percent = 100; 79 80 if (run != ena) 81 enabled_percent = 100 * run / ena; 82 fprintf(config->output, "%s%" PRIu64 "%s%.2f", 83 config->csv_sep, run, config->csv_sep, enabled_percent); 84 } 85 86 static void print_running_json(struct perf_stat_config *config, u64 run, u64 ena) 87 { 88 double enabled_percent = 100; 89 90 if (run != ena) 91 enabled_percent = 100 * run / ena; 92 fprintf(config->output, "\"event-runtime\" : %" PRIu64 ", \"pcnt-running\" : %.2f, ", 93 run, enabled_percent); 94 } 95 96 static void print_running(struct perf_stat_config *config, 97 u64 run, u64 ena, bool before_metric) 98 { 99 if (config->json_output) { 100 if (before_metric) 101 print_running_json(config, run, ena); 102 } else if (config->csv_output) { 103 if (before_metric) 104 print_running_csv(config, run, ena); 105 } else { 106 if (!before_metric) 107 print_running_std(config, run, ena); 108 } 109 } 110 111 static void print_noise_pct_std(struct perf_stat_config *config, 112 double pct) 113 { 114 if (pct) 115 fprintf(config->output, " ( +-%6.2f%% )", pct); 116 } 117 118 static void print_noise_pct_csv(struct perf_stat_config *config, 119 double pct) 120 { 121 fprintf(config->output, "%s%.2f%%", config->csv_sep, pct); 122 } 123 124 static void print_noise_pct_json(struct perf_stat_config *config, 125 double pct) 126 { 127 fprintf(config->output, "\"variance\" : %.2f, ", pct); 128 } 129 130 static void print_noise_pct(struct perf_stat_config *config, 131 double total, double avg, bool before_metric) 132 { 133 double pct = rel_stddev_stats(total, avg); 134 135 if (config->json_output) { 136 if (before_metric) 137 print_noise_pct_json(config, pct); 138 } else if (config->csv_output) { 139 if (before_metric) 140 print_noise_pct_csv(config, pct); 141 } else { 142 if (!before_metric) 143 print_noise_pct_std(config, pct); 144 } 145 } 146 147 static void print_noise(struct perf_stat_config *config, 148 struct evsel *evsel, double avg, bool before_metric) 149 { 150 struct perf_stat_evsel *ps; 151 152 if (config->run_count == 1) 153 return; 154 155 ps = evsel->stats; 156 print_noise_pct(config, stddev_stats(&ps->res_stats), avg, before_metric); 157 } 158 159 static void print_cgroup_std(struct perf_stat_config *config, const char *cgrp_name) 160 { 161 fprintf(config->output, " %-*s", CGROUP_LEN, cgrp_name); 162 } 163 164 static void print_cgroup_csv(struct perf_stat_config *config, const char *cgrp_name) 165 { 166 fprintf(config->output, "%s%s", config->csv_sep, cgrp_name); 167 } 168 169 static void print_cgroup_json(struct perf_stat_config *config, const char *cgrp_name) 170 { 171 fprintf(config->output, "\"cgroup\" : \"%s\", ", cgrp_name); 172 } 173 174 static void print_cgroup(struct perf_stat_config *config, struct cgroup *cgrp) 175 { 176 if (nr_cgroups || config->cgroup_list) { 177 const char *cgrp_name = cgrp ? cgrp->name : ""; 178 179 if (config->json_output) 180 print_cgroup_json(config, cgrp_name); 181 else if (config->csv_output) 182 print_cgroup_csv(config, cgrp_name); 183 else 184 print_cgroup_std(config, cgrp_name); 185 } 186 } 187 188 static void print_aggr_id_std(struct perf_stat_config *config, 189 struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr) 190 { 191 FILE *output = config->output; 192 int idx = config->aggr_mode; 193 char buf[128]; 194 195 switch (config->aggr_mode) { 196 case AGGR_CORE: 197 snprintf(buf, sizeof(buf), "S%d-D%d-C%d", id.socket, id.die, id.core); 198 break; 199 case AGGR_CACHE: 200 snprintf(buf, sizeof(buf), "S%d-D%d-L%d-ID%d", 201 id.socket, id.die, id.cache_lvl, id.cache); 202 break; 203 case AGGR_DIE: 204 snprintf(buf, sizeof(buf), "S%d-D%d", id.socket, id.die); 205 break; 206 case AGGR_SOCKET: 207 snprintf(buf, sizeof(buf), "S%d", id.socket); 208 break; 209 case AGGR_NODE: 210 snprintf(buf, sizeof(buf), "N%d", id.node); 211 break; 212 case AGGR_NONE: 213 if (evsel->percore && !config->percore_show_thread) { 214 snprintf(buf, sizeof(buf), "S%d-D%d-C%d ", 215 id.socket, id.die, id.core); 216 fprintf(output, "%-*s ", 217 aggr_header_lens[AGGR_CORE], buf); 218 } else if (id.cpu.cpu > -1) { 219 fprintf(output, "CPU%-*d ", 220 aggr_header_lens[AGGR_NONE] - 3, id.cpu.cpu); 221 } 222 return; 223 case AGGR_THREAD: 224 fprintf(output, "%*s-%-*d ", 225 COMM_LEN, perf_thread_map__comm(evsel->core.threads, id.thread_idx), 226 PID_LEN, perf_thread_map__pid(evsel->core.threads, id.thread_idx)); 227 return; 228 case AGGR_GLOBAL: 229 case AGGR_UNSET: 230 case AGGR_MAX: 231 default: 232 return; 233 } 234 235 fprintf(output, "%-*s %*d ", aggr_header_lens[idx], buf, 4, aggr_nr); 236 } 237 238 static void print_aggr_id_csv(struct perf_stat_config *config, 239 struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr) 240 { 241 FILE *output = config->output; 242 const char *sep = config->csv_sep; 243 244 switch (config->aggr_mode) { 245 case AGGR_CORE: 246 fprintf(output, "S%d-D%d-C%d%s%d%s", 247 id.socket, id.die, id.core, sep, aggr_nr, sep); 248 break; 249 case AGGR_CACHE: 250 fprintf(config->output, "S%d-D%d-L%d-ID%d%s%d%s", 251 id.socket, id.die, id.cache_lvl, id.cache, sep, aggr_nr, sep); 252 break; 253 case AGGR_DIE: 254 fprintf(output, "S%d-D%d%s%d%s", 255 id.socket, id.die, sep, aggr_nr, sep); 256 break; 257 case AGGR_SOCKET: 258 fprintf(output, "S%d%s%d%s", 259 id.socket, sep, aggr_nr, sep); 260 break; 261 case AGGR_NODE: 262 fprintf(output, "N%d%s%d%s", 263 id.node, sep, aggr_nr, sep); 264 break; 265 case AGGR_NONE: 266 if (evsel->percore && !config->percore_show_thread) { 267 fprintf(output, "S%d-D%d-C%d%s", 268 id.socket, id.die, id.core, sep); 269 } else if (id.cpu.cpu > -1) { 270 fprintf(output, "CPU%d%s", 271 id.cpu.cpu, sep); 272 } 273 break; 274 case AGGR_THREAD: 275 fprintf(output, "%s-%d%s", 276 perf_thread_map__comm(evsel->core.threads, id.thread_idx), 277 perf_thread_map__pid(evsel->core.threads, id.thread_idx), 278 sep); 279 break; 280 case AGGR_GLOBAL: 281 case AGGR_UNSET: 282 case AGGR_MAX: 283 default: 284 break; 285 } 286 } 287 288 static void print_aggr_id_json(struct perf_stat_config *config, 289 struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr) 290 { 291 FILE *output = config->output; 292 293 switch (config->aggr_mode) { 294 case AGGR_CORE: 295 fprintf(output, "\"core\" : \"S%d-D%d-C%d\", \"aggregate-number\" : %d, ", 296 id.socket, id.die, id.core, aggr_nr); 297 break; 298 case AGGR_CACHE: 299 fprintf(output, "\"cache\" : \"S%d-D%d-L%d-ID%d\", \"aggregate-number\" : %d, ", 300 id.socket, id.die, id.cache_lvl, id.cache, aggr_nr); 301 break; 302 case AGGR_DIE: 303 fprintf(output, "\"die\" : \"S%d-D%d\", \"aggregate-number\" : %d, ", 304 id.socket, id.die, aggr_nr); 305 break; 306 case AGGR_SOCKET: 307 fprintf(output, "\"socket\" : \"S%d\", \"aggregate-number\" : %d, ", 308 id.socket, aggr_nr); 309 break; 310 case AGGR_NODE: 311 fprintf(output, "\"node\" : \"N%d\", \"aggregate-number\" : %d, ", 312 id.node, aggr_nr); 313 break; 314 case AGGR_NONE: 315 if (evsel->percore && !config->percore_show_thread) { 316 fprintf(output, "\"core\" : \"S%d-D%d-C%d\"", 317 id.socket, id.die, id.core); 318 } else if (id.cpu.cpu > -1) { 319 fprintf(output, "\"cpu\" : \"%d\", ", 320 id.cpu.cpu); 321 } 322 break; 323 case AGGR_THREAD: 324 fprintf(output, "\"thread\" : \"%s-%d\", ", 325 perf_thread_map__comm(evsel->core.threads, id.thread_idx), 326 perf_thread_map__pid(evsel->core.threads, id.thread_idx)); 327 break; 328 case AGGR_GLOBAL: 329 case AGGR_UNSET: 330 case AGGR_MAX: 331 default: 332 break; 333 } 334 } 335 336 static void aggr_printout(struct perf_stat_config *config, 337 struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr) 338 { 339 if (config->json_output) 340 print_aggr_id_json(config, evsel, id, aggr_nr); 341 else if (config->csv_output) 342 print_aggr_id_csv(config, evsel, id, aggr_nr); 343 else 344 print_aggr_id_std(config, evsel, id, aggr_nr); 345 } 346 347 struct outstate { 348 FILE *fh; 349 bool newline; 350 bool first; 351 const char *prefix; 352 int nfields; 353 int aggr_nr; 354 struct aggr_cpu_id id; 355 struct evsel *evsel; 356 struct cgroup *cgrp; 357 }; 358 359 static void new_line_std(struct perf_stat_config *config __maybe_unused, 360 void *ctx) 361 { 362 struct outstate *os = ctx; 363 364 os->newline = true; 365 } 366 367 static void do_new_line_std(struct perf_stat_config *config, 368 struct outstate *os) 369 { 370 fputc('\n', os->fh); 371 if (os->prefix) 372 fputs(os->prefix, os->fh); 373 aggr_printout(config, os->evsel, os->id, os->aggr_nr); 374 if (config->aggr_mode == AGGR_NONE) 375 fprintf(os->fh, " "); 376 fprintf(os->fh, " "); 377 } 378 379 static void print_metric_std(struct perf_stat_config *config, 380 void *ctx, const char *color, const char *fmt, 381 const char *unit, double val) 382 { 383 struct outstate *os = ctx; 384 FILE *out = os->fh; 385 int n; 386 bool newline = os->newline; 387 388 os->newline = false; 389 390 if (unit == NULL || fmt == NULL) { 391 fprintf(out, "%-*s", METRIC_LEN, ""); 392 return; 393 } 394 395 if (newline) 396 do_new_line_std(config, os); 397 398 n = fprintf(out, " # "); 399 if (color) 400 n += color_fprintf(out, color, fmt, val); 401 else 402 n += fprintf(out, fmt, val); 403 fprintf(out, " %-*s", METRIC_LEN - n - 1, unit); 404 } 405 406 static void new_line_csv(struct perf_stat_config *config, void *ctx) 407 { 408 struct outstate *os = ctx; 409 int i; 410 411 fputc('\n', os->fh); 412 if (os->prefix) 413 fprintf(os->fh, "%s", os->prefix); 414 aggr_printout(config, os->evsel, os->id, os->aggr_nr); 415 for (i = 0; i < os->nfields; i++) 416 fputs(config->csv_sep, os->fh); 417 } 418 419 static void print_metric_csv(struct perf_stat_config *config __maybe_unused, 420 void *ctx, 421 const char *color __maybe_unused, 422 const char *fmt, const char *unit, double val) 423 { 424 struct outstate *os = ctx; 425 FILE *out = os->fh; 426 char buf[64], *vals, *ends; 427 428 if (unit == NULL || fmt == NULL) { 429 fprintf(out, "%s%s", config->csv_sep, config->csv_sep); 430 return; 431 } 432 snprintf(buf, sizeof(buf), fmt, val); 433 ends = vals = skip_spaces(buf); 434 while (isdigit(*ends) || *ends == '.') 435 ends++; 436 *ends = 0; 437 fprintf(out, "%s%s%s%s", config->csv_sep, vals, config->csv_sep, skip_spaces(unit)); 438 } 439 440 static void print_metric_json(struct perf_stat_config *config __maybe_unused, 441 void *ctx, 442 const char *color __maybe_unused, 443 const char *fmt __maybe_unused, 444 const char *unit, double val) 445 { 446 struct outstate *os = ctx; 447 FILE *out = os->fh; 448 449 fprintf(out, "\"metric-value\" : \"%f\", ", val); 450 fprintf(out, "\"metric-unit\" : \"%s\"", unit); 451 if (!config->metric_only) 452 fprintf(out, "}"); 453 } 454 455 static void new_line_json(struct perf_stat_config *config, void *ctx) 456 { 457 struct outstate *os = ctx; 458 459 fputs("\n{", os->fh); 460 if (os->prefix) 461 fprintf(os->fh, "%s", os->prefix); 462 aggr_printout(config, os->evsel, os->id, os->aggr_nr); 463 } 464 465 /* Filter out some columns that don't work well in metrics only mode */ 466 467 static bool valid_only_metric(const char *unit) 468 { 469 if (!unit) 470 return false; 471 if (strstr(unit, "/sec") || 472 strstr(unit, "CPUs utilized")) 473 return false; 474 return true; 475 } 476 477 static const char *fixunit(char *buf, struct evsel *evsel, 478 const char *unit) 479 { 480 if (!strncmp(unit, "of all", 6)) { 481 snprintf(buf, 1024, "%s %s", evsel__name(evsel), 482 unit); 483 return buf; 484 } 485 return unit; 486 } 487 488 static void print_metric_only(struct perf_stat_config *config, 489 void *ctx, const char *color, const char *fmt, 490 const char *unit, double val) 491 { 492 struct outstate *os = ctx; 493 FILE *out = os->fh; 494 char buf[1024], str[1024]; 495 unsigned mlen = config->metric_only_len; 496 497 if (!valid_only_metric(unit)) 498 return; 499 unit = fixunit(buf, os->evsel, unit); 500 if (mlen < strlen(unit)) 501 mlen = strlen(unit) + 1; 502 503 if (color) 504 mlen += strlen(color) + sizeof(PERF_COLOR_RESET) - 1; 505 506 color_snprintf(str, sizeof(str), color ?: "", fmt, val); 507 fprintf(out, "%*s ", mlen, str); 508 os->first = false; 509 } 510 511 static void print_metric_only_csv(struct perf_stat_config *config __maybe_unused, 512 void *ctx, const char *color __maybe_unused, 513 const char *fmt, 514 const char *unit, double val) 515 { 516 struct outstate *os = ctx; 517 FILE *out = os->fh; 518 char buf[64], *vals, *ends; 519 char tbuf[1024]; 520 521 if (!valid_only_metric(unit)) 522 return; 523 unit = fixunit(tbuf, os->evsel, unit); 524 snprintf(buf, sizeof buf, fmt, val); 525 ends = vals = skip_spaces(buf); 526 while (isdigit(*ends) || *ends == '.') 527 ends++; 528 *ends = 0; 529 fprintf(out, "%s%s", vals, config->csv_sep); 530 os->first = false; 531 } 532 533 static void print_metric_only_json(struct perf_stat_config *config __maybe_unused, 534 void *ctx, const char *color __maybe_unused, 535 const char *fmt, 536 const char *unit, double val) 537 { 538 struct outstate *os = ctx; 539 FILE *out = os->fh; 540 char buf[64], *vals, *ends; 541 char tbuf[1024]; 542 543 if (!valid_only_metric(unit)) 544 return; 545 unit = fixunit(tbuf, os->evsel, unit); 546 snprintf(buf, sizeof(buf), fmt, val); 547 ends = vals = skip_spaces(buf); 548 while (isdigit(*ends) || *ends == '.') 549 ends++; 550 *ends = 0; 551 if (!unit[0] || !vals[0]) 552 return; 553 fprintf(out, "%s\"%s\" : \"%s\"", os->first ? "" : ", ", unit, vals); 554 os->first = false; 555 } 556 557 static void new_line_metric(struct perf_stat_config *config __maybe_unused, 558 void *ctx __maybe_unused) 559 { 560 } 561 562 static void print_metric_header(struct perf_stat_config *config, 563 void *ctx, const char *color __maybe_unused, 564 const char *fmt __maybe_unused, 565 const char *unit, double val __maybe_unused) 566 { 567 struct outstate *os = ctx; 568 char tbuf[1024]; 569 570 /* In case of iostat, print metric header for first root port only */ 571 if (config->iostat_run && 572 os->evsel->priv != os->evsel->evlist->selected->priv) 573 return; 574 575 if (os->evsel->cgrp != os->cgrp) 576 return; 577 578 if (!valid_only_metric(unit)) 579 return; 580 unit = fixunit(tbuf, os->evsel, unit); 581 582 if (config->json_output) 583 return; 584 else if (config->csv_output) 585 fprintf(os->fh, "%s%s", unit, config->csv_sep); 586 else 587 fprintf(os->fh, "%*s ", config->metric_only_len, unit); 588 } 589 590 static void print_counter_value_std(struct perf_stat_config *config, 591 struct evsel *evsel, double avg, bool ok) 592 { 593 FILE *output = config->output; 594 double sc = evsel->scale; 595 const char *fmt; 596 const char *bad_count = evsel->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED; 597 598 if (config->big_num) 599 fmt = floor(sc) != sc ? "%'*.2f " : "%'*.0f "; 600 else 601 fmt = floor(sc) != sc ? "%*.2f " : "%*.0f "; 602 603 if (ok) 604 fprintf(output, fmt, COUNTS_LEN, avg); 605 else 606 fprintf(output, "%*s ", COUNTS_LEN, bad_count); 607 608 if (evsel->unit) 609 fprintf(output, "%-*s ", config->unit_width, evsel->unit); 610 611 fprintf(output, "%-*s", EVNAME_LEN, evsel__name(evsel)); 612 } 613 614 static void print_counter_value_csv(struct perf_stat_config *config, 615 struct evsel *evsel, double avg, bool ok) 616 { 617 FILE *output = config->output; 618 double sc = evsel->scale; 619 const char *sep = config->csv_sep; 620 const char *fmt = floor(sc) != sc ? "%.2f%s" : "%.0f%s"; 621 const char *bad_count = evsel->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED; 622 623 if (ok) 624 fprintf(output, fmt, avg, sep); 625 else 626 fprintf(output, "%s%s", bad_count, sep); 627 628 if (evsel->unit) 629 fprintf(output, "%s%s", evsel->unit, sep); 630 631 fprintf(output, "%s", evsel__name(evsel)); 632 } 633 634 static void print_counter_value_json(struct perf_stat_config *config, 635 struct evsel *evsel, double avg, bool ok) 636 { 637 FILE *output = config->output; 638 const char *bad_count = evsel->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED; 639 640 if (ok) 641 fprintf(output, "\"counter-value\" : \"%f\", ", avg); 642 else 643 fprintf(output, "\"counter-value\" : \"%s\", ", bad_count); 644 645 if (evsel->unit) 646 fprintf(output, "\"unit\" : \"%s\", ", evsel->unit); 647 648 fprintf(output, "\"event\" : \"%s\", ", evsel__name(evsel)); 649 } 650 651 static void print_counter_value(struct perf_stat_config *config, 652 struct evsel *evsel, double avg, bool ok) 653 { 654 if (config->json_output) 655 print_counter_value_json(config, evsel, avg, ok); 656 else if (config->csv_output) 657 print_counter_value_csv(config, evsel, avg, ok); 658 else 659 print_counter_value_std(config, evsel, avg, ok); 660 } 661 662 static void abs_printout(struct perf_stat_config *config, 663 struct aggr_cpu_id id, int aggr_nr, 664 struct evsel *evsel, double avg, bool ok) 665 { 666 aggr_printout(config, evsel, id, aggr_nr); 667 print_counter_value(config, evsel, avg, ok); 668 print_cgroup(config, evsel->cgrp); 669 } 670 671 static bool is_mixed_hw_group(struct evsel *counter) 672 { 673 struct evlist *evlist = counter->evlist; 674 u32 pmu_type = counter->core.attr.type; 675 struct evsel *pos; 676 677 if (counter->core.nr_members < 2) 678 return false; 679 680 evlist__for_each_entry(evlist, pos) { 681 /* software events can be part of any hardware group */ 682 if (pos->core.attr.type == PERF_TYPE_SOFTWARE) 683 continue; 684 if (pmu_type == PERF_TYPE_SOFTWARE) { 685 pmu_type = pos->core.attr.type; 686 continue; 687 } 688 if (pmu_type != pos->core.attr.type) 689 return true; 690 } 691 692 return false; 693 } 694 695 static bool evlist__has_hybrid(struct evlist *evlist) 696 { 697 struct evsel *evsel; 698 699 if (perf_pmus__num_core_pmus() == 1) 700 return false; 701 702 evlist__for_each_entry(evlist, evsel) { 703 if (evsel->core.is_pmu_core) 704 return true; 705 } 706 707 return false; 708 } 709 710 static void printout(struct perf_stat_config *config, struct outstate *os, 711 double uval, u64 run, u64 ena, double noise, int aggr_idx) 712 { 713 struct perf_stat_output_ctx out; 714 print_metric_t pm; 715 new_line_t nl; 716 bool ok = true; 717 struct evsel *counter = os->evsel; 718 719 if (config->csv_output) { 720 pm = config->metric_only ? print_metric_only_csv : print_metric_csv; 721 nl = config->metric_only ? new_line_metric : new_line_csv; 722 os->nfields = 4 + (counter->cgrp ? 1 : 0); 723 } else if (config->json_output) { 724 pm = config->metric_only ? print_metric_only_json : print_metric_json; 725 nl = config->metric_only ? new_line_metric : new_line_json; 726 } else { 727 pm = config->metric_only ? print_metric_only : print_metric_std; 728 nl = config->metric_only ? new_line_metric : new_line_std; 729 } 730 731 if (run == 0 || ena == 0 || counter->counts->scaled == -1) { 732 if (config->metric_only) { 733 pm(config, os, NULL, "", "", 0); 734 return; 735 } 736 737 ok = false; 738 739 if (counter->supported) { 740 if (!evlist__has_hybrid(counter->evlist)) { 741 config->print_free_counters_hint = 1; 742 if (is_mixed_hw_group(counter)) 743 config->print_mixed_hw_group_error = 1; 744 } 745 } 746 } 747 748 out.print_metric = pm; 749 out.new_line = nl; 750 out.ctx = os; 751 out.force_header = false; 752 753 if (!config->metric_only) { 754 abs_printout(config, os->id, os->aggr_nr, counter, uval, ok); 755 756 print_noise(config, counter, noise, /*before_metric=*/true); 757 print_running(config, run, ena, /*before_metric=*/true); 758 } 759 760 if (ok) { 761 perf_stat__print_shadow_stats(config, counter, uval, aggr_idx, 762 &out, &config->metric_events); 763 } else { 764 pm(config, os, /*color=*/NULL, /*format=*/NULL, /*unit=*/"", /*val=*/0); 765 } 766 767 if (!config->metric_only) { 768 print_noise(config, counter, noise, /*before_metric=*/false); 769 print_running(config, run, ena, /*before_metric=*/false); 770 } 771 } 772 773 static void uniquify_event_name(struct evsel *counter) 774 { 775 char *new_name; 776 char *config; 777 int ret = 0; 778 779 if (counter->uniquified_name || counter->use_config_name || 780 !counter->pmu_name || !strncmp(evsel__name(counter), counter->pmu_name, 781 strlen(counter->pmu_name))) 782 return; 783 784 config = strchr(counter->name, '/'); 785 if (config) { 786 if (asprintf(&new_name, 787 "%s%s", counter->pmu_name, config) > 0) { 788 free(counter->name); 789 counter->name = new_name; 790 } 791 } else { 792 if (evsel__is_hybrid(counter)) { 793 ret = asprintf(&new_name, "%s/%s/", 794 counter->pmu_name, counter->name); 795 } else { 796 ret = asprintf(&new_name, "%s [%s]", 797 counter->name, counter->pmu_name); 798 } 799 800 if (ret) { 801 free(counter->name); 802 counter->name = new_name; 803 } 804 } 805 806 counter->uniquified_name = true; 807 } 808 809 static bool hybrid_uniquify(struct evsel *evsel, struct perf_stat_config *config) 810 { 811 return evsel__is_hybrid(evsel) && !config->hybrid_merge; 812 } 813 814 static void uniquify_counter(struct perf_stat_config *config, struct evsel *counter) 815 { 816 if (config->no_merge || hybrid_uniquify(counter, config)) 817 uniquify_event_name(counter); 818 } 819 820 /** 821 * should_skip_zero_count() - Check if the event should print 0 values. 822 * @config: The perf stat configuration (including aggregation mode). 823 * @counter: The evsel with its associated cpumap. 824 * @id: The aggregation id that is being queried. 825 * 826 * Due to mismatch between the event cpumap or thread-map and the 827 * aggregation mode, sometimes it'd iterate the counter with the map 828 * which does not contain any values. 829 * 830 * For example, uncore events have dedicated CPUs to manage them, 831 * result for other CPUs should be zero and skipped. 832 * 833 * Return: %true if the value should NOT be printed, %false if the value 834 * needs to be printed like "<not counted>" or "<not supported>". 835 */ 836 static bool should_skip_zero_counter(struct perf_stat_config *config, 837 struct evsel *counter, 838 const struct aggr_cpu_id *id) 839 { 840 struct perf_cpu cpu; 841 int idx; 842 843 /* 844 * Skip value 0 when enabling --per-thread globally, 845 * otherwise it will have too many 0 output. 846 */ 847 if (config->aggr_mode == AGGR_THREAD && config->system_wide) 848 return true; 849 /* 850 * Skip value 0 when it's an uncore event and the given aggr id 851 * does not belong to the PMU cpumask. 852 */ 853 if (!counter->pmu || !counter->pmu->is_uncore) 854 return false; 855 856 perf_cpu_map__for_each_cpu(cpu, idx, counter->pmu->cpus) { 857 struct aggr_cpu_id own_id = config->aggr_get_id(config, cpu); 858 859 if (aggr_cpu_id__equal(id, &own_id)) 860 return false; 861 } 862 return true; 863 } 864 865 static void print_counter_aggrdata(struct perf_stat_config *config, 866 struct evsel *counter, int aggr_idx, 867 struct outstate *os) 868 { 869 FILE *output = config->output; 870 u64 ena, run, val; 871 double uval; 872 struct perf_stat_evsel *ps = counter->stats; 873 struct perf_stat_aggr *aggr = &ps->aggr[aggr_idx]; 874 struct aggr_cpu_id id = config->aggr_map->map[aggr_idx]; 875 double avg = aggr->counts.val; 876 bool metric_only = config->metric_only; 877 878 os->id = id; 879 os->aggr_nr = aggr->nr; 880 os->evsel = counter; 881 882 /* Skip already merged uncore/hybrid events */ 883 if (counter->merged_stat) 884 return; 885 886 uniquify_counter(config, counter); 887 888 val = aggr->counts.val; 889 ena = aggr->counts.ena; 890 run = aggr->counts.run; 891 892 if (val == 0 && should_skip_zero_counter(config, counter, &id)) 893 return; 894 895 if (!metric_only) { 896 if (config->json_output) 897 fputc('{', output); 898 if (os->prefix) 899 fprintf(output, "%s", os->prefix); 900 else if (config->summary && config->csv_output && 901 !config->no_csv_summary && !config->interval) 902 fprintf(output, "%s%s", "summary", config->csv_sep); 903 } 904 905 uval = val * counter->scale; 906 907 printout(config, os, uval, run, ena, avg, aggr_idx); 908 909 if (!metric_only) 910 fputc('\n', output); 911 } 912 913 static void print_metric_begin(struct perf_stat_config *config, 914 struct evlist *evlist, 915 struct outstate *os, int aggr_idx) 916 { 917 struct perf_stat_aggr *aggr; 918 struct aggr_cpu_id id; 919 struct evsel *evsel; 920 921 os->first = true; 922 if (!config->metric_only) 923 return; 924 925 if (config->json_output) 926 fputc('{', config->output); 927 if (os->prefix) 928 fprintf(config->output, "%s", os->prefix); 929 930 evsel = evlist__first(evlist); 931 id = config->aggr_map->map[aggr_idx]; 932 aggr = &evsel->stats->aggr[aggr_idx]; 933 aggr_printout(config, evsel, id, aggr->nr); 934 935 print_cgroup(config, os->cgrp ? : evsel->cgrp); 936 } 937 938 static void print_metric_end(struct perf_stat_config *config, struct outstate *os) 939 { 940 FILE *output = config->output; 941 942 if (!config->metric_only) 943 return; 944 945 if (config->json_output) { 946 if (os->first) 947 fputs("\"metric-value\" : \"none\"", output); 948 fputc('}', output); 949 } 950 fputc('\n', output); 951 } 952 953 static void print_aggr(struct perf_stat_config *config, 954 struct evlist *evlist, 955 struct outstate *os) 956 { 957 struct evsel *counter; 958 int aggr_idx; 959 960 if (!config->aggr_map || !config->aggr_get_id) 961 return; 962 963 /* 964 * With metric_only everything is on a single line. 965 * Without each counter has its own line. 966 */ 967 cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) { 968 print_metric_begin(config, evlist, os, aggr_idx); 969 970 evlist__for_each_entry(evlist, counter) { 971 print_counter_aggrdata(config, counter, aggr_idx, os); 972 } 973 print_metric_end(config, os); 974 } 975 } 976 977 static void print_aggr_cgroup(struct perf_stat_config *config, 978 struct evlist *evlist, 979 struct outstate *os) 980 { 981 struct evsel *counter, *evsel; 982 int aggr_idx; 983 984 if (!config->aggr_map || !config->aggr_get_id) 985 return; 986 987 evlist__for_each_entry(evlist, evsel) { 988 if (os->cgrp == evsel->cgrp) 989 continue; 990 991 os->cgrp = evsel->cgrp; 992 993 cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) { 994 print_metric_begin(config, evlist, os, aggr_idx); 995 996 evlist__for_each_entry(evlist, counter) { 997 if (counter->cgrp != os->cgrp) 998 continue; 999 1000 print_counter_aggrdata(config, counter, aggr_idx, os); 1001 } 1002 print_metric_end(config, os); 1003 } 1004 } 1005 } 1006 1007 static void print_counter(struct perf_stat_config *config, 1008 struct evsel *counter, struct outstate *os) 1009 { 1010 int aggr_idx; 1011 1012 /* AGGR_THREAD doesn't have config->aggr_get_id */ 1013 if (!config->aggr_map) 1014 return; 1015 1016 cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) { 1017 print_counter_aggrdata(config, counter, aggr_idx, os); 1018 } 1019 } 1020 1021 static void print_no_aggr_metric(struct perf_stat_config *config, 1022 struct evlist *evlist, 1023 struct outstate *os) 1024 { 1025 int all_idx; 1026 struct perf_cpu cpu; 1027 1028 perf_cpu_map__for_each_cpu(cpu, all_idx, evlist->core.user_requested_cpus) { 1029 struct evsel *counter; 1030 bool first = true; 1031 1032 evlist__for_each_entry(evlist, counter) { 1033 u64 ena, run, val; 1034 double uval; 1035 struct perf_stat_evsel *ps = counter->stats; 1036 int aggr_idx = perf_cpu_map__idx(evsel__cpus(counter), cpu); 1037 1038 if (aggr_idx < 0) 1039 continue; 1040 1041 os->evsel = counter; 1042 os->id = aggr_cpu_id__cpu(cpu, /*data=*/NULL); 1043 if (first) { 1044 print_metric_begin(config, evlist, os, aggr_idx); 1045 first = false; 1046 } 1047 val = ps->aggr[aggr_idx].counts.val; 1048 ena = ps->aggr[aggr_idx].counts.ena; 1049 run = ps->aggr[aggr_idx].counts.run; 1050 1051 uval = val * counter->scale; 1052 printout(config, os, uval, run, ena, 1.0, aggr_idx); 1053 } 1054 if (!first) 1055 print_metric_end(config, os); 1056 } 1057 } 1058 1059 static void print_metric_headers_std(struct perf_stat_config *config, 1060 bool no_indent) 1061 { 1062 fputc(' ', config->output); 1063 1064 if (!no_indent) { 1065 int len = aggr_header_lens[config->aggr_mode]; 1066 1067 if (nr_cgroups || config->cgroup_list) 1068 len += CGROUP_LEN + 1; 1069 1070 fprintf(config->output, "%*s", len, ""); 1071 } 1072 } 1073 1074 static void print_metric_headers_csv(struct perf_stat_config *config, 1075 bool no_indent __maybe_unused) 1076 { 1077 if (config->interval) 1078 fputs("time,", config->output); 1079 if (!config->iostat_run) 1080 fputs(aggr_header_csv[config->aggr_mode], config->output); 1081 } 1082 1083 static void print_metric_headers_json(struct perf_stat_config *config __maybe_unused, 1084 bool no_indent __maybe_unused) 1085 { 1086 } 1087 1088 static void print_metric_headers(struct perf_stat_config *config, 1089 struct evlist *evlist, bool no_indent) 1090 { 1091 struct evsel *counter; 1092 struct outstate os = { 1093 .fh = config->output 1094 }; 1095 struct perf_stat_output_ctx out = { 1096 .ctx = &os, 1097 .print_metric = print_metric_header, 1098 .new_line = new_line_metric, 1099 .force_header = true, 1100 }; 1101 1102 if (config->json_output) 1103 print_metric_headers_json(config, no_indent); 1104 else if (config->csv_output) 1105 print_metric_headers_csv(config, no_indent); 1106 else 1107 print_metric_headers_std(config, no_indent); 1108 1109 if (config->iostat_run) 1110 iostat_print_header_prefix(config); 1111 1112 if (config->cgroup_list) 1113 os.cgrp = evlist__first(evlist)->cgrp; 1114 1115 /* Print metrics headers only */ 1116 evlist__for_each_entry(evlist, counter) { 1117 os.evsel = counter; 1118 1119 perf_stat__print_shadow_stats(config, counter, 0, 1120 0, 1121 &out, 1122 &config->metric_events); 1123 } 1124 1125 if (!config->json_output) 1126 fputc('\n', config->output); 1127 } 1128 1129 static void prepare_interval(struct perf_stat_config *config, 1130 char *prefix, size_t len, struct timespec *ts) 1131 { 1132 if (config->iostat_run) 1133 return; 1134 1135 if (config->json_output) 1136 scnprintf(prefix, len, "\"interval\" : %lu.%09lu, ", 1137 (unsigned long) ts->tv_sec, ts->tv_nsec); 1138 else if (config->csv_output) 1139 scnprintf(prefix, len, "%lu.%09lu%s", 1140 (unsigned long) ts->tv_sec, ts->tv_nsec, config->csv_sep); 1141 else 1142 scnprintf(prefix, len, "%6lu.%09lu ", 1143 (unsigned long) ts->tv_sec, ts->tv_nsec); 1144 } 1145 1146 static void print_header_interval_std(struct perf_stat_config *config, 1147 struct target *_target __maybe_unused, 1148 struct evlist *evlist, 1149 int argc __maybe_unused, 1150 const char **argv __maybe_unused) 1151 { 1152 FILE *output = config->output; 1153 1154 switch (config->aggr_mode) { 1155 case AGGR_NODE: 1156 case AGGR_SOCKET: 1157 case AGGR_DIE: 1158 case AGGR_CACHE: 1159 case AGGR_CORE: 1160 fprintf(output, "#%*s %-*s cpus", 1161 INTERVAL_LEN - 1, "time", 1162 aggr_header_lens[config->aggr_mode], 1163 aggr_header_std[config->aggr_mode]); 1164 break; 1165 case AGGR_NONE: 1166 fprintf(output, "#%*s %-*s", 1167 INTERVAL_LEN - 1, "time", 1168 aggr_header_lens[config->aggr_mode], 1169 aggr_header_std[config->aggr_mode]); 1170 break; 1171 case AGGR_THREAD: 1172 fprintf(output, "#%*s %*s-%-*s", 1173 INTERVAL_LEN - 1, "time", 1174 COMM_LEN, "comm", PID_LEN, "pid"); 1175 break; 1176 case AGGR_GLOBAL: 1177 default: 1178 if (!config->iostat_run) 1179 fprintf(output, "#%*s", 1180 INTERVAL_LEN - 1, "time"); 1181 case AGGR_UNSET: 1182 case AGGR_MAX: 1183 break; 1184 } 1185 1186 if (config->metric_only) 1187 print_metric_headers(config, evlist, true); 1188 else 1189 fprintf(output, " %*s %*s events\n", 1190 COUNTS_LEN, "counts", config->unit_width, "unit"); 1191 } 1192 1193 static void print_header_std(struct perf_stat_config *config, 1194 struct target *_target, struct evlist *evlist, 1195 int argc, const char **argv) 1196 { 1197 FILE *output = config->output; 1198 int i; 1199 1200 fprintf(output, "\n"); 1201 fprintf(output, " Performance counter stats for "); 1202 if (_target->bpf_str) 1203 fprintf(output, "\'BPF program(s) %s", _target->bpf_str); 1204 else if (_target->system_wide) 1205 fprintf(output, "\'system wide"); 1206 else if (_target->cpu_list) 1207 fprintf(output, "\'CPU(s) %s", _target->cpu_list); 1208 else if (!target__has_task(_target)) { 1209 fprintf(output, "\'%s", argv ? argv[0] : "pipe"); 1210 for (i = 1; argv && (i < argc); i++) 1211 fprintf(output, " %s", argv[i]); 1212 } else if (_target->pid) 1213 fprintf(output, "process id \'%s", _target->pid); 1214 else 1215 fprintf(output, "thread id \'%s", _target->tid); 1216 1217 fprintf(output, "\'"); 1218 if (config->run_count > 1) 1219 fprintf(output, " (%d runs)", config->run_count); 1220 fprintf(output, ":\n\n"); 1221 1222 if (config->metric_only) 1223 print_metric_headers(config, evlist, false); 1224 } 1225 1226 static void print_header_csv(struct perf_stat_config *config, 1227 struct target *_target __maybe_unused, 1228 struct evlist *evlist, 1229 int argc __maybe_unused, 1230 const char **argv __maybe_unused) 1231 { 1232 if (config->metric_only) 1233 print_metric_headers(config, evlist, true); 1234 } 1235 static void print_header_json(struct perf_stat_config *config, 1236 struct target *_target __maybe_unused, 1237 struct evlist *evlist, 1238 int argc __maybe_unused, 1239 const char **argv __maybe_unused) 1240 { 1241 if (config->metric_only) 1242 print_metric_headers(config, evlist, true); 1243 } 1244 1245 static void print_header(struct perf_stat_config *config, 1246 struct target *_target, 1247 struct evlist *evlist, 1248 int argc, const char **argv) 1249 { 1250 static int num_print_iv; 1251 1252 fflush(stdout); 1253 1254 if (config->interval_clear) 1255 puts(CONSOLE_CLEAR); 1256 1257 if (num_print_iv == 0 || config->interval_clear) { 1258 if (config->json_output) 1259 print_header_json(config, _target, evlist, argc, argv); 1260 else if (config->csv_output) 1261 print_header_csv(config, _target, evlist, argc, argv); 1262 else if (config->interval) 1263 print_header_interval_std(config, _target, evlist, argc, argv); 1264 else 1265 print_header_std(config, _target, evlist, argc, argv); 1266 } 1267 1268 if (num_print_iv++ == 25) 1269 num_print_iv = 0; 1270 } 1271 1272 static int get_precision(double num) 1273 { 1274 if (num > 1) 1275 return 0; 1276 1277 return lround(ceil(-log10(num))); 1278 } 1279 1280 static void print_table(struct perf_stat_config *config, 1281 FILE *output, int precision, double avg) 1282 { 1283 char tmp[64]; 1284 int idx, indent = 0; 1285 1286 scnprintf(tmp, 64, " %17.*f", precision, avg); 1287 while (tmp[indent] == ' ') 1288 indent++; 1289 1290 fprintf(output, "%*s# Table of individual measurements:\n", indent, ""); 1291 1292 for (idx = 0; idx < config->run_count; idx++) { 1293 double run = (double) config->walltime_run[idx] / NSEC_PER_SEC; 1294 int h, n = 1 + abs((int) (100.0 * (run - avg)/run) / 5); 1295 1296 fprintf(output, " %17.*f (%+.*f) ", 1297 precision, run, precision, run - avg); 1298 1299 for (h = 0; h < n; h++) 1300 fprintf(output, "#"); 1301 1302 fprintf(output, "\n"); 1303 } 1304 1305 fprintf(output, "\n%*s# Final result:\n", indent, ""); 1306 } 1307 1308 static double timeval2double(struct timeval *t) 1309 { 1310 return t->tv_sec + (double) t->tv_usec/USEC_PER_SEC; 1311 } 1312 1313 static void print_footer(struct perf_stat_config *config) 1314 { 1315 double avg = avg_stats(config->walltime_nsecs_stats) / NSEC_PER_SEC; 1316 FILE *output = config->output; 1317 1318 if (config->interval || config->csv_output || config->json_output) 1319 return; 1320 1321 if (!config->null_run) 1322 fprintf(output, "\n"); 1323 1324 if (config->run_count == 1) { 1325 fprintf(output, " %17.9f seconds time elapsed", avg); 1326 1327 if (config->ru_display) { 1328 double ru_utime = timeval2double(&config->ru_data.ru_utime); 1329 double ru_stime = timeval2double(&config->ru_data.ru_stime); 1330 1331 fprintf(output, "\n\n"); 1332 fprintf(output, " %17.9f seconds user\n", ru_utime); 1333 fprintf(output, " %17.9f seconds sys\n", ru_stime); 1334 } 1335 } else { 1336 double sd = stddev_stats(config->walltime_nsecs_stats) / NSEC_PER_SEC; 1337 /* 1338 * Display at most 2 more significant 1339 * digits than the stddev inaccuracy. 1340 */ 1341 int precision = get_precision(sd) + 2; 1342 1343 if (config->walltime_run_table) 1344 print_table(config, output, precision, avg); 1345 1346 fprintf(output, " %17.*f +- %.*f seconds time elapsed", 1347 precision, avg, precision, sd); 1348 1349 print_noise_pct(config, sd, avg, /*before_metric=*/false); 1350 } 1351 fprintf(output, "\n\n"); 1352 1353 if (config->print_free_counters_hint && sysctl__nmi_watchdog_enabled()) 1354 fprintf(output, 1355 "Some events weren't counted. Try disabling the NMI watchdog:\n" 1356 " echo 0 > /proc/sys/kernel/nmi_watchdog\n" 1357 " perf stat ...\n" 1358 " echo 1 > /proc/sys/kernel/nmi_watchdog\n"); 1359 1360 if (config->print_mixed_hw_group_error) 1361 fprintf(output, 1362 "The events in group usually have to be from " 1363 "the same PMU. Try reorganizing the group.\n"); 1364 } 1365 1366 static void print_percore(struct perf_stat_config *config, 1367 struct evsel *counter, struct outstate *os) 1368 { 1369 bool metric_only = config->metric_only; 1370 FILE *output = config->output; 1371 struct cpu_aggr_map *core_map; 1372 int aggr_idx, core_map_len = 0; 1373 1374 if (!config->aggr_map || !config->aggr_get_id) 1375 return; 1376 1377 if (config->percore_show_thread) 1378 return print_counter(config, counter, os); 1379 1380 /* 1381 * core_map will hold the aggr_cpu_id for the cores that have been 1382 * printed so that each core is printed just once. 1383 */ 1384 core_map = cpu_aggr_map__empty_new(config->aggr_map->nr); 1385 if (core_map == NULL) { 1386 fprintf(output, "Cannot allocate per-core aggr map for display\n"); 1387 return; 1388 } 1389 1390 cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) { 1391 struct perf_cpu curr_cpu = config->aggr_map->map[aggr_idx].cpu; 1392 struct aggr_cpu_id core_id = aggr_cpu_id__core(curr_cpu, NULL); 1393 bool found = false; 1394 1395 for (int i = 0; i < core_map_len; i++) { 1396 if (aggr_cpu_id__equal(&core_map->map[i], &core_id)) { 1397 found = true; 1398 break; 1399 } 1400 } 1401 if (found) 1402 continue; 1403 1404 print_counter_aggrdata(config, counter, aggr_idx, os); 1405 1406 core_map->map[core_map_len++] = core_id; 1407 } 1408 free(core_map); 1409 1410 if (metric_only) 1411 fputc('\n', output); 1412 } 1413 1414 static void print_cgroup_counter(struct perf_stat_config *config, struct evlist *evlist, 1415 struct outstate *os) 1416 { 1417 struct evsel *counter; 1418 1419 evlist__for_each_entry(evlist, counter) { 1420 if (os->cgrp != counter->cgrp) { 1421 if (os->cgrp != NULL) 1422 print_metric_end(config, os); 1423 1424 os->cgrp = counter->cgrp; 1425 print_metric_begin(config, evlist, os, /*aggr_idx=*/0); 1426 } 1427 1428 print_counter(config, counter, os); 1429 } 1430 if (os->cgrp) 1431 print_metric_end(config, os); 1432 } 1433 1434 void evlist__print_counters(struct evlist *evlist, struct perf_stat_config *config, 1435 struct target *_target, struct timespec *ts, 1436 int argc, const char **argv) 1437 { 1438 bool metric_only = config->metric_only; 1439 int interval = config->interval; 1440 struct evsel *counter; 1441 char buf[64]; 1442 struct outstate os = { 1443 .fh = config->output, 1444 .first = true, 1445 }; 1446 1447 if (config->iostat_run) 1448 evlist->selected = evlist__first(evlist); 1449 1450 if (interval) { 1451 os.prefix = buf; 1452 prepare_interval(config, buf, sizeof(buf), ts); 1453 } 1454 1455 print_header(config, _target, evlist, argc, argv); 1456 1457 switch (config->aggr_mode) { 1458 case AGGR_CORE: 1459 case AGGR_CACHE: 1460 case AGGR_DIE: 1461 case AGGR_SOCKET: 1462 case AGGR_NODE: 1463 if (config->cgroup_list) 1464 print_aggr_cgroup(config, evlist, &os); 1465 else 1466 print_aggr(config, evlist, &os); 1467 break; 1468 case AGGR_THREAD: 1469 case AGGR_GLOBAL: 1470 if (config->iostat_run) { 1471 iostat_print_counters(evlist, config, ts, buf, 1472 (iostat_print_counter_t)print_counter, &os); 1473 } else if (config->cgroup_list) { 1474 print_cgroup_counter(config, evlist, &os); 1475 } else { 1476 print_metric_begin(config, evlist, &os, /*aggr_idx=*/0); 1477 evlist__for_each_entry(evlist, counter) { 1478 print_counter(config, counter, &os); 1479 } 1480 print_metric_end(config, &os); 1481 } 1482 break; 1483 case AGGR_NONE: 1484 if (metric_only) 1485 print_no_aggr_metric(config, evlist, &os); 1486 else { 1487 evlist__for_each_entry(evlist, counter) { 1488 if (counter->percore) 1489 print_percore(config, counter, &os); 1490 else 1491 print_counter(config, counter, &os); 1492 } 1493 } 1494 break; 1495 case AGGR_MAX: 1496 case AGGR_UNSET: 1497 default: 1498 break; 1499 } 1500 1501 print_footer(config); 1502 1503 fflush(config->output); 1504 } 1505