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