From: Ian Rogers <irogers@google.com>
To: Peter Zijlstra <peterz@infradead.org>,
Ingo Molnar <mingo@redhat.com>,
Arnaldo Carvalho de Melo <acme@kernel.org>,
Namhyung Kim <namhyung@kernel.org>, Jiri Olsa <jolsa@kernel.org>,
Ian Rogers <irogers@google.com>,
Adrian Hunter <adrian.hunter@intel.com>,
James Clark <james.clark@linaro.org>
Cc: linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH v2 19/30] perf metricgroup: Move matching of metrics to recorded events from perf script
Date: Fri, 13 Nov 2026 11:53:24 -0800 [thread overview]
Message-ID: <20261113195335.2865725-20-irogers@google.com> (raw)
In-Reply-To: <20261113195335.2865725-1-irogers@google.com>
perf script finds metrics whose events are all present in the
perf.data file's evlist so that they can be displayed alongside
samples. Move this code into metricgroup.c as
metricgroup__add_matching_metrics() so that it can be shared with
other commands, such as perf stat report. No functional change.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/builtin-script.c | 156 +--------------------------------
tools/perf/util/metricgroup.c | 157 ++++++++++++++++++++++++++++++++++
tools/perf/util/metricgroup.h | 12 +++
3 files changed, 172 insertions(+), 153 deletions(-)
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index f854eadb46eb..afed2353ef80 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -2350,151 +2350,6 @@ static void script_new_line(struct perf_stat_config *config __maybe_unused,
/* Each metric is printed on its own line by script_print_metric(). */
}
-struct script_find_metrics_args {
- struct evlist *evlist;
- bool system_wide;
-};
-
-static struct evsel *map_metric_evsel_to_script_evsel(struct evlist *script_evlist,
- struct evsel *metric_evsel)
-{
- struct evsel *script_evsel;
-
- evlist__for_each_entry(script_evlist, script_evsel) {
- /* Skip if perf_event_attr differ. */
- if (metric_evsel->core.attr.type != script_evsel->core.attr.type)
- continue;
- if (metric_evsel->core.attr.config != script_evsel->core.attr.config)
- continue;
- /* Skip if the script event has a metric_id that doesn't match. */
- if (script_evsel->metric_id &&
- strcmp(evsel__metric_id(metric_evsel), evsel__metric_id(script_evsel))) {
- pr_debug("Skipping matching evsel due to differing metric ids '%s' vs '%s'\n",
- evsel__metric_id(metric_evsel), evsel__metric_id(script_evsel));
- continue;
- }
- return script_evsel;
- }
- return NULL;
-}
-
-static int script_find_metrics(const struct pmu_metric *pm,
- const struct pmu_metrics_table *table __maybe_unused,
- void *data)
-{
- struct script_find_metrics_args *args = data;
- struct evlist *script_evlist = args->evlist;
- struct evlist *metric_evlist = evlist__new();
- struct evsel *metric_evsel;
- int ret = metricgroup__parse_groups(metric_evlist,
- /*pmu=*/"all",
- /*cputype_filter=*/false,
- pm->metric_name,
- /*metric_no_group=*/false,
- /*metric_no_merge=*/false,
- /*metric_no_threshold=*/true,
- /*user_requested_cpu_list=*/NULL,
- args->system_wide,
- /*hardware_aware_grouping=*/false);
-
- if (ret) {
- /* Metric parsing failed but continue the search. */
- goto out;
- }
-
- /*
- * Check the script_evlist has an entry for each metric_evlist entry. If
- * the script evsel was already set up avoid changing data that may
- * break it.
- */
- evlist__for_each_entry(metric_evlist, metric_evsel) {
- struct evsel *script_evsel =
- map_metric_evsel_to_script_evsel(script_evlist, metric_evsel);
- struct evsel *new_metric_leader;
-
- if (!script_evsel) {
- pr_debug("Skipping metric '%s' as evsel '%s' / '%s' is missing\n",
- pm->metric_name, evsel__name(metric_evsel),
- evsel__metric_id(metric_evsel));
- goto out;
- }
-
- if (script_evsel->metric_leader == NULL)
- continue;
-
- if (metric_evsel->metric_leader == metric_evsel) {
- new_metric_leader = script_evsel;
- } else {
- new_metric_leader =
- map_metric_evsel_to_script_evsel(script_evlist,
- metric_evsel->metric_leader);
- }
- /* Mismatching evsel leaders. */
- if (script_evsel->metric_leader != new_metric_leader) {
- pr_debug("Skipping metric '%s' due to mismatching evsel metric leaders '%s' vs '%s'\n",
- pm->metric_name, evsel__metric_id(metric_evsel),
- evsel__metric_id(script_evsel));
- goto out;
- }
- }
- /*
- * Metric events match those in the script evlist, copy metric evsel
- * data into the script evlist.
- */
- evlist__for_each_entry(metric_evlist, metric_evsel) {
- struct evsel *script_evsel =
- map_metric_evsel_to_script_evsel(script_evlist, metric_evsel);
- struct metric_event *metric_me =
- metricgroup__lookup(evlist__metric_events(metric_evlist),
- metric_evsel,
- /*create=*/false);
-
- if (script_evsel->metric_id == NULL) {
- script_evsel->metric_id = metric_evsel->metric_id;
- metric_evsel->metric_id = NULL;
- }
-
- if (script_evsel->metric_leader == NULL) {
- if (metric_evsel->metric_leader == metric_evsel) {
- script_evsel->metric_leader = script_evsel;
- } else {
- script_evsel->metric_leader =
- map_metric_evsel_to_script_evsel(script_evlist,
- metric_evsel->metric_leader);
- }
- }
-
- if (metric_me) {
- struct metric_expr *expr;
- struct metric_event *script_me =
- metricgroup__lookup(evlist__metric_events(script_evlist),
- script_evsel,
- /*create=*/true);
-
- if (!script_me) {
- /*
- * As the metric_expr is created, the only
- * failure is a lack of memory.
- */
- goto out;
- }
- list_splice_init(&metric_me->head, &script_me->head);
- list_for_each_entry(expr, &script_me->head, nd) {
- for (int i = 0; expr->metric_events[i]; i++) {
- expr->metric_events[i] =
- map_metric_evsel_to_script_evsel(script_evlist,
- expr->metric_events[i]);
- }
- }
- }
- }
- pr_debug("Found metric '%s' whose evsels match those of in the perf data\n",
- pm->metric_name);
-out:
- evlist__put(metric_evlist);
- return 0;
-}
-
static struct aggr_cpu_id script_aggr_cpu_id_get(struct perf_stat_config *config __maybe_unused,
struct perf_cpu cpu)
{
@@ -2523,12 +2378,6 @@ static void perf_sample__fprint_metric(struct thread *thread,
if (!init_metrics) {
/* One time initialization of stat_config and metric data. */
- struct perf_env *env = evlist__env(evsel->evlist) ?: host_env();
- struct script_find_metrics_args args = {
- .evlist = evsel->evlist,
- .system_wide = perf_thread_map__pid(evsel->core.threads, /*idx=*/0) == -1,
-
- };
if (!stat_config.output)
stat_config.output = stdout;
@@ -2542,8 +2391,9 @@ static void perf_sample__fprint_metric(struct thread *thread,
/*needs_sort=*/false);
}
- metricgroup__for_each_metric(perf_env__pmus(env), pmu_metrics_table__find(),
- script_find_metrics, &args);
+ metricgroup__add_matching_metrics(evsel->evlist,
+ perf_thread_map__pid(evsel->core.threads,
+ /*idx=*/0) == -1);
init_metrics = true;
}
diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index fba02619ffb5..9922445a6d15 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -453,6 +453,163 @@ int metricgroup__for_each_metric(struct perf_pmus *pmus, const struct pmu_metric
return pmu_for_each_sys_metric(metricgroup__sys_event_iter, &sys_data);
}
+struct find_matching_metrics_args {
+ struct evlist *evlist;
+ bool system_wide;
+};
+
+static struct evsel *map_metric_evsel_to_evlist(struct evlist *data_evlist,
+ struct evsel *metric_evsel)
+{
+ struct evsel *data_evsel;
+
+ evlist__for_each_entry(data_evlist, data_evsel) {
+ /* Skip if perf_event_attr differ. */
+ if (metric_evsel->core.attr.type != data_evsel->core.attr.type)
+ continue;
+ if (metric_evsel->core.attr.config != data_evsel->core.attr.config)
+ continue;
+ /* Skip if the data event has a metric_id that doesn't match. */
+ if (data_evsel->metric_id &&
+ strcmp(evsel__metric_id(metric_evsel), evsel__metric_id(data_evsel))) {
+ pr_debug("Skipping matching evsel due to differing metric ids '%s' vs '%s'\n",
+ evsel__metric_id(metric_evsel), evsel__metric_id(data_evsel));
+ continue;
+ }
+ return data_evsel;
+ }
+ return NULL;
+}
+
+static int find_matching_metrics_cb(const struct pmu_metric *pm,
+ const struct pmu_metrics_table *table __maybe_unused,
+ void *data)
+{
+ struct find_matching_metrics_args *args = data;
+ struct evlist *data_evlist = args->evlist;
+ struct evlist *metric_evlist = evlist__new();
+ struct evsel *metric_evsel;
+ int ret = metricgroup__parse_groups(metric_evlist,
+ /*pmu=*/"all",
+ /*cputype_filter=*/false,
+ pm->metric_name,
+ /*metric_no_group=*/false,
+ /*metric_no_merge=*/false,
+ /*metric_no_threshold=*/true,
+ /*user_requested_cpu_list=*/NULL,
+ args->system_wide,
+ /*hardware_aware_grouping=*/false);
+
+ if (ret) {
+ /* Metric parsing failed but continue the search. */
+ goto out;
+ }
+
+ /*
+ * Check the data_evlist has an entry for each metric_evlist entry. If
+ * the data evsel was already set up avoid changing data that may
+ * break it.
+ */
+ evlist__for_each_entry(metric_evlist, metric_evsel) {
+ struct evsel *data_evsel =
+ map_metric_evsel_to_evlist(data_evlist, metric_evsel);
+ struct evsel *new_metric_leader;
+
+ if (!data_evsel) {
+ pr_debug("Skipping metric '%s' as evsel '%s' / '%s' is missing\n",
+ pm->metric_name, evsel__name(metric_evsel),
+ evsel__metric_id(metric_evsel));
+ goto out;
+ }
+
+ if (data_evsel->metric_leader == NULL)
+ continue;
+
+ if (metric_evsel->metric_leader == metric_evsel) {
+ new_metric_leader = data_evsel;
+ } else {
+ new_metric_leader =
+ map_metric_evsel_to_evlist(data_evlist,
+ metric_evsel->metric_leader);
+ }
+ /* Mismatching evsel leaders. */
+ if (data_evsel->metric_leader != new_metric_leader) {
+ pr_debug("Skipping metric '%s' due to mismatching evsel metric leaders '%s' vs '%s'\n",
+ pm->metric_name, evsel__metric_id(metric_evsel),
+ evsel__metric_id(data_evsel));
+ goto out;
+ }
+ }
+ /*
+ * Metric events match those in the data evlist, copy metric evsel
+ * data into the data evlist.
+ */
+ evlist__for_each_entry(metric_evlist, metric_evsel) {
+ struct evsel *data_evsel =
+ map_metric_evsel_to_evlist(data_evlist, metric_evsel);
+ struct metric_event *metric_me =
+ metricgroup__lookup(evlist__metric_events(metric_evlist),
+ metric_evsel,
+ /*create=*/false);
+
+ if (data_evsel->metric_id == NULL) {
+ data_evsel->metric_id = metric_evsel->metric_id;
+ metric_evsel->metric_id = NULL;
+ }
+
+ if (data_evsel->metric_leader == NULL) {
+ if (metric_evsel->metric_leader == metric_evsel) {
+ data_evsel->metric_leader = data_evsel;
+ } else {
+ data_evsel->metric_leader =
+ map_metric_evsel_to_evlist(data_evlist,
+ metric_evsel->metric_leader);
+ }
+ }
+
+ if (metric_me) {
+ struct metric_expr *expr;
+ struct metric_event *data_me =
+ metricgroup__lookup(evlist__metric_events(data_evlist),
+ data_evsel,
+ /*create=*/true);
+
+ if (!data_me) {
+ /*
+ * As the metric_expr is created, the only
+ * failure is a lack of memory.
+ */
+ goto out;
+ }
+ list_splice_init(&metric_me->head, &data_me->head);
+ list_for_each_entry(expr, &data_me->head, nd) {
+ for (int i = 0; expr->metric_events[i]; i++) {
+ expr->metric_events[i] =
+ map_metric_evsel_to_evlist(data_evlist,
+ expr->metric_events[i]);
+ }
+ }
+ }
+ }
+ pr_debug("Found metric '%s' whose evsels match those of in the perf data\n",
+ pm->metric_name);
+out:
+ evlist__put(metric_evlist);
+ return 0;
+}
+
+int metricgroup__add_matching_metrics(struct evlist *evlist, bool system_wide)
+{
+ struct perf_env *env = evlist__env(evlist) ?: host_env();
+ struct find_matching_metrics_args args = {
+ .evlist = evlist,
+ .system_wide = system_wide,
+ };
+
+ return metricgroup__for_each_metric(perf_env__pmus(env), pmu_metrics_table__find(),
+ find_matching_metrics_cb, &args);
+}
+
static const char *code_characters = ",-=@";
static int encode_metric_id(struct strbuf *sb, const char *x)
diff --git a/tools/perf/util/metricgroup.h b/tools/perf/util/metricgroup.h
index 2cc28b3eb983..2069211913dc 100644
--- a/tools/perf/util/metricgroup.h
+++ b/tools/perf/util/metricgroup.h
@@ -97,6 +97,18 @@ int metricgroup__parse_groups_test(struct evlist *evlist,
*/
int metricgroup__for_each_metric(struct perf_pmus *pmus, const struct pmu_metrics_table *table,
pmu_metric_iter_fn fn, void *data);
+/**
+ * metricgroup__add_matching_metrics() - Add metrics whose events are in @evlist.
+ * @evlist: The evlist, typically read from a perf.data file, to add metrics to.
+ * @system_wide: Are the events in @evlist system wide?
+ *
+ * For every metric whose events all have a corresponding evsel in @evlist,
+ * add the metric to the metric events of @evlist so that it is computed and
+ * displayed along with the counts of the evsels.
+ *
+ * Return: 0 on success or a negative error code.
+ */
+int metricgroup__add_matching_metrics(struct evlist *evlist, bool system_wide);
bool metricgroup__has_metric_or_groups(struct perf_pmus *pmus, const char *pmu,
const char *metric_or_groups);
unsigned int metricgroups__topdown_max_level(void);
--
2.56.1.703.gf848ed807c-goog
next prev parent reply other threads:[~2026-11-13 19:54 UTC|newest]
Thread overview: 61+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-13 19:53 [PATCH v2 00/30] perf: Per-env PMUs, metric event sharing, and recorded event metrics Ian Rogers
2026-11-13 19:53 ` [PATCH v2 01/30] perf pmus: Don't skip core PMUs as duplicates Ian Rogers
2026-11-13 20:04 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 02/30] perf script: Fix metrics computed from samples Ian Rogers
2026-11-13 20:05 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 03/30] perf script: Don't drop metric samples for CPUs missing from the evsel Ian Rogers
2026-11-13 20:02 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 04/30] perf metricgroup: Don't prefer tool events for displaying a metric Ian Rogers
2026-11-13 19:59 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 05/30] perf env: Make lazy perf_env initialization thread-safe Ian Rogers
2026-11-13 20:05 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 06/30] perf env: Add host_env() singleton Ian Rogers
2026-11-13 20:47 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 07/30] perf pmu: Add from_env parameter to perf_pmu__init() Ian Rogers
2026-11-13 20:10 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 08/30] perf pmus: Introduce struct perf_pmus and perf_env__pmus() Ian Rogers
2026-11-13 20:36 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 09/30] perf pmus: Pass struct perf_pmus to find helpers Ian Rogers
2026-11-13 20:11 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 10/30] perf pmus: Pass struct perf_pmus to scan helpers Ian Rogers
2026-11-13 20:06 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 11/30] perf pmu-events: Add e_machine to struct pmu_events_map Ian Rogers
2026-11-13 20:12 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 12/30] perf pmu-events: Pass e_machine to find_core_*_table() Ian Rogers
2026-11-13 20:05 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 13/30] perf header: Pass e_machine to strcmp_cpuid_str() Ian Rogers
2026-11-13 20:01 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 14/30] perf header: Support CPUID matching across all architectures Ian Rogers
2026-11-13 20:05 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 15/30] perf test: Make the CPUID matching test architecture independent Ian Rogers
2026-11-13 20:02 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 16/30] perf test: Add CPUID matching tests for other architectures Ian Rogers
2026-11-13 20:03 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 17/30] perf dwarf-regs: Add e_machine__same_arch() Ian Rogers
2026-11-13 20:00 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 18/30] perf metricgroup: Pass struct perf_pmus to metricgroup__for_each_metric() Ian Rogers
2026-11-13 20:37 ` sashiko-bot
2026-11-13 19:53 ` Ian Rogers [this message]
2026-11-13 20:59 ` [PATCH v2 19/30] perf metricgroup: Move matching of metrics to recorded events from perf script sashiko-bot
2026-11-13 19:53 ` [PATCH v2 20/30] perf parse-events: Decode metric-id terms when adding events Ian Rogers
2026-11-13 20:33 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 21/30] perf evsel: Generalize metric_id to a set of metric_ids Ian Rogers
2026-11-13 20:31 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 22/30] perf pmu: Add perf_pmu__for_each_name_from_config() Ian Rogers
2026-11-13 20:13 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 23/30] perf metricgroup: Share metric events by their counter encoding Ian Rogers
2026-11-13 20:59 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 24/30] perf stat: Share events between metrics and command line events Ian Rogers
2026-11-13 20:14 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 25/30] perf pmus: Add perf_pmus__read_from_env() Ian Rogers
2026-11-13 21:05 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 26/30] perf metricgroup: Improve matching of metrics to recorded events Ian Rogers
2026-11-13 20:37 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 27/30] perf metricgroup: Add metricgroup__add_recorded_metrics() Ian Rogers
2026-11-13 20:42 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 28/30] perf stat report: Display metrics for recorded events Ian Rogers
2026-11-13 20:44 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 29/30] perf script: Display metrics for perf stat record data Ian Rogers
2026-11-13 20:47 ` sashiko-bot
2026-11-13 19:53 ` [PATCH v2 30/30] perf test: Add stat record metrics test Ian Rogers
2026-11-13 20:14 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261113195335.2865725-20-irogers@google.com \
--to=irogers@google.com \
--cc=acme@kernel.org \
--cc=adrian.hunter@intel.com \
--cc=james.clark@linaro.org \
--cc=jolsa@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=namhyung@kernel.org \
--cc=peterz@infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox