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>
Cc: Jiri Olsa <jolsa@kernel.org>,
Adrian Hunter <adrian.hunter@intel.com>,
James Clark <james.clark@linaro.org>,
linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org,
Ian Rogers <irogers@google.com>
Subject: [PATCH 02/11] perf pmu: Add metric and metricgroup storage on struct perf_pmu
Date: Fri, 13 Nov 2026 13:10:53 -0800 [thread overview]
Message-ID: <20261113211105.3118281-3-irogers@google.com> (raw)
In-Reply-To: <20261113211105.3118281-1-irogers@google.com>
Extend struct perf_pmu symmetrically with pmu->aliases to own resolved
metrics and metricgroup descriptions for the lifetime of the PMU while
keeping struct perf_pmu_metric and struct perf_pmu_metricgroup private
out of pmu.h:
- Add struct perf_pmu_metric, struct perf_pmu_metricgroup, and
pmu_metric_file_kind() in pmu.c for metric sidecar extensions
(.scale-unit, .desc, .long-desc, .group, .no-group, .default-group,
.threshold, .compat, .aggr-mode, .constraint, and .show-events).
- Initialize pmu->metrics, pmu->metricgroups, and pmu->metrics_table in
perf_pmu__init(), perf_pmu__lookup(), and
perf_pmu__create_placeholder_core_pmu().
- Free owned metric and metricgroup strings in perf_pmu__delete().
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/pmu.c | 230 ++++++++++++++++++++++++++++++++++++++++--
tools/perf/util/pmu.h | 18 ++++
2 files changed, 238 insertions(+), 10 deletions(-)
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index d10b8c0810fb..8baeba49d2e8 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/list.h>
#include <linux/compiler.h>
+#include <linux/err.h>
#include <linux/string.h>
#include <linux/zalloc.h>
#include <linux/ctype.h>
@@ -164,6 +165,54 @@ struct perf_pmu_alias {
bool has_retire_lat_max;
};
+enum pmu_metric_file_kind {
+ PMU_METRIC_FILE_BASE,
+ PMU_METRIC_FILE_SCALE_UNIT,
+ PMU_METRIC_FILE_DESC,
+ PMU_METRIC_FILE_LONG_DESC,
+ PMU_METRIC_FILE_GROUP,
+ PMU_METRIC_FILE_NO_GROUP,
+ PMU_METRIC_FILE_DEFAULT_GROUP,
+ PMU_METRIC_FILE_THRESHOLD,
+ PMU_METRIC_FILE_COMPAT,
+ PMU_METRIC_FILE_AGGR_MODE,
+ PMU_METRIC_FILE_CONSTRAINT,
+ PMU_METRIC_FILE_SHOW_EVENTS,
+};
+
+/**
+ * struct perf_pmu_metric - Owned metric entry cached on struct perf_pmu.
+ */
+struct perf_pmu_metric {
+ char *pmu;
+ char *metric_name;
+ char *metric_group;
+ char *metric_expr;
+ char *metric_threshold;
+ char *unit;
+ char *compat;
+ char *desc;
+ char *long_desc;
+ char *metricgroup_no_group;
+ char *default_metricgroup_name;
+ enum aggr_mode_class aggr_mode;
+ enum metric_event_groups event_grouping;
+ bool default_show_events;
+ bool has_aggr_mode;
+ bool has_event_grouping;
+ bool has_default_show_events;
+};
+
+/**
+ * struct perf_pmu_metricgroup - Owned metricgroup description cached on
+ * struct perf_pmu.
+ */
+struct perf_pmu_metricgroup {
+ char *name;
+ char *desc;
+ bool from_builtin;
+};
+
static int pmu_aliases_parse(struct perf_pmu *pmu);
static struct perf_pmu_format *perf_pmu__new_format(struct list_head *list, char *name)
@@ -382,6 +431,43 @@ static inline bool pmu_alias_info_file(const char *name)
return pmu_event_file_kind(name, NULL) != PMU_EVENT_FILE_BASE;
}
+static const struct {
+ const char *suffix;
+ size_t len;
+ enum pmu_metric_file_kind kind;
+} pmu_metric_suffixes[] = {
+ { ".desc", 5, PMU_METRIC_FILE_DESC },
+ { ".group", 6, PMU_METRIC_FILE_GROUP },
+ { ".compat", 7, PMU_METRIC_FILE_COMPAT },
+ { ".no-group", 9, PMU_METRIC_FILE_NO_GROUP },
+ { ".aggr-mode", 10, PMU_METRIC_FILE_AGGR_MODE },
+ { ".long-desc", 10, PMU_METRIC_FILE_LONG_DESC },
+ { ".threshold", 10, PMU_METRIC_FILE_THRESHOLD },
+ { ".scale-unit", 11, PMU_METRIC_FILE_SCALE_UNIT },
+ { ".constraint", 11, PMU_METRIC_FILE_CONSTRAINT },
+ { ".show-events", 12, PMU_METRIC_FILE_SHOW_EVENTS },
+ { ".default-group", 14, PMU_METRIC_FILE_DEFAULT_GROUP },
+};
+
+static __maybe_unused enum pmu_metric_file_kind
+pmu_metric_file_kind(const char *name, size_t *base_len)
+{
+ size_t len = strlen(name);
+
+ for (size_t i = 0; i < ARRAY_SIZE(pmu_metric_suffixes); i++) {
+ size_t slen = pmu_metric_suffixes[i].len;
+
+ if (len > slen && !strcasecmp(name + len - slen, pmu_metric_suffixes[i].suffix)) {
+ if (base_len)
+ *base_len = len - slen;
+ return pmu_metric_suffixes[i].kind;
+ }
+ }
+ if (base_len)
+ *base_len = len;
+ return PMU_METRIC_FILE_BASE;
+}
+
static int perf_pmu__parse_scale_at(int events_dir_fd, const char *event_name,
struct perf_pmu_alias *alias)
{
@@ -518,7 +604,7 @@ static void perf_pmu__del_aliases(struct perf_pmu *pmu)
struct hashmap_entry *entry;
size_t bkt;
- if (!pmu->aliases)
+ if (IS_ERR_OR_NULL(pmu->aliases))
return;
hashmap__for_each_entry(pmu->aliases, entry, bkt)
@@ -528,6 +614,87 @@ static void perf_pmu__del_aliases(struct perf_pmu *pmu)
pmu->aliases = NULL;
}
+static void perf_pmu_free_metric(struct perf_pmu_metric *metric)
+{
+ if (!metric)
+ return;
+
+ zfree(&metric->pmu);
+ zfree(&metric->metric_name);
+ zfree(&metric->metric_group);
+ zfree(&metric->metric_expr);
+ zfree(&metric->metric_threshold);
+ zfree(&metric->unit);
+ zfree(&metric->compat);
+ zfree(&metric->desc);
+ zfree(&metric->long_desc);
+ zfree(&metric->metricgroup_no_group);
+ zfree(&metric->default_metricgroup_name);
+ free(metric);
+}
+
+static void perf_pmu_free_metricgroup(struct perf_pmu_metricgroup *mg)
+{
+ if (!mg)
+ return;
+
+ zfree(&mg->name);
+ zfree(&mg->desc);
+ free(mg);
+}
+
+static __maybe_unused void
+perf_pmu_metric__to_pmu_metric(const struct perf_pmu_metric *src,
+ struct pmu_metric *dst)
+{
+ *dst = (struct pmu_metric){
+ .pmu = src->pmu,
+ .metric_name = src->metric_name,
+ .metric_group = src->metric_group,
+ .metric_expr = src->metric_expr,
+ .metric_threshold = src->metric_threshold,
+ .unit = src->unit,
+ .compat = src->compat,
+ .desc = src->desc,
+ .long_desc = src->long_desc,
+ .metricgroup_no_group = src->metricgroup_no_group,
+ .default_metricgroup_name = src->default_metricgroup_name,
+ .aggr_mode = src->aggr_mode,
+ .event_grouping = src->event_grouping,
+ .default_show_events = src->default_show_events,
+ };
+}
+
+static void perf_pmu__del_metrics(struct perf_pmu *pmu)
+{
+ struct hashmap_entry *entry;
+ size_t bkt;
+
+ if (IS_ERR_OR_NULL(pmu->metrics))
+ return;
+
+ hashmap__for_each_entry(pmu->metrics, entry, bkt)
+ perf_pmu_free_metric(entry->pvalue);
+
+ hashmap__free(pmu->metrics);
+ pmu->metrics = NULL;
+}
+
+static void perf_pmu__del_metricgroups(struct perf_pmu *pmu)
+{
+ struct hashmap_entry *entry;
+ size_t bkt;
+
+ if (IS_ERR_OR_NULL(pmu->metricgroups))
+ return;
+
+ hashmap__for_each_entry(pmu->metricgroups, entry, bkt)
+ perf_pmu_free_metricgroup(entry->pvalue);
+
+ hashmap__free(pmu->metricgroups);
+ pmu->metricgroups = NULL;
+}
+
static int perf_pmu__new_alias(struct perf_pmu *pmu, const char *name,
const char *desc, const char *val, int val_fd,
const struct pmu_event *pe, enum event_source src,
@@ -1327,6 +1494,33 @@ static bool aliases__equal(long key1, long key2, void *ctx __maybe_unused)
return strcasecmp((const char *)key1, (const char *)key2) == 0;
}
+static size_t metrics__hash(long key, void *ctx __maybe_unused)
+{
+ return str_hash((const char *)key);
+}
+
+static bool metrics__equal(long key1, long key2, void *ctx __maybe_unused)
+{
+ return strcmp((const char *)key1, (const char *)key2) == 0;
+}
+
+static const struct pmu_metrics_table *perf_pmu__lookup_metrics_table(struct perf_pmu *pmu)
+{
+ const struct pmu_metrics_table *table = NULL;
+ const char *arch = perf_env__arch(NULL);
+ struct perf_cpu cpu = pmu ? perf_cpu_map__min(pmu->cpus) : (struct perf_cpu){ -1 };
+ char *cpuid = get_cpuid_allow_env_override(cpu);
+
+ if (cpuid) {
+ if (arch)
+ table = find_core_metrics_table(arch, cpuid);
+ free(cpuid);
+ }
+ if (!table)
+ table = pmu_metrics_table__find();
+ return table;
+}
+
int perf_pmu__init(struct perf_pmu *pmu, __u32 type, const char *name)
{
pmu->type = type;
@@ -1338,8 +1532,22 @@ int perf_pmu__init(struct perf_pmu *pmu, __u32 type, const char *name)
return -ENOMEM;
pmu->aliases = hashmap__new(aliases__hash, aliases__equal, /*ctx=*/ NULL);
- if (!pmu->aliases)
+ if (IS_ERR(pmu->aliases)) {
+ pmu->aliases = NULL;
return -ENOMEM;
+ }
+
+ pmu->metrics = hashmap__new(metrics__hash, metrics__equal, /*ctx=*/ NULL);
+ if (IS_ERR(pmu->metrics)) {
+ pmu->metrics = NULL;
+ return -ENOMEM;
+ }
+
+ pmu->metricgroups = hashmap__new(metrics__hash, metrics__equal, /*ctx=*/ NULL);
+ if (IS_ERR(pmu->metricgroups)) {
+ pmu->metricgroups = NULL;
+ return -ENOMEM;
+ }
return 0;
}
@@ -1416,6 +1624,7 @@ struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char
pmu->max_precise = pmu_max_precise(dirfd, pmu);
pmu->alias_name = pmu_find_alias_name(pmu, dirfd);
pmu->events_table = perf_pmu__find_events_table(pmu);
+ pmu->metrics_table = perf_pmu__lookup_metrics_table(pmu);
/*
* Load the sys json events/aliases when loading the PMU as each event
* may have a different compat regular expression. We therefore can't
@@ -1427,8 +1636,10 @@ struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char
perf_pmu__arch_init(pmu);
- if (eager_load)
+ if (eager_load) {
pmu_aliases_parse_eager(pmu, dirfd);
+ pmu->eager_sysfs_loaded = true;
+ }
return pmu;
}
@@ -1441,19 +1652,16 @@ struct perf_pmu *perf_pmu__create_placeholder_core_pmu(struct list_head *core_pm
if (!pmu)
return NULL;
- pmu->name = strdup("cpu");
- if (!pmu->name) {
- free(pmu);
+ if (perf_pmu__init(pmu, PERF_TYPE_RAW, "cpu") != 0) {
+ perf_pmu__delete(pmu);
return NULL;
}
pmu->is_core = true;
- pmu->type = PERF_TYPE_RAW;
pmu->cpus = cpu_map__online();
+ pmu->events_table = perf_pmu__find_events_table(pmu);
+ pmu->metrics_table = perf_pmu__lookup_metrics_table(pmu);
- INIT_LIST_HEAD(&pmu->format);
- pmu->aliases = hashmap__new(aliases__hash, aliases__equal, /*ctx=*/ NULL);
- INIT_LIST_HEAD(&pmu->caps);
list_add_tail(&pmu->list, core_pmus);
return pmu;
}
@@ -2930,6 +3138,8 @@ void perf_pmu__delete(struct perf_pmu *pmu)
perf_pmu__del_formats(&pmu->format);
perf_pmu__del_aliases(pmu);
+ perf_pmu__del_metrics(pmu);
+ perf_pmu__del_metricgroups(pmu);
perf_pmu__del_caps(pmu);
perf_cpu_map__put(pmu->cpus);
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 2c8916ad073e..2587279fa0ce 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -148,6 +148,18 @@ struct perf_pmu {
* @events_table: The events table for json events in pmu-events.c.
*/
const struct pmu_events_table *events_table;
+ /**
+ * @metrics: Hashmap of struct perf_pmu_metric resolved across layers.
+ */
+ struct hashmap *metrics;
+ /**
+ * @metricgroups: Hashmap of struct perf_pmu_metricgroup descriptions.
+ */
+ struct hashmap *metricgroups;
+ /**
+ * @metrics_table: The metrics table for json metrics in pmu-events.c.
+ */
+ const struct pmu_metrics_table *metrics_table;
/** @sysfs_aliases: Number of sysfs aliases loaded. */
uint32_t sysfs_aliases;
/** @cpu_json_aliases: Number of json event aliases loaded specific to the CPUID. */
@@ -166,6 +178,12 @@ struct perf_pmu {
* been added?
*/
bool cpu_aliases_added;
+ /** @aliases_loaded: Have all layered event aliases been enumerated? */
+ bool aliases_loaded;
+ /** @metrics_loaded: Have all layered metrics been enumerated? */
+ bool metrics_loaded;
+ /** @eager_sysfs_loaded: Were sysfs events eagerly loaded from a custom dirfd? */
+ bool eager_sysfs_loaded;
/** @caps_initialized: Has the list caps been initialized? */
bool caps_initialized;
/** @nr_caps: The length of the list caps. */
--
2.56.1.703.gf848ed807c-goog
next prev parent reply other threads:[~2026-11-13 21:11 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-13 21:10 [PATCH 00/11] perf pmu: Layered sysfs-style PMU event and metric directories Ian Rogers
2026-11-13 21:10 ` [PATCH 01/11] perf pmu: Generalize pmu_alias_info_file() and lazy single-event sysfs loading Ian Rogers
2026-11-13 21:10 ` Ian Rogers [this message]
2026-11-13 21:10 ` [PATCH 03/11] perf pmu-layer: Introduce stateless struct pmu_layer abstraction and builtin adapter Ian Rogers
2026-11-13 21:10 ` [PATCH 04/11] perf pmu-layer: Add openat-based directory layer with literal CPUID/symlink lookup Ian Rogers
2026-11-13 21:10 ` [PATCH 05/11] perf config: Add pmu-events.dir configuration option Ian Rogers
2026-11-13 21:10 ` [PATCH 06/11] perf pmu: Switch alias resolution and enumeration to layered PMU stack Ian Rogers
2026-11-13 21:10 ` [PATCH 07/11] perf metricgroup: Route metric and metricgroup lookups through struct perf_pmu Ian Rogers
2026-11-13 21:10 ` [PATCH 08/11] perf list, jevents: Add --export-dir and --output-dir sysfs-style exporters Ian Rogers
2026-11-13 21:11 ` [PATCH 09/11] perf jevents: Add compiled-in model and sys table iterators Ian Rogers
2026-11-13 21:11 ` [PATCH 10/11] perf list: Add --export-cpuid to export compiled-in CPU models Ian Rogers
2026-11-13 21:11 ` [PATCH 11/11] perf tests: Add round-trip export, CPUID symlink, and pmu-events.dir overlay tests Ian Rogers
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=20261113211105.3118281-3-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
all inboxes | Powered by JetHome®