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 <perf/cpumap.h> 8 #include "color.h" 9 #include "counts.h" 10 #include "debug.h" 11 #include "evlist.h" 12 #include "evsel.h" 13 #include "stat.h" 14 #include "top.h" 15 #include "thread_map.h" 16 #include "cpumap.h" 17 #include "string2.h" 18 #include <linux/ctype.h> 19 #include "cgroup.h" 20 #include <api/fs/fs.h> 21 #include "util.h" 22 #include "iostat.h" 23 #include "pmu.h" 24 #include "pmus.h" 25 #include "tool_pmu.h" 26 27 #define CNTR_NOT_SUPPORTED "<not supported>" 28 #define CNTR_NOT_COUNTED "<not counted>" 29 30 #define MGROUP_LEN 50 31 #define METRIC_LEN 38 32 #define EVNAME_LEN 32 33 #define COUNTS_LEN 18 34 #define INTERVAL_LEN 16 35 #define CGROUP_LEN 16 36 #define COMM_LEN 16 37 #define PID_LEN 7 38 #define CPUS_LEN 4 39 40 static int aggr_header_lens[] = { 41 [AGGR_CORE] = 18, 42 [AGGR_CACHE] = 22, 43 [AGGR_CLUSTER] = 20, 44 [AGGR_DIE] = 12, 45 [AGGR_SOCKET] = 6, 46 [AGGR_NODE] = 6, 47 [AGGR_NONE] = 6, 48 [AGGR_THREAD] = 16, 49 [AGGR_GLOBAL] = 0, 50 }; 51 52 static const char *aggr_header_csv[] = { 53 [AGGR_CORE] = "core,cpus,", 54 [AGGR_CACHE] = "cache,cpus,", 55 [AGGR_CLUSTER] = "cluster,cpus,", 56 [AGGR_DIE] = "die,cpus,", 57 [AGGR_SOCKET] = "socket,cpus,", 58 [AGGR_NONE] = "cpu,", 59 [AGGR_THREAD] = "comm-pid,", 60 [AGGR_NODE] = "node,", 61 [AGGR_GLOBAL] = "" 62 }; 63 64 static const char *aggr_header_std[] = { 65 [AGGR_CORE] = "core", 66 [AGGR_CACHE] = "cache", 67 [AGGR_CLUSTER] = "cluster", 68 [AGGR_DIE] = "die", 69 [AGGR_SOCKET] = "socket", 70 [AGGR_NONE] = "cpu", 71 [AGGR_THREAD] = "comm-pid", 72 [AGGR_NODE] = "node", 73 [AGGR_GLOBAL] = "" 74 }; 75 76 const char *metric_threshold_classify__color(enum metric_threshold_classify thresh) 77 { 78 const char * const colors[] = { 79 "", /* unknown */ 80 PERF_COLOR_RED, /* bad */ 81 PERF_COLOR_MAGENTA, /* nearly bad */ 82 PERF_COLOR_YELLOW, /* less good */ 83 PERF_COLOR_GREEN, /* good */ 84 }; 85 static_assert(ARRAY_SIZE(colors) - 1 == METRIC_THRESHOLD_GOOD, "missing enum value"); 86 return colors[thresh]; 87 } 88 89 static const char *metric_threshold_classify__str(enum metric_threshold_classify thresh) 90 { 91 const char * const strs[] = { 92 "unknown", 93 "bad", 94 "nearly bad", 95 "less good", 96 "good", 97 }; 98 static_assert(ARRAY_SIZE(strs) - 1 == METRIC_THRESHOLD_GOOD, "missing enum value"); 99 return strs[thresh]; 100 } 101 102 static void print_running_std(struct perf_stat_config *config, u64 run, u64 ena) 103 { 104 if (run != ena) 105 fprintf(config->output, " (%.2f%%)", 100.0 * run / ena); 106 } 107 108 static void print_running_csv(struct perf_stat_config *config, u64 run, u64 ena) 109 { 110 double enabled_percent = 100; 111 112 if (run != ena) 113 enabled_percent = 100 * run / ena; 114 fprintf(config->output, "%s%" PRIu64 "%s%.2f", 115 config->csv_sep, run, config->csv_sep, enabled_percent); 116 } 117 118 static void print_running_json(struct perf_stat_config *config, u64 run, u64 ena) 119 { 120 double enabled_percent = 100; 121 122 if (run != ena) 123 enabled_percent = 100 * run / ena; 124 fprintf(config->output, "\"event-runtime\" : %" PRIu64 ", \"pcnt-running\" : %.2f, ", 125 run, enabled_percent); 126 } 127 128 static void print_running(struct perf_stat_config *config, 129 u64 run, u64 ena, bool before_metric) 130 { 131 if (config->json_output) { 132 if (before_metric) 133 print_running_json(config, run, ena); 134 } else if (config->csv_output) { 135 if (before_metric) 136 print_running_csv(config, run, ena); 137 } else { 138 if (!before_metric) 139 print_running_std(config, run, ena); 140 } 141 } 142 143 static void print_noise_pct_std(struct perf_stat_config *config, 144 double pct) 145 { 146 if (pct) 147 fprintf(config->output, " ( +-%6.2f%% )", pct); 148 } 149 150 static void print_noise_pct_csv(struct perf_stat_config *config, 151 double pct) 152 { 153 fprintf(config->output, "%s%.2f%%", config->csv_sep, pct); 154 } 155 156 static void print_noise_pct_json(struct perf_stat_config *config, 157 double pct) 158 { 159 fprintf(config->output, "\"variance\" : %.2f, ", pct); 160 } 161 162 static void print_noise_pct(struct perf_stat_config *config, 163 double total, double avg, bool before_metric) 164 { 165 double pct = rel_stddev_stats(total, avg); 166 167 if (config->json_output) { 168 if (before_metric) 169 print_noise_pct_json(config, pct); 170 } else if (config->csv_output) { 171 if (before_metric) 172 print_noise_pct_csv(config, pct); 173 } else { 174 if (!before_metric) 175 print_noise_pct_std(config, pct); 176 } 177 } 178 179 static void print_noise(struct perf_stat_config *config, 180 struct evsel *evsel, double avg, bool before_metric) 181 { 182 struct perf_stat_evsel *ps; 183 184 if (config->run_count == 1) 185 return; 186 187 ps = evsel->stats; 188 print_noise_pct(config, stddev_stats(&ps->res_stats), avg, before_metric); 189 } 190 191 static void print_cgroup_std(struct perf_stat_config *config, const char *cgrp_name) 192 { 193 fprintf(config->output, " %-*s", CGROUP_LEN, cgrp_name); 194 } 195 196 static void print_cgroup_csv(struct perf_stat_config *config, const char *cgrp_name) 197 { 198 fprintf(config->output, "%s%s", config->csv_sep, cgrp_name); 199 } 200 201 static void print_cgroup_json(struct perf_stat_config *config, const char *cgrp_name) 202 { 203 fprintf(config->output, "\"cgroup\" : \"%s\", ", cgrp_name); 204 } 205 206 static void print_cgroup(struct perf_stat_config *config, struct cgroup *cgrp) 207 { 208 if (nr_cgroups || config->cgroup_list) { 209 const char *cgrp_name = cgrp ? cgrp->name : ""; 210 211 if (config->json_output) 212 print_cgroup_json(config, cgrp_name); 213 else if (config->csv_output) 214 print_cgroup_csv(config, cgrp_name); 215 else 216 print_cgroup_std(config, cgrp_name); 217 } 218 } 219 220 static void print_aggr_id_std(struct perf_stat_config *config, 221 struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr) 222 { 223 FILE *output = config->output; 224 int idx = config->aggr_mode; 225 char buf[128]; 226 227 switch (config->aggr_mode) { 228 case AGGR_CORE: 229 snprintf(buf, sizeof(buf), "S%d-D%d-C%d", id.socket, id.die, id.core); 230 break; 231 case AGGR_CACHE: 232 snprintf(buf, sizeof(buf), "S%d-D%d-L%d-ID%d", 233 id.socket, id.die, id.cache_lvl, id.cache); 234 break; 235 case AGGR_CLUSTER: 236 snprintf(buf, sizeof(buf), "S%d-D%d-CLS%d", id.socket, id.die, id.cluster); 237 break; 238 case AGGR_DIE: 239 snprintf(buf, sizeof(buf), "S%d-D%d", id.socket, id.die); 240 break; 241 case AGGR_SOCKET: 242 snprintf(buf, sizeof(buf), "S%d", id.socket); 243 break; 244 case AGGR_NODE: 245 snprintf(buf, sizeof(buf), "N%d", id.node); 246 break; 247 case AGGR_NONE: 248 if (evsel->percore && !config->percore_show_thread) { 249 snprintf(buf, sizeof(buf), "S%d-D%d-C%d ", 250 id.socket, id.die, id.core); 251 fprintf(output, "%-*s ", 252 aggr_header_lens[AGGR_CORE], buf); 253 } else if (id.cpu.cpu > -1) { 254 fprintf(output, "CPU%-*d ", 255 aggr_header_lens[AGGR_NONE] - 3, id.cpu.cpu); 256 } 257 return; 258 case AGGR_THREAD: 259 fprintf(output, "%*s-%-*d ", 260 COMM_LEN, perf_thread_map__comm(evsel->core.threads, id.thread_idx), 261 PID_LEN, perf_thread_map__pid(evsel->core.threads, id.thread_idx)); 262 return; 263 case AGGR_GLOBAL: 264 case AGGR_UNSET: 265 case AGGR_MAX: 266 default: 267 return; 268 } 269 270 fprintf(output, "%-*s %*d ", aggr_header_lens[idx], buf, 4, aggr_nr); 271 } 272 273 static void print_aggr_id_csv(struct perf_stat_config *config, 274 struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr) 275 { 276 FILE *output = config->output; 277 const char *sep = config->csv_sep; 278 279 switch (config->aggr_mode) { 280 case AGGR_CORE: 281 fprintf(output, "S%d-D%d-C%d%s%d%s", 282 id.socket, id.die, id.core, sep, aggr_nr, sep); 283 break; 284 case AGGR_CACHE: 285 fprintf(config->output, "S%d-D%d-L%d-ID%d%s%d%s", 286 id.socket, id.die, id.cache_lvl, id.cache, sep, aggr_nr, sep); 287 break; 288 case AGGR_CLUSTER: 289 fprintf(config->output, "S%d-D%d-CLS%d%s%d%s", 290 id.socket, id.die, id.cluster, sep, aggr_nr, sep); 291 break; 292 case AGGR_DIE: 293 fprintf(output, "S%d-D%d%s%d%s", 294 id.socket, id.die, sep, aggr_nr, sep); 295 break; 296 case AGGR_SOCKET: 297 fprintf(output, "S%d%s%d%s", 298 id.socket, sep, aggr_nr, sep); 299 break; 300 case AGGR_NODE: 301 fprintf(output, "N%d%s%d%s", 302 id.node, sep, aggr_nr, sep); 303 break; 304 case AGGR_NONE: 305 if (evsel->percore && !config->percore_show_thread) { 306 fprintf(output, "S%d-D%d-C%d%s", 307 id.socket, id.die, id.core, sep); 308 } else if (id.cpu.cpu > -1) { 309 fprintf(output, "CPU%d%s", 310 id.cpu.cpu, sep); 311 } 312 break; 313 case AGGR_THREAD: 314 fprintf(output, "%s-%d%s", 315 perf_thread_map__comm(evsel->core.threads, id.thread_idx), 316 perf_thread_map__pid(evsel->core.threads, id.thread_idx), 317 sep); 318 break; 319 case AGGR_GLOBAL: 320 case AGGR_UNSET: 321 case AGGR_MAX: 322 default: 323 break; 324 } 325 } 326 327 static void print_aggr_id_json(struct perf_stat_config *config, 328 struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr) 329 { 330 FILE *output = config->output; 331 332 switch (config->aggr_mode) { 333 case AGGR_CORE: 334 fprintf(output, "\"core\" : \"S%d-D%d-C%d\", \"aggregate-number\" : %d, ", 335 id.socket, id.die, id.core, aggr_nr); 336 break; 337 case AGGR_CACHE: 338 fprintf(output, "\"cache\" : \"S%d-D%d-L%d-ID%d\", \"aggregate-number\" : %d, ", 339 id.socket, id.die, id.cache_lvl, id.cache, aggr_nr); 340 break; 341 case AGGR_CLUSTER: 342 fprintf(output, "\"cluster\" : \"S%d-D%d-CLS%d\", \"aggregate-number\" : %d, ", 343 id.socket, id.die, id.cluster, aggr_nr); 344 break; 345 case AGGR_DIE: 346 fprintf(output, "\"die\" : \"S%d-D%d\", \"aggregate-number\" : %d, ", 347 id.socket, id.die, aggr_nr); 348 break; 349 case AGGR_SOCKET: 350 fprintf(output, "\"socket\" : \"S%d\", \"aggregate-number\" : %d, ", 351 id.socket, aggr_nr); 352 break; 353 case AGGR_NODE: 354 fprintf(output, "\"node\" : \"N%d\", \"aggregate-number\" : %d, ", 355 id.node, aggr_nr); 356 break; 357 case AGGR_NONE: 358 if (evsel->percore && !config->percore_show_thread) { 359 fprintf(output, "\"core\" : \"S%d-D%d-C%d\"", 360 id.socket, id.die, id.core); 361 } else if (id.cpu.cpu > -1) { 362 fprintf(output, "\"cpu\" : \"%d\", ", 363 id.cpu.cpu); 364 } 365 break; 366 case AGGR_THREAD: 367 fprintf(output, "\"thread\" : \"%s-%d\", ", 368 perf_thread_map__comm(evsel->core.threads, id.thread_idx), 369 perf_thread_map__pid(evsel->core.threads, id.thread_idx)); 370 break; 371 case AGGR_GLOBAL: 372 case AGGR_UNSET: 373 case AGGR_MAX: 374 default: 375 break; 376 } 377 } 378 379 static void aggr_printout(struct perf_stat_config *config, 380 struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr) 381 { 382 if (config->json_output) 383 print_aggr_id_json(config, evsel, id, aggr_nr); 384 else if (config->csv_output) 385 print_aggr_id_csv(config, evsel, id, aggr_nr); 386 else 387 print_aggr_id_std(config, evsel, id, aggr_nr); 388 } 389 390 struct outstate { 391 FILE *fh; 392 bool newline; 393 bool first; 394 const char *prefix; 395 int nfields; 396 int aggr_nr; 397 struct aggr_cpu_id id; 398 struct evsel *evsel; 399 struct cgroup *cgrp; 400 }; 401 402 static void new_line_std(struct perf_stat_config *config __maybe_unused, 403 void *ctx) 404 { 405 struct outstate *os = ctx; 406 407 os->newline = true; 408 } 409 410 static inline void __new_line_std_csv(struct perf_stat_config *config, 411 struct outstate *os) 412 { 413 fputc('\n', os->fh); 414 if (os->prefix) 415 fputs(os->prefix, os->fh); 416 aggr_printout(config, os->evsel, os->id, os->aggr_nr); 417 } 418 419 static inline void __new_line_std(struct outstate *os) 420 { 421 fprintf(os->fh, " "); 422 } 423 424 static void do_new_line_std(struct perf_stat_config *config, 425 struct outstate *os) 426 { 427 __new_line_std_csv(config, os); 428 if (config->aggr_mode == AGGR_NONE) 429 fprintf(os->fh, " "); 430 __new_line_std(os); 431 } 432 433 static void print_metric_std(struct perf_stat_config *config, 434 void *ctx, enum metric_threshold_classify thresh, 435 const char *fmt, const char *unit, double val) 436 { 437 struct outstate *os = ctx; 438 FILE *out = os->fh; 439 int n; 440 bool newline = os->newline; 441 const char *color = metric_threshold_classify__color(thresh); 442 443 os->newline = false; 444 445 if (unit == NULL || fmt == NULL) { 446 fprintf(out, "%-*s", METRIC_LEN, ""); 447 return; 448 } 449 450 if (newline) 451 do_new_line_std(config, os); 452 453 n = fprintf(out, " # "); 454 if (color) 455 n += color_fprintf(out, color, fmt, val); 456 else 457 n += fprintf(out, fmt, val); 458 fprintf(out, " %-*s", METRIC_LEN - n - 1, unit); 459 } 460 461 static void new_line_csv(struct perf_stat_config *config, void *ctx) 462 { 463 struct outstate *os = ctx; 464 int i; 465 466 __new_line_std_csv(config, os); 467 for (i = 0; i < os->nfields; i++) 468 fputs(config->csv_sep, os->fh); 469 } 470 471 static void print_metric_csv(struct perf_stat_config *config __maybe_unused, 472 void *ctx, 473 enum metric_threshold_classify thresh __maybe_unused, 474 const char *fmt, const char *unit, double val) 475 { 476 struct outstate *os = ctx; 477 FILE *out = os->fh; 478 char buf[64], *vals, *ends; 479 480 if (unit == NULL || fmt == NULL) { 481 fprintf(out, "%s%s", config->csv_sep, config->csv_sep); 482 return; 483 } 484 snprintf(buf, sizeof(buf), fmt, val); 485 ends = vals = skip_spaces(buf); 486 while (isdigit(*ends) || *ends == '.') 487 ends++; 488 *ends = 0; 489 fprintf(out, "%s%s%s%s", config->csv_sep, vals, config->csv_sep, skip_spaces(unit)); 490 } 491 492 static void print_metric_json(struct perf_stat_config *config __maybe_unused, 493 void *ctx, 494 enum metric_threshold_classify thresh, 495 const char *fmt __maybe_unused, 496 const char *unit, double val) 497 { 498 struct outstate *os = ctx; 499 FILE *out = os->fh; 500 501 if (unit) { 502 fprintf(out, "\"metric-value\" : \"%f\", \"metric-unit\" : \"%s\"", val, unit); 503 if (thresh != METRIC_THRESHOLD_UNKNOWN) { 504 fprintf(out, ", \"metric-threshold\" : \"%s\"", 505 metric_threshold_classify__str(thresh)); 506 } 507 } 508 if (!config->metric_only) 509 fprintf(out, "}"); 510 } 511 512 static void new_line_json(struct perf_stat_config *config, void *ctx) 513 { 514 struct outstate *os = ctx; 515 516 fputs("\n{", os->fh); 517 if (os->prefix) 518 fprintf(os->fh, "%s", os->prefix); 519 aggr_printout(config, os->evsel, os->id, os->aggr_nr); 520 } 521 522 static void print_metricgroup_header_json(struct perf_stat_config *config, 523 void *ctx, 524 const char *metricgroup_name) 525 { 526 if (!metricgroup_name) 527 return; 528 529 fprintf(config->output, "\"metricgroup\" : \"%s\"}", metricgroup_name); 530 new_line_json(config, ctx); 531 } 532 533 static void print_metricgroup_header_csv(struct perf_stat_config *config, 534 void *ctx, 535 const char *metricgroup_name) 536 { 537 struct outstate *os = ctx; 538 int i; 539 540 if (!metricgroup_name) { 541 /* Leave space for running and enabling */ 542 for (i = 0; i < os->nfields - 2; i++) 543 fputs(config->csv_sep, os->fh); 544 return; 545 } 546 547 for (i = 0; i < os->nfields; i++) 548 fputs(config->csv_sep, os->fh); 549 fprintf(config->output, "%s", metricgroup_name); 550 new_line_csv(config, ctx); 551 } 552 553 static void print_metricgroup_header_std(struct perf_stat_config *config, 554 void *ctx, 555 const char *metricgroup_name) 556 { 557 struct outstate *os = ctx; 558 int n; 559 560 if (!metricgroup_name) { 561 __new_line_std(os); 562 return; 563 } 564 565 n = fprintf(config->output, " %*s", EVNAME_LEN, metricgroup_name); 566 567 fprintf(config->output, "%*s", MGROUP_LEN - n - 1, ""); 568 } 569 570 /* Filter out some columns that don't work well in metrics only mode */ 571 572 static bool valid_only_metric(const char *unit) 573 { 574 if (!unit) 575 return false; 576 if (strstr(unit, "/sec") || 577 strstr(unit, "CPUs utilized")) 578 return false; 579 return true; 580 } 581 582 static const char *fixunit(char *buf, struct evsel *evsel, 583 const char *unit) 584 { 585 if (!strncmp(unit, "of all", 6)) { 586 snprintf(buf, 1024, "%s %s", evsel__name(evsel), 587 unit); 588 return buf; 589 } 590 return unit; 591 } 592 593 static void print_metric_only(struct perf_stat_config *config, 594 void *ctx, enum metric_threshold_classify thresh, 595 const char *fmt, const char *unit, double val) 596 { 597 struct outstate *os = ctx; 598 FILE *out = os->fh; 599 char buf[1024], str[1024]; 600 unsigned mlen = config->metric_only_len; 601 const char *color = metric_threshold_classify__color(thresh); 602 603 if (!valid_only_metric(unit)) 604 return; 605 unit = fixunit(buf, os->evsel, unit); 606 if (mlen < strlen(unit)) 607 mlen = strlen(unit) + 1; 608 609 if (color) 610 mlen += strlen(color) + sizeof(PERF_COLOR_RESET) - 1; 611 612 color_snprintf(str, sizeof(str), color ?: "", fmt ?: "", val); 613 fprintf(out, "%*s ", mlen, str); 614 os->first = false; 615 } 616 617 static void print_metric_only_csv(struct perf_stat_config *config __maybe_unused, 618 void *ctx, 619 enum metric_threshold_classify thresh __maybe_unused, 620 const char *fmt, 621 const char *unit, double val) 622 { 623 struct outstate *os = ctx; 624 FILE *out = os->fh; 625 char buf[64], *vals, *ends; 626 char tbuf[1024]; 627 628 if (!valid_only_metric(unit)) 629 return; 630 unit = fixunit(tbuf, os->evsel, unit); 631 snprintf(buf, sizeof(buf), fmt ?: "", val); 632 ends = vals = skip_spaces(buf); 633 while (isdigit(*ends) || *ends == '.') 634 ends++; 635 *ends = 0; 636 fprintf(out, "%s%s", vals, config->csv_sep); 637 os->first = false; 638 } 639 640 static void print_metric_only_json(struct perf_stat_config *config __maybe_unused, 641 void *ctx, 642 enum metric_threshold_classify thresh __maybe_unused, 643 const char *fmt, 644 const char *unit, double val) 645 { 646 struct outstate *os = ctx; 647 FILE *out = os->fh; 648 char buf[64], *ends; 649 char tbuf[1024]; 650 const char *vals; 651 652 if (!valid_only_metric(unit)) 653 return; 654 unit = fixunit(tbuf, os->evsel, unit); 655 if (!unit[0]) 656 return; 657 snprintf(buf, sizeof(buf), fmt ?: "", val); 658 vals = ends = skip_spaces(buf); 659 while (isdigit(*ends) || *ends == '.') 660 ends++; 661 *ends = 0; 662 if (!vals[0]) 663 vals = "none"; 664 fprintf(out, "%s\"%s\" : \"%s\"", os->first ? "" : ", ", unit, vals); 665 os->first = false; 666 } 667 668 static void new_line_metric(struct perf_stat_config *config __maybe_unused, 669 void *ctx __maybe_unused) 670 { 671 } 672 673 static void print_metric_header(struct perf_stat_config *config, 674 void *ctx, 675 enum metric_threshold_classify thresh __maybe_unused, 676 const char *fmt __maybe_unused, 677 const char *unit, double val __maybe_unused) 678 { 679 struct outstate *os = ctx; 680 char tbuf[1024]; 681 682 /* In case of iostat, print metric header for first root port only */ 683 if (config->iostat_run && 684 os->evsel->priv != os->evsel->evlist->selected->priv) 685 return; 686 687 if (os->evsel->cgrp != os->cgrp) 688 return; 689 690 if (!valid_only_metric(unit)) 691 return; 692 unit = fixunit(tbuf, os->evsel, unit); 693 694 if (config->json_output) 695 return; 696 else if (config->csv_output) 697 fprintf(os->fh, "%s%s", unit, config->csv_sep); 698 else 699 fprintf(os->fh, "%*s ", config->metric_only_len, unit); 700 } 701 702 static void print_counter_value_std(struct perf_stat_config *config, 703 struct evsel *evsel, double avg, bool ok) 704 { 705 FILE *output = config->output; 706 double sc = evsel->scale; 707 const char *fmt; 708 const char *bad_count = evsel->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED; 709 710 if (config->big_num) 711 fmt = floor(sc) != sc ? "%'*.2f " : "%'*.0f "; 712 else 713 fmt = floor(sc) != sc ? "%*.2f " : "%*.0f "; 714 715 if (ok) 716 fprintf(output, fmt, COUNTS_LEN, avg); 717 else 718 fprintf(output, "%*s ", COUNTS_LEN, bad_count); 719 720 if (evsel->unit) 721 fprintf(output, "%-*s ", config->unit_width, evsel->unit); 722 723 fprintf(output, "%-*s", EVNAME_LEN, evsel__name(evsel)); 724 } 725 726 static void print_counter_value_csv(struct perf_stat_config *config, 727 struct evsel *evsel, double avg, bool ok) 728 { 729 FILE *output = config->output; 730 double sc = evsel->scale; 731 const char *sep = config->csv_sep; 732 const char *fmt = floor(sc) != sc ? "%.2f%s" : "%.0f%s"; 733 const char *bad_count = evsel->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED; 734 735 if (ok) 736 fprintf(output, fmt, avg, sep); 737 else 738 fprintf(output, "%s%s", bad_count, sep); 739 740 if (evsel->unit) 741 fprintf(output, "%s%s", evsel->unit, sep); 742 743 fprintf(output, "%s", evsel__name(evsel)); 744 } 745 746 static void print_counter_value_json(struct perf_stat_config *config, 747 struct evsel *evsel, double avg, bool ok) 748 { 749 FILE *output = config->output; 750 const char *bad_count = evsel->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED; 751 752 if (ok) 753 fprintf(output, "\"counter-value\" : \"%f\", ", avg); 754 else 755 fprintf(output, "\"counter-value\" : \"%s\", ", bad_count); 756 757 if (evsel->unit) 758 fprintf(output, "\"unit\" : \"%s\", ", evsel->unit); 759 760 fprintf(output, "\"event\" : \"%s\", ", evsel__name(evsel)); 761 } 762 763 static void print_counter_value(struct perf_stat_config *config, 764 struct evsel *evsel, double avg, bool ok) 765 { 766 if (config->json_output) 767 print_counter_value_json(config, evsel, avg, ok); 768 else if (config->csv_output) 769 print_counter_value_csv(config, evsel, avg, ok); 770 else 771 print_counter_value_std(config, evsel, avg, ok); 772 } 773 774 static void abs_printout(struct perf_stat_config *config, 775 struct aggr_cpu_id id, int aggr_nr, 776 struct evsel *evsel, double avg, bool ok) 777 { 778 aggr_printout(config, evsel, id, aggr_nr); 779 print_counter_value(config, evsel, avg, ok); 780 print_cgroup(config, evsel->cgrp); 781 } 782 783 static bool is_mixed_hw_group(struct evsel *counter) 784 { 785 struct evlist *evlist = counter->evlist; 786 u32 pmu_type = counter->core.attr.type; 787 struct evsel *pos; 788 789 if (counter->core.nr_members < 2) 790 return false; 791 792 evlist__for_each_entry(evlist, pos) { 793 /* software events can be part of any hardware group */ 794 if (pos->core.attr.type == PERF_TYPE_SOFTWARE) 795 continue; 796 if (pmu_type == PERF_TYPE_SOFTWARE) { 797 pmu_type = pos->core.attr.type; 798 continue; 799 } 800 if (pmu_type != pos->core.attr.type) 801 return true; 802 } 803 804 return false; 805 } 806 807 static bool evlist__has_hybrid(struct evlist *evlist) 808 { 809 struct evsel *evsel; 810 811 if (perf_pmus__num_core_pmus() == 1) 812 return false; 813 814 evlist__for_each_entry(evlist, evsel) { 815 if (evsel->core.is_pmu_core) 816 return true; 817 } 818 819 return false; 820 } 821 822 static void printout(struct perf_stat_config *config, struct outstate *os, 823 double uval, u64 run, u64 ena, double noise, int aggr_idx) 824 { 825 struct perf_stat_output_ctx out; 826 print_metric_t pm; 827 new_line_t nl; 828 print_metricgroup_header_t pmh; 829 bool ok = true; 830 struct evsel *counter = os->evsel; 831 832 if (config->csv_output) { 833 pm = config->metric_only ? print_metric_only_csv : print_metric_csv; 834 nl = config->metric_only ? new_line_metric : new_line_csv; 835 pmh = print_metricgroup_header_csv; 836 os->nfields = 4 + (counter->cgrp ? 1 : 0); 837 } else if (config->json_output) { 838 pm = config->metric_only ? print_metric_only_json : print_metric_json; 839 nl = config->metric_only ? new_line_metric : new_line_json; 840 pmh = print_metricgroup_header_json; 841 } else { 842 pm = config->metric_only ? print_metric_only : print_metric_std; 843 nl = config->metric_only ? new_line_metric : new_line_std; 844 pmh = print_metricgroup_header_std; 845 } 846 847 if (run == 0 || ena == 0 || counter->counts->scaled == -1) { 848 if (config->metric_only) { 849 pm(config, os, METRIC_THRESHOLD_UNKNOWN, "", "", 0); 850 return; 851 } 852 853 ok = false; 854 855 if (counter->supported) { 856 if (!evlist__has_hybrid(counter->evlist)) { 857 config->print_free_counters_hint = 1; 858 if (is_mixed_hw_group(counter)) 859 config->print_mixed_hw_group_error = 1; 860 } 861 } 862 } 863 864 out.print_metric = pm; 865 out.new_line = nl; 866 out.print_metricgroup_header = pmh; 867 out.ctx = os; 868 out.force_header = false; 869 870 if (!config->metric_only && !counter->default_metricgroup) { 871 abs_printout(config, os->id, os->aggr_nr, counter, uval, ok); 872 873 print_noise(config, counter, noise, /*before_metric=*/true); 874 print_running(config, run, ena, /*before_metric=*/true); 875 } 876 877 if (ok) { 878 if (!config->metric_only && counter->default_metricgroup) { 879 void *from = NULL; 880 881 aggr_printout(config, os->evsel, os->id, os->aggr_nr); 882 /* Print out all the metricgroup with the same metric event. */ 883 do { 884 int num = 0; 885 886 /* Print out the new line for the next new metricgroup. */ 887 if (from) { 888 if (config->json_output) 889 new_line_json(config, (void *)os); 890 else 891 __new_line_std_csv(config, os); 892 } 893 894 print_noise(config, counter, noise, /*before_metric=*/true); 895 print_running(config, run, ena, /*before_metric=*/true); 896 from = perf_stat__print_shadow_stats_metricgroup(config, counter, aggr_idx, 897 &num, from, &out, 898 &config->metric_events); 899 } while (from != NULL); 900 } else 901 perf_stat__print_shadow_stats(config, counter, uval, aggr_idx, 902 &out, &config->metric_events); 903 } else { 904 pm(config, os, METRIC_THRESHOLD_UNKNOWN, /*format=*/NULL, /*unit=*/"", /*val=*/0); 905 } 906 907 if (!config->metric_only) { 908 print_noise(config, counter, noise, /*before_metric=*/false); 909 print_running(config, run, ena, /*before_metric=*/false); 910 } 911 } 912 913 static void uniquify_event_name(struct evsel *counter) 914 { 915 const char *name, *pmu_name; 916 char *new_name, *config; 917 int ret; 918 919 /* The evsel was already uniquified. */ 920 if (counter->uniquified_name) 921 return; 922 923 /* Avoid checking to uniquify twice. */ 924 counter->uniquified_name = true; 925 926 /* The evsel has a "name=" config term or is from libpfm. */ 927 if (counter->use_config_name || counter->is_libpfm_event) 928 return; 929 930 /* Legacy no PMU event, don't uniquify. */ 931 if (!counter->pmu || 932 (counter->pmu->type < PERF_TYPE_MAX && counter->pmu->type != PERF_TYPE_RAW)) 933 return; 934 935 /* A sysfs or json event replacing a legacy event, don't uniquify. */ 936 if (counter->pmu->is_core && counter->alternate_hw_config != PERF_COUNT_HW_MAX) 937 return; 938 939 name = evsel__name(counter); 940 pmu_name = counter->pmu->name; 941 /* Already prefixed by the PMU name. */ 942 if (!strncmp(name, pmu_name, strlen(pmu_name))) 943 return; 944 945 config = strchr(name, '/'); 946 if (config) { 947 int len = config - name; 948 949 if (config[1] == '/') { 950 /* case: event// */ 951 ret = asprintf(&new_name, "%s/%.*s/%s", pmu_name, len, name, config + 2); 952 } else { 953 /* case: event/.../ */ 954 ret = asprintf(&new_name, "%s/%.*s,%s", pmu_name, len, name, config + 1); 955 } 956 } else { 957 config = strchr(name, ':'); 958 if (config) { 959 /* case: event:.. */ 960 int len = config - name; 961 962 ret = asprintf(&new_name, "%s/%.*s/%s", pmu_name, len, name, config + 1); 963 } else { 964 /* case: event */ 965 ret = asprintf(&new_name, "%s/%s/", pmu_name, name); 966 } 967 } 968 if (ret > 0) { 969 free(counter->name); 970 counter->name = new_name; 971 } else { 972 /* ENOMEM from asprintf. */ 973 counter->uniquified_name = false; 974 } 975 } 976 977 static bool hybrid_uniquify(struct evsel *evsel, struct perf_stat_config *config) 978 { 979 return evsel__is_hybrid(evsel) && !config->hybrid_merge; 980 } 981 982 static void uniquify_counter(struct perf_stat_config *config, struct evsel *counter) 983 { 984 if (config->aggr_mode == AGGR_NONE || hybrid_uniquify(counter, config)) 985 uniquify_event_name(counter); 986 } 987 988 /** 989 * should_skip_zero_count() - Check if the event should print 0 values. 990 * @config: The perf stat configuration (including aggregation mode). 991 * @counter: The evsel with its associated cpumap. 992 * @id: The aggregation id that is being queried. 993 * 994 * Due to mismatch between the event cpumap or thread-map and the 995 * aggregation mode, sometimes it'd iterate the counter with the map 996 * which does not contain any values. 997 * 998 * For example, uncore events have dedicated CPUs to manage them, 999 * result for other CPUs should be zero and skipped. 1000 * 1001 * Return: %true if the value should NOT be printed, %false if the value 1002 * needs to be printed like "<not counted>" or "<not supported>". 1003 */ 1004 static bool should_skip_zero_counter(struct perf_stat_config *config, 1005 struct evsel *counter, 1006 const struct aggr_cpu_id *id) 1007 { 1008 struct perf_cpu cpu; 1009 int idx; 1010 1011 /* 1012 * Skip unsupported default events when not verbose. (default events 1013 * are all marked 'skippable'). 1014 */ 1015 if (verbose == 0 && counter->skippable && !counter->supported) 1016 return true; 1017 1018 /* 1019 * Skip value 0 when enabling --per-thread globally, 1020 * otherwise it will have too many 0 output. 1021 */ 1022 if (config->aggr_mode == AGGR_THREAD && config->system_wide) 1023 return true; 1024 1025 /* 1026 * Many tool events are only gathered on the first index, skip other 1027 * zero values. 1028 */ 1029 if (evsel__is_tool(counter)) { 1030 struct aggr_cpu_id own_id = 1031 config->aggr_get_id(config, (struct perf_cpu){ .cpu = 0 }); 1032 1033 return !aggr_cpu_id__equal(id, &own_id); 1034 } 1035 1036 /* 1037 * Skip value 0 when it's an uncore event and the given aggr id 1038 * does not belong to the PMU cpumask. 1039 */ 1040 if (!counter->pmu || !counter->pmu->is_uncore) 1041 return false; 1042 1043 perf_cpu_map__for_each_cpu(cpu, idx, counter->pmu->cpus) { 1044 struct aggr_cpu_id own_id = config->aggr_get_id(config, cpu); 1045 1046 if (aggr_cpu_id__equal(id, &own_id)) 1047 return false; 1048 } 1049 return true; 1050 } 1051 1052 static void print_counter_aggrdata(struct perf_stat_config *config, 1053 struct evsel *counter, int aggr_idx, 1054 struct outstate *os) 1055 { 1056 FILE *output = config->output; 1057 u64 ena, run, val; 1058 double uval; 1059 struct perf_stat_evsel *ps = counter->stats; 1060 struct perf_stat_aggr *aggr = &ps->aggr[aggr_idx]; 1061 struct aggr_cpu_id id = config->aggr_map->map[aggr_idx]; 1062 double avg = aggr->counts.val; 1063 bool metric_only = config->metric_only; 1064 1065 os->id = id; 1066 os->aggr_nr = aggr->nr; 1067 os->evsel = counter; 1068 1069 /* Skip already merged uncore/hybrid events */ 1070 if (counter->merged_stat) 1071 return; 1072 1073 uniquify_counter(config, counter); 1074 1075 val = aggr->counts.val; 1076 ena = aggr->counts.ena; 1077 run = aggr->counts.run; 1078 1079 if (perf_stat__skip_metric_event(counter, &config->metric_events, ena, run)) 1080 return; 1081 1082 if (val == 0 && should_skip_zero_counter(config, counter, &id)) 1083 return; 1084 1085 if (!metric_only) { 1086 if (config->json_output) 1087 fputc('{', output); 1088 if (os->prefix) 1089 fprintf(output, "%s", os->prefix); 1090 else if (config->summary && config->csv_output && 1091 !config->no_csv_summary && !config->interval) 1092 fprintf(output, "%s%s", "summary", config->csv_sep); 1093 } 1094 1095 uval = val * counter->scale; 1096 1097 printout(config, os, uval, run, ena, avg, aggr_idx); 1098 1099 if (!metric_only) 1100 fputc('\n', output); 1101 } 1102 1103 static void print_metric_begin(struct perf_stat_config *config, 1104 struct evlist *evlist, 1105 struct outstate *os, int aggr_idx) 1106 { 1107 struct perf_stat_aggr *aggr; 1108 struct aggr_cpu_id id; 1109 struct evsel *evsel; 1110 1111 os->first = true; 1112 if (!config->metric_only) 1113 return; 1114 1115 if (config->json_output) 1116 fputc('{', config->output); 1117 if (os->prefix) 1118 fprintf(config->output, "%s", os->prefix); 1119 1120 evsel = evlist__first(evlist); 1121 id = config->aggr_map->map[aggr_idx]; 1122 aggr = &evsel->stats->aggr[aggr_idx]; 1123 aggr_printout(config, evsel, id, aggr->nr); 1124 1125 print_cgroup(config, os->cgrp ? : evsel->cgrp); 1126 } 1127 1128 static void print_metric_end(struct perf_stat_config *config, struct outstate *os) 1129 { 1130 FILE *output = config->output; 1131 1132 if (!config->metric_only) 1133 return; 1134 1135 if (config->json_output) { 1136 if (os->first) 1137 fputs("\"metric-value\" : \"none\"", output); 1138 fputc('}', output); 1139 } 1140 fputc('\n', output); 1141 } 1142 1143 static void print_aggr(struct perf_stat_config *config, 1144 struct evlist *evlist, 1145 struct outstate *os) 1146 { 1147 struct evsel *counter; 1148 int aggr_idx; 1149 1150 if (!config->aggr_map || !config->aggr_get_id) 1151 return; 1152 1153 /* 1154 * With metric_only everything is on a single line. 1155 * Without each counter has its own line. 1156 */ 1157 cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) { 1158 print_metric_begin(config, evlist, os, aggr_idx); 1159 1160 evlist__for_each_entry(evlist, counter) { 1161 print_counter_aggrdata(config, counter, aggr_idx, os); 1162 } 1163 print_metric_end(config, os); 1164 } 1165 } 1166 1167 static void print_aggr_cgroup(struct perf_stat_config *config, 1168 struct evlist *evlist, 1169 struct outstate *os) 1170 { 1171 struct evsel *counter, *evsel; 1172 int aggr_idx; 1173 1174 if (!config->aggr_map || !config->aggr_get_id) 1175 return; 1176 1177 evlist__for_each_entry(evlist, evsel) { 1178 if (os->cgrp == evsel->cgrp) 1179 continue; 1180 1181 os->cgrp = evsel->cgrp; 1182 1183 cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) { 1184 print_metric_begin(config, evlist, os, aggr_idx); 1185 1186 evlist__for_each_entry(evlist, counter) { 1187 if (counter->cgrp != os->cgrp) 1188 continue; 1189 1190 print_counter_aggrdata(config, counter, aggr_idx, os); 1191 } 1192 print_metric_end(config, os); 1193 } 1194 } 1195 } 1196 1197 static void print_counter(struct perf_stat_config *config, 1198 struct evsel *counter, struct outstate *os) 1199 { 1200 int aggr_idx; 1201 1202 /* AGGR_THREAD doesn't have config->aggr_get_id */ 1203 if (!config->aggr_map) 1204 return; 1205 1206 cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) { 1207 print_counter_aggrdata(config, counter, aggr_idx, os); 1208 } 1209 } 1210 1211 static void print_no_aggr_metric(struct perf_stat_config *config, 1212 struct evlist *evlist, 1213 struct outstate *os) 1214 { 1215 int all_idx; 1216 struct perf_cpu cpu; 1217 1218 perf_cpu_map__for_each_cpu(cpu, all_idx, evlist->core.user_requested_cpus) { 1219 struct evsel *counter; 1220 bool first = true; 1221 1222 evlist__for_each_entry(evlist, counter) { 1223 u64 ena, run, val; 1224 double uval; 1225 struct perf_stat_evsel *ps = counter->stats; 1226 int aggr_idx = 0; 1227 1228 if (!perf_cpu_map__has(evsel__cpus(counter), cpu)) 1229 continue; 1230 1231 cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) { 1232 if (config->aggr_map->map[aggr_idx].cpu.cpu == cpu.cpu) 1233 break; 1234 } 1235 1236 os->evsel = counter; 1237 os->id = aggr_cpu_id__cpu(cpu, /*data=*/NULL); 1238 if (first) { 1239 print_metric_begin(config, evlist, os, aggr_idx); 1240 first = false; 1241 } 1242 val = ps->aggr[aggr_idx].counts.val; 1243 ena = ps->aggr[aggr_idx].counts.ena; 1244 run = ps->aggr[aggr_idx].counts.run; 1245 1246 uval = val * counter->scale; 1247 printout(config, os, uval, run, ena, 1.0, aggr_idx); 1248 } 1249 if (!first) 1250 print_metric_end(config, os); 1251 } 1252 } 1253 1254 static void print_metric_headers_std(struct perf_stat_config *config, 1255 bool no_indent) 1256 { 1257 fputc(' ', config->output); 1258 1259 if (!no_indent) { 1260 int len = aggr_header_lens[config->aggr_mode]; 1261 1262 if (nr_cgroups || config->cgroup_list) 1263 len += CGROUP_LEN + 1; 1264 1265 fprintf(config->output, "%*s", len, ""); 1266 } 1267 } 1268 1269 static void print_metric_headers_csv(struct perf_stat_config *config, 1270 bool no_indent __maybe_unused) 1271 { 1272 const char *p; 1273 1274 if (config->interval) 1275 fprintf(config->output, "time%s", config->csv_sep); 1276 if (config->iostat_run) 1277 return; 1278 1279 p = aggr_header_csv[config->aggr_mode]; 1280 while (*p) { 1281 if (*p == ',') 1282 fputs(config->csv_sep, config->output); 1283 else 1284 fputc(*p, config->output); 1285 p++; 1286 } 1287 } 1288 1289 static void print_metric_headers_json(struct perf_stat_config *config __maybe_unused, 1290 bool no_indent __maybe_unused) 1291 { 1292 } 1293 1294 static void print_metric_headers(struct perf_stat_config *config, 1295 struct evlist *evlist, bool no_indent) 1296 { 1297 struct evsel *counter; 1298 struct outstate os = { 1299 .fh = config->output 1300 }; 1301 struct perf_stat_output_ctx out = { 1302 .ctx = &os, 1303 .print_metric = print_metric_header, 1304 .new_line = new_line_metric, 1305 .force_header = true, 1306 }; 1307 1308 if (config->json_output) 1309 print_metric_headers_json(config, no_indent); 1310 else if (config->csv_output) 1311 print_metric_headers_csv(config, no_indent); 1312 else 1313 print_metric_headers_std(config, no_indent); 1314 1315 if (config->iostat_run) 1316 iostat_print_header_prefix(config); 1317 1318 if (config->cgroup_list) 1319 os.cgrp = evlist__first(evlist)->cgrp; 1320 1321 /* Print metrics headers only */ 1322 evlist__for_each_entry(evlist, counter) { 1323 if (!config->iostat_run && 1324 config->aggr_mode != AGGR_NONE && counter->metric_leader != counter) 1325 continue; 1326 1327 os.evsel = counter; 1328 1329 perf_stat__print_shadow_stats(config, counter, 0, 1330 0, 1331 &out, 1332 &config->metric_events); 1333 } 1334 1335 if (!config->json_output) 1336 fputc('\n', config->output); 1337 } 1338 1339 static void prepare_interval(struct perf_stat_config *config, 1340 char *prefix, size_t len, struct timespec *ts) 1341 { 1342 if (config->iostat_run) 1343 return; 1344 1345 if (config->json_output) 1346 scnprintf(prefix, len, "\"interval\" : %lu.%09lu, ", 1347 (unsigned long) ts->tv_sec, ts->tv_nsec); 1348 else if (config->csv_output) 1349 scnprintf(prefix, len, "%lu.%09lu%s", 1350 (unsigned long) ts->tv_sec, ts->tv_nsec, config->csv_sep); 1351 else 1352 scnprintf(prefix, len, "%6lu.%09lu ", 1353 (unsigned long) ts->tv_sec, ts->tv_nsec); 1354 } 1355 1356 static void print_header_interval_std(struct perf_stat_config *config, 1357 struct target *_target __maybe_unused, 1358 struct evlist *evlist, 1359 int argc __maybe_unused, 1360 const char **argv __maybe_unused) 1361 { 1362 FILE *output = config->output; 1363 1364 switch (config->aggr_mode) { 1365 case AGGR_NODE: 1366 case AGGR_SOCKET: 1367 case AGGR_DIE: 1368 case AGGR_CLUSTER: 1369 case AGGR_CACHE: 1370 case AGGR_CORE: 1371 fprintf(output, "#%*s %-*s cpus", 1372 INTERVAL_LEN - 1, "time", 1373 aggr_header_lens[config->aggr_mode], 1374 aggr_header_std[config->aggr_mode]); 1375 break; 1376 case AGGR_NONE: 1377 fprintf(output, "#%*s %-*s", 1378 INTERVAL_LEN - 1, "time", 1379 aggr_header_lens[config->aggr_mode], 1380 aggr_header_std[config->aggr_mode]); 1381 break; 1382 case AGGR_THREAD: 1383 fprintf(output, "#%*s %*s-%-*s", 1384 INTERVAL_LEN - 1, "time", 1385 COMM_LEN, "comm", PID_LEN, "pid"); 1386 break; 1387 case AGGR_GLOBAL: 1388 default: 1389 if (!config->iostat_run) 1390 fprintf(output, "#%*s", 1391 INTERVAL_LEN - 1, "time"); 1392 case AGGR_UNSET: 1393 case AGGR_MAX: 1394 break; 1395 } 1396 1397 if (config->metric_only) 1398 print_metric_headers(config, evlist, true); 1399 else 1400 fprintf(output, " %*s %*s events\n", 1401 COUNTS_LEN, "counts", config->unit_width, "unit"); 1402 } 1403 1404 static void print_header_std(struct perf_stat_config *config, 1405 struct target *_target, struct evlist *evlist, 1406 int argc, const char **argv) 1407 { 1408 FILE *output = config->output; 1409 int i; 1410 1411 fprintf(output, "\n"); 1412 fprintf(output, " Performance counter stats for "); 1413 if (_target->bpf_str) 1414 fprintf(output, "\'BPF program(s) %s", _target->bpf_str); 1415 else if (_target->system_wide) 1416 fprintf(output, "\'system wide"); 1417 else if (_target->cpu_list) 1418 fprintf(output, "\'CPU(s) %s", _target->cpu_list); 1419 else if (!target__has_task(_target)) { 1420 fprintf(output, "\'%s", argv ? argv[0] : "pipe"); 1421 for (i = 1; argv && (i < argc); i++) 1422 fprintf(output, " %s", argv[i]); 1423 } else if (_target->pid) 1424 fprintf(output, "process id \'%s", _target->pid); 1425 else 1426 fprintf(output, "thread id \'%s", _target->tid); 1427 1428 fprintf(output, "\'"); 1429 if (config->run_count > 1) 1430 fprintf(output, " (%d runs)", config->run_count); 1431 fprintf(output, ":\n\n"); 1432 1433 if (config->metric_only) 1434 print_metric_headers(config, evlist, false); 1435 } 1436 1437 static void print_header_csv(struct perf_stat_config *config, 1438 struct target *_target __maybe_unused, 1439 struct evlist *evlist, 1440 int argc __maybe_unused, 1441 const char **argv __maybe_unused) 1442 { 1443 if (config->metric_only) 1444 print_metric_headers(config, evlist, true); 1445 } 1446 static void print_header_json(struct perf_stat_config *config, 1447 struct target *_target __maybe_unused, 1448 struct evlist *evlist, 1449 int argc __maybe_unused, 1450 const char **argv __maybe_unused) 1451 { 1452 if (config->metric_only) 1453 print_metric_headers(config, evlist, true); 1454 } 1455 1456 static void print_header(struct perf_stat_config *config, 1457 struct target *_target, 1458 struct evlist *evlist, 1459 int argc, const char **argv) 1460 { 1461 static int num_print_iv; 1462 1463 fflush(stdout); 1464 1465 if (config->interval_clear) 1466 puts(CONSOLE_CLEAR); 1467 1468 if (num_print_iv == 0 || config->interval_clear) { 1469 if (config->json_output) 1470 print_header_json(config, _target, evlist, argc, argv); 1471 else if (config->csv_output) 1472 print_header_csv(config, _target, evlist, argc, argv); 1473 else if (config->interval) 1474 print_header_interval_std(config, _target, evlist, argc, argv); 1475 else 1476 print_header_std(config, _target, evlist, argc, argv); 1477 } 1478 1479 if (num_print_iv++ == 25) 1480 num_print_iv = 0; 1481 } 1482 1483 static int get_precision(double num) 1484 { 1485 if (num > 1) 1486 return 0; 1487 1488 return lround(ceil(-log10(num))); 1489 } 1490 1491 static void print_table(struct perf_stat_config *config, 1492 FILE *output, int precision, double avg) 1493 { 1494 char tmp[64]; 1495 int idx, indent = 0; 1496 1497 scnprintf(tmp, 64, " %17.*f", precision, avg); 1498 while (tmp[indent] == ' ') 1499 indent++; 1500 1501 fprintf(output, "%*s# Table of individual measurements:\n", indent, ""); 1502 1503 for (idx = 0; idx < config->run_count; idx++) { 1504 double run = (double) config->walltime_run[idx] / NSEC_PER_SEC; 1505 int h, n = 1 + abs((int) (100.0 * (run - avg)/run) / 5); 1506 1507 fprintf(output, " %17.*f (%+.*f) ", 1508 precision, run, precision, run - avg); 1509 1510 for (h = 0; h < n; h++) 1511 fprintf(output, "#"); 1512 1513 fprintf(output, "\n"); 1514 } 1515 1516 fprintf(output, "\n%*s# Final result:\n", indent, ""); 1517 } 1518 1519 static double timeval2double(struct timeval *t) 1520 { 1521 return t->tv_sec + (double) t->tv_usec/USEC_PER_SEC; 1522 } 1523 1524 static void print_footer(struct perf_stat_config *config) 1525 { 1526 double avg = avg_stats(config->walltime_nsecs_stats) / NSEC_PER_SEC; 1527 FILE *output = config->output; 1528 1529 if (config->interval || config->csv_output || config->json_output) 1530 return; 1531 1532 if (!config->null_run) 1533 fprintf(output, "\n"); 1534 1535 if (config->run_count == 1) { 1536 fprintf(output, " %17.9f seconds time elapsed", avg); 1537 1538 if (config->ru_display) { 1539 double ru_utime = timeval2double(&config->ru_data.ru_utime); 1540 double ru_stime = timeval2double(&config->ru_data.ru_stime); 1541 1542 fprintf(output, "\n\n"); 1543 fprintf(output, " %17.9f seconds user\n", ru_utime); 1544 fprintf(output, " %17.9f seconds sys\n", ru_stime); 1545 } 1546 } else { 1547 double sd = stddev_stats(config->walltime_nsecs_stats) / NSEC_PER_SEC; 1548 /* 1549 * Display at most 2 more significant 1550 * digits than the stddev inaccuracy. 1551 */ 1552 int precision = get_precision(sd) + 2; 1553 1554 if (config->walltime_run_table) 1555 print_table(config, output, precision, avg); 1556 1557 fprintf(output, " %17.*f +- %.*f seconds time elapsed", 1558 precision, avg, precision, sd); 1559 1560 print_noise_pct(config, sd, avg, /*before_metric=*/false); 1561 } 1562 fprintf(output, "\n\n"); 1563 1564 if (config->print_free_counters_hint && sysctl__nmi_watchdog_enabled()) 1565 fprintf(output, 1566 "Some events weren't counted. Try disabling the NMI watchdog:\n" 1567 " echo 0 > /proc/sys/kernel/nmi_watchdog\n" 1568 " perf stat ...\n" 1569 " echo 1 > /proc/sys/kernel/nmi_watchdog\n"); 1570 1571 if (config->print_mixed_hw_group_error) 1572 fprintf(output, 1573 "The events in group usually have to be from " 1574 "the same PMU. Try reorganizing the group.\n"); 1575 } 1576 1577 static void print_percore(struct perf_stat_config *config, 1578 struct evsel *counter, struct outstate *os) 1579 { 1580 bool metric_only = config->metric_only; 1581 FILE *output = config->output; 1582 struct cpu_aggr_map *core_map; 1583 int aggr_idx, core_map_len = 0; 1584 1585 if (!config->aggr_map || !config->aggr_get_id) 1586 return; 1587 1588 if (config->percore_show_thread) 1589 return print_counter(config, counter, os); 1590 1591 /* 1592 * core_map will hold the aggr_cpu_id for the cores that have been 1593 * printed so that each core is printed just once. 1594 */ 1595 core_map = cpu_aggr_map__empty_new(config->aggr_map->nr); 1596 if (core_map == NULL) { 1597 fprintf(output, "Cannot allocate per-core aggr map for display\n"); 1598 return; 1599 } 1600 1601 cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) { 1602 struct perf_cpu curr_cpu = config->aggr_map->map[aggr_idx].cpu; 1603 struct aggr_cpu_id core_id = aggr_cpu_id__core(curr_cpu, NULL); 1604 bool found = false; 1605 1606 for (int i = 0; i < core_map_len; i++) { 1607 if (aggr_cpu_id__equal(&core_map->map[i], &core_id)) { 1608 found = true; 1609 break; 1610 } 1611 } 1612 if (found) 1613 continue; 1614 1615 print_counter_aggrdata(config, counter, aggr_idx, os); 1616 1617 core_map->map[core_map_len++] = core_id; 1618 } 1619 free(core_map); 1620 1621 if (metric_only) 1622 fputc('\n', output); 1623 } 1624 1625 static void print_cgroup_counter(struct perf_stat_config *config, struct evlist *evlist, 1626 struct outstate *os) 1627 { 1628 struct evsel *counter; 1629 1630 evlist__for_each_entry(evlist, counter) { 1631 if (os->cgrp != counter->cgrp) { 1632 if (os->cgrp != NULL) 1633 print_metric_end(config, os); 1634 1635 os->cgrp = counter->cgrp; 1636 print_metric_begin(config, evlist, os, /*aggr_idx=*/0); 1637 } 1638 1639 print_counter(config, counter, os); 1640 } 1641 if (os->cgrp) 1642 print_metric_end(config, os); 1643 } 1644 1645 static void disable_uniquify(struct evlist *evlist) 1646 { 1647 struct evsel *counter; 1648 struct perf_pmu *last_pmu = NULL; 1649 bool first = true; 1650 1651 evlist__for_each_entry(evlist, counter) { 1652 /* If PMUs vary then uniquify can be useful. */ 1653 if (!first && counter->pmu != last_pmu) 1654 return; 1655 first = false; 1656 if (counter->pmu) { 1657 /* Allow uniquify for uncore PMUs. */ 1658 if (!counter->pmu->is_core) 1659 return; 1660 /* Keep hybrid event names uniquified for clarity. */ 1661 if (perf_pmus__num_core_pmus() > 1) 1662 return; 1663 } 1664 } 1665 evlist__for_each_entry_continue(evlist, counter) { 1666 counter->uniquified_name = true; 1667 } 1668 } 1669 1670 void evlist__print_counters(struct evlist *evlist, struct perf_stat_config *config, 1671 struct target *_target, struct timespec *ts, 1672 int argc, const char **argv) 1673 { 1674 bool metric_only = config->metric_only; 1675 int interval = config->interval; 1676 struct evsel *counter; 1677 char buf[64]; 1678 struct outstate os = { 1679 .fh = config->output, 1680 .first = true, 1681 }; 1682 1683 disable_uniquify(evlist); 1684 1685 if (config->iostat_run) 1686 evlist->selected = evlist__first(evlist); 1687 1688 if (interval) { 1689 os.prefix = buf; 1690 prepare_interval(config, buf, sizeof(buf), ts); 1691 } 1692 1693 print_header(config, _target, evlist, argc, argv); 1694 1695 switch (config->aggr_mode) { 1696 case AGGR_CORE: 1697 case AGGR_CACHE: 1698 case AGGR_CLUSTER: 1699 case AGGR_DIE: 1700 case AGGR_SOCKET: 1701 case AGGR_NODE: 1702 if (config->cgroup_list) 1703 print_aggr_cgroup(config, evlist, &os); 1704 else 1705 print_aggr(config, evlist, &os); 1706 break; 1707 case AGGR_THREAD: 1708 case AGGR_GLOBAL: 1709 if (config->iostat_run) { 1710 iostat_print_counters(evlist, config, ts, buf, 1711 (iostat_print_counter_t)print_counter, &os); 1712 } else if (config->cgroup_list) { 1713 print_cgroup_counter(config, evlist, &os); 1714 } else { 1715 print_metric_begin(config, evlist, &os, /*aggr_idx=*/0); 1716 evlist__for_each_entry(evlist, counter) { 1717 print_counter(config, counter, &os); 1718 } 1719 print_metric_end(config, &os); 1720 } 1721 break; 1722 case AGGR_NONE: 1723 if (metric_only) 1724 print_no_aggr_metric(config, evlist, &os); 1725 else { 1726 evlist__for_each_entry(evlist, counter) { 1727 if (counter->percore) 1728 print_percore(config, counter, &os); 1729 else 1730 print_counter(config, counter, &os); 1731 } 1732 } 1733 break; 1734 case AGGR_MAX: 1735 case AGGR_UNSET: 1736 default: 1737 break; 1738 } 1739 1740 print_footer(config); 1741 1742 fflush(config->output); 1743 } 1744