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