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