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