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