Merge tag 'trace-printf-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/trace...
[drm/drm-misc.git] / tools / perf / util / stat-display.c
blob53dcdf07f5a21e21dfec99cee89acb6447da1b26
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <inttypes.h>
4 #include <linux/string.h>
5 #include <linux/time64.h>
6 #include <math.h>
7 #include <perf/cpumap.h>
8 #include "color.h"
9 #include "counts.h"
10 #include "debug.h"
11 #include "evlist.h"
12 #include "evsel.h"
13 #include "stat.h"
14 #include "top.h"
15 #include "thread_map.h"
16 #include "cpumap.h"
17 #include "string2.h"
18 #include <linux/ctype.h>
19 #include "cgroup.h"
20 #include <api/fs/fs.h>
21 #include "util.h"
22 #include "iostat.h"
23 #include "pmu.h"
24 #include "pmus.h"
25 #include "tool_pmu.h"
27 #define CNTR_NOT_SUPPORTED "<not supported>"
28 #define CNTR_NOT_COUNTED "<not counted>"
30 #define MGROUP_LEN 50
31 #define METRIC_LEN 38
32 #define EVNAME_LEN 32
33 #define COUNTS_LEN 18
34 #define INTERVAL_LEN 16
35 #define CGROUP_LEN 16
36 #define COMM_LEN 16
37 #define PID_LEN 7
38 #define CPUS_LEN 4
40 static int aggr_header_lens[] = {
41 [AGGR_CORE] = 18,
42 [AGGR_CACHE] = 22,
43 [AGGR_CLUSTER] = 20,
44 [AGGR_DIE] = 12,
45 [AGGR_SOCKET] = 6,
46 [AGGR_NODE] = 6,
47 [AGGR_NONE] = 6,
48 [AGGR_THREAD] = 16,
49 [AGGR_GLOBAL] = 0,
52 static const char *aggr_header_csv[] = {
53 [AGGR_CORE] = "core,cpus,",
54 [AGGR_CACHE] = "cache,cpus,",
55 [AGGR_CLUSTER] = "cluster,cpus,",
56 [AGGR_DIE] = "die,cpus,",
57 [AGGR_SOCKET] = "socket,cpus,",
58 [AGGR_NONE] = "cpu,",
59 [AGGR_THREAD] = "comm-pid,",
60 [AGGR_NODE] = "node,",
61 [AGGR_GLOBAL] = ""
64 static const char *aggr_header_std[] = {
65 [AGGR_CORE] = "core",
66 [AGGR_CACHE] = "cache",
67 [AGGR_CLUSTER] = "cluster",
68 [AGGR_DIE] = "die",
69 [AGGR_SOCKET] = "socket",
70 [AGGR_NONE] = "cpu",
71 [AGGR_THREAD] = "comm-pid",
72 [AGGR_NODE] = "node",
73 [AGGR_GLOBAL] = ""
76 const char *metric_threshold_classify__color(enum metric_threshold_classify thresh)
78 const char * const colors[] = {
79 "", /* unknown */
80 PERF_COLOR_RED, /* bad */
81 PERF_COLOR_MAGENTA, /* nearly bad */
82 PERF_COLOR_YELLOW, /* less good */
83 PERF_COLOR_GREEN, /* good */
85 static_assert(ARRAY_SIZE(colors) - 1 == METRIC_THRESHOLD_GOOD, "missing enum value");
86 return colors[thresh];
89 static const char *metric_threshold_classify__str(enum metric_threshold_classify thresh)
91 const char * const strs[] = {
92 "unknown",
93 "bad",
94 "nearly bad",
95 "less good",
96 "good",
98 static_assert(ARRAY_SIZE(strs) - 1 == METRIC_THRESHOLD_GOOD, "missing enum value");
99 return strs[thresh];
102 static void print_running_std(struct perf_stat_config *config, u64 run, u64 ena)
104 if (run != ena)
105 fprintf(config->output, " (%.2f%%)", 100.0 * run / ena);
108 static void print_running_csv(struct perf_stat_config *config, u64 run, u64 ena)
110 double enabled_percent = 100;
112 if (run != ena)
113 enabled_percent = 100 * run / ena;
114 fprintf(config->output, "%s%" PRIu64 "%s%.2f",
115 config->csv_sep, run, config->csv_sep, enabled_percent);
118 static void print_running_json(struct perf_stat_config *config, u64 run, u64 ena)
120 double enabled_percent = 100;
122 if (run != ena)
123 enabled_percent = 100 * run / ena;
124 fprintf(config->output, "\"event-runtime\" : %" PRIu64 ", \"pcnt-running\" : %.2f, ",
125 run, enabled_percent);
128 static void print_running(struct perf_stat_config *config,
129 u64 run, u64 ena, bool before_metric)
131 if (config->json_output) {
132 if (before_metric)
133 print_running_json(config, run, ena);
134 } else if (config->csv_output) {
135 if (before_metric)
136 print_running_csv(config, run, ena);
137 } else {
138 if (!before_metric)
139 print_running_std(config, run, ena);
143 static void print_noise_pct_std(struct perf_stat_config *config,
144 double pct)
146 if (pct)
147 fprintf(config->output, " ( +-%6.2f%% )", pct);
150 static void print_noise_pct_csv(struct perf_stat_config *config,
151 double pct)
153 fprintf(config->output, "%s%.2f%%", config->csv_sep, pct);
156 static void print_noise_pct_json(struct perf_stat_config *config,
157 double pct)
159 fprintf(config->output, "\"variance\" : %.2f, ", pct);
162 static void print_noise_pct(struct perf_stat_config *config,
163 double total, double avg, bool before_metric)
165 double pct = rel_stddev_stats(total, avg);
167 if (config->json_output) {
168 if (before_metric)
169 print_noise_pct_json(config, pct);
170 } else if (config->csv_output) {
171 if (before_metric)
172 print_noise_pct_csv(config, pct);
173 } else {
174 if (!before_metric)
175 print_noise_pct_std(config, pct);
179 static void print_noise(struct perf_stat_config *config,
180 struct evsel *evsel, double avg, bool before_metric)
182 struct perf_stat_evsel *ps;
184 if (config->run_count == 1)
185 return;
187 ps = evsel->stats;
188 print_noise_pct(config, stddev_stats(&ps->res_stats), avg, before_metric);
191 static void print_cgroup_std(struct perf_stat_config *config, const char *cgrp_name)
193 fprintf(config->output, " %-*s", CGROUP_LEN, cgrp_name);
196 static void print_cgroup_csv(struct perf_stat_config *config, const char *cgrp_name)
198 fprintf(config->output, "%s%s", config->csv_sep, cgrp_name);
201 static void print_cgroup_json(struct perf_stat_config *config, const char *cgrp_name)
203 fprintf(config->output, "\"cgroup\" : \"%s\", ", cgrp_name);
206 static void print_cgroup(struct perf_stat_config *config, struct cgroup *cgrp)
208 if (nr_cgroups || config->cgroup_list) {
209 const char *cgrp_name = cgrp ? cgrp->name : "";
211 if (config->json_output)
212 print_cgroup_json(config, cgrp_name);
213 else if (config->csv_output)
214 print_cgroup_csv(config, cgrp_name);
215 else
216 print_cgroup_std(config, cgrp_name);
220 static void print_aggr_id_std(struct perf_stat_config *config,
221 struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr)
223 FILE *output = config->output;
224 int idx = config->aggr_mode;
225 char buf[128];
227 switch (config->aggr_mode) {
228 case AGGR_CORE:
229 snprintf(buf, sizeof(buf), "S%d-D%d-C%d", id.socket, id.die, id.core);
230 break;
231 case AGGR_CACHE:
232 snprintf(buf, sizeof(buf), "S%d-D%d-L%d-ID%d",
233 id.socket, id.die, id.cache_lvl, id.cache);
234 break;
235 case AGGR_CLUSTER:
236 snprintf(buf, sizeof(buf), "S%d-D%d-CLS%d", id.socket, id.die, id.cluster);
237 break;
238 case AGGR_DIE:
239 snprintf(buf, sizeof(buf), "S%d-D%d", id.socket, id.die);
240 break;
241 case AGGR_SOCKET:
242 snprintf(buf, sizeof(buf), "S%d", id.socket);
243 break;
244 case AGGR_NODE:
245 snprintf(buf, sizeof(buf), "N%d", id.node);
246 break;
247 case AGGR_NONE:
248 if (evsel->percore && !config->percore_show_thread) {
249 snprintf(buf, sizeof(buf), "S%d-D%d-C%d ",
250 id.socket, id.die, id.core);
251 fprintf(output, "%-*s ",
252 aggr_header_lens[AGGR_CORE], buf);
253 } else if (id.cpu.cpu > -1) {
254 fprintf(output, "CPU%-*d ",
255 aggr_header_lens[AGGR_NONE] - 3, id.cpu.cpu);
257 return;
258 case AGGR_THREAD:
259 fprintf(output, "%*s-%-*d ",
260 COMM_LEN, perf_thread_map__comm(evsel->core.threads, id.thread_idx),
261 PID_LEN, perf_thread_map__pid(evsel->core.threads, id.thread_idx));
262 return;
263 case AGGR_GLOBAL:
264 case AGGR_UNSET:
265 case AGGR_MAX:
266 default:
267 return;
270 fprintf(output, "%-*s %*d ", aggr_header_lens[idx], buf, 4, aggr_nr);
273 static void print_aggr_id_csv(struct perf_stat_config *config,
274 struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr)
276 FILE *output = config->output;
277 const char *sep = config->csv_sep;
279 switch (config->aggr_mode) {
280 case AGGR_CORE:
281 fprintf(output, "S%d-D%d-C%d%s%d%s",
282 id.socket, id.die, id.core, sep, aggr_nr, sep);
283 break;
284 case AGGR_CACHE:
285 fprintf(config->output, "S%d-D%d-L%d-ID%d%s%d%s",
286 id.socket, id.die, id.cache_lvl, id.cache, sep, aggr_nr, sep);
287 break;
288 case AGGR_CLUSTER:
289 fprintf(config->output, "S%d-D%d-CLS%d%s%d%s",
290 id.socket, id.die, id.cluster, sep, aggr_nr, sep);
291 break;
292 case AGGR_DIE:
293 fprintf(output, "S%d-D%d%s%d%s",
294 id.socket, id.die, sep, aggr_nr, sep);
295 break;
296 case AGGR_SOCKET:
297 fprintf(output, "S%d%s%d%s",
298 id.socket, sep, aggr_nr, sep);
299 break;
300 case AGGR_NODE:
301 fprintf(output, "N%d%s%d%s",
302 id.node, sep, aggr_nr, sep);
303 break;
304 case AGGR_NONE:
305 if (evsel->percore && !config->percore_show_thread) {
306 fprintf(output, "S%d-D%d-C%d%s",
307 id.socket, id.die, id.core, sep);
308 } else if (id.cpu.cpu > -1) {
309 fprintf(output, "CPU%d%s",
310 id.cpu.cpu, sep);
312 break;
313 case AGGR_THREAD:
314 fprintf(output, "%s-%d%s",
315 perf_thread_map__comm(evsel->core.threads, id.thread_idx),
316 perf_thread_map__pid(evsel->core.threads, id.thread_idx),
317 sep);
318 break;
319 case AGGR_GLOBAL:
320 case AGGR_UNSET:
321 case AGGR_MAX:
322 default:
323 break;
327 static void print_aggr_id_json(struct perf_stat_config *config,
328 struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr)
330 FILE *output = config->output;
332 switch (config->aggr_mode) {
333 case AGGR_CORE:
334 fprintf(output, "\"core\" : \"S%d-D%d-C%d\", \"aggregate-number\" : %d, ",
335 id.socket, id.die, id.core, aggr_nr);
336 break;
337 case AGGR_CACHE:
338 fprintf(output, "\"cache\" : \"S%d-D%d-L%d-ID%d\", \"aggregate-number\" : %d, ",
339 id.socket, id.die, id.cache_lvl, id.cache, aggr_nr);
340 break;
341 case AGGR_CLUSTER:
342 fprintf(output, "\"cluster\" : \"S%d-D%d-CLS%d\", \"aggregate-number\" : %d, ",
343 id.socket, id.die, id.cluster, aggr_nr);
344 break;
345 case AGGR_DIE:
346 fprintf(output, "\"die\" : \"S%d-D%d\", \"aggregate-number\" : %d, ",
347 id.socket, id.die, aggr_nr);
348 break;
349 case AGGR_SOCKET:
350 fprintf(output, "\"socket\" : \"S%d\", \"aggregate-number\" : %d, ",
351 id.socket, aggr_nr);
352 break;
353 case AGGR_NODE:
354 fprintf(output, "\"node\" : \"N%d\", \"aggregate-number\" : %d, ",
355 id.node, aggr_nr);
356 break;
357 case AGGR_NONE:
358 if (evsel->percore && !config->percore_show_thread) {
359 fprintf(output, "\"core\" : \"S%d-D%d-C%d\"",
360 id.socket, id.die, id.core);
361 } else if (id.cpu.cpu > -1) {
362 fprintf(output, "\"cpu\" : \"%d\", ",
363 id.cpu.cpu);
365 break;
366 case AGGR_THREAD:
367 fprintf(output, "\"thread\" : \"%s-%d\", ",
368 perf_thread_map__comm(evsel->core.threads, id.thread_idx),
369 perf_thread_map__pid(evsel->core.threads, id.thread_idx));
370 break;
371 case AGGR_GLOBAL:
372 case AGGR_UNSET:
373 case AGGR_MAX:
374 default:
375 break;
379 static void aggr_printout(struct perf_stat_config *config,
380 struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr)
382 if (config->json_output)
383 print_aggr_id_json(config, evsel, id, aggr_nr);
384 else if (config->csv_output)
385 print_aggr_id_csv(config, evsel, id, aggr_nr);
386 else
387 print_aggr_id_std(config, evsel, id, aggr_nr);
390 struct outstate {
391 FILE *fh;
392 bool newline;
393 bool first;
394 const char *prefix;
395 int nfields;
396 int aggr_nr;
397 struct aggr_cpu_id id;
398 struct evsel *evsel;
399 struct cgroup *cgrp;
402 static void new_line_std(struct perf_stat_config *config __maybe_unused,
403 void *ctx)
405 struct outstate *os = ctx;
407 os->newline = true;
410 static inline void __new_line_std_csv(struct perf_stat_config *config,
411 struct outstate *os)
413 fputc('\n', os->fh);
414 if (os->prefix)
415 fputs(os->prefix, os->fh);
416 aggr_printout(config, os->evsel, os->id, os->aggr_nr);
419 static inline void __new_line_std(struct outstate *os)
421 fprintf(os->fh, " ");
424 static void do_new_line_std(struct perf_stat_config *config,
425 struct outstate *os)
427 __new_line_std_csv(config, os);
428 if (config->aggr_mode == AGGR_NONE)
429 fprintf(os->fh, " ");
430 __new_line_std(os);
433 static void print_metric_std(struct perf_stat_config *config,
434 void *ctx, enum metric_threshold_classify thresh,
435 const char *fmt, const char *unit, double val)
437 struct outstate *os = ctx;
438 FILE *out = os->fh;
439 int n;
440 bool newline = os->newline;
441 const char *color = metric_threshold_classify__color(thresh);
443 os->newline = false;
445 if (unit == NULL || fmt == NULL) {
446 fprintf(out, "%-*s", METRIC_LEN, "");
447 return;
450 if (newline)
451 do_new_line_std(config, os);
453 n = fprintf(out, " # ");
454 if (color)
455 n += color_fprintf(out, color, fmt, val);
456 else
457 n += fprintf(out, fmt, val);
458 fprintf(out, " %-*s", METRIC_LEN - n - 1, unit);
461 static void new_line_csv(struct perf_stat_config *config, void *ctx)
463 struct outstate *os = ctx;
464 int i;
466 __new_line_std_csv(config, os);
467 for (i = 0; i < os->nfields; i++)
468 fputs(config->csv_sep, os->fh);
471 static void print_metric_csv(struct perf_stat_config *config __maybe_unused,
472 void *ctx,
473 enum metric_threshold_classify thresh __maybe_unused,
474 const char *fmt, const char *unit, double val)
476 struct outstate *os = ctx;
477 FILE *out = os->fh;
478 char buf[64], *vals, *ends;
480 if (unit == NULL || fmt == NULL) {
481 fprintf(out, "%s%s", config->csv_sep, config->csv_sep);
482 return;
484 snprintf(buf, sizeof(buf), fmt, val);
485 ends = vals = skip_spaces(buf);
486 while (isdigit(*ends) || *ends == '.')
487 ends++;
488 *ends = 0;
489 fprintf(out, "%s%s%s%s", config->csv_sep, vals, config->csv_sep, skip_spaces(unit));
492 static void print_metric_json(struct perf_stat_config *config __maybe_unused,
493 void *ctx,
494 enum metric_threshold_classify thresh,
495 const char *fmt __maybe_unused,
496 const char *unit, double val)
498 struct outstate *os = ctx;
499 FILE *out = os->fh;
501 if (unit) {
502 fprintf(out, "\"metric-value\" : \"%f\", \"metric-unit\" : \"%s\"", val, unit);
503 if (thresh != METRIC_THRESHOLD_UNKNOWN) {
504 fprintf(out, ", \"metric-threshold\" : \"%s\"",
505 metric_threshold_classify__str(thresh));
508 if (!config->metric_only)
509 fprintf(out, "}");
512 static void new_line_json(struct perf_stat_config *config, void *ctx)
514 struct outstate *os = ctx;
516 fputs("\n{", os->fh);
517 if (os->prefix)
518 fprintf(os->fh, "%s", os->prefix);
519 aggr_printout(config, os->evsel, os->id, os->aggr_nr);
522 static void print_metricgroup_header_json(struct perf_stat_config *config,
523 void *ctx,
524 const char *metricgroup_name)
526 if (!metricgroup_name)
527 return;
529 fprintf(config->output, "\"metricgroup\" : \"%s\"}", metricgroup_name);
530 new_line_json(config, ctx);
533 static void print_metricgroup_header_csv(struct perf_stat_config *config,
534 void *ctx,
535 const char *metricgroup_name)
537 struct outstate *os = ctx;
538 int i;
540 if (!metricgroup_name) {
541 /* Leave space for running and enabling */
542 for (i = 0; i < os->nfields - 2; i++)
543 fputs(config->csv_sep, os->fh);
544 return;
547 for (i = 0; i < os->nfields; i++)
548 fputs(config->csv_sep, os->fh);
549 fprintf(config->output, "%s", metricgroup_name);
550 new_line_csv(config, ctx);
553 static void print_metricgroup_header_std(struct perf_stat_config *config,
554 void *ctx,
555 const char *metricgroup_name)
557 struct outstate *os = ctx;
558 int n;
560 if (!metricgroup_name) {
561 __new_line_std(os);
562 return;
565 n = fprintf(config->output, " %*s", EVNAME_LEN, metricgroup_name);
567 fprintf(config->output, "%*s", MGROUP_LEN - n - 1, "");
570 /* Filter out some columns that don't work well in metrics only mode */
572 static bool valid_only_metric(const char *unit)
574 if (!unit)
575 return false;
576 if (strstr(unit, "/sec") ||
577 strstr(unit, "CPUs utilized"))
578 return false;
579 return true;
582 static const char *fixunit(char *buf, struct evsel *evsel,
583 const char *unit)
585 if (!strncmp(unit, "of all", 6)) {
586 snprintf(buf, 1024, "%s %s", evsel__name(evsel),
587 unit);
588 return buf;
590 return unit;
593 static void print_metric_only(struct perf_stat_config *config,
594 void *ctx, enum metric_threshold_classify thresh,
595 const char *fmt, const char *unit, double val)
597 struct outstate *os = ctx;
598 FILE *out = os->fh;
599 char buf[1024], str[1024];
600 unsigned mlen = config->metric_only_len;
601 const char *color = metric_threshold_classify__color(thresh);
603 if (!valid_only_metric(unit))
604 return;
605 unit = fixunit(buf, os->evsel, unit);
606 if (mlen < strlen(unit))
607 mlen = strlen(unit) + 1;
609 if (color)
610 mlen += strlen(color) + sizeof(PERF_COLOR_RESET) - 1;
612 color_snprintf(str, sizeof(str), color ?: "", fmt ?: "", val);
613 fprintf(out, "%*s ", mlen, str);
614 os->first = false;
617 static void print_metric_only_csv(struct perf_stat_config *config __maybe_unused,
618 void *ctx,
619 enum metric_threshold_classify thresh __maybe_unused,
620 const char *fmt,
621 const char *unit, double val)
623 struct outstate *os = ctx;
624 FILE *out = os->fh;
625 char buf[64], *vals, *ends;
626 char tbuf[1024];
628 if (!valid_only_metric(unit))
629 return;
630 unit = fixunit(tbuf, os->evsel, unit);
631 snprintf(buf, sizeof(buf), fmt ?: "", val);
632 ends = vals = skip_spaces(buf);
633 while (isdigit(*ends) || *ends == '.')
634 ends++;
635 *ends = 0;
636 fprintf(out, "%s%s", vals, config->csv_sep);
637 os->first = false;
640 static void print_metric_only_json(struct perf_stat_config *config __maybe_unused,
641 void *ctx,
642 enum metric_threshold_classify thresh __maybe_unused,
643 const char *fmt,
644 const char *unit, double val)
646 struct outstate *os = ctx;
647 FILE *out = os->fh;
648 char buf[64], *ends;
649 char tbuf[1024];
650 const char *vals;
652 if (!valid_only_metric(unit))
653 return;
654 unit = fixunit(tbuf, os->evsel, unit);
655 if (!unit[0])
656 return;
657 snprintf(buf, sizeof(buf), fmt ?: "", val);
658 vals = ends = skip_spaces(buf);
659 while (isdigit(*ends) || *ends == '.')
660 ends++;
661 *ends = 0;
662 if (!vals[0])
663 vals = "none";
664 fprintf(out, "%s\"%s\" : \"%s\"", os->first ? "" : ", ", unit, vals);
665 os->first = false;
668 static void new_line_metric(struct perf_stat_config *config __maybe_unused,
669 void *ctx __maybe_unused)
673 static void print_metric_header(struct perf_stat_config *config,
674 void *ctx,
675 enum metric_threshold_classify thresh __maybe_unused,
676 const char *fmt __maybe_unused,
677 const char *unit, double val __maybe_unused)
679 struct outstate *os = ctx;
680 char tbuf[1024];
682 /* In case of iostat, print metric header for first root port only */
683 if (config->iostat_run &&
684 os->evsel->priv != os->evsel->evlist->selected->priv)
685 return;
687 if (os->evsel->cgrp != os->cgrp)
688 return;
690 if (!valid_only_metric(unit))
691 return;
692 unit = fixunit(tbuf, os->evsel, unit);
694 if (config->json_output)
695 return;
696 else if (config->csv_output)
697 fprintf(os->fh, "%s%s", unit, config->csv_sep);
698 else
699 fprintf(os->fh, "%*s ", config->metric_only_len, unit);
702 static void print_counter_value_std(struct perf_stat_config *config,
703 struct evsel *evsel, double avg, bool ok)
705 FILE *output = config->output;
706 double sc = evsel->scale;
707 const char *fmt;
708 const char *bad_count = evsel->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED;
710 if (config->big_num)
711 fmt = floor(sc) != sc ? "%'*.2f " : "%'*.0f ";
712 else
713 fmt = floor(sc) != sc ? "%*.2f " : "%*.0f ";
715 if (ok)
716 fprintf(output, fmt, COUNTS_LEN, avg);
717 else
718 fprintf(output, "%*s ", COUNTS_LEN, bad_count);
720 if (evsel->unit)
721 fprintf(output, "%-*s ", config->unit_width, evsel->unit);
723 fprintf(output, "%-*s", EVNAME_LEN, evsel__name(evsel));
726 static void print_counter_value_csv(struct perf_stat_config *config,
727 struct evsel *evsel, double avg, bool ok)
729 FILE *output = config->output;
730 double sc = evsel->scale;
731 const char *sep = config->csv_sep;
732 const char *fmt = floor(sc) != sc ? "%.2f%s" : "%.0f%s";
733 const char *bad_count = evsel->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED;
735 if (ok)
736 fprintf(output, fmt, avg, sep);
737 else
738 fprintf(output, "%s%s", bad_count, sep);
740 if (evsel->unit)
741 fprintf(output, "%s%s", evsel->unit, sep);
743 fprintf(output, "%s", evsel__name(evsel));
746 static void print_counter_value_json(struct perf_stat_config *config,
747 struct evsel *evsel, double avg, bool ok)
749 FILE *output = config->output;
750 const char *bad_count = evsel->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED;
752 if (ok)
753 fprintf(output, "\"counter-value\" : \"%f\", ", avg);
754 else
755 fprintf(output, "\"counter-value\" : \"%s\", ", bad_count);
757 if (evsel->unit)
758 fprintf(output, "\"unit\" : \"%s\", ", evsel->unit);
760 fprintf(output, "\"event\" : \"%s\", ", evsel__name(evsel));
763 static void print_counter_value(struct perf_stat_config *config,
764 struct evsel *evsel, double avg, bool ok)
766 if (config->json_output)
767 print_counter_value_json(config, evsel, avg, ok);
768 else if (config->csv_output)
769 print_counter_value_csv(config, evsel, avg, ok);
770 else
771 print_counter_value_std(config, evsel, avg, ok);
774 static void abs_printout(struct perf_stat_config *config,
775 struct aggr_cpu_id id, int aggr_nr,
776 struct evsel *evsel, double avg, bool ok)
778 aggr_printout(config, evsel, id, aggr_nr);
779 print_counter_value(config, evsel, avg, ok);
780 print_cgroup(config, evsel->cgrp);
783 static bool is_mixed_hw_group(struct evsel *counter)
785 struct evlist *evlist = counter->evlist;
786 u32 pmu_type = counter->core.attr.type;
787 struct evsel *pos;
789 if (counter->core.nr_members < 2)
790 return false;
792 evlist__for_each_entry(evlist, pos) {
793 /* software events can be part of any hardware group */
794 if (pos->core.attr.type == PERF_TYPE_SOFTWARE)
795 continue;
796 if (pmu_type == PERF_TYPE_SOFTWARE) {
797 pmu_type = pos->core.attr.type;
798 continue;
800 if (pmu_type != pos->core.attr.type)
801 return true;
804 return false;
807 static bool evlist__has_hybrid(struct evlist *evlist)
809 struct evsel *evsel;
811 if (perf_pmus__num_core_pmus() == 1)
812 return false;
814 evlist__for_each_entry(evlist, evsel) {
815 if (evsel->core.is_pmu_core)
816 return true;
819 return false;
822 static void printout(struct perf_stat_config *config, struct outstate *os,
823 double uval, u64 run, u64 ena, double noise, int aggr_idx)
825 struct perf_stat_output_ctx out;
826 print_metric_t pm;
827 new_line_t nl;
828 print_metricgroup_header_t pmh;
829 bool ok = true;
830 struct evsel *counter = os->evsel;
832 if (config->csv_output) {
833 pm = config->metric_only ? print_metric_only_csv : print_metric_csv;
834 nl = config->metric_only ? new_line_metric : new_line_csv;
835 pmh = print_metricgroup_header_csv;
836 os->nfields = 4 + (counter->cgrp ? 1 : 0);
837 } else if (config->json_output) {
838 pm = config->metric_only ? print_metric_only_json : print_metric_json;
839 nl = config->metric_only ? new_line_metric : new_line_json;
840 pmh = print_metricgroup_header_json;
841 } else {
842 pm = config->metric_only ? print_metric_only : print_metric_std;
843 nl = config->metric_only ? new_line_metric : new_line_std;
844 pmh = print_metricgroup_header_std;
847 if (run == 0 || ena == 0 || counter->counts->scaled == -1) {
848 if (config->metric_only) {
849 pm(config, os, METRIC_THRESHOLD_UNKNOWN, "", "", 0);
850 return;
853 ok = false;
855 if (counter->supported) {
856 if (!evlist__has_hybrid(counter->evlist)) {
857 config->print_free_counters_hint = 1;
858 if (is_mixed_hw_group(counter))
859 config->print_mixed_hw_group_error = 1;
864 out.print_metric = pm;
865 out.new_line = nl;
866 out.print_metricgroup_header = pmh;
867 out.ctx = os;
868 out.force_header = false;
870 if (!config->metric_only && !counter->default_metricgroup) {
871 abs_printout(config, os->id, os->aggr_nr, counter, uval, ok);
873 print_noise(config, counter, noise, /*before_metric=*/true);
874 print_running(config, run, ena, /*before_metric=*/true);
877 if (ok) {
878 if (!config->metric_only && counter->default_metricgroup) {
879 void *from = NULL;
881 aggr_printout(config, os->evsel, os->id, os->aggr_nr);
882 /* Print out all the metricgroup with the same metric event. */
883 do {
884 int num = 0;
886 /* Print out the new line for the next new metricgroup. */
887 if (from) {
888 if (config->json_output)
889 new_line_json(config, (void *)os);
890 else
891 __new_line_std_csv(config, os);
894 print_noise(config, counter, noise, /*before_metric=*/true);
895 print_running(config, run, ena, /*before_metric=*/true);
896 from = perf_stat__print_shadow_stats_metricgroup(config, counter, aggr_idx,
897 &num, from, &out,
898 &config->metric_events);
899 } while (from != NULL);
900 } else
901 perf_stat__print_shadow_stats(config, counter, uval, aggr_idx,
902 &out, &config->metric_events);
903 } else {
904 pm(config, os, METRIC_THRESHOLD_UNKNOWN, /*format=*/NULL, /*unit=*/"", /*val=*/0);
907 if (!config->metric_only) {
908 print_noise(config, counter, noise, /*before_metric=*/false);
909 print_running(config, run, ena, /*before_metric=*/false);
913 static void uniquify_event_name(struct evsel *counter)
915 const char *name, *pmu_name;
916 char *new_name, *config;
917 int ret;
919 /* The evsel was already uniquified. */
920 if (counter->uniquified_name)
921 return;
923 /* Avoid checking to uniquify twice. */
924 counter->uniquified_name = true;
926 /* The evsel has a "name=" config term or is from libpfm. */
927 if (counter->use_config_name || counter->is_libpfm_event)
928 return;
930 /* Legacy no PMU event, don't uniquify. */
931 if (!counter->pmu ||
932 (counter->pmu->type < PERF_TYPE_MAX && counter->pmu->type != PERF_TYPE_RAW))
933 return;
935 /* A sysfs or json event replacing a legacy event, don't uniquify. */
936 if (counter->pmu->is_core && counter->alternate_hw_config != PERF_COUNT_HW_MAX)
937 return;
939 name = evsel__name(counter);
940 pmu_name = counter->pmu->name;
941 /* Already prefixed by the PMU name. */
942 if (!strncmp(name, pmu_name, strlen(pmu_name)))
943 return;
945 config = strchr(name, '/');
946 if (config) {
947 int len = config - name;
949 if (config[1] == '/') {
950 /* case: event// */
951 ret = asprintf(&new_name, "%s/%.*s/%s", pmu_name, len, name, config + 2);
952 } else {
953 /* case: event/.../ */
954 ret = asprintf(&new_name, "%s/%.*s,%s", pmu_name, len, name, config + 1);
956 } else {
957 config = strchr(name, ':');
958 if (config) {
959 /* case: event:.. */
960 int len = config - name;
962 ret = asprintf(&new_name, "%s/%.*s/%s", pmu_name, len, name, config + 1);
963 } else {
964 /* case: event */
965 ret = asprintf(&new_name, "%s/%s/", pmu_name, name);
968 if (ret > 0) {
969 free(counter->name);
970 counter->name = new_name;
971 } else {
972 /* ENOMEM from asprintf. */
973 counter->uniquified_name = false;
977 static bool hybrid_uniquify(struct evsel *evsel, struct perf_stat_config *config)
979 return evsel__is_hybrid(evsel) && !config->hybrid_merge;
982 static void uniquify_counter(struct perf_stat_config *config, struct evsel *counter)
984 if (config->aggr_mode == AGGR_NONE || hybrid_uniquify(counter, config))
985 uniquify_event_name(counter);
989 * should_skip_zero_count() - Check if the event should print 0 values.
990 * @config: The perf stat configuration (including aggregation mode).
991 * @counter: The evsel with its associated cpumap.
992 * @id: The aggregation id that is being queried.
994 * Due to mismatch between the event cpumap or thread-map and the
995 * aggregation mode, sometimes it'd iterate the counter with the map
996 * which does not contain any values.
998 * For example, uncore events have dedicated CPUs to manage them,
999 * result for other CPUs should be zero and skipped.
1001 * Return: %true if the value should NOT be printed, %false if the value
1002 * needs to be printed like "<not counted>" or "<not supported>".
1004 static bool should_skip_zero_counter(struct perf_stat_config *config,
1005 struct evsel *counter,
1006 const struct aggr_cpu_id *id)
1008 struct perf_cpu cpu;
1009 int idx;
1012 * Skip unsupported default events when not verbose. (default events
1013 * are all marked 'skippable').
1015 if (verbose == 0 && counter->skippable && !counter->supported)
1016 return true;
1019 * Skip value 0 when enabling --per-thread globally,
1020 * otherwise it will have too many 0 output.
1022 if (config->aggr_mode == AGGR_THREAD && config->system_wide)
1023 return true;
1026 * Many tool events are only gathered on the first index, skip other
1027 * zero values.
1029 if (evsel__is_tool(counter)) {
1030 struct aggr_cpu_id own_id =
1031 config->aggr_get_id(config, (struct perf_cpu){ .cpu = 0 });
1033 return !aggr_cpu_id__equal(id, &own_id);
1037 * Skip value 0 when it's an uncore event and the given aggr id
1038 * does not belong to the PMU cpumask.
1040 if (!counter->pmu || !counter->pmu->is_uncore)
1041 return false;
1043 perf_cpu_map__for_each_cpu(cpu, idx, counter->pmu->cpus) {
1044 struct aggr_cpu_id own_id = config->aggr_get_id(config, cpu);
1046 if (aggr_cpu_id__equal(id, &own_id))
1047 return false;
1049 return true;
1052 static void print_counter_aggrdata(struct perf_stat_config *config,
1053 struct evsel *counter, int aggr_idx,
1054 struct outstate *os)
1056 FILE *output = config->output;
1057 u64 ena, run, val;
1058 double uval;
1059 struct perf_stat_evsel *ps = counter->stats;
1060 struct perf_stat_aggr *aggr = &ps->aggr[aggr_idx];
1061 struct aggr_cpu_id id = config->aggr_map->map[aggr_idx];
1062 double avg = aggr->counts.val;
1063 bool metric_only = config->metric_only;
1065 os->id = id;
1066 os->aggr_nr = aggr->nr;
1067 os->evsel = counter;
1069 /* Skip already merged uncore/hybrid events */
1070 if (counter->merged_stat)
1071 return;
1073 uniquify_counter(config, counter);
1075 val = aggr->counts.val;
1076 ena = aggr->counts.ena;
1077 run = aggr->counts.run;
1079 if (perf_stat__skip_metric_event(counter, &config->metric_events, ena, run))
1080 return;
1082 if (val == 0 && should_skip_zero_counter(config, counter, &id))
1083 return;
1085 if (!metric_only) {
1086 if (config->json_output)
1087 fputc('{', output);
1088 if (os->prefix)
1089 fprintf(output, "%s", os->prefix);
1090 else if (config->summary && config->csv_output &&
1091 !config->no_csv_summary && !config->interval)
1092 fprintf(output, "%s%s", "summary", config->csv_sep);
1095 uval = val * counter->scale;
1097 printout(config, os, uval, run, ena, avg, aggr_idx);
1099 if (!metric_only)
1100 fputc('\n', output);
1103 static void print_metric_begin(struct perf_stat_config *config,
1104 struct evlist *evlist,
1105 struct outstate *os, int aggr_idx)
1107 struct perf_stat_aggr *aggr;
1108 struct aggr_cpu_id id;
1109 struct evsel *evsel;
1111 os->first = true;
1112 if (!config->metric_only)
1113 return;
1115 if (config->json_output)
1116 fputc('{', config->output);
1117 if (os->prefix)
1118 fprintf(config->output, "%s", os->prefix);
1120 evsel = evlist__first(evlist);
1121 id = config->aggr_map->map[aggr_idx];
1122 aggr = &evsel->stats->aggr[aggr_idx];
1123 aggr_printout(config, evsel, id, aggr->nr);
1125 print_cgroup(config, os->cgrp ? : evsel->cgrp);
1128 static void print_metric_end(struct perf_stat_config *config, struct outstate *os)
1130 FILE *output = config->output;
1132 if (!config->metric_only)
1133 return;
1135 if (config->json_output) {
1136 if (os->first)
1137 fputs("\"metric-value\" : \"none\"", output);
1138 fputc('}', output);
1140 fputc('\n', output);
1143 static void print_aggr(struct perf_stat_config *config,
1144 struct evlist *evlist,
1145 struct outstate *os)
1147 struct evsel *counter;
1148 int aggr_idx;
1150 if (!config->aggr_map || !config->aggr_get_id)
1151 return;
1154 * With metric_only everything is on a single line.
1155 * Without each counter has its own line.
1157 cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) {
1158 print_metric_begin(config, evlist, os, aggr_idx);
1160 evlist__for_each_entry(evlist, counter) {
1161 print_counter_aggrdata(config, counter, aggr_idx, os);
1163 print_metric_end(config, os);
1167 static void print_aggr_cgroup(struct perf_stat_config *config,
1168 struct evlist *evlist,
1169 struct outstate *os)
1171 struct evsel *counter, *evsel;
1172 int aggr_idx;
1174 if (!config->aggr_map || !config->aggr_get_id)
1175 return;
1177 evlist__for_each_entry(evlist, evsel) {
1178 if (os->cgrp == evsel->cgrp)
1179 continue;
1181 os->cgrp = evsel->cgrp;
1183 cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) {
1184 print_metric_begin(config, evlist, os, aggr_idx);
1186 evlist__for_each_entry(evlist, counter) {
1187 if (counter->cgrp != os->cgrp)
1188 continue;
1190 print_counter_aggrdata(config, counter, aggr_idx, os);
1192 print_metric_end(config, os);
1197 static void print_counter(struct perf_stat_config *config,
1198 struct evsel *counter, struct outstate *os)
1200 int aggr_idx;
1202 /* AGGR_THREAD doesn't have config->aggr_get_id */
1203 if (!config->aggr_map)
1204 return;
1206 cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) {
1207 print_counter_aggrdata(config, counter, aggr_idx, os);
1211 static void print_no_aggr_metric(struct perf_stat_config *config,
1212 struct evlist *evlist,
1213 struct outstate *os)
1215 int all_idx;
1216 struct perf_cpu cpu;
1218 perf_cpu_map__for_each_cpu(cpu, all_idx, evlist->core.user_requested_cpus) {
1219 struct evsel *counter;
1220 bool first = true;
1222 evlist__for_each_entry(evlist, counter) {
1223 u64 ena, run, val;
1224 double uval;
1225 struct perf_stat_evsel *ps = counter->stats;
1226 int aggr_idx = 0;
1228 if (!perf_cpu_map__has(evsel__cpus(counter), cpu))
1229 continue;
1231 cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) {
1232 if (config->aggr_map->map[aggr_idx].cpu.cpu == cpu.cpu)
1233 break;
1236 os->evsel = counter;
1237 os->id = aggr_cpu_id__cpu(cpu, /*data=*/NULL);
1238 if (first) {
1239 print_metric_begin(config, evlist, os, aggr_idx);
1240 first = false;
1242 val = ps->aggr[aggr_idx].counts.val;
1243 ena = ps->aggr[aggr_idx].counts.ena;
1244 run = ps->aggr[aggr_idx].counts.run;
1246 uval = val * counter->scale;
1247 printout(config, os, uval, run, ena, 1.0, aggr_idx);
1249 if (!first)
1250 print_metric_end(config, os);
1254 static void print_metric_headers_std(struct perf_stat_config *config,
1255 bool no_indent)
1257 fputc(' ', config->output);
1259 if (!no_indent) {
1260 int len = aggr_header_lens[config->aggr_mode];
1262 if (nr_cgroups || config->cgroup_list)
1263 len += CGROUP_LEN + 1;
1265 fprintf(config->output, "%*s", len, "");
1269 static void print_metric_headers_csv(struct perf_stat_config *config,
1270 bool no_indent __maybe_unused)
1272 const char *p;
1274 if (config->interval)
1275 fprintf(config->output, "time%s", config->csv_sep);
1276 if (config->iostat_run)
1277 return;
1279 p = aggr_header_csv[config->aggr_mode];
1280 while (*p) {
1281 if (*p == ',')
1282 fputs(config->csv_sep, config->output);
1283 else
1284 fputc(*p, config->output);
1285 p++;
1289 static void print_metric_headers_json(struct perf_stat_config *config __maybe_unused,
1290 bool no_indent __maybe_unused)
1294 static void print_metric_headers(struct perf_stat_config *config,
1295 struct evlist *evlist, bool no_indent)
1297 struct evsel *counter;
1298 struct outstate os = {
1299 .fh = config->output
1301 struct perf_stat_output_ctx out = {
1302 .ctx = &os,
1303 .print_metric = print_metric_header,
1304 .new_line = new_line_metric,
1305 .force_header = true,
1308 if (config->json_output)
1309 print_metric_headers_json(config, no_indent);
1310 else if (config->csv_output)
1311 print_metric_headers_csv(config, no_indent);
1312 else
1313 print_metric_headers_std(config, no_indent);
1315 if (config->iostat_run)
1316 iostat_print_header_prefix(config);
1318 if (config->cgroup_list)
1319 os.cgrp = evlist__first(evlist)->cgrp;
1321 /* Print metrics headers only */
1322 evlist__for_each_entry(evlist, counter) {
1323 if (!config->iostat_run &&
1324 config->aggr_mode != AGGR_NONE && counter->metric_leader != counter)
1325 continue;
1327 os.evsel = counter;
1329 perf_stat__print_shadow_stats(config, counter, 0,
1331 &out,
1332 &config->metric_events);
1335 if (!config->json_output)
1336 fputc('\n', config->output);
1339 static void prepare_interval(struct perf_stat_config *config,
1340 char *prefix, size_t len, struct timespec *ts)
1342 if (config->iostat_run)
1343 return;
1345 if (config->json_output)
1346 scnprintf(prefix, len, "\"interval\" : %lu.%09lu, ",
1347 (unsigned long) ts->tv_sec, ts->tv_nsec);
1348 else if (config->csv_output)
1349 scnprintf(prefix, len, "%lu.%09lu%s",
1350 (unsigned long) ts->tv_sec, ts->tv_nsec, config->csv_sep);
1351 else
1352 scnprintf(prefix, len, "%6lu.%09lu ",
1353 (unsigned long) ts->tv_sec, ts->tv_nsec);
1356 static void print_header_interval_std(struct perf_stat_config *config,
1357 struct target *_target __maybe_unused,
1358 struct evlist *evlist,
1359 int argc __maybe_unused,
1360 const char **argv __maybe_unused)
1362 FILE *output = config->output;
1364 switch (config->aggr_mode) {
1365 case AGGR_NODE:
1366 case AGGR_SOCKET:
1367 case AGGR_DIE:
1368 case AGGR_CLUSTER:
1369 case AGGR_CACHE:
1370 case AGGR_CORE:
1371 fprintf(output, "#%*s %-*s cpus",
1372 INTERVAL_LEN - 1, "time",
1373 aggr_header_lens[config->aggr_mode],
1374 aggr_header_std[config->aggr_mode]);
1375 break;
1376 case AGGR_NONE:
1377 fprintf(output, "#%*s %-*s",
1378 INTERVAL_LEN - 1, "time",
1379 aggr_header_lens[config->aggr_mode],
1380 aggr_header_std[config->aggr_mode]);
1381 break;
1382 case AGGR_THREAD:
1383 fprintf(output, "#%*s %*s-%-*s",
1384 INTERVAL_LEN - 1, "time",
1385 COMM_LEN, "comm", PID_LEN, "pid");
1386 break;
1387 case AGGR_GLOBAL:
1388 default:
1389 if (!config->iostat_run)
1390 fprintf(output, "#%*s",
1391 INTERVAL_LEN - 1, "time");
1392 case AGGR_UNSET:
1393 case AGGR_MAX:
1394 break;
1397 if (config->metric_only)
1398 print_metric_headers(config, evlist, true);
1399 else
1400 fprintf(output, " %*s %*s events\n",
1401 COUNTS_LEN, "counts", config->unit_width, "unit");
1404 static void print_header_std(struct perf_stat_config *config,
1405 struct target *_target, struct evlist *evlist,
1406 int argc, const char **argv)
1408 FILE *output = config->output;
1409 int i;
1411 fprintf(output, "\n");
1412 fprintf(output, " Performance counter stats for ");
1413 if (_target->bpf_str)
1414 fprintf(output, "\'BPF program(s) %s", _target->bpf_str);
1415 else if (_target->system_wide)
1416 fprintf(output, "\'system wide");
1417 else if (_target->cpu_list)
1418 fprintf(output, "\'CPU(s) %s", _target->cpu_list);
1419 else if (!target__has_task(_target)) {
1420 fprintf(output, "\'%s", argv ? argv[0] : "pipe");
1421 for (i = 1; argv && (i < argc); i++)
1422 fprintf(output, " %s", argv[i]);
1423 } else if (_target->pid)
1424 fprintf(output, "process id \'%s", _target->pid);
1425 else
1426 fprintf(output, "thread id \'%s", _target->tid);
1428 fprintf(output, "\'");
1429 if (config->run_count > 1)
1430 fprintf(output, " (%d runs)", config->run_count);
1431 fprintf(output, ":\n\n");
1433 if (config->metric_only)
1434 print_metric_headers(config, evlist, false);
1437 static void print_header_csv(struct perf_stat_config *config,
1438 struct target *_target __maybe_unused,
1439 struct evlist *evlist,
1440 int argc __maybe_unused,
1441 const char **argv __maybe_unused)
1443 if (config->metric_only)
1444 print_metric_headers(config, evlist, true);
1446 static void print_header_json(struct perf_stat_config *config,
1447 struct target *_target __maybe_unused,
1448 struct evlist *evlist,
1449 int argc __maybe_unused,
1450 const char **argv __maybe_unused)
1452 if (config->metric_only)
1453 print_metric_headers(config, evlist, true);
1456 static void print_header(struct perf_stat_config *config,
1457 struct target *_target,
1458 struct evlist *evlist,
1459 int argc, const char **argv)
1461 static int num_print_iv;
1463 fflush(stdout);
1465 if (config->interval_clear)
1466 puts(CONSOLE_CLEAR);
1468 if (num_print_iv == 0 || config->interval_clear) {
1469 if (config->json_output)
1470 print_header_json(config, _target, evlist, argc, argv);
1471 else if (config->csv_output)
1472 print_header_csv(config, _target, evlist, argc, argv);
1473 else if (config->interval)
1474 print_header_interval_std(config, _target, evlist, argc, argv);
1475 else
1476 print_header_std(config, _target, evlist, argc, argv);
1479 if (num_print_iv++ == 25)
1480 num_print_iv = 0;
1483 static int get_precision(double num)
1485 if (num > 1)
1486 return 0;
1488 return lround(ceil(-log10(num)));
1491 static void print_table(struct perf_stat_config *config,
1492 FILE *output, int precision, double avg)
1494 char tmp[64];
1495 int idx, indent = 0;
1497 scnprintf(tmp, 64, " %17.*f", precision, avg);
1498 while (tmp[indent] == ' ')
1499 indent++;
1501 fprintf(output, "%*s# Table of individual measurements:\n", indent, "");
1503 for (idx = 0; idx < config->run_count; idx++) {
1504 double run = (double) config->walltime_run[idx] / NSEC_PER_SEC;
1505 int h, n = 1 + abs((int) (100.0 * (run - avg)/run) / 5);
1507 fprintf(output, " %17.*f (%+.*f) ",
1508 precision, run, precision, run - avg);
1510 for (h = 0; h < n; h++)
1511 fprintf(output, "#");
1513 fprintf(output, "\n");
1516 fprintf(output, "\n%*s# Final result:\n", indent, "");
1519 static double timeval2double(struct timeval *t)
1521 return t->tv_sec + (double) t->tv_usec/USEC_PER_SEC;
1524 static void print_footer(struct perf_stat_config *config)
1526 double avg = avg_stats(config->walltime_nsecs_stats) / NSEC_PER_SEC;
1527 FILE *output = config->output;
1529 if (config->interval || config->csv_output || config->json_output)
1530 return;
1532 if (!config->null_run)
1533 fprintf(output, "\n");
1535 if (config->run_count == 1) {
1536 fprintf(output, " %17.9f seconds time elapsed", avg);
1538 if (config->ru_display) {
1539 double ru_utime = timeval2double(&config->ru_data.ru_utime);
1540 double ru_stime = timeval2double(&config->ru_data.ru_stime);
1542 fprintf(output, "\n\n");
1543 fprintf(output, " %17.9f seconds user\n", ru_utime);
1544 fprintf(output, " %17.9f seconds sys\n", ru_stime);
1546 } else {
1547 double sd = stddev_stats(config->walltime_nsecs_stats) / NSEC_PER_SEC;
1549 * Display at most 2 more significant
1550 * digits than the stddev inaccuracy.
1552 int precision = get_precision(sd) + 2;
1554 if (config->walltime_run_table)
1555 print_table(config, output, precision, avg);
1557 fprintf(output, " %17.*f +- %.*f seconds time elapsed",
1558 precision, avg, precision, sd);
1560 print_noise_pct(config, sd, avg, /*before_metric=*/false);
1562 fprintf(output, "\n\n");
1564 if (config->print_free_counters_hint && sysctl__nmi_watchdog_enabled())
1565 fprintf(output,
1566 "Some events weren't counted. Try disabling the NMI watchdog:\n"
1567 " echo 0 > /proc/sys/kernel/nmi_watchdog\n"
1568 " perf stat ...\n"
1569 " echo 1 > /proc/sys/kernel/nmi_watchdog\n");
1571 if (config->print_mixed_hw_group_error)
1572 fprintf(output,
1573 "The events in group usually have to be from "
1574 "the same PMU. Try reorganizing the group.\n");
1577 static void print_percore(struct perf_stat_config *config,
1578 struct evsel *counter, struct outstate *os)
1580 bool metric_only = config->metric_only;
1581 FILE *output = config->output;
1582 struct cpu_aggr_map *core_map;
1583 int aggr_idx, core_map_len = 0;
1585 if (!config->aggr_map || !config->aggr_get_id)
1586 return;
1588 if (config->percore_show_thread)
1589 return print_counter(config, counter, os);
1592 * core_map will hold the aggr_cpu_id for the cores that have been
1593 * printed so that each core is printed just once.
1595 core_map = cpu_aggr_map__empty_new(config->aggr_map->nr);
1596 if (core_map == NULL) {
1597 fprintf(output, "Cannot allocate per-core aggr map for display\n");
1598 return;
1601 cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) {
1602 struct perf_cpu curr_cpu = config->aggr_map->map[aggr_idx].cpu;
1603 struct aggr_cpu_id core_id = aggr_cpu_id__core(curr_cpu, NULL);
1604 bool found = false;
1606 for (int i = 0; i < core_map_len; i++) {
1607 if (aggr_cpu_id__equal(&core_map->map[i], &core_id)) {
1608 found = true;
1609 break;
1612 if (found)
1613 continue;
1615 print_counter_aggrdata(config, counter, aggr_idx, os);
1617 core_map->map[core_map_len++] = core_id;
1619 free(core_map);
1621 if (metric_only)
1622 fputc('\n', output);
1625 static void print_cgroup_counter(struct perf_stat_config *config, struct evlist *evlist,
1626 struct outstate *os)
1628 struct evsel *counter;
1630 evlist__for_each_entry(evlist, counter) {
1631 if (os->cgrp != counter->cgrp) {
1632 if (os->cgrp != NULL)
1633 print_metric_end(config, os);
1635 os->cgrp = counter->cgrp;
1636 print_metric_begin(config, evlist, os, /*aggr_idx=*/0);
1639 print_counter(config, counter, os);
1641 if (os->cgrp)
1642 print_metric_end(config, os);
1645 static void disable_uniquify(struct evlist *evlist)
1647 struct evsel *counter;
1648 struct perf_pmu *last_pmu = NULL;
1649 bool first = true;
1651 evlist__for_each_entry(evlist, counter) {
1652 /* If PMUs vary then uniquify can be useful. */
1653 if (!first && counter->pmu != last_pmu)
1654 return;
1655 first = false;
1656 if (counter->pmu) {
1657 /* Allow uniquify for uncore PMUs. */
1658 if (!counter->pmu->is_core)
1659 return;
1660 /* Keep hybrid event names uniquified for clarity. */
1661 if (perf_pmus__num_core_pmus() > 1)
1662 return;
1665 evlist__for_each_entry_continue(evlist, counter) {
1666 counter->uniquified_name = true;
1670 void evlist__print_counters(struct evlist *evlist, struct perf_stat_config *config,
1671 struct target *_target, struct timespec *ts,
1672 int argc, const char **argv)
1674 bool metric_only = config->metric_only;
1675 int interval = config->interval;
1676 struct evsel *counter;
1677 char buf[64];
1678 struct outstate os = {
1679 .fh = config->output,
1680 .first = true,
1683 disable_uniquify(evlist);
1685 if (config->iostat_run)
1686 evlist->selected = evlist__first(evlist);
1688 if (interval) {
1689 os.prefix = buf;
1690 prepare_interval(config, buf, sizeof(buf), ts);
1693 print_header(config, _target, evlist, argc, argv);
1695 switch (config->aggr_mode) {
1696 case AGGR_CORE:
1697 case AGGR_CACHE:
1698 case AGGR_CLUSTER:
1699 case AGGR_DIE:
1700 case AGGR_SOCKET:
1701 case AGGR_NODE:
1702 if (config->cgroup_list)
1703 print_aggr_cgroup(config, evlist, &os);
1704 else
1705 print_aggr(config, evlist, &os);
1706 break;
1707 case AGGR_THREAD:
1708 case AGGR_GLOBAL:
1709 if (config->iostat_run) {
1710 iostat_print_counters(evlist, config, ts, buf,
1711 (iostat_print_counter_t)print_counter, &os);
1712 } else if (config->cgroup_list) {
1713 print_cgroup_counter(config, evlist, &os);
1714 } else {
1715 print_metric_begin(config, evlist, &os, /*aggr_idx=*/0);
1716 evlist__for_each_entry(evlist, counter) {
1717 print_counter(config, counter, &os);
1719 print_metric_end(config, &os);
1721 break;
1722 case AGGR_NONE:
1723 if (metric_only)
1724 print_no_aggr_metric(config, evlist, &os);
1725 else {
1726 evlist__for_each_entry(evlist, counter) {
1727 if (counter->percore)
1728 print_percore(config, counter, &os);
1729 else
1730 print_counter(config, counter, &os);
1733 break;
1734 case AGGR_MAX:
1735 case AGGR_UNSET:
1736 default:
1737 break;
1740 print_footer(config);
1742 fflush(config->output);