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