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