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 = 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 if (config->interval) 1187 fputs("time,", config->output); 1188 if (!config->iostat_run) 1189 fputs(aggr_header_csv[config->aggr_mode], config->output); 1190 } 1191 1192 static void print_metric_headers_json(struct perf_stat_config *config __maybe_unused, 1193 bool no_indent __maybe_unused) 1194 { 1195 } 1196 1197 static void print_metric_headers(struct perf_stat_config *config, 1198 struct evlist *evlist, bool no_indent) 1199 { 1200 struct evsel *counter; 1201 struct outstate os = { 1202 .fh = config->output 1203 }; 1204 struct perf_stat_output_ctx out = { 1205 .ctx = &os, 1206 .print_metric = print_metric_header, 1207 .new_line = new_line_metric, 1208 .force_header = true, 1209 }; 1210 1211 if (config->json_output) 1212 print_metric_headers_json(config, no_indent); 1213 else if (config->csv_output) 1214 print_metric_headers_csv(config, no_indent); 1215 else 1216 print_metric_headers_std(config, no_indent); 1217 1218 if (config->iostat_run) 1219 iostat_print_header_prefix(config); 1220 1221 if (config->cgroup_list) 1222 os.cgrp = evlist__first(evlist)->cgrp; 1223 1224 /* Print metrics headers only */ 1225 evlist__for_each_entry(evlist, counter) { 1226 os.evsel = counter; 1227 1228 perf_stat__print_shadow_stats(config, counter, 0, 1229 0, 1230 &out, 1231 &config->metric_events); 1232 } 1233 1234 if (!config->json_output) 1235 fputc('\n', config->output); 1236 } 1237 1238 static void prepare_interval(struct perf_stat_config *config, 1239 char *prefix, size_t len, struct timespec *ts) 1240 { 1241 if (config->iostat_run) 1242 return; 1243 1244 if (config->json_output) 1245 scnprintf(prefix, len, "\"interval\" : %lu.%09lu, ", 1246 (unsigned long) ts->tv_sec, ts->tv_nsec); 1247 else if (config->csv_output) 1248 scnprintf(prefix, len, "%lu.%09lu%s", 1249 (unsigned long) ts->tv_sec, ts->tv_nsec, config->csv_sep); 1250 else 1251 scnprintf(prefix, len, "%6lu.%09lu ", 1252 (unsigned long) ts->tv_sec, ts->tv_nsec); 1253 } 1254 1255 static void print_header_interval_std(struct perf_stat_config *config, 1256 struct target *_target __maybe_unused, 1257 struct evlist *evlist, 1258 int argc __maybe_unused, 1259 const char **argv __maybe_unused) 1260 { 1261 FILE *output = config->output; 1262 1263 switch (config->aggr_mode) { 1264 case AGGR_NODE: 1265 case AGGR_SOCKET: 1266 case AGGR_DIE: 1267 case AGGR_CLUSTER: 1268 case AGGR_CACHE: 1269 case AGGR_CORE: 1270 fprintf(output, "#%*s %-*s cpus", 1271 INTERVAL_LEN - 1, "time", 1272 aggr_header_lens[config->aggr_mode], 1273 aggr_header_std[config->aggr_mode]); 1274 break; 1275 case AGGR_NONE: 1276 fprintf(output, "#%*s %-*s", 1277 INTERVAL_LEN - 1, "time", 1278 aggr_header_lens[config->aggr_mode], 1279 aggr_header_std[config->aggr_mode]); 1280 break; 1281 case AGGR_THREAD: 1282 fprintf(output, "#%*s %*s-%-*s", 1283 INTERVAL_LEN - 1, "time", 1284 COMM_LEN, "comm", PID_LEN, "pid"); 1285 break; 1286 case AGGR_GLOBAL: 1287 default: 1288 if (!config->iostat_run) 1289 fprintf(output, "#%*s", 1290 INTERVAL_LEN - 1, "time"); 1291 case AGGR_UNSET: 1292 case AGGR_MAX: 1293 break; 1294 } 1295 1296 if (config->metric_only) 1297 print_metric_headers(config, evlist, true); 1298 else 1299 fprintf(output, " %*s %*s events\n", 1300 COUNTS_LEN, "counts", config->unit_width, "unit"); 1301 } 1302 1303 static void print_header_std(struct perf_stat_config *config, 1304 struct target *_target, struct evlist *evlist, 1305 int argc, const char **argv) 1306 { 1307 FILE *output = config->output; 1308 int i; 1309 1310 fprintf(output, "\n"); 1311 fprintf(output, " Performance counter stats for "); 1312 if (_target->bpf_str) 1313 fprintf(output, "\'BPF program(s) %s", _target->bpf_str); 1314 else if (_target->system_wide) 1315 fprintf(output, "\'system wide"); 1316 else if (_target->cpu_list) 1317 fprintf(output, "\'CPU(s) %s", _target->cpu_list); 1318 else if (!target__has_task(_target)) { 1319 fprintf(output, "\'%s", argv ? argv[0] : "pipe"); 1320 for (i = 1; argv && (i < argc); i++) 1321 fprintf(output, " %s", argv[i]); 1322 } else if (_target->pid) 1323 fprintf(output, "process id \'%s", _target->pid); 1324 else 1325 fprintf(output, "thread id \'%s", _target->tid); 1326 1327 fprintf(output, "\'"); 1328 if (config->run_count > 1) 1329 fprintf(output, " (%d runs)", config->run_count); 1330 fprintf(output, ":\n\n"); 1331 1332 if (config->metric_only) 1333 print_metric_headers(config, evlist, false); 1334 } 1335 1336 static void print_header_csv(struct perf_stat_config *config, 1337 struct target *_target __maybe_unused, 1338 struct evlist *evlist, 1339 int argc __maybe_unused, 1340 const char **argv __maybe_unused) 1341 { 1342 if (config->metric_only) 1343 print_metric_headers(config, evlist, true); 1344 } 1345 static void print_header_json(struct perf_stat_config *config, 1346 struct target *_target __maybe_unused, 1347 struct evlist *evlist, 1348 int argc __maybe_unused, 1349 const char **argv __maybe_unused) 1350 { 1351 if (config->metric_only) 1352 print_metric_headers(config, evlist, true); 1353 } 1354 1355 static void print_header(struct perf_stat_config *config, 1356 struct target *_target, 1357 struct evlist *evlist, 1358 int argc, const char **argv) 1359 { 1360 static int num_print_iv; 1361 1362 fflush(stdout); 1363 1364 if (config->interval_clear) 1365 puts(CONSOLE_CLEAR); 1366 1367 if (num_print_iv == 0 || config->interval_clear) { 1368 if (config->json_output) 1369 print_header_json(config, _target, evlist, argc, argv); 1370 else if (config->csv_output) 1371 print_header_csv(config, _target, evlist, argc, argv); 1372 else if (config->interval) 1373 print_header_interval_std(config, _target, evlist, argc, argv); 1374 else 1375 print_header_std(config, _target, evlist, argc, argv); 1376 } 1377 1378 if (num_print_iv++ == 25) 1379 num_print_iv = 0; 1380 } 1381 1382 static int get_precision(double num) 1383 { 1384 if (num > 1) 1385 return 0; 1386 1387 return lround(ceil(-log10(num))); 1388 } 1389 1390 static void print_table(struct perf_stat_config *config, 1391 FILE *output, int precision, double avg) 1392 { 1393 char tmp[64]; 1394 int idx, indent = 0; 1395 1396 scnprintf(tmp, 64, " %17.*f", precision, avg); 1397 while (tmp[indent] == ' ') 1398 indent++; 1399 1400 fprintf(output, "%*s# Table of individual measurements:\n", indent, ""); 1401 1402 for (idx = 0; idx < config->run_count; idx++) { 1403 double run = (double) config->walltime_run[idx] / NSEC_PER_SEC; 1404 int h, n = 1 + abs((int) (100.0 * (run - avg)/run) / 5); 1405 1406 fprintf(output, " %17.*f (%+.*f) ", 1407 precision, run, precision, run - avg); 1408 1409 for (h = 0; h < n; h++) 1410 fprintf(output, "#"); 1411 1412 fprintf(output, "\n"); 1413 } 1414 1415 fprintf(output, "\n%*s# Final result:\n", indent, ""); 1416 } 1417 1418 static double timeval2double(struct timeval *t) 1419 { 1420 return t->tv_sec + (double) t->tv_usec/USEC_PER_SEC; 1421 } 1422 1423 static void print_footer(struct perf_stat_config *config) 1424 { 1425 double avg = avg_stats(config->walltime_nsecs_stats) / NSEC_PER_SEC; 1426 FILE *output = config->output; 1427 1428 if (config->interval || config->csv_output || config->json_output) 1429 return; 1430 1431 if (!config->null_run) 1432 fprintf(output, "\n"); 1433 1434 if (config->run_count == 1) { 1435 fprintf(output, " %17.9f seconds time elapsed", avg); 1436 1437 if (config->ru_display) { 1438 double ru_utime = timeval2double(&config->ru_data.ru_utime); 1439 double ru_stime = timeval2double(&config->ru_data.ru_stime); 1440 1441 fprintf(output, "\n\n"); 1442 fprintf(output, " %17.9f seconds user\n", ru_utime); 1443 fprintf(output, " %17.9f seconds sys\n", ru_stime); 1444 } 1445 } else { 1446 double sd = stddev_stats(config->walltime_nsecs_stats) / NSEC_PER_SEC; 1447 /* 1448 * Display at most 2 more significant 1449 * digits than the stddev inaccuracy. 1450 */ 1451 int precision = get_precision(sd) + 2; 1452 1453 if (config->walltime_run_table) 1454 print_table(config, output, precision, avg); 1455 1456 fprintf(output, " %17.*f +- %.*f seconds time elapsed", 1457 precision, avg, precision, sd); 1458 1459 print_noise_pct(config, sd, avg, /*before_metric=*/false); 1460 } 1461 fprintf(output, "\n\n"); 1462 1463 if (config->print_free_counters_hint && sysctl__nmi_watchdog_enabled()) 1464 fprintf(output, 1465 "Some events weren't counted. Try disabling the NMI watchdog:\n" 1466 " echo 0 > /proc/sys/kernel/nmi_watchdog\n" 1467 " perf stat ...\n" 1468 " echo 1 > /proc/sys/kernel/nmi_watchdog\n"); 1469 1470 if (config->print_mixed_hw_group_error) 1471 fprintf(output, 1472 "The events in group usually have to be from " 1473 "the same PMU. Try reorganizing the group.\n"); 1474 } 1475 1476 static void print_percore(struct perf_stat_config *config, 1477 struct evsel *counter, struct outstate *os) 1478 { 1479 bool metric_only = config->metric_only; 1480 FILE *output = config->output; 1481 struct cpu_aggr_map *core_map; 1482 int aggr_idx, core_map_len = 0; 1483 1484 if (!config->aggr_map || !config->aggr_get_id) 1485 return; 1486 1487 if (config->percore_show_thread) 1488 return print_counter(config, counter, os); 1489 1490 /* 1491 * core_map will hold the aggr_cpu_id for the cores that have been 1492 * printed so that each core is printed just once. 1493 */ 1494 core_map = cpu_aggr_map__empty_new(config->aggr_map->nr); 1495 if (core_map == NULL) { 1496 fprintf(output, "Cannot allocate per-core aggr map for display\n"); 1497 return; 1498 } 1499 1500 cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) { 1501 struct perf_cpu curr_cpu = config->aggr_map->map[aggr_idx].cpu; 1502 struct aggr_cpu_id core_id = aggr_cpu_id__core(curr_cpu, NULL); 1503 bool found = false; 1504 1505 for (int i = 0; i < core_map_len; i++) { 1506 if (aggr_cpu_id__equal(&core_map->map[i], &core_id)) { 1507 found = true; 1508 break; 1509 } 1510 } 1511 if (found) 1512 continue; 1513 1514 print_counter_aggrdata(config, counter, aggr_idx, os); 1515 1516 core_map->map[core_map_len++] = core_id; 1517 } 1518 free(core_map); 1519 1520 if (metric_only) 1521 fputc('\n', output); 1522 } 1523 1524 static void print_cgroup_counter(struct perf_stat_config *config, struct evlist *evlist, 1525 struct outstate *os) 1526 { 1527 struct evsel *counter; 1528 1529 evlist__for_each_entry(evlist, counter) { 1530 if (os->cgrp != counter->cgrp) { 1531 if (os->cgrp != NULL) 1532 print_metric_end(config, os); 1533 1534 os->cgrp = counter->cgrp; 1535 print_metric_begin(config, evlist, os, /*aggr_idx=*/0); 1536 } 1537 1538 print_counter(config, counter, os); 1539 } 1540 if (os->cgrp) 1541 print_metric_end(config, os); 1542 } 1543 1544 void evlist__print_counters(struct evlist *evlist, struct perf_stat_config *config, 1545 struct target *_target, struct timespec *ts, 1546 int argc, const char **argv) 1547 { 1548 bool metric_only = config->metric_only; 1549 int interval = config->interval; 1550 struct evsel *counter; 1551 char buf[64]; 1552 struct outstate os = { 1553 .fh = config->output, 1554 .first = true, 1555 }; 1556 1557 if (config->iostat_run) 1558 evlist->selected = evlist__first(evlist); 1559 1560 if (interval) { 1561 os.prefix = buf; 1562 prepare_interval(config, buf, sizeof(buf), ts); 1563 } 1564 1565 print_header(config, _target, evlist, argc, argv); 1566 1567 switch (config->aggr_mode) { 1568 case AGGR_CORE: 1569 case AGGR_CACHE: 1570 case AGGR_CLUSTER: 1571 case AGGR_DIE: 1572 case AGGR_SOCKET: 1573 case AGGR_NODE: 1574 if (config->cgroup_list) 1575 print_aggr_cgroup(config, evlist, &os); 1576 else 1577 print_aggr(config, evlist, &os); 1578 break; 1579 case AGGR_THREAD: 1580 case AGGR_GLOBAL: 1581 if (config->iostat_run) { 1582 iostat_print_counters(evlist, config, ts, buf, 1583 (iostat_print_counter_t)print_counter, &os); 1584 } else if (config->cgroup_list) { 1585 print_cgroup_counter(config, evlist, &os); 1586 } else { 1587 print_metric_begin(config, evlist, &os, /*aggr_idx=*/0); 1588 evlist__for_each_entry(evlist, counter) { 1589 print_counter(config, counter, &os); 1590 } 1591 print_metric_end(config, &os); 1592 } 1593 break; 1594 case AGGR_NONE: 1595 if (metric_only) 1596 print_no_aggr_metric(config, evlist, &os); 1597 else { 1598 evlist__for_each_entry(evlist, counter) { 1599 if (counter->percore) 1600 print_percore(config, counter, &os); 1601 else 1602 print_counter(config, counter, &os); 1603 } 1604 } 1605 break; 1606 case AGGR_MAX: 1607 case AGGR_UNSET: 1608 default: 1609 break; 1610 } 1611 1612 print_footer(config); 1613 1614 fflush(config->output); 1615 } 1616