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