perf metrics: Support all tool events
authorIan Rogers <irogers@google.com>
Sat, 7 May 2022 05:34:09 +0000 (22:34 -0700)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Mon, 9 May 2022 13:16:49 +0000 (10:16 -0300)
Previously duration_time was hard coded, which was ok until commit
b03b89b350034f22 ("perf stat: Add user_time and system_time events")
added additional tool events. Do for all tool events what was previously
done just for duration_time.

Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Florian Fischer <florian.fischer@muhq.space>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: John Garry <john.garry@huawei.com>
Cc: Kim Phillips <kim.phillips@amd.com>
Cc: Madhavan Srinivasan <maddy@linux.ibm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Riccardo Mancini <rickyman7@gmail.com>
Cc: Shunsuke Nakamura <nakamura.shun@fujitsu.com>
Cc: Stephane Eranian <eranian@google.com>
Cc: Xing Zhengjun <zhengjun.xing@linux.intel.com>
Link: https://lore.kernel.org/r/20220507053410.3798748-5-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/metricgroup.c
tools/perf/util/stat-shadow.c

index d8492e33952187d23056adb24a8b5f4666014a99..7a5f488aef02b3057b14329ff3250d76273b191a 100644 (file)
@@ -728,22 +728,23 @@ static int metricgroup__build_event_string(struct strbuf *events,
 {
        struct hashmap_entry *cur;
        size_t bkt;
-       bool no_group = true, has_duration = false;
+       bool no_group = true, has_tool_events = false;
+       bool tool_events[PERF_TOOL_MAX] = {false};
        int ret = 0;
 
 #define RETURN_IF_NON_ZERO(x) do { if (x) return x; } while (0)
 
        hashmap__for_each_entry(ctx->ids, cur, bkt) {
                const char *sep, *rsep, *id = cur->key;
+               enum perf_tool_event ev;
 
                pr_debug("found event %s\n", id);
-               /*
-                * Duration time maps to a software event and can make
-                * groups not count. Always use it outside a
-                * group.
-                */
-               if (!strcmp(id, "duration_time")) {
-                       has_duration = true;
+
+               /* Always move tool events outside of the group. */
+               ev = perf_tool_event__from_str(id);
+               if (ev != PERF_TOOL_NONE) {
+                       has_tool_events = true;
+                       tool_events[ev] = true;
                        continue;
                }
                /* Separate events with commas and open the group if necessary. */
@@ -802,16 +803,25 @@ static int metricgroup__build_event_string(struct strbuf *events,
                        RETURN_IF_NON_ZERO(ret);
                }
        }
-       if (has_duration) {
-               if (no_group) {
-                       /* Strange case of a metric of just duration_time. */
-                       ret = strbuf_addf(events, "duration_time");
-               } else if (!has_constraint)
-                       ret = strbuf_addf(events, "}:W,duration_time");
-               else
-                       ret = strbuf_addf(events, ",duration_time");
-       } else if (!no_group && !has_constraint)
+       if (!no_group && !has_constraint) {
                ret = strbuf_addf(events, "}:W");
+               RETURN_IF_NON_ZERO(ret);
+       }
+       if (has_tool_events) {
+               int i;
+
+               perf_tool_event__for_each_event(i) {
+                       if (tool_events[i]) {
+                               if (!no_group) {
+                                       ret = strbuf_addch(events, ',');
+                                       RETURN_IF_NON_ZERO(ret);
+                               }
+                               no_group = false;
+                               ret = strbuf_addstr(events, perf_tool_event__to_str(i));
+                               RETURN_IF_NON_ZERO(ret);
+                       }
+               }
+       }
 
        return ret;
 #undef RETURN_IF_NON_ZERO
@@ -1117,7 +1127,7 @@ out:
 
 /**
  * metric_list_cmp - list_sort comparator that sorts metrics with more events to
- *                   the front. duration_time is excluded from the count.
+ *                   the front. tool events are excluded from the count.
  */
 static int metric_list_cmp(void *priv __maybe_unused, const struct list_head *l,
                           const struct list_head *r)
@@ -1125,15 +1135,19 @@ static int metric_list_cmp(void *priv __maybe_unused, const struct list_head *l,
        const struct metric *left = container_of(l, struct metric, nd);
        const struct metric *right = container_of(r, struct metric, nd);
        struct expr_id_data *data;
-       int left_count, right_count;
+       int i, left_count, right_count;
 
        left_count = hashmap__size(left->pctx->ids);
-       if (!expr__get_id(left->pctx, "duration_time", &data))
-               left_count--;
+       perf_tool_event__for_each_event(i) {
+               if (!expr__get_id(left->pctx, perf_tool_event__to_str(i), &data))
+                       left_count--;
+       }
 
        right_count = hashmap__size(right->pctx->ids);
-       if (!expr__get_id(right->pctx, "duration_time", &data))
-               right_count--;
+       perf_tool_event__for_each_event(i) {
+               if (!expr__get_id(right->pctx, perf_tool_event__to_str(i), &data))
+                       right_count--;
+       }
 
        return right_count - left_count;
 }
@@ -1331,26 +1345,27 @@ static int parse_ids(bool metric_no_merge, struct perf_pmu *fake_pmu,
 
        *out_evlist = NULL;
        if (!metric_no_merge || hashmap__size(ids->ids) == 0) {
-               char *tmp;
+               int i;
                /*
-                * We may fail to share events between metrics because
-                * duration_time isn't present in one metric. For example, a
-                * ratio of cache misses doesn't need duration_time but the same
-                * events may be used for a misses per second. Events without
-                * sharing implies multiplexing, that is best avoided, so place
-                * duration_time in every group.
+                * We may fail to share events between metrics because a tool
+                * event isn't present in one metric. For example, a ratio of
+                * cache misses doesn't need duration_time but the same events
+                * may be used for a misses per second. Events without sharing
+                * implies multiplexing, that is best avoided, so place
+                * all tool events in every group.
                 *
                 * Also, there may be no ids/events in the expression parsing
                 * context because of constant evaluation, e.g.:
                 *    event1 if #smt_on else 0
-                * Add a duration_time event to avoid a parse error on an empty
-                * string.
+                * Add a tool event to avoid a parse error on an empty string.
                 */
-               tmp = strdup("duration_time");
-               if (!tmp)
-                       return -ENOMEM;
+               perf_tool_event__for_each_event(i) {
+                       char *tmp = strdup(perf_tool_event__to_str(i));
 
-               ids__insert(ids->ids, tmp);
+                       if (!tmp)
+                               return -ENOMEM;
+                       ids__insert(ids->ids, tmp);
+               }
        }
        ret = metricgroup__build_event_string(&events, ids, modifier,
                                              has_constraint);
index ea4c35e4f1da26f08a5347394bacbe3aeeab58c1..979c8cb918f724f3ff71b15b1200667a7363ba38 100644 (file)
@@ -833,10 +833,31 @@ static int prepare_metric(struct evsel **metric_events,
                u64 metric_total = 0;
                int source_count;
 
-               if (!strcmp(metric_events[i]->name, "duration_time")) {
-                       stats = &walltime_nsecs_stats;
-                       scale = 1e-9;
+               if (evsel__is_tool(metric_events[i])) {
                        source_count = 1;
+                       switch (metric_events[i]->tool_event) {
+                       case PERF_TOOL_DURATION_TIME:
+                               stats = &walltime_nsecs_stats;
+                               scale = 1e-9;
+                               break;
+                       case PERF_TOOL_USER_TIME:
+                               stats = &ru_stats.ru_utime_usec_stat;
+                               scale = 1e-6;
+                               break;
+                       case PERF_TOOL_SYSTEM_TIME:
+                               stats = &ru_stats.ru_stime_usec_stat;
+                               scale = 1e-6;
+                               break;
+                       case PERF_TOOL_NONE:
+                               pr_err("Invalid tool event 'none'");
+                               abort();
+                       case PERF_TOOL_MAX:
+                               pr_err("Invalid tool event 'max'");
+                               abort();
+                       default:
+                               pr_err("Unknown tool event '%s'", evsel__name(metric_events[i]));
+                               abort();
+                       }
                } else {
                        v = saved_value_lookup(metric_events[i], cpu_map_idx, false,
                                               STAT_NONE, 0, st,