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 03/11] perf pmu-layer: Introduce stateless struct pmu_layer abstraction and builtin adapter
Date: Fri, 13 Nov 2026 13:10:54 -0800 [thread overview]
Message-ID: <20261113211105.3118281-4-irogers@google.com> (raw)
In-Reply-To: <20261113211105.3118281-1-irogers@google.com>
Introduce tools/perf/util/pmu-layer.[ch] defining a stateless VTable
abstraction (struct pmu_layer_ops) for resolving and enumerating PMU
events, metrics, and metricgroup descriptions top-down into struct
perf_pmu's owned aliases, metrics, and metricgroups hashmaps, sharing
struct perf_pmu_alias, struct perf_pmu_metric, and struct
perf_pmu_metricgroup privately between pmu.c and pmu-layer.c via
pmu-layer.h rather than exposing them in pmu.h.
Implement the compiled-in block-compressed adapters
(PMU_LAYER_BUILTIN_CPU and PMU_LAYER_BUILTIN_COMMON) wrapping
pmu_events_table, pmu_metrics_table, uncore/system PMU compat tables,
and describe_metricgroup() with "first non-empty attribute wins"
merging semantics.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/Build | 1 +
tools/perf/util/pmu-layer.c | 1003 +++++++++++++++++++++++++++++++++++
tools/perf/util/pmu-layer.h | 170 ++++++
tools/perf/util/pmu.c | 175 +-----
4 files changed, 1182 insertions(+), 167 deletions(-)
create mode 100644 tools/perf/util/pmu-layer.c
create mode 100644 tools/perf/util/pmu-layer.h
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 2c1f880c4c47..ae681b863ea7 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -86,6 +86,7 @@ perf-util-y += thread_map.o
perf-util-y += parse-events-flex.o
perf-util-y += parse-events-bison.o
perf-util-y += pmu.o
+perf-util-y += pmu-layer.o
perf-util-y += pmus.o
perf-util-y += pmu-flex.o
perf-util-y += pmu-bison.o
diff --git a/tools/perf/util/pmu-layer.c b/tools/perf/util/pmu-layer.c
new file mode 100644
index 000000000000..e4c50cc6d1ea
--- /dev/null
+++ b/tools/perf/util/pmu-layer.c
@@ -0,0 +1,1003 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <linux/compiler.h>
+#include <linux/err.h>
+#include <linux/zalloc.h>
+#include "debug.h"
+#include "hashmap.h"
+#include "pmu.h"
+#include "pmu-layer.h"
+#include "pmu-events/pmu-events.h"
+
+static struct pmu_layer *pmu_layers;
+static size_t nr_pmu_layers;
+static bool pmu_layers_initialized;
+
+static struct perf_pmu_alias *pmu_layer_alloc_alias(const char *name)
+{
+ struct perf_pmu_alias *alias = zalloc(sizeof(*alias));
+
+ if (!alias)
+ return NULL;
+
+ alias->name = strdup(name);
+ if (!alias->name) {
+ free(alias);
+ return NULL;
+ }
+ alias->scale = 1.0;
+ alias->unit[0] = '\0';
+ return alias;
+}
+
+static struct perf_pmu_alias *pmu_layer_get_or_create_alias(struct perf_pmu *pmu,
+ const char *name)
+{
+ struct perf_pmu_alias *alias = NULL, *old_alias = NULL;
+
+ if (hashmap__find(pmu->aliases, name, &alias))
+ return alias;
+
+ alias = pmu_layer_alloc_alias(name);
+ if (!alias)
+ return NULL;
+
+ if (hashmap__set(pmu->aliases, alias->name, alias,
+ /*old_key=*/NULL, &old_alias)) {
+ perf_pmu_free_alias(alias);
+ return NULL;
+ }
+ perf_pmu_free_alias(old_alias);
+ return alias;
+}
+
+static struct perf_pmu_metric *pmu_layer_alloc_metric(const char *name)
+{
+ struct perf_pmu_metric *metric = zalloc(sizeof(*metric));
+
+ if (!metric)
+ return NULL;
+
+ metric->metric_name = strdup(name);
+ if (!metric->metric_name) {
+ free(metric);
+ return NULL;
+ }
+ return metric;
+}
+
+static struct perf_pmu_metric *pmu_layer_get_or_create_metric(struct perf_pmu *pmu,
+ const char *name)
+{
+ struct perf_pmu_metric *metric = NULL, *old_metric = NULL;
+
+ if (hashmap__find(pmu->metrics, name, &metric))
+ return metric;
+
+ metric = pmu_layer_alloc_metric(name);
+ if (!metric)
+ return NULL;
+
+ if (hashmap__set(pmu->metrics, metric->metric_name, metric,
+ /*old_key=*/NULL, &old_metric)) {
+ perf_pmu_free_metric(metric);
+ return NULL;
+ }
+ perf_pmu_free_metric(old_metric);
+ return metric;
+}
+
+static int pmu_layer_merge_pe(struct perf_pmu_alias *alias,
+ const struct pmu_event *pe,
+ bool is_common)
+{
+ int ret = 0;
+
+ if (pe->event) {
+ if (is_common) {
+ if (!alias->legacy_terms) {
+ alias->legacy_terms = strdup(pe->event);
+ if (!alias->legacy_terms)
+ return -ENOMEM;
+ }
+ } else if (!alias->terms) {
+ alias->terms = strdup(pe->event);
+ if (!alias->terms)
+ return -ENOMEM;
+ }
+ }
+
+ if (!alias->desc && pe->desc) {
+ alias->desc = strdup(pe->desc);
+ if (!alias->desc)
+ return -ENOMEM;
+ }
+ if (!alias->long_desc && pe->long_desc) {
+ alias->long_desc = strdup(pe->long_desc);
+ if (!alias->long_desc)
+ return -ENOMEM;
+ }
+ if (!alias->topic && pe->topic) {
+ alias->topic = strdup(pe->topic);
+ if (!alias->topic)
+ return -ENOMEM;
+ }
+ if (!alias->pmu_name && pe->pmu && strcmp(pe->pmu, "default_core")) {
+ alias->pmu_name = strdup(pe->pmu);
+ if (!alias->pmu_name)
+ return -ENOMEM;
+ }
+ if (!alias->compat && pe->compat) {
+ alias->compat = strdup(pe->compat);
+ if (!alias->compat)
+ return -ENOMEM;
+ }
+
+ if (!alias->has_per_pkg && pe->perpkg) {
+ alias->per_pkg = true;
+ alias->has_per_pkg = true;
+ }
+ if (!alias->has_deprecated && pe->deprecated) {
+ alias->deprecated = true;
+ alias->has_deprecated = true;
+ }
+
+ if (pe->unit && (!alias->has_scale || !alias->has_unit)) {
+ char *unit_str = NULL;
+ double sval = 1.0;
+
+ ret = perf_pmu__convert_scale(pe->unit, &unit_str, &sval);
+ if (ret)
+ return ret;
+ if (!alias->has_scale) {
+ alias->scale = sval;
+ alias->has_scale = true;
+ }
+ if (!alias->has_unit && unit_str) {
+ snprintf(alias->unit, sizeof(alias->unit), "%s", unit_str);
+ alias->has_unit = true;
+ }
+ }
+
+ if (!alias->has_retire_lat_mean && pe->retirement_latency_mean) {
+ ret = perf_pmu__convert_scale(pe->retirement_latency_mean, NULL,
+ &alias->retirement_latency_mean);
+ if (ret)
+ return ret;
+ alias->has_retire_lat_mean = true;
+ }
+ if (!alias->has_retire_lat_min && pe->retirement_latency_min) {
+ ret = perf_pmu__convert_scale(pe->retirement_latency_min, NULL,
+ &alias->retirement_latency_min);
+ if (ret)
+ return ret;
+ alias->has_retire_lat_min = true;
+ }
+ if (!alias->has_retire_lat_max && pe->retirement_latency_max) {
+ ret = perf_pmu__convert_scale(pe->retirement_latency_max, NULL,
+ &alias->retirement_latency_max);
+ if (ret)
+ return ret;
+ alias->has_retire_lat_max = true;
+ }
+
+ return 0;
+}
+
+static int pmu_layer_merge_pm(struct perf_pmu_metric *metric,
+ const struct pmu_metric *pm)
+{
+ if (!metric->pmu && pm->pmu) {
+ metric->pmu = strdup(pm->pmu);
+ if (!metric->pmu)
+ return -ENOMEM;
+ }
+ if (!metric->metric_expr && pm->metric_expr) {
+ metric->metric_expr = strdup(pm->metric_expr);
+ if (!metric->metric_expr)
+ return -ENOMEM;
+ }
+ if (!metric->metric_group && pm->metric_group) {
+ metric->metric_group = strdup(pm->metric_group);
+ if (!metric->metric_group)
+ return -ENOMEM;
+ }
+ if (!metric->metric_threshold && pm->metric_threshold) {
+ metric->metric_threshold = strdup(pm->metric_threshold);
+ if (!metric->metric_threshold)
+ return -ENOMEM;
+ }
+ if (!metric->unit && pm->unit) {
+ metric->unit = strdup(pm->unit);
+ if (!metric->unit)
+ return -ENOMEM;
+ }
+ if (!metric->compat && pm->compat) {
+ metric->compat = strdup(pm->compat);
+ if (!metric->compat)
+ return -ENOMEM;
+ }
+ if (!metric->desc && pm->desc) {
+ metric->desc = strdup(pm->desc);
+ if (!metric->desc)
+ return -ENOMEM;
+ }
+ if (!metric->long_desc && pm->long_desc) {
+ metric->long_desc = strdup(pm->long_desc);
+ if (!metric->long_desc)
+ return -ENOMEM;
+ }
+ if (!metric->metricgroup_no_group && pm->metricgroup_no_group) {
+ metric->metricgroup_no_group = strdup(pm->metricgroup_no_group);
+ if (!metric->metricgroup_no_group)
+ return -ENOMEM;
+ }
+ if (!metric->default_metricgroup_name && pm->default_metricgroup_name) {
+ metric->default_metricgroup_name = strdup(pm->default_metricgroup_name);
+ if (!metric->default_metricgroup_name)
+ return -ENOMEM;
+ }
+ if (!metric->has_aggr_mode && pm->aggr_mode) {
+ metric->aggr_mode = pm->aggr_mode;
+ metric->has_aggr_mode = true;
+ }
+ if (!metric->has_event_grouping && pm->event_grouping != MetricGroupEvents) {
+ metric->event_grouping = pm->event_grouping;
+ metric->has_event_grouping = true;
+ }
+ if (!metric->has_default_show_events && pm->default_show_events) {
+ metric->default_show_events = true;
+ metric->has_default_show_events = true;
+ }
+ return 0;
+}
+
+struct builtin_find_event_ctx {
+ struct perf_pmu *pmu;
+ const char *name;
+ struct perf_pmu_alias *alias;
+ bool is_common;
+};
+
+static int builtin_find_event_cb(const struct pmu_event *pe,
+ const struct pmu_events_table *table __maybe_unused,
+ void *vdata)
+{
+ struct builtin_find_event_ctx *ctx = vdata;
+
+ return pmu_layer_merge_pe(ctx->alias, pe, ctx->is_common);
+}
+
+static int builtin_find_sys_event_cb(const struct pmu_event *pe,
+ const struct pmu_events_table *table __maybe_unused,
+ void *vdata)
+{
+ struct builtin_find_event_ctx *ctx = vdata;
+
+ if (!pe->name || !pe->compat || !pe->pmu)
+ return 0;
+ if (strcasecmp(pe->name, ctx->name) != 0)
+ return 0;
+ if (!perf_pmu__name_wildcard_match(ctx->pmu, pe->pmu))
+ return 0;
+ if (!pmu_uncore_identifier_match(pe->compat, ctx->pmu->id))
+ return 0;
+ return pmu_layer_merge_pe(ctx->alias, pe, /*is_common=*/false);
+}
+
+struct builtin_iter_event_ctx {
+ struct perf_pmu *pmu;
+ bool is_common;
+ bool override_existing;
+};
+
+static int pmu_layer_override_pe(struct perf_pmu_alias *alias,
+ const struct pmu_event *pe)
+{
+ if (pe->event) {
+ char *dup_terms = strdup(pe->event);
+
+ if (!dup_terms)
+ return -ENOMEM;
+ if (alias->terms_from_legacy && !alias->legacy_terms) {
+ alias->legacy_terms = alias->terms;
+ alias->terms = NULL;
+ alias->terms_from_legacy = false;
+ } else {
+ zfree(&alias->terms);
+ }
+ alias->terms = dup_terms;
+ }
+ if (pe->desc)
+ zfree(&alias->desc);
+ if (pe->long_desc)
+ zfree(&alias->long_desc);
+ if (pe->topic)
+ zfree(&alias->topic);
+ if (pe->pmu && strcmp(pe->pmu, "default_core"))
+ zfree(&alias->pmu_name);
+ if (pe->compat)
+ zfree(&alias->compat);
+ if (pe->perpkg)
+ alias->has_per_pkg = false;
+ if (pe->deprecated)
+ alias->has_deprecated = false;
+ if (pe->unit) {
+ alias->has_scale = false;
+ alias->has_unit = false;
+ }
+ if (pe->retirement_latency_mean)
+ alias->has_retire_lat_mean = false;
+ if (pe->retirement_latency_min)
+ alias->has_retire_lat_min = false;
+ if (pe->retirement_latency_max)
+ alias->has_retire_lat_max = false;
+
+ return pmu_layer_merge_pe(alias, pe, /*is_common=*/false);
+}
+
+static int builtin_for_each_event_cb(const struct pmu_event *pe,
+ const struct pmu_events_table *table __maybe_unused,
+ void *vdata)
+{
+ struct builtin_iter_event_ctx *ctx = vdata;
+ struct perf_pmu_alias *alias;
+
+ if (!pe->name)
+ return 0;
+
+ alias = pmu_layer_get_or_create_alias(ctx->pmu, pe->name);
+ if (!alias)
+ return -ENOMEM;
+ if (ctx->override_existing)
+ return pmu_layer_override_pe(alias, pe);
+ if (alias->resolved)
+ return 0;
+
+ return pmu_layer_merge_pe(alias, pe, ctx->is_common);
+}
+
+static int builtin_for_each_sys_event_cb(const struct pmu_event *pe,
+ const struct pmu_events_table *table __maybe_unused,
+ void *vdata)
+{
+ struct builtin_iter_event_ctx *ctx = vdata;
+ struct perf_pmu_alias *alias;
+
+ if (!pe->name || !pe->compat || !pe->pmu)
+ return 0;
+ if (!perf_pmu__name_wildcard_match(ctx->pmu, pe->pmu))
+ return 0;
+ if (!pmu_uncore_identifier_match(pe->compat, ctx->pmu->id))
+ return 0;
+
+ alias = pmu_layer_get_or_create_alias(ctx->pmu, pe->name);
+ if (!alias)
+ return -ENOMEM;
+ if (ctx->override_existing)
+ return pmu_layer_override_pe(alias, pe);
+ if (alias->resolved)
+ return 0;
+
+ return pmu_layer_merge_pe(alias, pe, /*is_common=*/false);
+}
+
+struct builtin_find_metric_ctx {
+ struct perf_pmu *pmu;
+ const char *name;
+ struct perf_pmu_metric *metric;
+};
+
+static int builtin_find_metric_cb(const struct pmu_metric *pm,
+ const struct pmu_metrics_table *table __maybe_unused,
+ void *vdata)
+{
+ struct builtin_find_metric_ctx *ctx = vdata;
+
+ return pmu_layer_merge_pm(ctx->metric, pm);
+}
+
+static int builtin_find_sys_metric_cb(const struct pmu_metric *pm,
+ const struct pmu_metrics_table *table __maybe_unused,
+ void *vdata)
+{
+ struct builtin_find_metric_ctx *ctx = vdata;
+
+ if (!pm->metric_name || !pm->compat || !pm->pmu)
+ return 0;
+ if (strcmp(pm->metric_name, ctx->name) != 0)
+ return 0;
+ if (!perf_pmu__name_wildcard_match(ctx->pmu, pm->pmu))
+ return 0;
+ if (!pmu_uncore_identifier_match(pm->compat, ctx->pmu->id))
+ return 0;
+ return pmu_layer_merge_pm(ctx->metric, pm);
+}
+
+static int builtin_for_each_metric_cb(const struct pmu_metric *pm,
+ const struct pmu_metrics_table *table __maybe_unused,
+ void *vdata)
+{
+ struct perf_pmu *pmu = vdata;
+ struct perf_pmu_metric *metric;
+
+ if (!pm->metric_name)
+ return 0;
+ if (pm->pmu && !perf_pmu__name_wildcard_match(pmu, pm->pmu))
+ return 0;
+
+ metric = pmu_layer_get_or_create_metric(pmu, pm->metric_name);
+ if (!metric)
+ return -ENOMEM;
+
+ return pmu_layer_merge_pm(metric, pm);
+}
+
+static int builtin_for_each_sys_metric_cb(const struct pmu_metric *pm,
+ const struct pmu_metrics_table *table __maybe_unused,
+ void *vdata)
+{
+ struct perf_pmu *pmu = vdata;
+ struct perf_pmu_metric *metric;
+
+ if (!pm->metric_name || !pm->compat || !pm->pmu)
+ return 0;
+ if (!perf_pmu__name_wildcard_match(pmu, pm->pmu))
+ return 0;
+ if (!pmu_uncore_identifier_match(pm->compat, pmu->id))
+ return 0;
+
+ metric = pmu_layer_get_or_create_metric(pmu, pm->metric_name);
+ if (!metric)
+ return -ENOMEM;
+
+ return pmu_layer_merge_pm(metric, pm);
+}
+
+static int builtin_cpu_find_event(const struct pmu_layer *layer __maybe_unused,
+ struct perf_pmu *pmu, const char *name,
+ struct perf_pmu_alias *alias)
+{
+ struct builtin_find_event_ctx ctx = {
+ .pmu = pmu,
+ .name = name,
+ .alias = alias,
+ .is_common = false,
+ };
+ int ret;
+
+ if (pmu->events_table) {
+ ret = pmu_events_table__find_event(pmu->events_table, pmu, name,
+ builtin_find_event_cb, &ctx);
+ if (ret && ret != PMU_EVENTS__NOT_FOUND)
+ return ret;
+ }
+ if (pmu->id) {
+ ret = pmu_for_each_sys_event(builtin_find_sys_event_cb, &ctx);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int builtin_cpu_for_each_event(const struct pmu_layer *layer __maybe_unused,
+ struct perf_pmu *pmu)
+{
+ struct builtin_iter_event_ctx ctx = {
+ .pmu = pmu,
+ .is_common = false,
+ };
+ int ret;
+
+ if (pmu->events_table) {
+ ret = pmu_events_table__for_each_event(pmu->events_table, pmu,
+ builtin_for_each_event_cb, &ctx);
+ if (ret)
+ return ret;
+ }
+ if (pmu->id) {
+ ret = pmu_for_each_sys_event(builtin_for_each_sys_event_cb, &ctx);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int builtin_cpu_find_metric(const struct pmu_layer *layer __maybe_unused,
+ struct perf_pmu *pmu, const char *name,
+ struct perf_pmu_metric *metric)
+{
+ struct builtin_find_metric_ctx ctx = {
+ .pmu = pmu,
+ .name = name,
+ .metric = metric,
+ };
+ int ret;
+
+ if (pmu->metrics_table) {
+ ret = pmu_metrics_table__find_metric(pmu->metrics_table, pmu, name,
+ builtin_find_metric_cb, &ctx);
+ if (ret && ret != PMU_METRICS__NOT_FOUND)
+ return ret;
+ }
+ if (pmu->id) {
+ ret = pmu_for_each_sys_metric(builtin_find_sys_metric_cb, &ctx);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int builtin_cpu_for_each_metric(const struct pmu_layer *layer __maybe_unused,
+ struct perf_pmu *pmu)
+{
+ int ret;
+
+ if (pmu->metrics_table) {
+ ret = pmu_metrics_table__for_each_metric(pmu->metrics_table,
+ builtin_for_each_metric_cb, pmu);
+ if (ret)
+ return ret;
+ }
+ if (pmu->id) {
+ ret = pmu_for_each_sys_metric(builtin_for_each_sys_metric_cb, pmu);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static const struct pmu_layer_ops builtin_cpu_layer_ops = {
+ .find_event = builtin_cpu_find_event,
+ .for_each_event = builtin_cpu_for_each_event,
+ .find_metric = builtin_cpu_find_metric,
+ .for_each_metric = builtin_cpu_for_each_metric,
+};
+
+static int builtin_common_find_event(const struct pmu_layer *layer __maybe_unused,
+ struct perf_pmu *pmu, const char *name,
+ struct perf_pmu_alias *alias)
+{
+ const struct pmu_events_table *table;
+ struct builtin_find_event_ctx ctx = {
+ .pmu = pmu,
+ .name = name,
+ .alias = alias,
+ .is_common = true,
+ };
+ int ret;
+
+ if (!pmu->is_core)
+ return 0;
+
+ table = perf_pmu__default_core_events_table();
+ if (!table)
+ return 0;
+
+ ret = pmu_events_table__find_event(table, pmu, name,
+ builtin_find_event_cb, &ctx);
+ if (ret == PMU_EVENTS__NOT_FOUND)
+ return 0;
+ return ret;
+}
+
+static int builtin_common_for_each_event(const struct pmu_layer *layer __maybe_unused,
+ struct perf_pmu *pmu)
+{
+ const struct pmu_events_table *table;
+ struct builtin_iter_event_ctx ctx = {
+ .pmu = pmu,
+ .is_common = true,
+ };
+
+ if (!pmu->is_core)
+ return 0;
+
+ table = perf_pmu__default_core_events_table();
+ if (!table)
+ return 0;
+
+ return pmu_events_table__for_each_event(table, pmu,
+ builtin_for_each_event_cb, &ctx);
+}
+
+static int builtin_common_find_metric(const struct pmu_layer *layer __maybe_unused,
+ struct perf_pmu *pmu, const char *name,
+ struct perf_pmu_metric *metric)
+{
+ const struct pmu_metrics_table *table;
+ struct builtin_find_metric_ctx ctx = {
+ .pmu = pmu,
+ .name = name,
+ .metric = metric,
+ };
+ int ret;
+
+ if (!pmu->is_core)
+ return 0;
+
+ table = pmu_metrics_table__default();
+ if (!table)
+ return 0;
+
+ ret = pmu_metrics_table__find_metric(table, pmu, name,
+ builtin_find_metric_cb, &ctx);
+ if (ret == PMU_METRICS__NOT_FOUND)
+ return 0;
+ return ret;
+}
+
+static int builtin_common_for_each_metric(const struct pmu_layer *layer __maybe_unused,
+ struct perf_pmu *pmu)
+{
+ const struct pmu_metrics_table *table;
+
+ if (!pmu->is_core)
+ return 0;
+
+ table = pmu_metrics_table__default();
+ if (!table)
+ return 0;
+
+ return pmu_metrics_table__for_each_metric(table,
+ builtin_for_each_metric_cb, pmu);
+}
+
+static int builtin_common_load_metricgroup(const struct pmu_layer *layer __maybe_unused,
+ struct perf_pmu *pmu,
+ const char *group,
+ struct perf_pmu_metricgroup *mg)
+{
+ const char *desc;
+
+ if (!pmu->is_core || mg->desc)
+ return 0;
+
+ desc = describe_metricgroup(group);
+ if (!desc)
+ return 0;
+
+ mg->desc = strdup(desc);
+ if (!mg->desc)
+ return -ENOMEM;
+ mg->from_builtin = true;
+ return 0;
+}
+
+static const struct pmu_layer_ops builtin_common_layer_ops = {
+ .find_event = builtin_common_find_event,
+ .for_each_event = builtin_common_for_each_event,
+ .find_metric = builtin_common_find_metric,
+ .for_each_metric = builtin_common_for_each_metric,
+ .load_metricgroup = builtin_common_load_metricgroup,
+};
+
+static int pmu_layers_append(enum pmu_layer_kind kind,
+ const struct pmu_layer_ops *ops,
+ const char *root_path)
+{
+ struct pmu_layer *next;
+ char *dup_path = NULL;
+
+ if (root_path) {
+ dup_path = strdup(root_path);
+ if (!dup_path)
+ return -ENOMEM;
+ }
+
+ next = realloc(pmu_layers, (nr_pmu_layers + 1) * sizeof(*pmu_layers));
+ if (!next) {
+ free(dup_path);
+ return -ENOMEM;
+ }
+ pmu_layers = next;
+ pmu_layers[nr_pmu_layers++] = (struct pmu_layer){
+ .kind = kind,
+ .ops = ops,
+ .root_path = dup_path,
+ };
+ return 0;
+}
+
+void pmu_layers__exit(void)
+{
+ for (size_t i = 0; i < nr_pmu_layers; i++)
+ zfree(&pmu_layers[i].root_path);
+ zfree(&pmu_layers);
+ nr_pmu_layers = 0;
+ pmu_layers_initialized = false;
+}
+
+static int pmu_layers_ensure_init(void)
+{
+ int ret;
+
+ if (pmu_layers_initialized)
+ return 0;
+
+ ret = pmu_layers_append(PMU_LAYER_BUILTIN_CPU, &builtin_cpu_layer_ops, NULL);
+ if (ret)
+ goto err;
+
+ ret = pmu_layers_append(PMU_LAYER_BUILTIN_COMMON, &builtin_common_layer_ops, NULL);
+ if (ret)
+ goto err;
+
+ pmu_layers_initialized = true;
+ return 0;
+
+err:
+ pmu_layers__exit();
+ return ret;
+}
+
+static void pmu_layer_finalize_aliases(struct perf_pmu *pmu, bool promote_legacy)
+{
+ struct hashmap_entry *cur, *tmp;
+ size_t bkt;
+
+ hashmap__for_each_entry_safe(pmu->aliases, cur, tmp, bkt) {
+ struct perf_pmu_alias *alias = cur->pvalue;
+
+ if (promote_legacy && !alias->terms && alias->legacy_terms) {
+ alias->terms = alias->legacy_terms;
+ alias->legacy_terms = NULL;
+ alias->terms_from_legacy = true;
+ }
+ if (!alias->terms) {
+ hashmap__delete(pmu->aliases, cur->pkey, NULL, NULL);
+ perf_pmu_free_alias(alias);
+ } else if (promote_legacy) {
+ alias->resolved = true;
+ }
+ }
+}
+
+static void pmu_layer_prune_incomplete_metrics(struct perf_pmu *pmu)
+{
+ struct hashmap_entry *cur, *tmp;
+ size_t bkt;
+
+ hashmap__for_each_entry_safe(pmu->metrics, cur, tmp, bkt) {
+ struct perf_pmu_metric *metric = cur->pvalue;
+
+ if (!metric->metric_expr) {
+ hashmap__delete(pmu->metrics, cur->pkey, NULL, NULL);
+ perf_pmu_free_metric(metric);
+ }
+ }
+}
+
+struct perf_pmu_alias *pmu_layer__find_event(struct perf_pmu *pmu,
+ const char *name)
+{
+ struct perf_pmu_alias *alias = NULL, *old_alias = NULL;
+
+ if (!pmu || !name || !name[0] || name[0] == '.' ||
+ pmu_event_file_kind(name, NULL) != PMU_EVENT_FILE_BASE)
+ return NULL;
+
+ if (hashmap__find(pmu->aliases, name, &alias))
+ return alias->terms ? alias : NULL;
+
+ if (pmu->aliases_loaded)
+ return NULL;
+
+ if (pmu_layers_ensure_init())
+ return NULL;
+
+ alias = pmu_layer_alloc_alias(name);
+ if (!alias)
+ return NULL;
+
+ for (size_t i = 0; i < nr_pmu_layers; i++) {
+ const struct pmu_layer *layer = &pmu_layers[i];
+
+ if (!layer->ops->find_event)
+ continue;
+ if (layer->ops->find_event(layer, pmu, name, alias) < 0) {
+ perf_pmu_free_alias(alias);
+ return NULL;
+ }
+ }
+
+ if (!alias->terms && alias->legacy_terms) {
+ alias->terms = alias->legacy_terms;
+ alias->legacy_terms = NULL;
+ alias->terms_from_legacy = true;
+ }
+
+ if (!alias->terms) {
+ perf_pmu_free_alias(alias);
+ return NULL;
+ }
+
+ alias->resolved = true;
+ if (hashmap__set(pmu->aliases, alias->name, alias,
+ /*old_key=*/NULL, &old_alias)) {
+ perf_pmu_free_alias(alias);
+ return NULL;
+ }
+ perf_pmu_free_alias(old_alias);
+ return alias;
+}
+
+int pmu_layer__load_events(struct perf_pmu *pmu)
+{
+ int ret;
+
+ if (!pmu || pmu->aliases_loaded)
+ return 0;
+
+ ret = pmu_layers_ensure_init();
+ if (ret)
+ return ret;
+
+ /*
+ * Any single-event entries cached before full enumeration remain in
+ * pmu->aliases; top-down iteration fills any missing events cleanly.
+ */
+ for (size_t i = 0; i < nr_pmu_layers; i++) {
+ const struct pmu_layer *layer = &pmu_layers[i];
+
+ if (!layer->ops->for_each_event)
+ continue;
+ ret = layer->ops->for_each_event(layer, pmu);
+ if (ret) {
+ pmu_layer_finalize_aliases(pmu, /*promote_legacy=*/false);
+ return ret;
+ }
+ }
+
+ pmu_layer_finalize_aliases(pmu, /*promote_legacy=*/true);
+ pmu->aliases_loaded = true;
+ return 0;
+}
+
+int pmu_layer__merge_event_table(struct perf_pmu *pmu,
+ const struct pmu_events_table *table)
+{
+ struct builtin_iter_event_ctx ctx = {
+ .pmu = pmu,
+ .is_common = false,
+ .override_existing = true,
+ };
+
+ if (!pmu || !table)
+ return 0;
+
+ return pmu_events_table__for_each_event(table, pmu,
+ builtin_for_each_event_cb, &ctx);
+}
+
+struct perf_pmu_metric *pmu_layer__find_metric(struct perf_pmu *pmu,
+ const char *name)
+{
+ struct perf_pmu_metric *metric = NULL, *old_metric = NULL;
+
+ if (!pmu || !name || !name[0] || name[0] == '.' ||
+ pmu_metric_file_kind(name, NULL) != PMU_METRIC_FILE_BASE)
+ return NULL;
+
+ if (hashmap__find(pmu->metrics, name, &metric))
+ return metric->metric_expr ? metric : NULL;
+
+ if (pmu->metrics_loaded)
+ return NULL;
+
+ if (pmu_layers_ensure_init())
+ return NULL;
+
+ metric = pmu_layer_alloc_metric(name);
+ if (!metric)
+ return NULL;
+
+ for (size_t i = 0; i < nr_pmu_layers; i++) {
+ const struct pmu_layer *layer = &pmu_layers[i];
+
+ if (!layer->ops->find_metric)
+ continue;
+ if (layer->ops->find_metric(layer, pmu, name, metric) < 0) {
+ perf_pmu_free_metric(metric);
+ return NULL;
+ }
+ }
+
+ if (!metric->metric_expr) {
+ perf_pmu_free_metric(metric);
+ return NULL;
+ }
+
+ if (hashmap__set(pmu->metrics, metric->metric_name, metric,
+ /*old_key=*/NULL, &old_metric)) {
+ perf_pmu_free_metric(metric);
+ return NULL;
+ }
+ perf_pmu_free_metric(old_metric);
+ return metric;
+}
+
+int pmu_layer__load_metrics(struct perf_pmu *pmu)
+{
+ int ret;
+
+ if (!pmu || pmu->metrics_loaded)
+ return 0;
+
+ ret = pmu_layers_ensure_init();
+ if (ret)
+ return ret;
+
+ for (size_t i = 0; i < nr_pmu_layers; i++) {
+ const struct pmu_layer *layer = &pmu_layers[i];
+
+ if (!layer->ops->for_each_metric)
+ continue;
+ ret = layer->ops->for_each_metric(layer, pmu);
+ if (ret) {
+ pmu_layer_prune_incomplete_metrics(pmu);
+ return ret;
+ }
+ }
+
+ pmu_layer_prune_incomplete_metrics(pmu);
+ pmu->metrics_loaded = true;
+ return 0;
+}
+
+const char *pmu_layer__describe_metricgroup(struct perf_pmu *pmu,
+ const char *group)
+{
+ struct perf_pmu_metricgroup *mg = NULL, *old_mg = NULL;
+
+ if (!pmu || !group || !group[0] || group[0] == '.')
+ return NULL;
+
+ if (hashmap__find(pmu->metricgroups, group, &mg))
+ return mg->desc;
+
+ if (pmu_layers_ensure_init())
+ return NULL;
+
+ mg = zalloc(sizeof(*mg));
+ if (!mg)
+ return NULL;
+
+ mg->name = strdup(group);
+ if (!mg->name) {
+ free(mg);
+ return NULL;
+ }
+
+ for (size_t i = 0; i < nr_pmu_layers; i++) {
+ const struct pmu_layer *layer = &pmu_layers[i];
+
+ if (!layer->ops->load_metricgroup)
+ continue;
+ if (layer->ops->load_metricgroup(layer, pmu, group, mg) < 0) {
+ perf_pmu_free_metricgroup(mg);
+ return NULL;
+ }
+ if (mg->desc)
+ break;
+ }
+
+ if (!mg->desc) {
+ perf_pmu_free_metricgroup(mg);
+ return NULL;
+ }
+
+ if (hashmap__set(pmu->metricgroups, mg->name, mg,
+ /*old_key=*/NULL, &old_mg)) {
+ perf_pmu_free_metricgroup(mg);
+ return NULL;
+ }
+ perf_pmu_free_metricgroup(old_mg);
+ return mg->desc;
+}
diff --git a/tools/perf/util/pmu-layer.h b/tools/perf/util/pmu-layer.h
new file mode 100644
index 000000000000..330af9415448
--- /dev/null
+++ b/tools/perf/util/pmu-layer.h
@@ -0,0 +1,170 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __PMU_LAYER_H
+#define __PMU_LAYER_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include "pmu-events/pmu-events.h"
+
+#define UNIT_MAX_LEN 31 /* max length for event unit name */
+
+struct perf_pmu;
+struct pmu_layer;
+
+enum pmu_event_file_kind {
+ PMU_EVENT_FILE_BASE,
+ PMU_EVENT_FILE_UNIT,
+ PMU_EVENT_FILE_SCALE,
+ PMU_EVENT_FILE_PER_PKG,
+ PMU_EVENT_FILE_SNAPSHOT,
+ PMU_EVENT_FILE_SCALE_UNIT,
+ PMU_EVENT_FILE_DESC,
+ PMU_EVENT_FILE_LONG_DESC,
+ PMU_EVENT_FILE_TOPIC,
+ PMU_EVENT_FILE_COMPAT,
+ PMU_EVENT_FILE_DEPRECATED,
+ PMU_EVENT_FILE_LEGACY_TERMS,
+ PMU_EVENT_FILE_RETIRE_LAT_MEAN,
+ PMU_EVENT_FILE_RETIRE_LAT_MIN,
+ PMU_EVENT_FILE_RETIRE_LAT_MAX,
+};
+
+/**
+ * struct perf_pmu_alias - Internal resolved event entry cached on
+ * struct perf_pmu->aliases (shared only between pmu.c and pmu-layer.c).
+ */
+struct perf_pmu_alias {
+ char *name;
+ char *desc;
+ char *long_desc;
+ char *topic;
+ char *terms;
+ char *legacy_terms;
+ char *pmu_name;
+ char *compat;
+ char unit[UNIT_MAX_LEN + 1];
+ double scale;
+ double retirement_latency_mean;
+ double retirement_latency_min;
+ double retirement_latency_max;
+ bool per_pkg;
+ bool snapshot;
+ bool deprecated;
+ bool legacy_deprecated_checked;
+ bool from_sysfs;
+ bool info_loaded;
+ bool resolved;
+ bool terms_from_legacy;
+ bool has_scale;
+ bool has_unit;
+ bool has_per_pkg;
+ bool has_snapshot;
+ bool has_deprecated;
+ bool has_retire_lat_mean;
+ bool has_retire_lat_min;
+ 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 - Internal resolved metric entry cached on
+ * struct perf_pmu->metrics (shared only between pmu.c and pmu-layer.c).
+ */
+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 - Internal metricgroup description cached on
+ * struct perf_pmu->metricgroups.
+ */
+struct perf_pmu_metricgroup {
+ char *name;
+ char *desc;
+ bool from_builtin;
+};
+
+enum pmu_layer_kind {
+ PMU_LAYER_USER_DIR,
+ PMU_LAYER_BUILTIN_CPU,
+ PMU_LAYER_SYSFS,
+ PMU_LAYER_BUILTIN_COMMON,
+};
+
+struct pmu_layer_ops {
+ int (*find_event)(const struct pmu_layer *layer, struct perf_pmu *pmu,
+ const char *name, struct perf_pmu_alias *alias);
+ int (*for_each_event)(const struct pmu_layer *layer,
+ struct perf_pmu *pmu);
+
+ int (*find_metric)(const struct pmu_layer *layer, struct perf_pmu *pmu,
+ const char *name, struct perf_pmu_metric *metric);
+ int (*for_each_metric)(const struct pmu_layer *layer,
+ struct perf_pmu *pmu);
+
+ int (*load_metricgroup)(const struct pmu_layer *layer,
+ struct perf_pmu *pmu,
+ const char *group,
+ struct perf_pmu_metricgroup *mg);
+};
+
+struct pmu_layer {
+ enum pmu_layer_kind kind;
+ const struct pmu_layer_ops *ops;
+ char *root_path;
+};
+
+void pmu_layers__exit(void);
+
+enum pmu_event_file_kind pmu_event_file_kind(const char *name, size_t *base_len);
+enum pmu_metric_file_kind pmu_metric_file_kind(const char *name, size_t *base_len);
+void perf_pmu_free_alias(struct perf_pmu_alias *alias);
+void perf_pmu_free_metric(struct perf_pmu_metric *metric);
+void perf_pmu_free_metricgroup(struct perf_pmu_metricgroup *mg);
+void perf_pmu_metric__to_pmu_metric(const struct perf_pmu_metric *src,
+ struct pmu_metric *dst);
+
+struct perf_pmu_alias *pmu_layer__find_event(struct perf_pmu *pmu,
+ const char *name);
+int pmu_layer__load_events(struct perf_pmu *pmu);
+int pmu_layer__merge_event_table(struct perf_pmu *pmu,
+ const struct pmu_events_table *table);
+
+struct perf_pmu_metric *pmu_layer__find_metric(struct perf_pmu *pmu,
+ const char *name);
+int pmu_layer__load_metrics(struct perf_pmu *pmu);
+
+const char *pmu_layer__describe_metricgroup(struct perf_pmu *pmu,
+ const char *group);
+
+#endif /* __PMU_LAYER_H */
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 8baeba49d2e8..685c61935f50 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -26,6 +26,7 @@
#include "debug.h"
#include "evsel.h"
#include "pmu.h"
+#include "pmu-layer.h"
#include "drm_pmu.h"
#include "hwmon_pmu.h"
#include "pmus.h"
@@ -43,8 +44,6 @@
#include "util/evsel_config.h"
#include <regex.h>
-#define UNIT_MAX_LEN 31 /* max length for event unit name */
-
enum event_source {
/* An event loaded from /sys/bus/event_source/devices/<pmu>/events. */
EVENT_SRC_SYSFS,
@@ -57,162 +56,6 @@ enum event_source {
EVENT_SRC_SYS_JSON,
};
-enum pmu_event_file_kind {
- PMU_EVENT_FILE_BASE,
- PMU_EVENT_FILE_UNIT,
- PMU_EVENT_FILE_SCALE,
- PMU_EVENT_FILE_PER_PKG,
- PMU_EVENT_FILE_SNAPSHOT,
- PMU_EVENT_FILE_SCALE_UNIT,
- PMU_EVENT_FILE_DESC,
- PMU_EVENT_FILE_LONG_DESC,
- PMU_EVENT_FILE_TOPIC,
- PMU_EVENT_FILE_COMPAT,
- PMU_EVENT_FILE_DEPRECATED,
- PMU_EVENT_FILE_LEGACY_TERMS,
- PMU_EVENT_FILE_RETIRE_LAT_MEAN,
- PMU_EVENT_FILE_RETIRE_LAT_MIN,
- PMU_EVENT_FILE_RETIRE_LAT_MAX,
-};
-
-/**
- * struct perf_pmu_alias - An event either read from sysfs, a directory
- * overlay, or builtin in pmu-events.c, created by parsing the pmu-events json
- * files.
- */
-struct perf_pmu_alias {
- /** @name: Name of the event like "mem-loads". */
- char *name;
- /** @desc: Optional short description of the event. */
- char *desc;
- /** @long_desc: Optional long description. */
- char *long_desc;
- /**
- * @topic: Optional topic such as cache or pipeline, particularly for
- * json events.
- */
- char *topic;
- /** @terms: Owned copy of the event terms. */
- char *terms;
- /**
- * @legacy_terms: If the event aliases a legacy event, holds a copy
- * of the legacy event string.
- */
- char *legacy_terms;
- /**
- * @pmu_name: The name copied from the json struct pmu_event. This can
- * differ from the PMU name as it won't have suffixes.
- */
- char *pmu_name;
- /** @compat: Optional uncore/system PMU identifier match pattern. */
- char *compat;
- /** @unit: Units for the event, such as bytes or cache lines. */
- char unit[UNIT_MAX_LEN + 1];
- /** @scale: Value to scale read counter values by. */
- double scale;
- /** @retirement_latency_mean: Value to be given for unsampled retirement latency mean. */
- double retirement_latency_mean;
- /** @retirement_latency_min: Value to be given for unsampled retirement latency min. */
- double retirement_latency_min;
- /** @retirement_latency_max: Value to be given for unsampled retirement latency max. */
- double retirement_latency_max;
- /**
- * @per_pkg: Does the file
- * <sysfs>/bus/event_source/devices/<pmu_name>/events/<name>.per-pkg or
- * equivalent json value exist and have the value 1.
- */
- bool per_pkg;
- /**
- * @snapshot: Does the file
- * <sysfs>/bus/event_source/devices/<pmu_name>/events/<name>.snapshot
- * exist and have the value 1.
- */
- bool snapshot;
- /**
- * @deprecated: Is the event hidden and so not shown in perf list by
- * default.
- */
- bool deprecated;
- /**
- * @legacy_deprecated_checked: Legacy events may not be supported by the
- * PMU and need to be checked. If they aren't supported they are marked
- * deprecated.
- */
- bool legacy_deprecated_checked;
- /** @from_sysfs: Was the alias from sysfs or a json event? */
- bool from_sysfs;
- /** @info_loaded: Have the scale, unit and other values been read from disk? */
- bool info_loaded;
- /** @resolved: Fully resolved across all PMU layers. */
- bool resolved;
- /** @terms_from_legacy: @terms was promoted from @legacy_terms. */
- bool terms_from_legacy;
- /** @has_scale: Has scale been explicitly set by a layer? */
- bool has_scale;
- /** @has_unit: Has unit been explicitly set by a layer? */
- bool has_unit;
- /** @has_per_pkg: Has per_pkg been explicitly set by a layer? */
- bool has_per_pkg;
- /** @has_snapshot: Has snapshot been explicitly set by a layer? */
- bool has_snapshot;
- /** @has_deprecated: Has deprecated been explicitly set by a layer? */
- bool has_deprecated;
- /** @has_retire_lat_mean: Has retirement_latency_mean been explicitly set? */
- bool has_retire_lat_mean;
- /** @has_retire_lat_min: Has retirement_latency_min been explicitly set? */
- bool has_retire_lat_min;
- /** @has_retire_lat_max: Has retirement_latency_max been explicitly set? */
- 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)
@@ -408,7 +251,7 @@ static const struct {
{ ".retire-lat-mean", 16, PMU_EVENT_FILE_RETIRE_LAT_MEAN },
};
-static enum pmu_event_file_kind pmu_event_file_kind(const char *name, size_t *base_len)
+enum pmu_event_file_kind pmu_event_file_kind(const char *name, size_t *base_len)
{
size_t len = strlen(name);
@@ -449,8 +292,7 @@ static const struct {
{ ".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)
+enum pmu_metric_file_kind pmu_metric_file_kind(const char *name, size_t *base_len)
{
size_t len = strlen(name);
@@ -583,7 +425,7 @@ static void read_alias_info_at(int events_dir_fd, struct perf_pmu_alias *alias)
}
/* Delete an alias entry. */
-static void perf_pmu_free_alias(struct perf_pmu_alias *alias)
+void perf_pmu_free_alias(struct perf_pmu_alias *alias)
{
if (!alias)
return;
@@ -614,7 +456,7 @@ static void perf_pmu__del_aliases(struct perf_pmu *pmu)
pmu->aliases = NULL;
}
-static void perf_pmu_free_metric(struct perf_pmu_metric *metric)
+void perf_pmu_free_metric(struct perf_pmu_metric *metric)
{
if (!metric)
return;
@@ -633,7 +475,7 @@ static void perf_pmu_free_metric(struct perf_pmu_metric *metric)
free(metric);
}
-static void perf_pmu_free_metricgroup(struct perf_pmu_metricgroup *mg)
+void perf_pmu_free_metricgroup(struct perf_pmu_metricgroup *mg)
{
if (!mg)
return;
@@ -643,9 +485,8 @@ static void perf_pmu_free_metricgroup(struct perf_pmu_metricgroup *mg)
free(mg);
}
-static __maybe_unused void
-perf_pmu_metric__to_pmu_metric(const struct perf_pmu_metric *src,
- struct pmu_metric *dst)
+void perf_pmu_metric__to_pmu_metric(const struct perf_pmu_metric *src,
+ struct pmu_metric *dst)
{
*dst = (struct pmu_metric){
.pmu = src->pmu,
--
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 ` [PATCH 02/11] perf pmu: Add metric and metricgroup storage on struct perf_pmu Ian Rogers
2026-11-13 21:10 ` Ian Rogers [this message]
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-4-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®