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