mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
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


  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®