perf stat: Split print_cgroup() function
[platform/kernel/linux-starfive.git] / tools / perf / util / stat-display.c
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-hybrid.h"
23 #include "evlist-hybrid.h"
24
25 #define CNTR_NOT_SUPPORTED      "<not supported>"
26 #define CNTR_NOT_COUNTED        "<not counted>"
27
28 static void print_running_std(struct perf_stat_config *config, u64 run, u64 ena)
29 {
30         if (run != ena)
31                 fprintf(config->output, "  (%.2f%%)", 100.0 * run / ena);
32 }
33
34 static void print_running_csv(struct perf_stat_config *config, u64 run, u64 ena)
35 {
36         double enabled_percent = 100;
37
38         if (run != ena)
39                 enabled_percent = 100 * run / ena;
40         fprintf(config->output, "%s%" PRIu64 "%s%.2f",
41                 config->csv_sep, run, config->csv_sep, enabled_percent);
42 }
43
44 static void print_running_json(struct perf_stat_config *config, u64 run, u64 ena)
45 {
46         double enabled_percent = 100;
47
48         if (run != ena)
49                 enabled_percent = 100 * run / ena;
50         fprintf(config->output, "\"event-runtime\" : %" PRIu64 ", \"pcnt-running\" : %.2f, ",
51                 run, enabled_percent);
52 }
53
54 static void print_running(struct perf_stat_config *config,
55                           u64 run, u64 ena)
56 {
57         if (config->json_output)
58                 print_running_json(config, run, ena);
59         else if (config->csv_output)
60                 print_running_csv(config, run, ena);
61         else
62                 print_running_std(config, run, ena);
63 }
64
65 static void print_noise_pct_std(struct perf_stat_config *config,
66                                 double pct)
67 {
68         if (pct)
69                 fprintf(config->output, "  ( +-%6.2f%% )", pct);
70 }
71
72 static void print_noise_pct_csv(struct perf_stat_config *config,
73                                 double pct)
74 {
75         fprintf(config->output, "%s%.2f%%", config->csv_sep, pct);
76 }
77
78 static void print_noise_pct_json(struct perf_stat_config *config,
79                                  double pct)
80 {
81         fprintf(config->output, "\"variance\" : %.2f, ", pct);
82 }
83
84 static void print_noise_pct(struct perf_stat_config *config,
85                             double total, double avg)
86 {
87         double pct = rel_stddev_stats(total, avg);
88
89         if (config->json_output)
90                 print_noise_pct_json(config, pct);
91         else if (config->csv_output)
92                 print_noise_pct_csv(config, pct);
93         else
94                 print_noise_pct_std(config, pct);
95 }
96
97 static void print_noise(struct perf_stat_config *config,
98                         struct evsel *evsel, double avg)
99 {
100         struct perf_stat_evsel *ps;
101
102         if (config->run_count == 1)
103                 return;
104
105         ps = evsel->stats;
106         print_noise_pct(config, stddev_stats(&ps->res_stats), avg);
107 }
108
109 static void print_cgroup_std(struct perf_stat_config *config, const char *cgrp_name)
110 {
111         fprintf(config->output, " %s", cgrp_name);
112 }
113
114 static void print_cgroup_csv(struct perf_stat_config *config, const char *cgrp_name)
115 {
116         fprintf(config->output, "%s%s", config->csv_sep, cgrp_name);
117 }
118
119 static void print_cgroup_json(struct perf_stat_config *config, const char *cgrp_name)
120 {
121         fprintf(config->output, "\"cgroup\" : \"%s\", ", cgrp_name);
122 }
123
124 static void print_cgroup(struct perf_stat_config *config, struct evsel *evsel)
125 {
126         if (nr_cgroups) {
127                 const char *cgrp_name = evsel->cgrp ? evsel->cgrp->name  : "";
128
129                 if (config->json_output)
130                         print_cgroup_json(config, cgrp_name);
131                 if (config->csv_output)
132                         print_cgroup_csv(config, cgrp_name);
133                 else
134                         print_cgroup_std(config, cgrp_name);
135         }
136 }
137
138
139 static void aggr_printout(struct perf_stat_config *config,
140                           struct evsel *evsel, struct aggr_cpu_id id, int nr)
141 {
142
143
144         if (config->json_output && !config->interval)
145                 fprintf(config->output, "{");
146
147         switch (config->aggr_mode) {
148         case AGGR_CORE:
149                 if (config->json_output) {
150                         fprintf(config->output,
151                                 "\"core\" : \"S%d-D%d-C%d\", \"aggregate-number\" : %d, ",
152                                 id.socket,
153                                 id.die,
154                                 id.core,
155                                 nr);
156                 } else {
157                         fprintf(config->output, "S%d-D%d-C%*d%s%*d%s",
158                                 id.socket,
159                                 id.die,
160                                 config->csv_output ? 0 : -8,
161                                 id.core,
162                                 config->csv_sep,
163                                 config->csv_output ? 0 : 4,
164                                 nr,
165                                 config->csv_sep);
166                 }
167                 break;
168         case AGGR_DIE:
169                 if (config->json_output) {
170                         fprintf(config->output,
171                                 "\"die\" : \"S%d-D%d\", \"aggregate-number\" : %d, ",
172                                 id.socket,
173                                 id.die,
174                                 nr);
175                 } else {
176                         fprintf(config->output, "S%d-D%*d%s%*d%s",
177                                 id.socket,
178                                 config->csv_output ? 0 : -8,
179                                 id.die,
180                                 config->csv_sep,
181                                 config->csv_output ? 0 : 4,
182                                 nr,
183                                 config->csv_sep);
184                 }
185                 break;
186         case AGGR_SOCKET:
187                 if (config->json_output) {
188                         fprintf(config->output,
189                                 "\"socket\" : \"S%d\", \"aggregate-number\" : %d, ",
190                                 id.socket,
191                                 nr);
192                 } else {
193                         fprintf(config->output, "S%*d%s%*d%s",
194                                 config->csv_output ? 0 : -5,
195                                 id.socket,
196                                 config->csv_sep,
197                                 config->csv_output ? 0 : 4,
198                                 nr,
199                                 config->csv_sep);
200                 }
201                 break;
202         case AGGR_NODE:
203                 if (config->json_output) {
204                         fprintf(config->output, "\"node\" : \"N%d\", \"aggregate-number\" : %d, ",
205                                 id.node,
206                                 nr);
207                 } else {
208                         fprintf(config->output, "N%*d%s%*d%s",
209                                 config->csv_output ? 0 : -5,
210                                 id.node,
211                                 config->csv_sep,
212                                 config->csv_output ? 0 : 4,
213                                 nr,
214                                 config->csv_sep);
215                 }
216                 break;
217         case AGGR_NONE:
218                 if (config->json_output) {
219                         if (evsel->percore && !config->percore_show_thread) {
220                                 fprintf(config->output, "\"core\" : \"S%d-D%d-C%d\"",
221                                         id.socket,
222                                         id.die,
223                                         id.core);
224                         } else if (id.cpu.cpu > -1) {
225                                 fprintf(config->output, "\"cpu\" : \"%d\", ",
226                                         id.cpu.cpu);
227                         }
228                 } else {
229                         if (evsel->percore && !config->percore_show_thread) {
230                                 fprintf(config->output, "S%d-D%d-C%*d%s",
231                                         id.socket,
232                                         id.die,
233                                         config->csv_output ? 0 : -3,
234                                         id.core, config->csv_sep);
235                         } else if (id.cpu.cpu > -1) {
236                                 fprintf(config->output, "CPU%*d%s",
237                                         config->csv_output ? 0 : -7,
238                                         id.cpu.cpu, config->csv_sep);
239                         }
240                 }
241                 break;
242         case AGGR_THREAD:
243                 if (config->json_output) {
244                         fprintf(config->output, "\"thread\" : \"%s-%d\", ",
245                                 perf_thread_map__comm(evsel->core.threads, id.thread_idx),
246                                 perf_thread_map__pid(evsel->core.threads, id.thread_idx));
247                 } else {
248                         fprintf(config->output, "%*s-%*d%s",
249                                 config->csv_output ? 0 : 16,
250                                 perf_thread_map__comm(evsel->core.threads, id.thread_idx),
251                                 config->csv_output ? 0 : -8,
252                                 perf_thread_map__pid(evsel->core.threads, id.thread_idx),
253                                 config->csv_sep);
254                 }
255                 break;
256         case AGGR_GLOBAL:
257         case AGGR_UNSET:
258         case AGGR_MAX:
259         default:
260                 break;
261         }
262 }
263
264 struct outstate {
265         FILE *fh;
266         bool newline;
267         const char *prefix;
268         int  nfields;
269         int  nr;
270         struct aggr_cpu_id id;
271         struct evsel *evsel;
272 };
273
274 #define METRIC_LEN  38
275
276 static void new_line_std(struct perf_stat_config *config __maybe_unused,
277                          void *ctx)
278 {
279         struct outstate *os = ctx;
280
281         os->newline = true;
282 }
283
284 static void do_new_line_std(struct perf_stat_config *config,
285                             struct outstate *os)
286 {
287         fputc('\n', os->fh);
288         fputs(os->prefix, os->fh);
289         aggr_printout(config, os->evsel, os->id, os->nr);
290         if (config->aggr_mode == AGGR_NONE)
291                 fprintf(os->fh, "        ");
292         fprintf(os->fh, "                                                 ");
293 }
294
295 static void print_metric_std(struct perf_stat_config *config,
296                              void *ctx, const char *color, const char *fmt,
297                              const char *unit, double val)
298 {
299         struct outstate *os = ctx;
300         FILE *out = os->fh;
301         int n;
302         bool newline = os->newline;
303
304         os->newline = false;
305
306         if (unit == NULL || fmt == NULL) {
307                 fprintf(out, "%-*s", METRIC_LEN, "");
308                 return;
309         }
310
311         if (newline)
312                 do_new_line_std(config, os);
313
314         n = fprintf(out, " # ");
315         if (color)
316                 n += color_fprintf(out, color, fmt, val);
317         else
318                 n += fprintf(out, fmt, val);
319         fprintf(out, " %-*s", METRIC_LEN - n - 1, unit);
320 }
321
322 static void new_line_csv(struct perf_stat_config *config, void *ctx)
323 {
324         struct outstate *os = ctx;
325         int i;
326
327         fputc('\n', os->fh);
328         if (os->prefix)
329                 fprintf(os->fh, "%s", os->prefix);
330         aggr_printout(config, os->evsel, os->id, os->nr);
331         for (i = 0; i < os->nfields; i++)
332                 fputs(config->csv_sep, os->fh);
333 }
334
335 static void print_metric_csv(struct perf_stat_config *config __maybe_unused,
336                              void *ctx,
337                              const char *color __maybe_unused,
338                              const char *fmt, const char *unit, double val)
339 {
340         struct outstate *os = ctx;
341         FILE *out = os->fh;
342         char buf[64], *vals, *ends;
343
344         if (unit == NULL || fmt == NULL) {
345                 fprintf(out, "%s%s", config->csv_sep, config->csv_sep);
346                 return;
347         }
348         snprintf(buf, sizeof(buf), fmt, val);
349         ends = vals = skip_spaces(buf);
350         while (isdigit(*ends) || *ends == '.')
351                 ends++;
352         *ends = 0;
353         fprintf(out, "%s%s%s%s", config->csv_sep, vals, config->csv_sep, skip_spaces(unit));
354 }
355
356 static void print_metric_json(struct perf_stat_config *config __maybe_unused,
357                              void *ctx,
358                              const char *color __maybe_unused,
359                              const char *fmt __maybe_unused,
360                              const char *unit, double val)
361 {
362         struct outstate *os = ctx;
363         FILE *out = os->fh;
364
365         fprintf(out, "\"metric-value\" : %f, ", val);
366         fprintf(out, "\"metric-unit\" : \"%s\"", unit);
367         if (!config->metric_only)
368                 fprintf(out, "}");
369 }
370
371 static void new_line_json(struct perf_stat_config *config, void *ctx)
372 {
373         struct outstate *os = ctx;
374
375         fputc('\n', os->fh);
376         if (os->prefix)
377                 fprintf(os->fh, "%s", os->prefix);
378         aggr_printout(config, os->evsel, os->id, os->nr);
379 }
380
381 /* Filter out some columns that don't work well in metrics only mode */
382
383 static bool valid_only_metric(const char *unit)
384 {
385         if (!unit)
386                 return false;
387         if (strstr(unit, "/sec") ||
388             strstr(unit, "CPUs utilized"))
389                 return false;
390         return true;
391 }
392
393 static const char *fixunit(char *buf, struct evsel *evsel,
394                            const char *unit)
395 {
396         if (!strncmp(unit, "of all", 6)) {
397                 snprintf(buf, 1024, "%s %s", evsel__name(evsel),
398                          unit);
399                 return buf;
400         }
401         return unit;
402 }
403
404 static void print_metric_only(struct perf_stat_config *config,
405                               void *ctx, const char *color, const char *fmt,
406                               const char *unit, double val)
407 {
408         struct outstate *os = ctx;
409         FILE *out = os->fh;
410         char buf[1024], str[1024];
411         unsigned mlen = config->metric_only_len;
412
413         if (!valid_only_metric(unit))
414                 return;
415         unit = fixunit(buf, os->evsel, unit);
416         if (mlen < strlen(unit))
417                 mlen = strlen(unit) + 1;
418
419         if (color)
420                 mlen += strlen(color) + sizeof(PERF_COLOR_RESET) - 1;
421
422         color_snprintf(str, sizeof(str), color ?: "", fmt, val);
423         fprintf(out, "%*s ", mlen, str);
424 }
425
426 static void print_metric_only_csv(struct perf_stat_config *config __maybe_unused,
427                                   void *ctx, const char *color __maybe_unused,
428                                   const char *fmt,
429                                   const char *unit, double val)
430 {
431         struct outstate *os = ctx;
432         FILE *out = os->fh;
433         char buf[64], *vals, *ends;
434         char tbuf[1024];
435
436         if (!valid_only_metric(unit))
437                 return;
438         unit = fixunit(tbuf, os->evsel, unit);
439         snprintf(buf, sizeof buf, fmt, val);
440         ends = vals = skip_spaces(buf);
441         while (isdigit(*ends) || *ends == '.')
442                 ends++;
443         *ends = 0;
444         fprintf(out, "%s%s", vals, config->csv_sep);
445 }
446
447 static void print_metric_only_json(struct perf_stat_config *config __maybe_unused,
448                                   void *ctx, const char *color __maybe_unused,
449                                   const char *fmt,
450                                   const char *unit, double val)
451 {
452         struct outstate *os = ctx;
453         FILE *out = os->fh;
454         char buf[64], *vals, *ends;
455         char tbuf[1024];
456
457         if (!valid_only_metric(unit))
458                 return;
459         unit = fixunit(tbuf, os->evsel, unit);
460         snprintf(buf, sizeof(buf), fmt, val);
461         ends = vals = skip_spaces(buf);
462         while (isdigit(*ends) || *ends == '.')
463                 ends++;
464         *ends = 0;
465         fprintf(out, "{\"metric-value\" : \"%s\"}", vals);
466 }
467
468 static void new_line_metric(struct perf_stat_config *config __maybe_unused,
469                             void *ctx __maybe_unused)
470 {
471 }
472
473 static void print_metric_header(struct perf_stat_config *config,
474                                 void *ctx, const char *color __maybe_unused,
475                                 const char *fmt __maybe_unused,
476                                 const char *unit, double val __maybe_unused)
477 {
478         struct outstate *os = ctx;
479         char tbuf[1024];
480
481         /* In case of iostat, print metric header for first root port only */
482         if (config->iostat_run &&
483             os->evsel->priv != os->evsel->evlist->selected->priv)
484                 return;
485
486         if (!valid_only_metric(unit))
487                 return;
488         unit = fixunit(tbuf, os->evsel, unit);
489
490         if (config->json_output)
491                 fprintf(os->fh, "{\"unit\" : \"%s\"}", unit);
492         else if (config->csv_output)
493                 fprintf(os->fh, "%s%s", unit, config->csv_sep);
494         else
495                 fprintf(os->fh, "%*s ", config->metric_only_len, unit);
496 }
497
498 static void abs_printout(struct perf_stat_config *config,
499                          struct aggr_cpu_id id, int nr, struct evsel *evsel, double avg)
500 {
501         FILE *output = config->output;
502         double sc =  evsel->scale;
503         const char *fmt;
504
505         if (config->csv_output) {
506                 fmt = floor(sc) != sc ?  "%.2f%s" : "%.0f%s";
507         } else {
508                 if (config->big_num)
509                         fmt = floor(sc) != sc ? "%'18.2f%s" : "%'18.0f%s";
510                 else
511                         fmt = floor(sc) != sc ? "%18.2f%s" : "%18.0f%s";
512         }
513
514         aggr_printout(config, evsel, id, nr);
515
516         if (config->json_output)
517                 fprintf(output, "\"counter-value\" : \"%f\", ", avg);
518         else
519                 fprintf(output, fmt, avg, config->csv_sep);
520
521         if (config->json_output) {
522                 if (evsel->unit) {
523                         fprintf(output, "\"unit\" : \"%s\", ",
524                                 evsel->unit);
525                 }
526         } else {
527                 if (evsel->unit)
528                         fprintf(output, "%-*s%s",
529                                 config->csv_output ? 0 : config->unit_width,
530                                 evsel->unit, config->csv_sep);
531         }
532
533         if (config->json_output)
534                 fprintf(output, "\"event\" : \"%s\", ", evsel__name(evsel));
535         else
536                 fprintf(output, "%-*s", config->csv_output ? 0 : 32, evsel__name(evsel));
537
538         print_cgroup(config, evsel);
539 }
540
541 static bool is_mixed_hw_group(struct evsel *counter)
542 {
543         struct evlist *evlist = counter->evlist;
544         u32 pmu_type = counter->core.attr.type;
545         struct evsel *pos;
546
547         if (counter->core.nr_members < 2)
548                 return false;
549
550         evlist__for_each_entry(evlist, pos) {
551                 /* software events can be part of any hardware group */
552                 if (pos->core.attr.type == PERF_TYPE_SOFTWARE)
553                         continue;
554                 if (pmu_type == PERF_TYPE_SOFTWARE) {
555                         pmu_type = pos->core.attr.type;
556                         continue;
557                 }
558                 if (pmu_type != pos->core.attr.type)
559                         return true;
560         }
561
562         return false;
563 }
564
565 static void printout(struct perf_stat_config *config, struct aggr_cpu_id id, int nr,
566                      struct evsel *counter, double uval,
567                      char *prefix, u64 run, u64 ena, double noise,
568                      struct runtime_stat *st, int map_idx)
569 {
570         struct perf_stat_output_ctx out;
571         struct outstate os = {
572                 .fh = config->output,
573                 .prefix = prefix ? prefix : "",
574                 .id = id,
575                 .nr = nr,
576                 .evsel = counter,
577         };
578         print_metric_t pm;
579         new_line_t nl;
580
581         if (config->csv_output) {
582                 static const int aggr_fields[AGGR_MAX] = {
583                         [AGGR_NONE] = 1,
584                         [AGGR_GLOBAL] = 0,
585                         [AGGR_SOCKET] = 2,
586                         [AGGR_DIE] = 2,
587                         [AGGR_CORE] = 2,
588                         [AGGR_THREAD] = 1,
589                         [AGGR_UNSET] = 0,
590                         [AGGR_NODE] = 1,
591                 };
592
593                 pm = config->metric_only ? print_metric_only_csv : print_metric_csv;
594                 nl = config->metric_only ? new_line_metric : new_line_csv;
595                 os.nfields = 3 + aggr_fields[config->aggr_mode] + (counter->cgrp ? 1 : 0);
596         } else if (config->json_output) {
597                 pm = config->metric_only ? print_metric_only_json : print_metric_json;
598                 nl = config->metric_only ? new_line_metric : new_line_json;
599         } else {
600                 pm = config->metric_only ? print_metric_only : print_metric_std;
601                 nl = config->metric_only ? new_line_metric : new_line_std;
602         }
603
604         if (!config->no_csv_summary && config->csv_output &&
605             config->summary && !config->interval && !config->metric_only) {
606                 fprintf(config->output, "%16s%s", "summary", config->csv_sep);
607         }
608
609         if (run == 0 || ena == 0 || counter->counts->scaled == -1) {
610                 if (config->metric_only) {
611                         pm(config, &os, NULL, "", "", 0);
612                         return;
613                 }
614                 aggr_printout(config, counter, id, nr);
615
616                 if (config->json_output) {
617                         fprintf(config->output, "\"counter-value\" : \"%s\", ",
618                                         counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED);
619                 } else {
620                         fprintf(config->output, "%*s%s",
621                                 config->csv_output ? 0 : 18,
622                                 counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
623                                 config->csv_sep);
624                 }
625
626                 if (counter->supported) {
627                         if (!evlist__has_hybrid(counter->evlist)) {
628                                 config->print_free_counters_hint = 1;
629                                 if (is_mixed_hw_group(counter))
630                                         config->print_mixed_hw_group_error = 1;
631                         }
632                 }
633
634                 if (config->json_output) {
635                         fprintf(config->output, "\"unit\" : \"%s\", ", counter->unit);
636                 } else {
637                         fprintf(config->output, "%-*s%s",
638                                 config->csv_output ? 0 : config->unit_width,
639                                 counter->unit, config->csv_sep);
640                 }
641
642                 if (config->json_output) {
643                         fprintf(config->output, "\"event\" : \"%s\", ",
644                                 evsel__name(counter));
645                 } else {
646                         fprintf(config->output, "%*s",
647                                  config->csv_output ? 0 : -25, evsel__name(counter));
648                 }
649
650                 print_cgroup(config, counter);
651
652                 if (!config->csv_output && !config->json_output)
653                         pm(config, &os, NULL, NULL, "", 0);
654                 print_noise(config, counter, noise);
655                 print_running(config, run, ena);
656                 if (config->csv_output || config->json_output)
657                         pm(config, &os, NULL, NULL, "", 0);
658                 return;
659         }
660
661         if (!config->metric_only)
662                 abs_printout(config, id, nr, counter, uval);
663
664         out.print_metric = pm;
665         out.new_line = nl;
666         out.ctx = &os;
667         out.force_header = false;
668
669         if (config->csv_output && !config->metric_only) {
670                 print_noise(config, counter, noise);
671                 print_running(config, run, ena);
672         } else if (config->json_output && !config->metric_only) {
673                 print_noise(config, counter, noise);
674                 print_running(config, run, ena);
675         }
676
677         perf_stat__print_shadow_stats(config, counter, uval, map_idx,
678                                 &out, &config->metric_events, st);
679         if (!config->csv_output && !config->metric_only && !config->json_output) {
680                 print_noise(config, counter, noise);
681                 print_running(config, run, ena);
682         }
683 }
684
685 static void uniquify_event_name(struct evsel *counter)
686 {
687         char *new_name;
688         char *config;
689         int ret = 0;
690
691         if (counter->uniquified_name || counter->use_config_name ||
692             !counter->pmu_name || !strncmp(counter->name, counter->pmu_name,
693                                            strlen(counter->pmu_name)))
694                 return;
695
696         config = strchr(counter->name, '/');
697         if (config) {
698                 if (asprintf(&new_name,
699                              "%s%s", counter->pmu_name, config) > 0) {
700                         free(counter->name);
701                         counter->name = new_name;
702                 }
703         } else {
704                 if (evsel__is_hybrid(counter)) {
705                         ret = asprintf(&new_name, "%s/%s/",
706                                        counter->pmu_name, counter->name);
707                 } else {
708                         ret = asprintf(&new_name, "%s [%s]",
709                                        counter->name, counter->pmu_name);
710                 }
711
712                 if (ret) {
713                         free(counter->name);
714                         counter->name = new_name;
715                 }
716         }
717
718         counter->uniquified_name = true;
719 }
720
721 static bool hybrid_uniquify(struct evsel *evsel, struct perf_stat_config *config)
722 {
723         return evsel__is_hybrid(evsel) && !config->hybrid_merge;
724 }
725
726 static void uniquify_counter(struct perf_stat_config *config, struct evsel *counter)
727 {
728         if (config->no_merge || hybrid_uniquify(counter, config))
729                 uniquify_event_name(counter);
730 }
731
732 static void print_counter_aggrdata(struct perf_stat_config *config,
733                                    struct evsel *counter, int s,
734                                    char *prefix, bool metric_only,
735                                    bool *first)
736 {
737         FILE *output = config->output;
738         u64 ena, run, val;
739         double uval;
740         struct perf_stat_evsel *ps = counter->stats;
741         struct perf_stat_aggr *aggr = &ps->aggr[s];
742         struct aggr_cpu_id id = config->aggr_map->map[s];
743         double avg = aggr->counts.val;
744
745         if (counter->supported && aggr->nr == 0)
746                 return;
747
748         uniquify_counter(config, counter);
749
750         val = aggr->counts.val;
751         ena = aggr->counts.ena;
752         run = aggr->counts.run;
753
754         if (*first && metric_only) {
755                 *first = false;
756                 aggr_printout(config, counter, id, aggr->nr);
757         }
758         if (prefix && !metric_only)
759                 fprintf(output, "%s", prefix);
760
761         uval = val * counter->scale;
762
763         printout(config, id, aggr->nr, counter, uval,
764                  prefix, run, ena, avg, &rt_stat, s);
765
766         if (!metric_only)
767                 fputc('\n', output);
768 }
769
770 static void print_aggr(struct perf_stat_config *config,
771                        struct evlist *evlist,
772                        char *prefix)
773 {
774         bool metric_only = config->metric_only;
775         FILE *output = config->output;
776         struct evsel *counter;
777         int s;
778         bool first;
779
780         if (!config->aggr_map || !config->aggr_get_id)
781                 return;
782
783         /*
784          * With metric_only everything is on a single line.
785          * Without each counter has its own line.
786          */
787         for (s = 0; s < config->aggr_map->nr; s++) {
788                 if (metric_only) {
789                         if (prefix)
790                                 fprintf(output, "%s", prefix);
791                         else if (config->summary && !config->no_csv_summary &&
792                                  config->csv_output && !config->interval)
793                                 fprintf(output, "%16s%s", "summary", config->csv_sep);
794                 }
795
796                 first = true;
797                 evlist__for_each_entry(evlist, counter) {
798                         if (counter->merged_stat)
799                                 continue;
800
801                         print_counter_aggrdata(config, counter, s,
802                                                prefix, metric_only,
803                                                &first);
804                 }
805                 if (metric_only)
806                         fputc('\n', output);
807         }
808 }
809
810 static void print_counter(struct perf_stat_config *config,
811                           struct evsel *counter, char *prefix)
812 {
813         bool metric_only = config->metric_only;
814         bool first = false;
815         int s;
816
817         /* AGGR_THREAD doesn't have config->aggr_get_id */
818         if (!config->aggr_map)
819                 return;
820
821         if (counter->merged_stat)
822                 return;
823
824         for (s = 0; s < config->aggr_map->nr; s++) {
825                 print_counter_aggrdata(config, counter, s,
826                                        prefix, metric_only,
827                                        &first);
828         }
829 }
830
831 static void print_no_aggr_metric(struct perf_stat_config *config,
832                                  struct evlist *evlist,
833                                  char *prefix)
834 {
835         int all_idx;
836         struct perf_cpu cpu;
837
838         perf_cpu_map__for_each_cpu(cpu, all_idx, evlist->core.user_requested_cpus) {
839                 struct evsel *counter;
840                 bool first = true;
841
842                 evlist__for_each_entry(evlist, counter) {
843                         u64 ena, run, val;
844                         double uval;
845                         struct aggr_cpu_id id;
846                         struct perf_stat_evsel *ps = counter->stats;
847                         int counter_idx = perf_cpu_map__idx(evsel__cpus(counter), cpu);
848
849                         if (counter_idx < 0)
850                                 continue;
851
852                         id = aggr_cpu_id__cpu(cpu, /*data=*/NULL);
853                         if (first) {
854                                 if (prefix)
855                                         fputs(prefix, config->output);
856                                 aggr_printout(config, counter, id, 0);
857                                 first = false;
858                         }
859                         val = ps->aggr[counter_idx].counts.val;
860                         ena = ps->aggr[counter_idx].counts.ena;
861                         run = ps->aggr[counter_idx].counts.run;
862
863                         uval = val * counter->scale;
864                         printout(config, id, 0, counter, uval, prefix,
865                                  run, ena, 1.0, &rt_stat, counter_idx);
866                 }
867                 if (!first)
868                         fputc('\n', config->output);
869         }
870 }
871
872 static int aggr_header_lens[] = {
873         [AGGR_CORE] = 24,
874         [AGGR_DIE] = 18,
875         [AGGR_SOCKET] = 12,
876         [AGGR_NONE] = 6,
877         [AGGR_THREAD] = 24,
878         [AGGR_NODE] = 6,
879         [AGGR_GLOBAL] = 0,
880 };
881
882 static const char *aggr_header_csv[] = {
883         [AGGR_CORE]     =       "core,cpus,",
884         [AGGR_DIE]      =       "die,cpus,",
885         [AGGR_SOCKET]   =       "socket,cpus,",
886         [AGGR_NONE]     =       "cpu,",
887         [AGGR_THREAD]   =       "comm-pid,",
888         [AGGR_NODE]     =       "node,",
889         [AGGR_GLOBAL]   =       ""
890 };
891
892 static void print_metric_headers(struct perf_stat_config *config,
893                                  struct evlist *evlist,
894                                  const char *prefix, bool no_indent)
895 {
896         struct evsel *counter;
897         struct outstate os = {
898                 .fh = config->output
899         };
900         struct perf_stat_output_ctx out = {
901                 .ctx = &os,
902                 .print_metric = print_metric_header,
903                 .new_line = new_line_metric,
904                 .force_header = true,
905         };
906
907         if (prefix && !config->json_output)
908                 fprintf(config->output, "%s", prefix);
909
910         if (!config->csv_output && !config->json_output && !no_indent)
911                 fprintf(config->output, "%*s",
912                         aggr_header_lens[config->aggr_mode], "");
913         if (config->csv_output) {
914                 if (config->interval)
915                         fputs("time,", config->output);
916                 if (!config->iostat_run)
917                         fputs(aggr_header_csv[config->aggr_mode], config->output);
918         }
919         if (config->json_output) {
920                 if (config->interval)
921                         fputs("{\"unit\" : \"sec\"}", config->output);
922         }
923         if (config->iostat_run)
924                 iostat_print_header_prefix(config);
925
926         /* Print metrics headers only */
927         evlist__for_each_entry(evlist, counter) {
928                 os.evsel = counter;
929
930                 perf_stat__print_shadow_stats(config, counter, 0,
931                                               0,
932                                               &out,
933                                               &config->metric_events,
934                                               &rt_stat);
935         }
936         fputc('\n', config->output);
937 }
938
939 static void print_interval(struct perf_stat_config *config,
940                            struct evlist *evlist,
941                            char *prefix, struct timespec *ts)
942 {
943         bool metric_only = config->metric_only;
944         unsigned int unit_width = config->unit_width;
945         FILE *output = config->output;
946         static int num_print_interval;
947
948         if (config->interval_clear && isatty(fileno(output)))
949                 puts(CONSOLE_CLEAR);
950
951         if (!config->iostat_run && !config->json_output)
952                 sprintf(prefix, "%6lu.%09lu%s", (unsigned long) ts->tv_sec,
953                                  ts->tv_nsec, config->csv_sep);
954         if (!config->iostat_run && config->json_output && !config->metric_only)
955                 sprintf(prefix, "{\"interval\" : %lu.%09lu, ", (unsigned long)
956                                  ts->tv_sec, ts->tv_nsec);
957         if (!config->iostat_run && config->json_output && config->metric_only)
958                 sprintf(prefix, "{\"interval\" : %lu.%09lu}", (unsigned long)
959                                  ts->tv_sec, ts->tv_nsec);
960
961         if ((num_print_interval == 0 || config->interval_clear) &&
962                         !config->csv_output && !config->json_output) {
963                 switch (config->aggr_mode) {
964                 case AGGR_NODE:
965                         fprintf(output, "#           time node   cpus");
966                         if (!metric_only)
967                                 fprintf(output, "             counts %*s events\n", unit_width, "unit");
968                         break;
969                 case AGGR_SOCKET:
970                         fprintf(output, "#           time socket cpus");
971                         if (!metric_only)
972                                 fprintf(output, "             counts %*s events\n", unit_width, "unit");
973                         break;
974                 case AGGR_DIE:
975                         fprintf(output, "#           time die          cpus");
976                         if (!metric_only)
977                                 fprintf(output, "             counts %*s events\n", unit_width, "unit");
978                         break;
979                 case AGGR_CORE:
980                         fprintf(output, "#           time core            cpus");
981                         if (!metric_only)
982                                 fprintf(output, "             counts %*s events\n", unit_width, "unit");
983                         break;
984                 case AGGR_NONE:
985                         fprintf(output, "#           time CPU    ");
986                         if (!metric_only)
987                                 fprintf(output, "                counts %*s events\n", unit_width, "unit");
988                         break;
989                 case AGGR_THREAD:
990                         fprintf(output, "#           time             comm-pid");
991                         if (!metric_only)
992                                 fprintf(output, "                  counts %*s events\n", unit_width, "unit");
993                         break;
994                 case AGGR_GLOBAL:
995                 default:
996                         if (!config->iostat_run) {
997                                 fprintf(output, "#           time");
998                                 if (!metric_only)
999                                         fprintf(output, "             counts %*s events\n", unit_width, "unit");
1000                         }
1001                 case AGGR_UNSET:
1002                 case AGGR_MAX:
1003                         break;
1004                 }
1005         }
1006
1007         if ((num_print_interval == 0 || config->interval_clear) && metric_only)
1008                 print_metric_headers(config, evlist, " ", true);
1009         if (++num_print_interval == 25)
1010                 num_print_interval = 0;
1011 }
1012
1013 static void print_header(struct perf_stat_config *config,
1014                          struct target *_target,
1015                          int argc, const char **argv)
1016 {
1017         FILE *output = config->output;
1018         int i;
1019
1020         fflush(stdout);
1021
1022         if (!config->csv_output && !config->json_output) {
1023                 fprintf(output, "\n");
1024                 fprintf(output, " Performance counter stats for ");
1025                 if (_target->bpf_str)
1026                         fprintf(output, "\'BPF program(s) %s", _target->bpf_str);
1027                 else if (_target->system_wide)
1028                         fprintf(output, "\'system wide");
1029                 else if (_target->cpu_list)
1030                         fprintf(output, "\'CPU(s) %s", _target->cpu_list);
1031                 else if (!target__has_task(_target)) {
1032                         fprintf(output, "\'%s", argv ? argv[0] : "pipe");
1033                         for (i = 1; argv && (i < argc); i++)
1034                                 fprintf(output, " %s", argv[i]);
1035                 } else if (_target->pid)
1036                         fprintf(output, "process id \'%s", _target->pid);
1037                 else
1038                         fprintf(output, "thread id \'%s", _target->tid);
1039
1040                 fprintf(output, "\'");
1041                 if (config->run_count > 1)
1042                         fprintf(output, " (%d runs)", config->run_count);
1043                 fprintf(output, ":\n\n");
1044         }
1045 }
1046
1047 static int get_precision(double num)
1048 {
1049         if (num > 1)
1050                 return 0;
1051
1052         return lround(ceil(-log10(num)));
1053 }
1054
1055 static void print_table(struct perf_stat_config *config,
1056                         FILE *output, int precision, double avg)
1057 {
1058         char tmp[64];
1059         int idx, indent = 0;
1060
1061         scnprintf(tmp, 64, " %17.*f", precision, avg);
1062         while (tmp[indent] == ' ')
1063                 indent++;
1064
1065         fprintf(output, "%*s# Table of individual measurements:\n", indent, "");
1066
1067         for (idx = 0; idx < config->run_count; idx++) {
1068                 double run = (double) config->walltime_run[idx] / NSEC_PER_SEC;
1069                 int h, n = 1 + abs((int) (100.0 * (run - avg)/run) / 5);
1070
1071                 fprintf(output, " %17.*f (%+.*f) ",
1072                         precision, run, precision, run - avg);
1073
1074                 for (h = 0; h < n; h++)
1075                         fprintf(output, "#");
1076
1077                 fprintf(output, "\n");
1078         }
1079
1080         fprintf(output, "\n%*s# Final result:\n", indent, "");
1081 }
1082
1083 static double timeval2double(struct timeval *t)
1084 {
1085         return t->tv_sec + (double) t->tv_usec/USEC_PER_SEC;
1086 }
1087
1088 static void print_footer(struct perf_stat_config *config)
1089 {
1090         double avg = avg_stats(config->walltime_nsecs_stats) / NSEC_PER_SEC;
1091         FILE *output = config->output;
1092
1093         if (!config->null_run)
1094                 fprintf(output, "\n");
1095
1096         if (config->run_count == 1) {
1097                 fprintf(output, " %17.9f seconds time elapsed", avg);
1098
1099                 if (config->ru_display) {
1100                         double ru_utime = timeval2double(&config->ru_data.ru_utime);
1101                         double ru_stime = timeval2double(&config->ru_data.ru_stime);
1102
1103                         fprintf(output, "\n\n");
1104                         fprintf(output, " %17.9f seconds user\n", ru_utime);
1105                         fprintf(output, " %17.9f seconds sys\n", ru_stime);
1106                 }
1107         } else {
1108                 double sd = stddev_stats(config->walltime_nsecs_stats) / NSEC_PER_SEC;
1109                 /*
1110                  * Display at most 2 more significant
1111                  * digits than the stddev inaccuracy.
1112                  */
1113                 int precision = get_precision(sd) + 2;
1114
1115                 if (config->walltime_run_table)
1116                         print_table(config, output, precision, avg);
1117
1118                 fprintf(output, " %17.*f +- %.*f seconds time elapsed",
1119                         precision, avg, precision, sd);
1120
1121                 print_noise_pct(config, sd, avg);
1122         }
1123         fprintf(output, "\n\n");
1124
1125         if (config->print_free_counters_hint && sysctl__nmi_watchdog_enabled())
1126                 fprintf(output,
1127 "Some events weren't counted. Try disabling the NMI watchdog:\n"
1128 "       echo 0 > /proc/sys/kernel/nmi_watchdog\n"
1129 "       perf stat ...\n"
1130 "       echo 1 > /proc/sys/kernel/nmi_watchdog\n");
1131
1132         if (config->print_mixed_hw_group_error)
1133                 fprintf(output,
1134                         "The events in group usually have to be from "
1135                         "the same PMU. Try reorganizing the group.\n");
1136 }
1137
1138 static void print_percore(struct perf_stat_config *config,
1139                           struct evsel *counter, char *prefix)
1140 {
1141         bool metric_only = config->metric_only;
1142         FILE *output = config->output;
1143         struct cpu_aggr_map *core_map;
1144         int s, c, i;
1145         bool first = true;
1146
1147         if (!config->aggr_map || !config->aggr_get_id)
1148                 return;
1149
1150         if (config->percore_show_thread)
1151                 return print_counter(config, counter, prefix);
1152
1153         core_map = cpu_aggr_map__empty_new(config->aggr_map->nr);
1154         if (core_map == NULL) {
1155                 fprintf(output, "Cannot allocate per-core aggr map for display\n");
1156                 return;
1157         }
1158
1159         for (s = 0, c = 0; s < config->aggr_map->nr; s++) {
1160                 struct perf_cpu curr_cpu = config->aggr_map->map[s].cpu;
1161                 struct aggr_cpu_id core_id = aggr_cpu_id__core(curr_cpu, NULL);
1162                 bool found = false;
1163
1164                 for (i = 0; i < c; i++) {
1165                         if (aggr_cpu_id__equal(&core_map->map[i], &core_id)) {
1166                                 found = true;
1167                                 break;
1168                         }
1169                 }
1170                 if (found)
1171                         continue;
1172
1173                 if (prefix && metric_only)
1174                         fprintf(output, "%s", prefix);
1175
1176                 print_counter_aggrdata(config, counter, s,
1177                                        prefix, metric_only, &first);
1178
1179                 core_map->map[c++] = core_id;
1180         }
1181         free(core_map);
1182
1183         if (metric_only)
1184                 fputc('\n', output);
1185 }
1186
1187 void evlist__print_counters(struct evlist *evlist, struct perf_stat_config *config,
1188                             struct target *_target, struct timespec *ts, int argc, const char **argv)
1189 {
1190         bool metric_only = config->metric_only;
1191         int interval = config->interval;
1192         struct evsel *counter;
1193         char buf[64], *prefix = NULL;
1194
1195         if (config->iostat_run)
1196                 evlist->selected = evlist__first(evlist);
1197
1198         if (interval)
1199                 print_interval(config, evlist, prefix = buf, ts);
1200         else
1201                 print_header(config, _target, argc, argv);
1202
1203         if (metric_only) {
1204                 static int num_print_iv;
1205
1206                 if (num_print_iv == 0 && !interval)
1207                         print_metric_headers(config, evlist, prefix, false);
1208                 if (num_print_iv++ == 25)
1209                         num_print_iv = 0;
1210                 if (config->aggr_mode == AGGR_GLOBAL && prefix && !config->iostat_run)
1211                         fprintf(config->output, "%s", prefix);
1212
1213                 if (config->json_output && !config->metric_only)
1214                         fprintf(config->output, "}");
1215         }
1216
1217         switch (config->aggr_mode) {
1218         case AGGR_CORE:
1219         case AGGR_DIE:
1220         case AGGR_SOCKET:
1221         case AGGR_NODE:
1222                 print_aggr(config, evlist, prefix);
1223                 break;
1224         case AGGR_THREAD:
1225         case AGGR_GLOBAL:
1226                 if (config->iostat_run)
1227                         iostat_print_counters(evlist, config, ts, prefix = buf,
1228                                               print_counter);
1229                 else {
1230                         evlist__for_each_entry(evlist, counter) {
1231                                 print_counter(config, counter, prefix);
1232                         }
1233                         if (metric_only)
1234                                 fputc('\n', config->output);
1235                 }
1236                 break;
1237         case AGGR_NONE:
1238                 if (metric_only)
1239                         print_no_aggr_metric(config, evlist, prefix);
1240                 else {
1241                         evlist__for_each_entry(evlist, counter) {
1242                                 if (counter->percore)
1243                                         print_percore(config, counter, prefix);
1244                                 else
1245                                         print_counter(config, counter, prefix);
1246                         }
1247                 }
1248                 break;
1249         case AGGR_MAX:
1250         case AGGR_UNSET:
1251         default:
1252                 break;
1253         }
1254
1255         if (!interval && !config->csv_output && !config->json_output)
1256                 print_footer(config);
1257
1258         fflush(config->output);
1259 }