]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - tools/perf/builtin-stat.c
perf record: Synthesize unit/scale/... in event update
[linux.git] / tools / perf / builtin-stat.c
index 69523ed55894338cb202804db6ffbf202410b74f..a027b4712e486ae575d01198d6c8a05d8af04aac 100644 (file)
@@ -65,6 +65,7 @@
 #include "util/tool.h"
 #include "util/group.h"
 #include "util/string2.h"
+#include "util/metricgroup.h"
 #include "asm/bug.h"
 
 #include <linux/time64.h>
@@ -133,6 +134,8 @@ static const char *smi_cost_attrs = {
 
 static struct perf_evlist      *evsel_list;
 
+static struct rblist            metric_events;
+
 static struct target target = {
        .uid    = UINT_MAX,
 };
@@ -172,7 +175,7 @@ static int                  print_free_counters_hint;
 
 struct perf_stat {
        bool                     record;
-       struct perf_data_file    file;
+       struct perf_data         data;
        struct perf_session     *session;
        u64                      bytes_written;
        struct perf_tool         tool;
@@ -192,6 +195,11 @@ static struct perf_stat_config stat_config = {
        .scale          = true,
 };
 
+static bool is_duration_time(struct perf_evsel *evsel)
+{
+       return !strcmp(evsel->name, "duration_time");
+}
+
 static inline void diff_timespec(struct timespec *r, struct timespec *a,
                                 struct timespec *b)
 {
@@ -245,7 +253,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel)
         * by attr->sample_type != 0, and we can't run it on
         * stat sessions.
         */
-       if (!(STAT_RECORD && perf_stat.file.is_pipe))
+       if (!(STAT_RECORD && perf_stat.data.is_pipe))
                attr->sample_type = PERF_SAMPLE_IDENTIFIER;
 
        /*
@@ -287,7 +295,7 @@ static int process_synthesized_event(struct perf_tool *tool __maybe_unused,
                                     struct perf_sample *sample __maybe_unused,
                                     struct machine *machine __maybe_unused)
 {
-       if (perf_data_file__write(&perf_stat.file, event, event->header.size) < 0) {
+       if (perf_data__write(&perf_stat.data, event, event->header.size) < 0) {
                pr_err("failed to write perf data, error: %m\n");
                return -1;
        }
@@ -407,6 +415,8 @@ static void process_interval(void)
                        pr_err("failed to write stat round event\n");
        }
 
+       init_stats(&walltime_nsecs_stats);
+       update_stats(&walltime_nsecs_stats, stat_config.interval * 1000000);
        print_counters(&rs, 0, NULL);
 }
 
@@ -448,19 +458,8 @@ static void workload_exec_failed_signal(int signo __maybe_unused, siginfo_t *inf
        workload_exec_errno = info->si_value.sival_int;
 }
 
-static bool has_unit(struct perf_evsel *counter)
-{
-       return counter->unit && *counter->unit;
-}
-
-static bool has_scale(struct perf_evsel *counter)
-{
-       return counter->scale != 1;
-}
-
 static int perf_stat_synthesize_config(bool is_pipe)
 {
-       struct perf_evsel *counter;
        int err;
 
        if (is_pipe) {
@@ -472,53 +471,10 @@ static int perf_stat_synthesize_config(bool is_pipe)
                }
        }
 
-       /*
-        * Synthesize other events stuff not carried within
-        * attr event - unit, scale, name
-        */
-       evlist__for_each_entry(evsel_list, counter) {
-               if (!counter->supported)
-                       continue;
-
-               /*
-                * Synthesize unit and scale only if it's defined.
-                */
-               if (has_unit(counter)) {
-                       err = perf_event__synthesize_event_update_unit(NULL, counter, process_synthesized_event);
-                       if (err < 0) {
-                               pr_err("Couldn't synthesize evsel unit.\n");
-                               return err;
-                       }
-               }
-
-               if (has_scale(counter)) {
-                       err = perf_event__synthesize_event_update_scale(NULL, counter, process_synthesized_event);
-                       if (err < 0) {
-                               pr_err("Couldn't synthesize evsel scale.\n");
-                               return err;
-                       }
-               }
-
-               if (counter->own_cpus) {
-                       err = perf_event__synthesize_event_update_cpus(NULL, counter, process_synthesized_event);
-                       if (err < 0) {
-                               pr_err("Couldn't synthesize evsel scale.\n");
-                               return err;
-                       }
-               }
-
-               /*
-                * Name is needed only for pipe output,
-                * perf.data carries event names.
-                */
-               if (is_pipe) {
-                       err = perf_event__synthesize_event_update_name(NULL, counter, process_synthesized_event);
-                       if (err < 0) {
-                               pr_err("Couldn't synthesize evsel name.\n");
-                               return err;
-                       }
-               }
-       }
+       err = perf_event__synthesize_extra_attr(NULL,
+                                               evsel_list,
+                                               process_synthesized_event,
+                                               is_pipe);
 
        err = perf_event__synthesize_thread_map2(NULL, evsel_list->threads,
                                                process_synthesized_event,
@@ -582,6 +538,32 @@ static bool perf_evsel__should_store_id(struct perf_evsel *counter)
        return STAT_RECORD || counter->attr.read_format & PERF_FORMAT_ID;
 }
 
+static struct perf_evsel *perf_evsel__reset_weak_group(struct perf_evsel *evsel)
+{
+       struct perf_evsel *c2, *leader;
+       bool is_open = true;
+
+       leader = evsel->leader;
+       pr_debug("Weak group for %s/%d failed\n",
+                       leader->name, leader->nr_members);
+
+       /*
+        * for_each_group_member doesn't work here because it doesn't
+        * include the first entry.
+        */
+       evlist__for_each_entry(evsel_list, c2) {
+               if (c2 == evsel)
+                       is_open = false;
+               if (c2->leader == leader) {
+                       if (is_open)
+                               perf_evsel__close(c2);
+                       c2->leader = c2;
+                       c2->nr_members = 0;
+               }
+       }
+       return leader;
+}
+
 static int __run_perf_stat(int argc, const char **argv)
 {
        int interval = stat_config.interval;
@@ -592,7 +574,7 @@ static int __run_perf_stat(int argc, const char **argv)
        size_t l;
        int status = 0;
        const bool forks = (argc > 0);
-       bool is_pipe = STAT_RECORD ? perf_stat.file.is_pipe : false;
+       bool is_pipe = STAT_RECORD ? perf_stat.data.is_pipe : false;
        struct perf_evsel_config_term *err_term;
 
        if (interval) {
@@ -618,6 +600,15 @@ static int __run_perf_stat(int argc, const char **argv)
        evlist__for_each_entry(evsel_list, counter) {
 try_again:
                if (create_perf_stat_counter(counter) < 0) {
+
+                       /* Weak group failed. Reset the group. */
+                       if ((errno == EINVAL || errno == EBADF) &&
+                           counter->leader != counter &&
+                           counter->weak_group) {
+                               counter = perf_evsel__reset_weak_group(counter);
+                               goto try_again;
+                       }
+
                        /*
                         * PPC returns ENXIO for HW counters until 2.6.37
                         * (behavior changed with commit b0a873e).
@@ -674,10 +665,10 @@ static int __run_perf_stat(int argc, const char **argv)
        }
 
        if (STAT_RECORD) {
-               int err, fd = perf_data_file__fd(&perf_stat.file);
+               int err, fd = perf_data__fd(&perf_stat.data);
 
                if (is_pipe) {
-                       err = perf_header__write_pipe(perf_data_file__fd(&perf_stat.file));
+                       err = perf_header__write_pipe(perf_data__fd(&perf_stat.data));
                } else {
                        err = perf_session__write_header(perf_stat.session, evsel_list,
                                                         fd, false);
@@ -800,7 +791,7 @@ static void print_noise(struct perf_evsel *evsel, double avg)
        if (run_count == 1)
                return;
 
-       ps = evsel->priv;
+       ps = evsel->stats;
        print_noise_pct(stddev_stats(&ps->res_stats[0]), avg);
 }
 
@@ -1199,7 +1190,7 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval,
 
        perf_stat__print_shadow_stats(counter, uval,
                                first_shadow_cpu(counter, id),
-                               &out);
+                               &out, &metric_events);
        if (!csv_output && !metric_only) {
                print_noise(counter, noise);
                print_running(run, ena);
@@ -1222,8 +1213,7 @@ static void aggr_update_shadow(void)
                                        continue;
                                val += perf_counts(counter->counts, cpu, 0)->val;
                        }
-                       val = val * counter->scale;
-                       perf_stat__update_shadow_stats(counter, &val,
+                       perf_stat__update_shadow_stats(counter, val,
                                                       first_shadow_cpu(counter, id));
                }
        }
@@ -1325,6 +1315,9 @@ static void print_aggr(char *prefix)
                ad.id = id = aggr_map->map[s];
                first = true;
                evlist__for_each_entry(evsel_list, counter) {
+                       if (is_duration_time(counter))
+                               continue;
+
                        ad.val = ad.ena = ad.run = 0;
                        ad.nr = 0;
                        if (!collect_data(counter, aggr_cb, &ad))
@@ -1384,7 +1377,7 @@ static void counter_aggr_cb(struct perf_evsel *counter, void *data,
                            bool first __maybe_unused)
 {
        struct caggr_data *cd = data;
-       struct perf_stat_evsel *ps = counter->priv;
+       struct perf_stat_evsel *ps = counter->stats;
 
        cd->avg += avg_stats(&ps->res_stats[0]);
        cd->avg_enabled += avg_stats(&ps->res_stats[1]);
@@ -1468,6 +1461,8 @@ static void print_no_aggr_metric(char *prefix)
                if (prefix)
                        fputs(prefix, stat_config.output);
                evlist__for_each_entry(evsel_list, counter) {
+                       if (is_duration_time(counter))
+                               continue;
                        if (first) {
                                aggr_printout(counter, cpu, 0);
                                first = false;
@@ -1522,6 +1517,8 @@ static void print_metric_headers(const char *prefix, bool no_indent)
 
        /* Print metrics headers only */
        evlist__for_each_entry(evsel_list, counter) {
+               if (is_duration_time(counter))
+                       continue;
                os.evsel = counter;
                out.ctx = &os;
                out.print_metric = print_metric_header;
@@ -1530,7 +1527,8 @@ static void print_metric_headers(const char *prefix, bool no_indent)
                os.evsel = counter;
                perf_stat__print_shadow_stats(counter, 0,
                                              0,
-                                             &out);
+                                             &out,
+                                             &metric_events);
        }
        fputc('\n', stat_config.output);
 }
@@ -1643,7 +1641,7 @@ static void print_counters(struct timespec *ts, int argc, const char **argv)
        char buf[64], *prefix = NULL;
 
        /* Do not print anything if we record to the pipe. */
-       if (STAT_RECORD && perf_stat.file.is_pipe)
+       if (STAT_RECORD && perf_stat.data.is_pipe)
                return;
 
        if (interval)
@@ -1668,12 +1666,18 @@ static void print_counters(struct timespec *ts, int argc, const char **argv)
                print_aggr(prefix);
                break;
        case AGGR_THREAD:
-               evlist__for_each_entry(evsel_list, counter)
+               evlist__for_each_entry(evsel_list, counter) {
+                       if (is_duration_time(counter))
+                               continue;
                        print_aggr_thread(counter, prefix);
+               }
                break;
        case AGGR_GLOBAL:
-               evlist__for_each_entry(evsel_list, counter)
+               evlist__for_each_entry(evsel_list, counter) {
+                       if (is_duration_time(counter))
+                               continue;
                        print_counter_aggr(counter, prefix);
+               }
                if (metric_only)
                        fputc('\n', stat_config.output);
                break;
@@ -1681,8 +1685,11 @@ static void print_counters(struct timespec *ts, int argc, const char **argv)
                if (metric_only)
                        print_no_aggr_metric(prefix);
                else {
-                       evlist__for_each_entry(evsel_list, counter)
+                       evlist__for_each_entry(evsel_list, counter) {
+                               if (is_duration_time(counter))
+                                       continue;
                                print_counter(counter, prefix);
+                       }
                }
                break;
        case AGGR_UNSET:
@@ -1754,6 +1761,13 @@ static int enable_metric_only(const struct option *opt __maybe_unused,
        return 0;
 }
 
+static int parse_metric_groups(const struct option *opt,
+                              const char *str,
+                              int unset __maybe_unused)
+{
+       return metricgroup__parse_groups(opt, str, &metric_events);
+}
+
 static const struct option stat_options[] = {
        OPT_BOOLEAN('T', "transaction", &transaction_run,
                    "hardware transaction statistics"),
@@ -1819,6 +1833,9 @@ static const struct option stat_options[] = {
                        "measure topdown level 1 statistics"),
        OPT_BOOLEAN(0, "smi-cost", &smi_cost,
                        "measure SMI cost"),
+       OPT_CALLBACK('M', "metrics", &evsel_list, "metric/metric group list",
+                    "monitor specified metrics or metric groups (separated by ,)",
+                    parse_metric_groups),
        OPT_END()
 };
 
@@ -2334,20 +2351,20 @@ static void init_features(struct perf_session *session)
 static int __cmd_record(int argc, const char **argv)
 {
        struct perf_session *session;
-       struct perf_data_file *file = &perf_stat.file;
+       struct perf_data *data = &perf_stat.data;
 
        argc = parse_options(argc, argv, stat_options, stat_record_usage,
                             PARSE_OPT_STOP_AT_NON_OPTION);
 
        if (output_name)
-               file->path = output_name;
+               data->file.path = output_name;
 
        if (run_count != 1 || forever) {
                pr_err("Cannot use -r option with perf stat record.\n");
                return -1;
        }
 
-       session = perf_session__new(file, false, NULL);
+       session = perf_session__new(data, false, NULL);
        if (session == NULL) {
                pr_err("Perf session creation failed.\n");
                return -1;
@@ -2405,7 +2422,7 @@ int process_stat_config_event(struct perf_tool *tool,
        if (st->aggr_mode != AGGR_UNSET)
                stat_config.aggr_mode = st->aggr_mode;
 
-       if (perf_stat.file.is_pipe)
+       if (perf_stat.data.is_pipe)
                perf_stat_init_aggr_mode();
        else
                perf_stat_init_aggr_mode_file(st);
@@ -2513,10 +2530,10 @@ static int __cmd_report(int argc, const char **argv)
                        input_name = "perf.data";
        }
 
-       perf_stat.file.path = input_name;
-       perf_stat.file.mode = PERF_DATA_MODE_READ;
+       perf_stat.data.file.path = input_name;
+       perf_stat.data.mode      = PERF_DATA_MODE_READ;
 
-       session = perf_session__new(&perf_stat.file, false, &perf_stat.tool);
+       session = perf_session__new(&perf_stat.data, false, &perf_stat.tool);
        if (session == NULL)
                return -1;
 
@@ -2787,7 +2804,7 @@ int cmd_stat(int argc, const char **argv)
                 * records, but the need to suppress the kptr_restrict messages in older
                 * tools remain  -acme
                 */
-               int fd = perf_data_file__fd(&perf_stat.file);
+               int fd = perf_data__fd(&perf_stat.data);
                int err = perf_event__synthesize_kernel_mmap((void *)&perf_stat,
                                                             process_synthesized_event,
                                                             &perf_stat.session->machines.host);
@@ -2801,7 +2818,7 @@ int cmd_stat(int argc, const char **argv)
                                pr_err("failed to write stat round event\n");
                }
 
-               if (!perf_stat.file.is_pipe) {
+               if (!perf_stat.data.is_pipe) {
                        perf_stat.session->header.data_size += perf_stat.bytes_written;
                        perf_session__write_header(perf_stat.session, evsel_list, fd, true);
                }