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