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