Linux Perf Users
 help / color / mirror / Atom feed
From: Ian Rogers <irogers@google.com>
To: linux-perf-users@vger.kernel.org,
	 Arnaldo Carvalho de Melo <acme@kernel.org>,
	Namhyung Kim <namhyung@kernel.org>
Cc: linux-kernel@vger.kernel.org,
	Peter Zijlstra <peterz@infradead.org>,
	 Ingo Molnar <mingo@redhat.com>, Jiri Olsa <jolsa@kernel.org>,
	 Adrian Hunter <adrian.hunter@intel.com>,
	James Clark <james.clark@linaro.org>,
	 Kan Liang <kan.liang@linux.intel.com>,
	Dapeng Mi <dapeng1.mi@linux.intel.com>,
	 Thomas Falcon <thomas.falcon@intel.com>,
	John Garry <john.garry@linux.dev>,  Will Deacon <will@kernel.org>,
	Leo Yan <leo.yan@arm.com>,
	 Athira Rajeev <atrajeev@linux.ibm.com>,
	Thomas Richter <tmricht@linux.ibm.com>,
	 Ravi Bangoria <ravi.bangoria@amd.com>,
	Sandipan Das <sandipan.das@amd.com>,
	 Ian Rogers <irogers@google.com>
Subject: [PATCH v1 04/41] perf pmus: Introduce struct perf_pmus and perf_env__pmus()
Date: Tue,  3 Nov 2026 15:44:20 -0800	[thread overview]
Message-ID: <20261103234502.1008382-5-irogers@google.com> (raw)
In-Reply-To: <20261103234502.1008382-1-irogers@google.com>

Move the static global core_pmus, other_pmus, read_pmu_types,
num_core_pmus, and extended_type state from pmus.c into a new
struct perf_pmus embedded in struct perf_env and accessed via
perf_env__pmus().

Rename perf_pmus__destroy() to perf_pmus__exit() to match the
perf_env__init()/perf_env__exit() naming convention, and update it
along with perf_pmus__find_by_attr(), perf_pmus__scan_for_event(),
perf_pmus__scan_matching_wildcard(), perf_pmus__scan_for_uncore_id(),
perf_pmus__pmu_for_pmu_filter(), perf_pmus__print_pmu_events(),
perf_pmus__print_raw_pmu_events(), perf_pmus__have_event(),
perf_pmus__num_core_pmus(), perf_pmus__supports_extended_type(),
perf_pmus__add_test_pmu(), and perf_pmus__add_test_hwmon_pmu() to take
struct perf_pmus *.

To keep this commit reviewable, the high-fanout perf_pmus__find(),
perf_pmus__find_by_type(), perf_pmus__find_core_pmu(),
perf_pmus__scan(), and perf_pmus__scan_core() helpers are updated to
take struct perf_pmus * in the next two commits.

Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/arch/x86/tests/hybrid.c            |   3 +-
 tools/perf/bench/pmu-scan.c                   |   7 +-
 tools/perf/builtin-list.c                     |  19 +-
 tools/perf/builtin-record.c                   |   2 +-
 tools/perf/builtin-stat.c                     |   2 +-
 tools/perf/tests/hwmon_pmu.c                  |   4 +-
 tools/perf/tests/mmap-basic.c                 |   4 +-
 tools/perf/tests/parse-events.c               |  15 +-
 tools/perf/tests/pmu.c                        |   3 +-
 tools/perf/util/cputopo.c                     |   3 +-
 tools/perf/util/env.c                         |  12 +-
 tools/perf/util/env.h                         |  10 +
 tools/perf/util/evlist.c                      |  13 +-
 tools/perf/util/evlist.h                      |   2 +
 tools/perf/util/evsel.c                       |  14 +-
 .../util/kvm-stat-arch/kvm-stat-powerpc.c     |   3 +-
 tools/perf/util/metricgroup.c                 |   6 +-
 tools/perf/util/parse-events.c                |  44 ++-
 tools/perf/util/parse-events.h                |   3 +
 .../perf/util/perf-regs-arch/perf_regs_x86.c  |   7 +-
 tools/perf/util/pmu.c                         |   9 +-
 tools/perf/util/pmu.h                         |   3 +
 tools/perf/util/pmus.c                        | 343 +++++++++++-------
 tools/perf/util/pmus.h                        | 205 ++++++++++-
 tools/perf/util/print-events.c                |   7 +-
 tools/perf/util/session.c                     |  11 +
 tools/perf/util/stat-display.c                |   4 +-
 tools/perf/util/stat-shadow.c                 |   3 +-
 28 files changed, 560 insertions(+), 201 deletions(-)

diff --git a/tools/perf/arch/x86/tests/hybrid.c b/tools/perf/arch/x86/tests/hybrid.c
index 0477e17b8e53..011217998a3e 100644
--- a/tools/perf/arch/x86/tests/hybrid.c
+++ b/tools/perf/arch/x86/tests/hybrid.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 #include "arch-tests.h"
 #include "debug.h"
+#include "env.h"
 #include "evlist.h"
 #include "evsel.h"
 #include "pmu.h"
@@ -303,7 +304,7 @@ static int test_events(const struct evlist_test *events, int cnt)
 
 int test__hybrid(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
 {
-	if (perf_pmus__num_core_pmus() == 1)
+	if (perf_pmus__num_core_pmus(perf_env__pmus(host_env())) == 1)
 		return TEST_SKIP;
 
 	return test_events(test__hybrid_events, ARRAY_SIZE(test__hybrid_events));
diff --git a/tools/perf/bench/pmu-scan.c b/tools/perf/bench/pmu-scan.c
index 14a464ad8cea..1d7dd4b203e0 100644
--- a/tools/perf/bench/pmu-scan.c
+++ b/tools/perf/bench/pmu-scan.c
@@ -8,6 +8,7 @@
 #include <stdio.h>
 #include "bench.h"
 #include "util/debug.h"
+#include "util/env.h"
 #include "util/pmu.h"
 #include "util/pmus.h"
 #include "util/stat.h"
@@ -42,6 +43,7 @@ static struct pmu_scan_result *results;
 
 static int save_result(void)
 {
+	struct perf_pmus *pmus = perf_env__pmus(host_env());
 	struct perf_pmu *pmu = NULL;
 	struct list_head *list;
 	struct pmu_scan_result *r;
@@ -69,7 +71,7 @@ static int save_result(void)
 		nr_pmus++;
 	}
 
-	perf_pmus__destroy();
+	perf_pmus__exit(pmus);
 	return 0;
 }
 
@@ -128,6 +130,7 @@ static void delete_result(void)
 
 static int run_pmu_scan(void)
 {
+	struct perf_pmus *pmus = perf_env__pmus(host_env());
 	struct stats stats;
 	struct timeval start, end, diff;
 	double time_average, time_stddev;
@@ -158,7 +161,7 @@ static int run_pmu_scan(void)
 			update_stats(&stats, runtime_us);
 
 			ret = check_result(core_only);
-			perf_pmus__destroy();
+			perf_pmus__exit(pmus);
 			if (ret < 0)
 				break;
 		}
diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
index 47668d66c395..573d13baa7fd 100644
--- a/tools/perf/builtin-list.c
+++ b/tools/perf/builtin-list.c
@@ -11,6 +11,7 @@
 #include "builtin.h"
 
 #include "util/print-events.h"
+#include "util/env.h"
 #include "util/pmus.h"
 #include "util/pmu.h"
 #include "util/debug.h"
@@ -567,6 +568,7 @@ static int list__tui(int argc, const char **argv)
 
 int cmd_list(int argc, const char **argv)
 {
+	struct perf_pmus *pmus = perf_env__pmus(host_env());
 	int i, ret = 0;
 	struct print_state default_ps = {
 		.fp = stdout,
@@ -660,7 +662,8 @@ int cmd_list(int argc, const char **argv)
 		if (unit_name)
 			ps->pmu_glob = strdup(unit_name);
 		else if (cputype) {
-			const struct perf_pmu *pmu = perf_pmus__pmu_for_pmu_filter(cputype);
+			const struct perf_pmu *pmu =
+				perf_pmus__pmu_for_pmu_filter(pmus, cputype);
 
 			if (!pmu) {
 				pr_err("ERROR: cputype is not supported!\n");
@@ -692,7 +695,7 @@ int cmd_list(int argc, const char **argv)
 				ret = -1;
 				goto out;
 			}
-			perf_pmus__print_pmu_events(&print_cb, ps);
+			perf_pmus__print_pmu_events(pmus, &print_cb, ps);
 			zfree(&default_ps.pmu_glob);
 			default_ps.pmu_glob = old_pmu_glob;
 		} else if (strcmp(argv[i], "hw") == 0 ||
@@ -704,7 +707,7 @@ int cmd_list(int argc, const char **argv)
 				ret = -1;
 				goto out;
 			}
-			perf_pmus__print_pmu_events(&print_cb, ps);
+			perf_pmus__print_pmu_events(pmus, &print_cb, ps);
 			zfree(&ps->event_glob);
 			ps->event_glob = old_event_glob;
 		} else if (strcmp(argv[i], "sw") == 0 ||
@@ -718,7 +721,7 @@ int cmd_list(int argc, const char **argv)
 					ret = -1;
 					goto out;
 				}
-				perf_pmus__print_pmu_events(&print_cb, ps);
+				perf_pmus__print_pmu_events(pmus, &print_cb, ps);
 				zfree(&ps->pmu_glob);
 			}
 			ps->pmu_glob = old_pmu_glob;
@@ -731,12 +734,12 @@ int cmd_list(int argc, const char **argv)
 				ret = -1;
 				goto out;
 			}
-			perf_pmus__print_pmu_events(&print_cb, ps);
+			perf_pmus__print_pmu_events(pmus, &print_cb, ps);
 			zfree(&ps->event_glob);
 			ps->event_glob = old_event_glob;
 		} else if (strcmp(argv[i], "pmu") == 0) {
 			ps->exclude_abi = true;
-			perf_pmus__print_pmu_events(&print_cb, ps);
+			perf_pmus__print_pmu_events(pmus, &print_cb, ps);
 			ps->exclude_abi = false;
 		} else if (strcmp(argv[i], "sdt") == 0)
 			print_sdt_events(&print_cb, ps);
@@ -770,7 +773,7 @@ int cmd_list(int argc, const char **argv)
 				ret = -1;
 				goto out;
 			}
-			perf_pmus__print_pmu_events(&print_cb, ps);
+			perf_pmus__print_pmu_events(pmus, &print_cb, ps);
 			zfree(&ps->pmu_glob);
 			ps->pmu_glob = old_pmu_glob;
 			print_sdt_events(&print_cb, ps);
@@ -785,7 +788,7 @@ int cmd_list(int argc, const char **argv)
 				continue;
 			}
 			ps->event_glob = s;
-			perf_pmus__print_pmu_events(&print_cb, ps);
+			perf_pmus__print_pmu_events(pmus, &print_cb, ps);
 			print_sdt_events(&print_cb, ps);
 			ps->metrics = true;
 			ps->metricgroups = true;
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index ad81458989b5..7de04919d194 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -936,7 +936,7 @@ static int record__config_tracking_events(struct record *rec)
 	 * delay of waiting or event synthesis.
 	 */
 	if (opts->target.initial_delay || target__has_cpu(&opts->target) ||
-	    perf_pmus__num_core_pmus() > 1) {
+	    perf_pmus__num_core_pmus(perf_env__pmus(host_env())) > 1) {
 		/*
 		 * User space tasks can migrate between CPUs, so when tracing
 		 * selected CPUs, sideband for all CPUs is still needed.
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 37752d6bfef4..8706798290eb 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -1209,7 +1209,7 @@ static int parse_cputype(const struct option *opt,
 		return -1;
 	}
 
-	pmu = perf_pmus__pmu_for_pmu_filter(str);
+	pmu = perf_pmus__pmu_for_pmu_filter(perf_env__pmus(host_env()), str);
 	if (!pmu) {
 		fprintf(stderr, "--cputype %s is not supported!\n", str);
 		return -1;
diff --git a/tools/perf/tests/hwmon_pmu.c b/tools/perf/tests/hwmon_pmu.c
index e26b3fe3fab1..8f3d09d3f2a1 100644
--- a/tools/perf/tests/hwmon_pmu.c
+++ b/tools/perf/tests/hwmon_pmu.c
@@ -11,6 +11,7 @@
 #include <sys/stat.h>
 
 #include "debug.h"
+#include "env.h"
 #include "evlist.h"
 #include "parse-events.h"
 #include "pmus.h"
@@ -137,7 +138,8 @@ static struct perf_pmu *test_pmu_get(char *dir, size_t sz)
 	}
 
 	/* Make the PMU reading the files created above. */
-	hwm = perf_pmus__add_test_hwmon_pmu(dir, "hwmon1234", test_hwmon_name);
+	hwm = perf_pmus__add_test_hwmon_pmu(perf_env__pmus(host_env()),
+					    dir, "hwmon1234", test_hwmon_name);
 	if (!hwm)
 		pr_err("Test hwmon creation failed\n");
 
diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c
index 602a78d48b10..dada4af5fb68 100644
--- a/tools/perf/tests/mmap-basic.c
+++ b/tools/perf/tests/mmap-basic.c
@@ -17,6 +17,7 @@
 
 #include "cpumap.h"
 #include "debug.h"
+#include "env.h"
 #include "event.h"
 #include "evlist.h"
 #include "evsel.h"
@@ -264,6 +265,7 @@ static enum user_read_state set_user_read(struct perf_pmu *pmu,
 
 static int test_stat_user_read(u64 event, enum user_read_state enabled)
 {
+	struct perf_pmus *pmus = perf_env__pmus(host_env());
 	struct perf_pmu *pmu = NULL;
 	struct perf_thread_map *threads = perf_thread_map__new_dummy();
 	int ret = TEST_OK;
@@ -279,7 +281,7 @@ static int test_stat_user_read(u64 event, enum user_read_state enabled)
 		enum user_read_state saved_user_read_state = set_user_read(pmu, enabled);
 		struct perf_event_attr attr = {
 			.type	= PERF_TYPE_HARDWARE,
-			.config	= perf_pmus__supports_extended_type()
+			.config	= perf_pmus__supports_extended_type(pmus)
 			? event | ((u64)pmu->type << PERF_PMU_TYPE_SHIFT)
 				: event,
 		};
diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c
index 5f6f95c8a5b4..8969bcf004ea 100644
--- a/tools/perf/tests/parse-events.c
+++ b/tools/perf/tests/parse-events.c
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 #include "parse-events.h"
+#include "env.h"
 #include "evsel.h"
 #include "evsel_fprintf.h"
 #include "evlist.h"
@@ -68,7 +69,7 @@ static int num_core_entries(struct evlist *evlist)
 		if (!evsel->pmu->is_core)
 			continue;
 		if (core_pmu != evsel->pmu && core_pmu != NULL)
-			return perf_pmus__num_core_pmus();
+			return perf_pmus__num_core_pmus(perf_env__pmus(host_env()));
 		core_pmu = evsel->pmu;
 	}
 	return 1;
@@ -1022,7 +1023,8 @@ static int test__group3(struct evlist *evlist __maybe_unused)
 	struct evsel *evsel, *group1_leader = NULL, *group2_leader = NULL;
 
 	TEST_ASSERT_EVLIST("wrong number of entries",
-			   evlist__nr_entries(evlist) == (3 * perf_pmus__num_core_pmus() + 2),
+			   evlist__nr_entries(evlist) ==
+			   (3 * perf_pmus__num_core_pmus(perf_env__pmus(host_env())) + 2),
 			   evlist);
 	/*
 	 * Currently the software event won't be grouped with the hardware event
@@ -1582,6 +1584,7 @@ static int test__checkevent_pinned_modifier(struct evlist *evlist)
 
 static int test__pinned_group(struct evlist *evlist)
 {
+	struct perf_pmus *pmus = perf_env__pmus(host_env());
 	struct evsel *evsel = NULL, *leader;
 
 	TEST_ASSERT_EVLIST("wrong number of entries",
@@ -1597,7 +1600,7 @@ static int test__pinned_group(struct evlist *evlist)
 		TEST_ASSERT_EVSEL("wrong group name", !evsel->group_name, evsel);
 		TEST_ASSERT_EVSEL("wrong leader", evsel__has_leader(evsel, leader), evsel);
 		/* TODO: The group modifier is not copied to the split group leader. */
-		if (perf_pmus__num_core_pmus() == 1)
+		if (perf_pmus__num_core_pmus(pmus) == 1)
 			TEST_ASSERT_EVSEL("wrong pinned", evsel->core.attr.pinned, evsel);
 
 		/* cache-misses - can not be pinned, but will go on with the leader */
@@ -1635,6 +1638,7 @@ static int test__checkevent_exclusive_modifier(struct evlist *evlist)
 
 static int test__exclusive_group(struct evlist *evlist)
 {
+	struct perf_pmus *pmus = perf_env__pmus(host_env());
 	struct evsel *evsel = NULL, *leader;
 
 	TEST_ASSERT_EVLIST("wrong number of entries",
@@ -1650,7 +1654,7 @@ static int test__exclusive_group(struct evlist *evlist)
 		TEST_ASSERT_EVSEL("wrong group name", !evsel->group_name, evsel);
 		TEST_ASSERT_EVSEL("wrong leader", evsel__has_leader(evsel, leader), evsel);
 		/* TODO: The group modifier is not copied to the split group leader. */
-		if (perf_pmus__num_core_pmus() == 1)
+		if (perf_pmus__num_core_pmus(pmus) == 1)
 			TEST_ASSERT_EVSEL("wrong exclusive", evsel->core.attr.exclusive, evsel);
 
 		/* cache-misses - can not be pinned, but will go on with the leader */
@@ -1803,7 +1807,8 @@ static int test__ratio_to_prev(struct evlist *evlist)
 	struct evsel *evsel, *leader;
 
 	TEST_ASSERT_VAL("wrong number of entries",
-			2 * perf_pmus__num_core_pmus() == evlist__nr_entries(evlist));
+			2 * perf_pmus__num_core_pmus(perf_env__pmus(host_env())) ==
+			evlist__nr_entries(evlist));
 
 	evlist__for_each_entry(evlist, evsel) {
 		if (evsel != evsel__leader(evsel) ||
diff --git a/tools/perf/tests/pmu.c b/tools/perf/tests/pmu.c
index 13e8d7fa80af..0a4d52d989bc 100644
--- a/tools/perf/tests/pmu.c
+++ b/tools/perf/tests/pmu.c
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 #include "evlist.h"
+#include "env.h"
 #include "evsel.h"
 #include "parse-events.h"
 #include "pmu.h"
@@ -143,7 +144,7 @@ static struct perf_pmu *test_pmu_get(char *dir, size_t sz)
 	close(file);
 
 	/* Make the PMU reading the files created above. */
-	pmu = perf_pmus__add_test_pmu(dirfd, "perf-pmu-test");
+	pmu = perf_pmus__add_test_pmu(perf_env__pmus(host_env()), dirfd, "perf-pmu-test");
 	if (!pmu)
 		pr_err("Test PMU creation failed\n");
 
diff --git a/tools/perf/util/cputopo.c b/tools/perf/util/cputopo.c
index e0091804fe98..04ab4d010149 100644
--- a/tools/perf/util/cputopo.c
+++ b/tools/perf/util/cputopo.c
@@ -470,9 +470,10 @@ static int load_hybrid_node(struct hybrid_topology_node *node,
 
 struct hybrid_topology *hybrid_topology__new(void)
 {
+	struct perf_pmus *pmus = perf_env__pmus(host_env());
 	struct perf_pmu *pmu = NULL;
 	struct hybrid_topology *tp = NULL;
-	int nr = perf_pmus__num_core_pmus(), i = 0;
+	int nr = perf_pmus__num_core_pmus(pmus), i = 0;
 
 	if (nr <= 1)
 		return NULL;
diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
index 1fe6ce84e607..5f16920f0a89 100644
--- a/tools/perf/util/env.c
+++ b/tools/perf/util/env.c
@@ -261,6 +261,7 @@ static struct perf_env __host_env = {
 	},
 	.kernel_is_64_bit = -1,
 	.lock = { .lock = __PERF_MUTEX_INITIALIZER },
+	.pmus = PERF_PMUS_INIT(__host_env.pmus),
 };
 
 void perf_env__exit(struct perf_env *env)
@@ -316,6 +317,8 @@ void perf_env__exit(struct perf_env *env)
 	zfree(&env->pmu_caps);
 	zfree(&env->memory_ranges);
 	free_cpu_domain_info(env->cpu_domain, env->schedstat_version, env->nr_cpus_avail);
+	perf_pmus__exit(&env->pmus);
+	mutex_destroy(&env->pmus.lock);
 }
 
 void perf_env__init(struct perf_env *env)
@@ -328,6 +331,7 @@ void perf_env__init(struct perf_env *env)
 #endif
 	env->kernel_is_64_bit = -1;
 	mutex_init(&env->lock);
+	perf_pmus__init(&env->pmus);
 }
 
 struct perf_env *host_env(void)
@@ -335,6 +339,11 @@ struct perf_env *host_env(void)
 	return &__host_env;
 }
 
+struct perf_pmus *perf_env__pmus(struct perf_env *env)
+{
+	return &env->pmus;
+}
+
 static const char *uts_machine(void)
 {
 	static struct utsname uts;
@@ -595,12 +604,13 @@ static int __perf_env__read_core_pmu_caps(const struct perf_pmu *pmu,
 
 int perf_env__read_core_pmu_caps(struct perf_env *env)
 {
+	struct perf_pmus *pmus = perf_env__pmus(host_env());
 	struct pmu_caps *pmu_caps;
 	struct perf_pmu *pmu = NULL;
 	int nr_pmu, i = 0, j;
 	int ret;
 
-	nr_pmu = perf_pmus__num_core_pmus();
+	nr_pmu = perf_pmus__num_core_pmus(pmus);
 
 	if (!nr_pmu)
 		return -ENODEV;
diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h
index 134102148a5c..e38e953060c9 100644
--- a/tools/perf/util/env.h
+++ b/tools/perf/util/env.h
@@ -5,6 +5,7 @@
 #include <linux/types.h>
 #include <linux/rbtree.h>
 #include "cpumap.h"
+#include "pmus.h"
 #include "rwsem.h"
 #include "mutex.h"
 
@@ -169,6 +170,7 @@ struct perf_env {
 	} clock;
 	/* Protects lazy environment initialization (e.g. os_release, e_machine). */
 	struct mutex		lock;
+	struct perf_pmus	pmus;
 };
 
 enum perf_compress_type {
@@ -231,6 +233,14 @@ void perf_env__init(struct perf_env *env);
  * Return: Pointer to the live host's struct perf_env.
  */
 struct perf_env *host_env(void);
+
+/**
+ * perf_env__pmus() - Return the struct perf_pmus associated with @env.
+ * @env: Environment whose PMU collection to retrieve.
+ *
+ * Return: Pointer to the struct perf_pmus for @env.
+ */
+struct perf_pmus *perf_env__pmus(struct perf_env *env);
 #ifdef HAVE_LIBBPF_SUPPORT
 bool __perf_env__insert_bpf_prog_info(struct perf_env *env,
 				      struct bpf_prog_info_node *info_node);
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index c2402e4791b6..4e641ccc176f 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -64,6 +64,7 @@
 #include "pmus.h"
 #include "record.h"
 #include "sample.h"
+#include "session.h"
 #include "stat.h"
 #include "strbuf.h"
 #include "string2.h"
@@ -2941,11 +2942,14 @@ static bool evlist__disable_uniquify(struct evlist *evlist)
 			return false;
 		first = false;
 		if (counter->pmu) {
+			struct perf_pmus *pmus =
+				counter->pmu->pmus ?: perf_env__pmus(host_env());
+
 			/* Allow uniquify for uncore PMUs. */
 			if (!counter->pmu->is_core)
 				return false;
 			/* Keep hybrid event names uniquified for clarity. */
-			if (perf_pmus__num_core_pmus() > 1)
+			if (perf_pmus__num_core_pmus(pmus) > 1)
 				return false;
 		}
 		last_pmu = counter->pmu;
@@ -3006,3 +3010,10 @@ bool evlist__needs_bpf_sb_event(struct evlist *evlist)
 
 	return false;
 }
+
+struct perf_env *evlist__env(struct evlist *evlist)
+{
+	struct perf_session *session = evlist ? evlist__session(evlist) : NULL;
+
+	return session ? perf_session__env(session) : NULL;
+}
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index 0f380421e03d..fc32979e08b8 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -23,6 +23,7 @@
 #include "rblist.h"
 
 struct perf_cpu_map;
+struct perf_env;
 struct perf_stat_config;
 struct pollfd;
 struct record_opts;
@@ -684,5 +685,6 @@ void evlist__warn_user_requested_cpus(struct evlist *evlist, const char *cpu_lis
 void evlist__uniquify_evsel_names(struct evlist *evlist, const struct perf_stat_config *config);
 bool evlist__has_bpf_output(struct evlist *evlist);
 bool evlist__needs_bpf_sb_event(struct evlist *evlist);
+struct perf_env *evlist__env(struct evlist *evlist);
 
 #endif /* __PERF_EVLIST_H */
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index d060fca8090f..34ee1db63b9c 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -2290,9 +2290,12 @@ bool __evsel__match(const struct evsel *evsel, u32 type, u64 config)
 		return true;
 	if (type != PERF_TYPE_HARDWARE && type != PERF_TYPE_HW_CACHE)
 		return false;
-	if ((e_type == PERF_TYPE_HARDWARE || e_type == PERF_TYPE_HW_CACHE) &&
-		perf_pmus__supports_extended_type())
-		e_config &= PERF_HW_EVENT_MASK;
+	if (e_type == PERF_TYPE_HARDWARE || e_type == PERF_TYPE_HW_CACHE) {
+		struct perf_env *env = evsel__env((struct evsel *)evsel) ?: host_env();
+
+		if (perf_pmus__supports_extended_type(perf_env__pmus(env)))
+			e_config &= PERF_HW_EVENT_MASK;
+	}
 	if (e_type == type && e_config == config)
 		return true;
 	if (type == PERF_TYPE_HARDWARE && evsel->pmu && evsel->pmu->is_core &&
@@ -4779,10 +4782,13 @@ void evsel__zero_per_pkg(struct evsel *evsel)
  */
 bool evsel__is_hybrid(const struct evsel *evsel)
 {
+	struct perf_env *env;
+
 	if (!evsel->core.is_pmu_core)
 		return false;
 
-	return perf_pmus__num_core_pmus() > 1;
+	env = evsel__env((struct evsel *)evsel) ?: host_env();
+	return perf_pmus__num_core_pmus(perf_env__pmus(env)) > 1;
 }
 
 struct evsel *evsel__leader(const struct evsel *evsel)
diff --git a/tools/perf/util/kvm-stat-arch/kvm-stat-powerpc.c b/tools/perf/util/kvm-stat-arch/kvm-stat-powerpc.c
index 315b24f9a592..d3435174106b 100644
--- a/tools/perf/util/kvm-stat-arch/kvm-stat-powerpc.c
+++ b/tools/perf/util/kvm-stat-arch/kvm-stat-powerpc.c
@@ -3,6 +3,7 @@
 #include "../kvm-stat.h"
 #include "../parse-events.h"
 #include "../debug.h"
+#include "../env.h"
 #include "../evsel.h"
 #include "../evlist.h"
 #include "../pmus.h"
@@ -178,7 +179,7 @@ int __kvm_add_default_arch_event_powerpc(int *argc, const char **argv)
 {
 	int j = *argc;
 
-	if (!perf_pmus__have_event("trace_imc", "trace_cycles"))
+	if (!perf_pmus__have_event(perf_env__pmus(host_env()), "trace_imc", "trace_cycles"))
 		return -EINVAL;
 
 	argv[j++] = "-e";
diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index 779fe56fe8d0..c8d64573e4c0 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -297,8 +297,10 @@ static int setup_metric_events(const char *pmu, struct hashmap *ids,
 	const char *metric_id;
 	struct evsel *ev;
 	size_t ids_size, matched_events, i;
+	struct perf_pmus *pmus = perf_env__pmus(evlist__env(metric_evlist) ?: host_env());
 	bool all_pmus = !strcmp(pmu, "all") || !strcmp(pmu, "default_core") ||
-			perf_pmus__num_core_pmus() == 1 || !is_pmu_core(pmu);
+			perf_pmus__num_core_pmus(pmus) == 1 ||
+			!is_pmu_core(pmu);
 
 	*out_metric_events = NULL;
 	ids_size = hashmap__size(ids);
@@ -419,7 +421,7 @@ static int metricgroup__sys_event_iter(const struct pmu_metric *pm,
 		return 0;
 
 	/* Only process with the iterator if there is a a PMU that matches the ID. */
-	pmu = perf_pmus__scan_for_uncore_id(pmu, pm->compat);
+	pmu = perf_pmus__scan_for_uncore_id(perf_env__pmus(host_env()), pmu, pm->compat);
 	return pmu ? d->fn(pm, table, d->data) : 0;
 }
 
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 5ca6b93d8a2a..b56f0e2d1542 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -174,7 +174,8 @@ static struct evsel *
 __add_event(struct list_head *list, int *idx,
 	    struct perf_event_attr *attr,
 	    bool init_attr,
-	    const char *name, const char *metric_id, struct perf_pmu *pmu,
+	    const char *name, const char *metric_id,
+	    struct perf_pmus *pmus, struct perf_pmu *pmu,
 	    struct list_head *config_terms, struct evsel *first_wildcard_match,
 	    struct perf_cpu_map *user_cpus, u64 alternate_hw_config)
 {
@@ -224,7 +225,7 @@ __add_event(struct list_head *list, int *idx,
 	 * warnings won't be generated.
 	 */
 	if (!pmu)
-		pmu = perf_pmus__find_by_attr(attr);
+		pmu = perf_pmus__find_by_attr(pmus, attr);
 
 	if (pmu) {
 		is_pmu_core = pmu->is_core;
@@ -298,19 +299,21 @@ struct evsel *parse_events__add_event(int idx, struct perf_event_attr *attr,
 				      const char *name, const char *metric_id,
 				      struct perf_pmu *pmu)
 {
+	struct perf_pmus *pmus = pmu && pmu->pmus ? pmu->pmus : perf_env__pmus(host_env());
+
 	return __add_event(/*list=*/NULL, &idx, attr, /*init_attr=*/false, name,
-			   metric_id, pmu, /*config_terms=*/NULL,
+			   metric_id, pmus, pmu, /*config_terms=*/NULL,
 			   /*first_wildcard_match=*/NULL, /*cpu_list=*/NULL,
 			   /*alternate_hw_config=*/PERF_COUNT_HW_MAX);
 }
 
-static int add_event(struct list_head *list, int *idx,
+static int add_event(struct list_head *list, struct parse_events_state *parse_state,
 		     struct perf_event_attr *attr, const char *name,
 		     const char *metric_id, struct list_head *config_terms,
 		     u64 alternate_hw_config)
 {
-	return __add_event(list, idx, attr, /*init_attr*/true, name, metric_id,
-			   /*pmu=*/NULL, config_terms,
+	return __add_event(list, &parse_state->idx, attr, /*init_attr*/true, name, metric_id,
+			   parse_state->pmus, /*pmu=*/NULL, config_terms,
 			   /*first_wildcard_match=*/NULL, /*cpu_list=*/NULL,
 			   alternate_hw_config) ? 0 : -ENOMEM;
 }
@@ -415,7 +418,7 @@ int parse_events__decode_legacy_cache(const char *name, int extended_pmu_type, _
 		cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS;
 
 	*config = cache_type | (cache_op << 8) | (cache_result << 16);
-	if (perf_pmus__supports_extended_type())
+	if (perf_pmus__host_supports_extended_type())
 		*config |= (__u64)extended_pmu_type << PERF_PMU_TYPE_SHIFT;
 	return 0;
 }
@@ -671,7 +674,7 @@ int parse_events_add_breakpoint(struct parse_events_state *parse_state,
 
 	name = get_config_name(head_config);
 
-	return add_event(list, &parse_state->idx, &attr, name, /*mertic_id=*/NULL,
+	return add_event(list, parse_state, &attr, name, /*mertic_id=*/NULL,
 			&config_terms, /*alternate_hw_config=*/PERF_COUNT_HW_MAX);
 }
 
@@ -1012,7 +1015,7 @@ static int config_term_pmu(struct perf_event_attr *attr,
 		if (!check_pmu_is_core(attr->type, term, parse_state->error))
 			return -EINVAL;
 		attr->config = term->val.num;
-		if (perf_pmus__supports_extended_type())
+		if (perf_pmus__supports_extended_type(parse_state->pmus))
 			attr->config |= (__u64)attr->type << PERF_PMU_TYPE_SHIFT;
 		attr->type = PERF_TYPE_HARDWARE;
 		return 0;
@@ -1037,7 +1040,7 @@ static int config_term_pmu(struct perf_event_attr *attr,
 		if (!check_pmu_is_core(attr->type, term, parse_state->error))
 			return -EINVAL;
 		attr->config = term->val.num;
-		if (perf_pmus__supports_extended_type())
+		if (perf_pmus__supports_extended_type(parse_state->pmus))
 			attr->config |= (__u64)attr->type << PERF_PMU_TYPE_SHIFT;
 		attr->type = PERF_TYPE_HW_CACHE;
 		return 0;
@@ -1412,7 +1415,7 @@ static int __parse_events_add_numeric(struct parse_events_state *parse_state,
 	attr.type = type;
 	attr.config = config;
 	if (extended_type && (type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE)) {
-		assert(perf_pmus__supports_extended_type());
+		assert(perf_pmus__supports_extended_type(parse_state->pmus));
 		attr.config |= (u64)extended_type << PERF_PMU_TYPE_SHIFT;
 	}
 
@@ -1428,8 +1431,9 @@ static int __parse_events_add_numeric(struct parse_events_state *parse_state,
 	metric_id = get_config_metric_id(head_config);
 	cpus = get_config_cpu(head_config, parse_state->fake_pmu);
 	ret = __add_event(list, &parse_state->idx, &attr, /*init_attr*/true, name,
-			metric_id, pmu, &config_terms, first_wildcard_match,
-			cpus, /*alternate_hw_config=*/PERF_COUNT_HW_MAX) ? 0 : -ENOMEM;
+			metric_id, parse_state->pmus, pmu, &config_terms,
+			first_wildcard_match, cpus,
+			/*alternate_hw_config=*/PERF_COUNT_HW_MAX) ? 0 : -ENOMEM;
 	perf_cpu_map__put(cpus);
 	free_config_terms(&config_terms);
 	return ret;
@@ -1445,7 +1449,7 @@ int parse_events_add_numeric(struct parse_events_state *parse_state,
 	bool found_supported = false;
 
 	/* Wildcards on numeric values are only supported by core PMUs. */
-	if (wildcard && perf_pmus__supports_extended_type()) {
+	if (wildcard && perf_pmus__supports_extended_type(parse_state->pmus)) {
 		struct evsel *first_wildcard_match = NULL;
 		while ((pmu = perf_pmus__scan_core(pmu)) != NULL) {
 			int ret;
@@ -1524,7 +1528,7 @@ static int parse_events_add_pmu(struct parse_events_state *parse_state,
 	if (!const_parsed_terms || list_empty(&const_parsed_terms->terms)) {
 		evsel = __add_event(list, &parse_state->idx, &attr,
 				    /*init_attr=*/true, /*name=*/NULL,
-				    /*metric_id=*/NULL, pmu,
+				    /*metric_id=*/NULL, parse_state->pmus, pmu,
 				    /*config_terms=*/NULL, first_wildcard_match,
 				    /*cpu_list=*/NULL, alternate_hw_config);
 		return evsel ? 0 : -ENOMEM;
@@ -1591,7 +1595,7 @@ static int parse_events_add_pmu(struct parse_events_state *parse_state,
 	term_cpu = get_config_cpu(&parsed_terms, parse_state->fake_pmu);
 	evsel = __add_event(list, &parse_state->idx, &attr, /*init_attr=*/true,
 			    get_config_name(&parsed_terms),
-			    get_config_metric_id(&parsed_terms), pmu,
+			    get_config_metric_id(&parsed_terms), parse_state->pmus, pmu,
 			    &config_terms, first_wildcard_match, term_cpu, alternate_hw_config);
 	perf_cpu_map__put(term_cpu);
 	if (!evsel) {
@@ -1661,7 +1665,8 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state,
 
 	INIT_LIST_HEAD(list);
 
-	while ((pmu = perf_pmus__scan_for_event(pmu, event_name)) != NULL) {
+	while ((pmu = perf_pmus__scan_for_event(parse_state->pmus,
+						pmu, event_name)) != NULL) {
 
 		if (parse_events__filter_pmu(parse_state, pmu))
 			continue;
@@ -1739,7 +1744,8 @@ int parse_events_multi_pmu_add_or_add_pmu(struct parse_events_state *parse_state
 
 	pmu = NULL;
 	/* Failed to add, try wildcard expansion of event_or_pmu as a PMU name. */
-	while ((pmu = perf_pmus__scan_matching_wildcard(pmu, event_or_pmu)) != NULL) {
+	while ((pmu = perf_pmus__scan_matching_wildcard(parse_state->pmus,
+							pmu, event_or_pmu)) != NULL) {
 
 		if (parse_events__filter_pmu(parse_state, pmu))
 			continue;
@@ -1964,6 +1970,7 @@ int parse_events_terms(struct parse_events_terms *terms, const char *str)
 {
 	struct parse_events_state parse_state = {
 		.terms  = NULL,
+		.pmus   = perf_env__pmus(host_env()),
 		.stoken = PE_START_TERMS,
 	};
 	int ret;
@@ -2296,6 +2303,7 @@ int __parse_events(struct evlist *evlist, const char *str,
 		.list			= LIST_HEAD_INIT(parse_state.list),
 		.idx			= evlist__nr_entries(evlist),
 		.error			= err,
+		.pmus			= perf_env__pmus(evlist__env(evlist) ?: host_env()),
 		.stoken			= PE_START_EVENTS,
 		.fake_pmu		= fake_pmu,
 		.fake_tp		= fake_tp,
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index b14c832b03a1..57eabdb97890 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -18,6 +18,7 @@ struct parse_events_error;
 
 struct option;
 struct perf_pmu;
+struct perf_pmus;
 struct strbuf;
 
 const char *event_type(size_t type);
@@ -156,6 +157,8 @@ struct parse_events_state {
 	struct parse_events_error *error;
 	/* Holds returned terms for term parsing. */
 	struct parse_events_terms *terms;
+	/* The PMUs to search during parsing. */
+	struct perf_pmus	  *pmus;
 	/* Start token. */
 	int			   stoken;
 	/* Use the fake PMU marker for testing. */
diff --git a/tools/perf/util/perf-regs-arch/perf_regs_x86.c b/tools/perf/util/perf-regs-arch/perf_regs_x86.c
index ea1d402b0893..2d80f119629a 100644
--- a/tools/perf/util/perf-regs-arch/perf_regs_x86.c
+++ b/tools/perf/util/perf-regs-arch/perf_regs_x86.c
@@ -9,6 +9,7 @@
 #include <linux/zalloc.h>
 
 #include "../debug.h"
+#include "../env.h"
 #include "../event.h"
 #include "../pmu.h"
 #include "../pmus.h"
@@ -248,6 +249,7 @@ static uint64_t __arch__reg_mask(u64 sample_type, u64 mask, bool has_simd_regs)
 		.exclude_kernel			= 1,
 		.sample_simd_regs_enabled	= has_simd_regs,
 	};
+	struct perf_pmus *pmus = perf_env__pmus(host_env());
 	int fd;
 	/*
 	 * In an unnamed union, init it here to build on older gcc versions
@@ -258,7 +260,7 @@ static uint64_t __arch__reg_mask(u64 sample_type, u64 mask, bool has_simd_regs)
 	else
 		attr.sample_regs_user = mask;
 
-	if (perf_pmus__num_core_pmus() > 1) {
+	if (perf_pmus__num_core_pmus(pmus) > 1) {
 		struct perf_pmu *pmu = NULL;
 		__u64 type = PERF_TYPE_RAW;
 
@@ -512,6 +514,7 @@ __support_simd_reg_class(uint64_t sample_type, uint16_t qwords,
 		.exclude_kernel			= 1,
 		.sample_simd_regs_enabled	= 1,
 	};
+	struct perf_pmus *pmus = perf_env__pmus(host_env());
 	int fd;
 
 	attr.sample_period = 1;
@@ -530,7 +533,7 @@ __support_simd_reg_class(uint64_t sample_type, uint16_t qwords,
 			attr.sample_simd_pred_reg_user = PERF_X86_SIMD_PRED_MASK;
 	}
 
-	if (perf_pmus__num_core_pmus() > 1) {
+	if (perf_pmus__num_core_pmus(pmus) > 1) {
 		__u64 type = perf_pmus__find_core_pmu()->type;
 
 		attr.config |= type << PERF_PMU_TYPE_SHIFT;
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 531641e66ef4..ee6a217dde91 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -1552,7 +1552,8 @@ static int pmu_config_term(const struct perf_pmu *pmu,
 			assert(term->val.num < PERF_COUNT_HW_MAX);
 			assert(pmu->is_core);
 			attr->config = term->val.num;
-			if (perf_pmus__supports_extended_type())
+			if (pmu->pmus ? perf_pmus__supports_extended_type(pmu->pmus)
+				      : perf_pmus__host_supports_extended_type())
 				attr->config |= (__u64)pmu->type << PERF_PMU_TYPE_SHIFT;
 			attr->type = PERF_TYPE_HARDWARE;
 			break;
@@ -1570,7 +1571,8 @@ static int pmu_config_term(const struct perf_pmu *pmu,
 			assert((term->val.num & ~0xFFFFFF) == 0);
 			assert(pmu->is_core);
 			attr->config = term->val.num;
-			if (perf_pmus__supports_extended_type())
+			if (pmu->pmus ? perf_pmus__supports_extended_type(pmu->pmus)
+				      : perf_pmus__host_supports_extended_type())
 				attr->config |= (__u64)pmu->type << PERF_PMU_TYPE_SHIFT;
 			attr->type = PERF_TYPE_HW_CACHE;
 			break;
@@ -2109,7 +2111,8 @@ bool perf_pmu__supports_legacy_cache(const struct perf_pmu *pmu)
 
 bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu)
 {
-	return !pmu->is_core || perf_pmus__num_core_pmus() == 1;
+	return !pmu->is_core ||
+	       perf_pmus__num_core_pmus(pmu->pmus ?: perf_env__pmus(host_env())) == 1;
 }
 
 bool perf_pmu__have_event(struct perf_pmu *pmu, const char *name)
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index ea55882ce670..7605c3859337 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -17,6 +17,7 @@ struct evsel_config_term;
 struct hashmap;
 struct perf_cpu_map;
 struct perf_env;
+struct perf_pmus;
 struct print_callbacks;
 
 enum {
@@ -163,6 +164,8 @@ struct perf_pmu {
 	bool sysfs_aliases_loaded;
 	/** @from_env: Was the PMU reconstructed from a perf_env header? */
 	bool from_env;
+	/** @pmus: The PMU collection this PMU belongs to. */
+	struct perf_pmus *pmus;
 	/**
 	 * @cpu_aliases_added: Have all json events table entries for the PMU
 	 * been added?
diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c
index 1355b317f3ed..472d62ccfc8f 100644
--- a/tools/perf/util/pmus.c
+++ b/tools/perf/util/pmus.c
@@ -14,6 +14,7 @@
 #include "cpumap.h"
 #include "debug.h"
 #include "drm_pmu.h"
+#include "env.h"
 #include "evsel.h"
 #include "pmus.h"
 #include "pmu.h"
@@ -38,8 +39,6 @@
  *             are captured under other_pmus. PMUs belonging to other_pmus
  *             must have pmu->is_core=0 but pmu->is_uncore could be 0 or 1.
  */
-static LIST_HEAD(core_pmus);
-static LIST_HEAD(other_pmus);
 enum perf_tool_pmu_type {
 	PERF_TOOL_PMU_TYPE_PE_CORE,
 	PERF_TOOL_PMU_TYPE_PE_OTHER,
@@ -59,9 +58,8 @@ enum perf_tool_pmu_type {
 					PERF_TOOL_PMU_TYPE_HWMON_MASK | \
 					PERF_TOOL_PMU_TYPE_DRM_MASK)
 };
-static unsigned int read_pmu_types;
 
-static void pmu_read_sysfs(unsigned int to_read_pmus);
+static void pmu_read_sysfs(struct perf_pmus *pmus, unsigned int to_read_pmus);
 
 size_t pmu_name_len_no_suffix(const char *str)
 {
@@ -109,33 +107,50 @@ int pmu_name_cmp(const char *lhs_pmu_name, const char *rhs_pmu_name)
 	return lhs_num < rhs_num ? -1 : (lhs_num > rhs_num ? 1 : 0);
 }
 
-void perf_pmus__destroy(void)
+void perf_pmus__init(struct perf_pmus *pmus)
+{
+	INIT_LIST_HEAD(&pmus->core_pmus);
+	INIT_LIST_HEAD(&pmus->other_pmus);
+	pmus->read_pmu_types = 0;
+	pmus->num_core_pmus = 0;
+	pmus->extended_type = PERF_PMUS_EXTENDED_TYPE_UNKNOWN;
+	mutex_init(&pmus->lock);
+}
+
+void perf_pmus__exit(struct perf_pmus *pmus)
 {
 	struct perf_pmu *pmu, *tmp;
 
-	list_for_each_entry_safe(pmu, tmp, &core_pmus, list) {
+	if (!pmus->core_pmus.next || !pmus->other_pmus.next) {
+		perf_pmus__init(pmus);
+		return;
+	}
+
+	list_for_each_entry_safe(pmu, tmp, &pmus->core_pmus, list) {
 		list_del(&pmu->list);
 
 		perf_pmu__delete(pmu);
 	}
-	list_for_each_entry_safe(pmu, tmp, &other_pmus, list) {
+	list_for_each_entry_safe(pmu, tmp, &pmus->other_pmus, list) {
 		list_del(&pmu->list);
 
 		perf_pmu__delete(pmu);
 	}
-	read_pmu_types = 0;
+	pmus->read_pmu_types = 0;
+	pmus->num_core_pmus = 0;
+	pmus->extended_type = PERF_PMUS_EXTENDED_TYPE_UNKNOWN;
 }
 
-static struct perf_pmu *pmu_find(const char *name)
+static struct perf_pmu *pmu_find(struct perf_pmus *pmus, const char *name)
 {
 	struct perf_pmu *pmu;
 
-	list_for_each_entry(pmu, &core_pmus, list) {
+	list_for_each_entry(pmu, &pmus->core_pmus, list) {
 		if (!strcmp(pmu->name, name) ||
 		    (pmu->alias_name && !strcmp(pmu->alias_name, name)))
 			return pmu;
 	}
-	list_for_each_entry(pmu, &other_pmus, list) {
+	list_for_each_entry(pmu, &pmus->other_pmus, list) {
 		if (!strcmp(pmu->name, name) ||
 		    (pmu->alias_name && !strcmp(pmu->alias_name, name)))
 			return pmu;
@@ -144,7 +159,7 @@ static struct perf_pmu *pmu_find(const char *name)
 	return NULL;
 }
 
-struct perf_pmu *perf_pmus__find(const char *name)
+static struct perf_pmu *__perf_pmus__find(struct perf_pmus *pmus, const char *name)
 {
 	struct perf_pmu *pmu;
 	int dirfd;
@@ -158,24 +173,26 @@ struct perf_pmu *perf_pmus__find(const char *name)
 	 * so we keep us from multiple reading/parsing
 	 * the pmu format definitions.
 	 */
-	pmu = pmu_find(name);
+	pmu = pmu_find(pmus, name);
 	if (pmu)
 		return pmu;
 
-	if (read_pmu_types == PERF_TOOL_PMU_TYPE_ALL_MASK)
+	if (pmus->read_pmu_types == PERF_TOOL_PMU_TYPE_ALL_MASK)
 		return NULL;
 
 	core_pmu = is_pmu_core(name);
-	if (core_pmu && (read_pmu_types & PERF_TOOL_PMU_TYPE_PE_CORE_MASK))
+	if (core_pmu && (pmus->read_pmu_types & PERF_TOOL_PMU_TYPE_PE_CORE_MASK))
 		return NULL;
 
 	dirfd = perf_pmu__event_source_devices_fd();
-	pmu = perf_pmu__lookup(core_pmu ? &core_pmus : &other_pmus, dirfd, name,
+	pmu = perf_pmu__lookup(core_pmu ? &pmus->core_pmus : &pmus->other_pmus, dirfd, name,
 			       /*eager_load=*/false);
 	close(dirfd);
 
-	if (pmu)
+	if (pmu) {
+		pmu->pmus = pmus;
 		return pmu;
+	}
 
 	/* Looking up an individual perf event PMU failed, check if a tool PMU should be read. */
 	if (!strncmp(name, "hwmon_", 6))
@@ -186,8 +203,8 @@ struct perf_pmu *perf_pmus__find(const char *name)
 		to_read_pmus |= PERF_TOOL_PMU_TYPE_TOOL_MASK;
 
 	if (to_read_pmus) {
-		pmu_read_sysfs(to_read_pmus);
-		pmu = pmu_find(name);
+		pmu_read_sysfs(pmus, to_read_pmus);
+		pmu = pmu_find(pmus, name);
 		if (pmu)
 			return pmu;
 	}
@@ -195,11 +212,16 @@ struct perf_pmu *perf_pmus__find(const char *name)
 	to_read_pmus = PERF_TOOL_PMU_TYPE_PE_CORE_MASK;
 	if (!core_pmu)
 		to_read_pmus |= PERF_TOOL_PMU_TYPE_PE_OTHER_MASK;
-	pmu_read_sysfs(to_read_pmus);
-	return pmu_find(name);
+	pmu_read_sysfs(pmus, to_read_pmus);
+	return pmu_find(pmus, name);
+}
+
+struct perf_pmu *perf_pmus__find(const char *name)
+{
+	return __perf_pmus__find(perf_env__pmus(host_env()), name);
 }
 
-static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name)
+static struct perf_pmu *perf_pmu__find2(struct perf_pmus *pmus, int dirfd, const char *name)
 {
 	struct perf_pmu *pmu;
 	bool core_pmu;
@@ -209,19 +231,22 @@ static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name)
 	 * so we keep us from multiple reading/parsing
 	 * the pmu format definitions.
 	 */
-	pmu = pmu_find(name);
+	pmu = pmu_find(pmus, name);
 	if (pmu)
 		return pmu;
 
-	if (read_pmu_types == PERF_TOOL_PMU_TYPE_ALL_MASK)
+	if (pmus->read_pmu_types == PERF_TOOL_PMU_TYPE_ALL_MASK)
 		return NULL;
 
 	core_pmu = is_pmu_core(name);
-	if (core_pmu && (read_pmu_types & PERF_TOOL_PMU_TYPE_PE_CORE_MASK))
+	if (core_pmu && (pmus->read_pmu_types & PERF_TOOL_PMU_TYPE_PE_CORE_MASK))
 		return NULL;
 
-	return perf_pmu__lookup(core_pmu ? &core_pmus : &other_pmus, dirfd, name,
-				/*eager_load=*/false);
+	pmu = perf_pmu__lookup(core_pmu ? &pmus->core_pmus : &pmus->other_pmus, dirfd, name,
+			       /*eager_load=*/false);
+	if (pmu)
+		pmu->pmus = pmus;
+	return pmu;
 }
 
 static int pmus_cmp(void *priv __maybe_unused,
@@ -234,11 +259,11 @@ static int pmus_cmp(void *priv __maybe_unused,
 }
 
 /* Add all pmus in sysfs to pmu list: */
-static void pmu_read_sysfs(unsigned int to_read_types)
+static void pmu_read_sysfs(struct perf_pmus *pmus, unsigned int to_read_types)
 {
-	struct perf_pmu *tool_pmu;
+	struct perf_pmu *pmu, *tool_pmu;
 
-	if ((read_pmu_types & to_read_types) == to_read_types) {
+	if ((pmus->read_pmu_types & to_read_types) == to_read_types) {
 		/* All requested PMU types have been read. */
 		return;
 	}
@@ -259,60 +284,68 @@ static void pmu_read_sysfs(unsigned int to_read_types)
 				continue;
 			if (core_only && !is_pmu_core(dent->d_name))
 				continue;
-			/* add to static LIST_HEAD(core_pmus) or LIST_HEAD(other_pmus): */
-			perf_pmu__find2(fd, dent->d_name);
+			/* add to core_pmus or other_pmus: */
+			perf_pmu__find2(pmus, fd, dent->d_name);
 		}
 
 		close(fd);
 	}
 skip_pe_pmus:
-	if ((to_read_types & PERF_TOOL_PMU_TYPE_PE_CORE_MASK) && list_empty(&core_pmus)) {
-		if (!perf_pmu__create_placeholder_core_pmu(&core_pmus))
+	if ((to_read_types & PERF_TOOL_PMU_TYPE_PE_CORE_MASK) && list_empty(&pmus->core_pmus)) {
+		pmu = perf_pmu__create_placeholder_core_pmu(&pmus->core_pmus);
+		if (!pmu)
 			pr_err("Failure to set up any core PMUs\n");
+		else
+			pmu->pmus = pmus;
 	}
-	list_sort(NULL, &core_pmus, pmus_cmp);
+	list_sort(NULL, &pmus->core_pmus, pmus_cmp);
 
 	if ((to_read_types & PERF_TOOL_PMU_TYPE_TOOL_MASK) != 0 &&
-	    (read_pmu_types & PERF_TOOL_PMU_TYPE_TOOL_MASK) == 0) {
+	    (pmus->read_pmu_types & PERF_TOOL_PMU_TYPE_TOOL_MASK) == 0) {
 		tool_pmu = tool_pmu__new();
-		if (tool_pmu)
-			list_add_tail(&tool_pmu->list, &other_pmus);
+		if (tool_pmu) {
+			tool_pmu->pmus = pmus;
+			list_add_tail(&tool_pmu->list, &pmus->other_pmus);
+		}
 	}
 	if ((to_read_types & PERF_TOOL_PMU_TYPE_HWMON_MASK) != 0 &&
-	    (read_pmu_types & PERF_TOOL_PMU_TYPE_HWMON_MASK) == 0)
-		perf_pmus__read_hwmon_pmus(&other_pmus);
+	    (pmus->read_pmu_types & PERF_TOOL_PMU_TYPE_HWMON_MASK) == 0)
+		perf_pmus__read_hwmon_pmus(&pmus->other_pmus);
 
 	if ((to_read_types & PERF_TOOL_PMU_TYPE_DRM_MASK) != 0 &&
-	    (read_pmu_types & PERF_TOOL_PMU_TYPE_DRM_MASK) == 0)
-		perf_pmus__read_drm_pmus(&other_pmus);
+	    (pmus->read_pmu_types & PERF_TOOL_PMU_TYPE_DRM_MASK) == 0)
+		perf_pmus__read_drm_pmus(&pmus->other_pmus);
+
+	list_for_each_entry(pmu, &pmus->other_pmus, list)
+		pmu->pmus = pmus;
 
-	list_sort(NULL, &other_pmus, pmus_cmp);
+	list_sort(NULL, &pmus->other_pmus, pmus_cmp);
 
-	read_pmu_types |= to_read_types;
+	pmus->read_pmu_types |= to_read_types;
 }
 
-static struct perf_pmu *__perf_pmus__find_by_type(unsigned int type)
+static struct perf_pmu *__perf_pmus__find_by_type(struct perf_pmus *pmus, unsigned int type)
 {
 	struct perf_pmu *pmu;
 
-	list_for_each_entry(pmu, &core_pmus, list) {
+	list_for_each_entry(pmu, &pmus->core_pmus, list) {
 		if (pmu->type == type)
 			return pmu;
 	}
 
-	list_for_each_entry(pmu, &other_pmus, list) {
+	list_for_each_entry(pmu, &pmus->other_pmus, list) {
 		if (pmu->type == type)
 			return pmu;
 	}
 	return NULL;
 }
 
-struct perf_pmu *perf_pmus__find_by_type(unsigned int type)
+static struct perf_pmu *pmu_find_by_type(struct perf_pmus *pmus, unsigned int type)
 {
 	unsigned int to_read_pmus;
-	struct perf_pmu *pmu = __perf_pmus__find_by_type(type);
+	struct perf_pmu *pmu = __perf_pmus__find_by_type(pmus, type);
 
-	if (pmu || (read_pmu_types == PERF_TOOL_PMU_TYPE_ALL_MASK))
+	if (pmu || (pmus->read_pmu_types == PERF_TOOL_PMU_TYPE_ALL_MASK))
 		return pmu;
 
 	if (type >= PERF_PMU_TYPE_PE_START && type <= PERF_PMU_TYPE_PE_END) {
@@ -325,48 +358,64 @@ struct perf_pmu *perf_pmus__find_by_type(unsigned int type)
 	} else {
 		to_read_pmus = PERF_TOOL_PMU_TYPE_TOOL_MASK;
 	}
-	pmu_read_sysfs(to_read_pmus);
-	pmu = __perf_pmus__find_by_type(type);
+	pmu_read_sysfs(pmus, to_read_pmus);
+	pmu = __perf_pmus__find_by_type(pmus, type);
 	return pmu;
 }
 
+struct perf_pmu *perf_pmus__find_by_type(unsigned int type)
+{
+	return pmu_find_by_type(perf_env__pmus(host_env()), type);
+}
+
 /*
  * pmu iterator: If pmu is NULL, we start at the begin, otherwise return the
  * next pmu. Returns NULL on end.
  */
-struct perf_pmu *perf_pmus__scan(struct perf_pmu *pmu)
+static struct perf_pmu *pmu_scan(struct perf_pmus *pmus, struct perf_pmu *pmu)
 {
 	bool use_core_pmus = !pmu || pmu->is_core;
 
 	if (!pmu) {
-		pmu_read_sysfs(PERF_TOOL_PMU_TYPE_ALL_MASK);
-		pmu = list_prepare_entry(pmu, &core_pmus, list);
+		pmu_read_sysfs(pmus, PERF_TOOL_PMU_TYPE_ALL_MASK);
+		pmu = list_prepare_entry(pmu, &pmus->core_pmus, list);
 	}
 	if (use_core_pmus) {
-		list_for_each_entry_continue(pmu, &core_pmus, list)
+		list_for_each_entry_continue(pmu, &pmus->core_pmus, list)
 			return pmu;
 
 		pmu = NULL;
-		pmu = list_prepare_entry(pmu, &other_pmus, list);
+		pmu = list_prepare_entry(pmu, &pmus->other_pmus, list);
 	}
-	list_for_each_entry_continue(pmu, &other_pmus, list)
+	list_for_each_entry_continue(pmu, &pmus->other_pmus, list)
 		return pmu;
 	return NULL;
 }
 
-struct perf_pmu *perf_pmus__scan_core(struct perf_pmu *pmu)
+struct perf_pmu *perf_pmus__scan(struct perf_pmu *pmu)
+{
+	return pmu_scan(perf_env__pmus(host_env()), pmu);
+}
+
+static struct perf_pmu *pmu_scan_core(struct perf_pmus *pmus, struct perf_pmu *pmu)
 {
 	if (!pmu) {
-		pmu_read_sysfs(PERF_TOOL_PMU_TYPE_PE_CORE_MASK);
-		return list_first_entry_or_null(&core_pmus, typeof(*pmu), list);
+		pmu_read_sysfs(pmus, PERF_TOOL_PMU_TYPE_PE_CORE_MASK);
+		return list_first_entry_or_null(&pmus->core_pmus, typeof(*pmu), list);
 	}
-	list_for_each_entry_continue(pmu, &core_pmus, list)
+	list_for_each_entry_continue(pmu, &pmus->core_pmus, list)
 		return pmu;
 
 	return NULL;
 }
 
-struct perf_pmu *perf_pmus__scan_for_event(struct perf_pmu *pmu, const char *event)
+struct perf_pmu *perf_pmus__scan_core(struct perf_pmu *pmu)
+{
+	return pmu_scan_core(perf_env__pmus(host_env()), pmu);
+}
+
+struct perf_pmu *perf_pmus__scan_for_event(struct perf_pmus *pmus,
+					   struct perf_pmu *pmu, const char *event)
 {
 	bool use_core_pmus = !pmu || pmu->is_core;
 
@@ -390,22 +439,23 @@ struct perf_pmu *perf_pmus__scan_for_event(struct perf_pmu *pmu, const char *eve
 		if (strlen(event) > 4 && strncmp("drm-", event, 4) == 0)
 			to_read_pmus |= PERF_TOOL_PMU_TYPE_DRM_MASK;
 
-		pmu_read_sysfs(to_read_pmus);
-		pmu = list_prepare_entry(pmu, &core_pmus, list);
+		pmu_read_sysfs(pmus, to_read_pmus);
+		pmu = list_prepare_entry(pmu, &pmus->core_pmus, list);
 	}
 	if (use_core_pmus) {
-		list_for_each_entry_continue(pmu, &core_pmus, list)
+		list_for_each_entry_continue(pmu, &pmus->core_pmus, list)
 			return pmu;
 
 		pmu = NULL;
-		pmu = list_prepare_entry(pmu, &other_pmus, list);
+		pmu = list_prepare_entry(pmu, &pmus->other_pmus, list);
 	}
-	list_for_each_entry_continue(pmu, &other_pmus, list)
+	list_for_each_entry_continue(pmu, &pmus->other_pmus, list)
 		return pmu;
 	return NULL;
 }
 
-struct perf_pmu *perf_pmus__scan_matching_wildcard(struct perf_pmu *pmu, const char *wildcard)
+struct perf_pmu *perf_pmus__scan_matching_wildcard(struct perf_pmus *pmus,
+						   struct perf_pmu *pmu, const char *wildcard)
 {
 	bool use_core_pmus = !pmu || pmu->is_core;
 
@@ -434,38 +484,38 @@ struct perf_pmu *perf_pmus__scan_matching_wildcard(struct perf_pmu *pmu, const c
 			to_read_pmus |= PERF_TOOL_PMU_TYPE_HWMON_MASK;
 		}
 
-		pmu_read_sysfs(to_read_pmus);
-		pmu = list_prepare_entry(pmu, &core_pmus, list);
+		pmu_read_sysfs(pmus, to_read_pmus);
+		pmu = list_prepare_entry(pmu, &pmus->core_pmus, list);
 	}
 	if (use_core_pmus) {
-		list_for_each_entry_continue(pmu, &core_pmus, list) {
+		list_for_each_entry_continue(pmu, &pmus->core_pmus, list) {
 			if (perf_pmu__wildcard_match(pmu, wildcard))
 				return pmu;
 		}
 		pmu = NULL;
-		pmu = list_prepare_entry(pmu, &other_pmus, list);
+		pmu = list_prepare_entry(pmu, &pmus->other_pmus, list);
 	}
-	list_for_each_entry_continue(pmu, &other_pmus, list) {
+	list_for_each_entry_continue(pmu, &pmus->other_pmus, list) {
 		if (perf_pmu__wildcard_match(pmu, wildcard))
 			return pmu;
 	}
 	return NULL;
 }
 
-static struct perf_pmu *perf_pmus__scan_skip_duplicates(struct perf_pmu *pmu)
+static struct perf_pmu *pmu_scan_skip_duplicates(struct perf_pmus *pmus, struct perf_pmu *pmu)
 {
 	bool use_core_pmus = !pmu || pmu->is_core;
 	int last_pmu_name_len = 0;
 	const char *last_pmu_name = (pmu && pmu->name) ? pmu->name : "";
 
 	if (!pmu) {
-		pmu_read_sysfs(PERF_TOOL_PMU_TYPE_ALL_MASK);
-		pmu = list_prepare_entry(pmu, &core_pmus, list);
+		pmu_read_sysfs(pmus, PERF_TOOL_PMU_TYPE_ALL_MASK);
+		pmu = list_prepare_entry(pmu, &pmus->core_pmus, list);
 	} else
 		last_pmu_name_len = pmu_name_len_no_suffix(pmu->name ?: "");
 
 	if (use_core_pmus) {
-		list_for_each_entry_continue(pmu, &core_pmus, list) {
+		list_for_each_entry_continue(pmu, &pmus->core_pmus, list) {
 			int pmu_name_len = pmu_name_len_no_suffix(pmu->name ?: "");
 
 			if (last_pmu_name_len == pmu_name_len &&
@@ -475,9 +525,9 @@ static struct perf_pmu *perf_pmus__scan_skip_duplicates(struct perf_pmu *pmu)
 			return pmu;
 		}
 		pmu = NULL;
-		pmu = list_prepare_entry(pmu, &other_pmus, list);
+		pmu = list_prepare_entry(pmu, &pmus->other_pmus, list);
 	}
-	list_for_each_entry_continue(pmu, &other_pmus, list) {
+	list_for_each_entry_continue(pmu, &pmus->other_pmus, list) {
 		int pmu_name_len = pmu_name_len_no_suffix(pmu->name ?: "");
 
 		if (last_pmu_name_len == pmu_name_len &&
@@ -489,27 +539,28 @@ static struct perf_pmu *perf_pmus__scan_skip_duplicates(struct perf_pmu *pmu)
 	return NULL;
 }
 
-struct perf_pmu *perf_pmus__scan_for_uncore_id(struct perf_pmu *pmu, const char *compat)
+struct perf_pmu *perf_pmus__scan_for_uncore_id(struct perf_pmus *pmus,
+					       struct perf_pmu *pmu, const char *compat)
 {
 	if (!pmu) {
 		/* Only uncore PMUs can have identifiers. */
 		unsigned int to_read_pmus = PERF_TOOL_PMU_TYPE_PE_OTHER_MASK;
 
-		pmu_read_sysfs(to_read_pmus);
-		pmu = list_prepare_entry(pmu, &other_pmus, list);
+		pmu_read_sysfs(pmus, to_read_pmus);
+		pmu = list_prepare_entry(pmu, &pmus->other_pmus, list);
 	}
-	list_for_each_entry_continue(pmu, &other_pmus, list) {
+	list_for_each_entry_continue(pmu, &pmus->other_pmus, list) {
 		if (pmu->id && pmu_uncore_identifier_match(compat, pmu->id))
 			return pmu;
 	}
 	return NULL;
 }
 
-const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str)
+const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(struct perf_pmus *pmus, const char *str)
 {
 	struct perf_pmu *pmu = NULL;
 
-	while ((pmu = perf_pmus__scan(pmu)) != NULL) {
+	while ((pmu = pmu_scan(pmus, pmu)) != NULL) {
 		if (!strcmp(pmu->name, str))
 			return pmu;
 		/* Ignore "uncore_" prefix. */
@@ -630,7 +681,8 @@ static int perf_pmus__print_pmu_events__callback(void *vstate,
 	return 0;
 }
 
-void perf_pmus__print_pmu_events(const struct print_callbacks *print_cb, void *print_state)
+void perf_pmus__print_pmu_events(struct perf_pmus *pmus,
+				 const struct print_callbacks *print_cb, void *print_state)
 {
 	struct perf_pmu *pmu;
 	int printed = 0;
@@ -638,16 +690,16 @@ void perf_pmus__print_pmu_events(const struct print_callbacks *print_cb, void *p
 	struct sevent *aliases;
 	struct events_callback_state state;
 	bool skip_duplicate_pmus = print_cb->skip_duplicate_pmus(print_state);
-	struct perf_pmu *(*scan_fn)(struct perf_pmu *);
+	struct perf_pmu *(*scan_fn)(struct perf_pmus *p, struct perf_pmu *cur);
 
 	if (skip_duplicate_pmus)
-		scan_fn = perf_pmus__scan_skip_duplicates;
+		scan_fn = pmu_scan_skip_duplicates;
 	else
-		scan_fn = perf_pmus__scan;
+		scan_fn = pmu_scan;
 
 	pmu = NULL;
 	len = 0;
-	while ((pmu = scan_fn(pmu)) != NULL)
+	while ((pmu = scan_fn(pmus, pmu)) != NULL)
 		len += perf_pmu__num_events(pmu);
 
 	aliases = calloc(len, sizeof(struct sevent));
@@ -661,7 +713,7 @@ void perf_pmus__print_pmu_events(const struct print_callbacks *print_cb, void *p
 		.aliases_len = len,
 		.index = 0,
 	};
-	while ((pmu = scan_fn(pmu)) != NULL) {
+	while ((pmu = scan_fn(pmus, pmu)) != NULL) {
 		perf_pmu__for_each_event(pmu, skip_duplicate_pmus, &state,
 					 perf_pmus__print_pmu_events__callback);
 	}
@@ -745,18 +797,19 @@ static int build_format_string(void *state, const char *name, int config,
 	return ret1 < 0 ? ret1 : (ret2 < 0 ? ret2 : 0);
 }
 
-void perf_pmus__print_raw_pmu_events(const struct print_callbacks *print_cb, void *print_state)
+void perf_pmus__print_raw_pmu_events(struct perf_pmus *pmus,
+				     const struct print_callbacks *print_cb, void *print_state)
 {
 	bool skip_duplicate_pmus = print_cb->skip_duplicate_pmus(print_state);
-	struct perf_pmu *(*scan_fn)(struct perf_pmu *);
+	struct perf_pmu *(*scan_fn)(struct perf_pmus *p, struct perf_pmu *cur);
 	struct perf_pmu *pmu = NULL;
 
 	if (skip_duplicate_pmus)
-		scan_fn = perf_pmus__scan_skip_duplicates;
+		scan_fn = pmu_scan_skip_duplicates;
 	else
-		scan_fn = perf_pmus__scan;
+		scan_fn = pmu_scan;
 
-	while ((pmu = scan_fn(pmu)) != NULL) {
+	while ((pmu = scan_fn(pmus, pmu)) != NULL) {
 		struct build_format_string_args format_args = {
 			.short_string = STRBUF_INIT,
 			.long_string = STRBUF_INIT,
@@ -796,67 +849,79 @@ void perf_pmus__print_raw_pmu_events(const struct print_callbacks *print_cb, voi
 	}
 }
 
-bool perf_pmus__have_event(const char *pname, const char *name)
+bool perf_pmus__have_event(struct perf_pmus *pmus, const char *pname, const char *name)
 {
-	struct perf_pmu *pmu = perf_pmus__find(pname);
+	struct perf_pmu *pmu = __perf_pmus__find(pmus, pname);
 
 	return pmu && perf_pmu__have_event(pmu, name);
 }
 
-int perf_pmus__num_core_pmus(void)
+int perf_pmus__num_core_pmus(struct perf_pmus *pmus)
 {
-	static int count;
-
-	if (!count) {
+	if (!pmus->num_core_pmus) {
 		struct perf_pmu *pmu = NULL;
 
-		while ((pmu = perf_pmus__scan_core(pmu)) != NULL)
-			count++;
+		while ((pmu = pmu_scan_core(pmus, pmu)) != NULL)
+			pmus->num_core_pmus++;
 	}
-	return count;
+	return pmus->num_core_pmus;
 }
 
-static bool __perf_pmus__supports_extended_type(void)
+static bool __perf_pmus__supports_extended_type(struct perf_pmus *pmus)
 {
 	struct perf_pmu *pmu = NULL;
 
-	if (perf_pmus__num_core_pmus() <= 1)
+	if (perf_pmus__num_core_pmus(pmus) <= 1)
 		return false;
 
-	while ((pmu = perf_pmus__scan_core(pmu)) != NULL) {
-		if (!is_event_supported(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES | ((__u64)pmu->type << PERF_PMU_TYPE_SHIFT)))
+	while ((pmu = pmu_scan_core(pmus, pmu)) != NULL) {
+		if (!pmu->from_env &&
+		    !is_event_supported(PERF_TYPE_HARDWARE,
+					PERF_COUNT_HW_CPU_CYCLES |
+					((__u64)pmu->type << PERF_PMU_TYPE_SHIFT)))
 			return false;
 	}
 
 	return true;
 }
 
-static bool perf_pmus__do_support_extended_type;
-
-static void perf_pmus__init_supports_extended_type(void)
+bool perf_pmus__supports_extended_type(struct perf_pmus *pmus)
 {
-	perf_pmus__do_support_extended_type = __perf_pmus__supports_extended_type();
-}
+	enum perf_pmus_extended_type extended_type =
+		__atomic_load_n(&pmus->extended_type, __ATOMIC_ACQUIRE);
 
-bool perf_pmus__supports_extended_type(void)
-{
-	static pthread_once_t extended_type_once = PTHREAD_ONCE_INIT;
+	if (extended_type == PERF_PMUS_EXTENDED_TYPE_UNKNOWN) {
+		mutex_lock(&pmus->lock);
+		extended_type = pmus->extended_type;
+		if (extended_type == PERF_PMUS_EXTENDED_TYPE_UNKNOWN) {
+			extended_type = __perf_pmus__supports_extended_type(pmus)
+				? PERF_PMUS_EXTENDED_TYPE_SUPPORTED
+				: PERF_PMUS_EXTENDED_TYPE_NOT_SUPPORTED;
+			__atomic_store_n(&pmus->extended_type, extended_type,
+					 __ATOMIC_RELEASE);
+		}
+		mutex_unlock(&pmus->lock);
+	}
 
-	pthread_once(&extended_type_once, perf_pmus__init_supports_extended_type);
+	return extended_type == PERF_PMUS_EXTENDED_TYPE_SUPPORTED;
+}
 
-	return perf_pmus__do_support_extended_type;
+bool perf_pmus__host_supports_extended_type(void)
+{
+	return perf_pmus__supports_extended_type(perf_env__pmus(host_env()));
 }
 
-struct perf_pmu *perf_pmus__find_by_attr(const struct perf_event_attr *attr)
+struct perf_pmu *perf_pmus__find_by_attr(struct perf_pmus *pmus,
+					 const struct perf_event_attr *attr)
 {
-	struct perf_pmu *pmu = perf_pmus__find_by_type(attr->type);
+	struct perf_pmu *pmu = pmu_find_by_type(pmus, attr->type);
 	u32 type = attr->type;
 	bool legacy_core_type = type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE;
 
-	if (!pmu && legacy_core_type && perf_pmus__supports_extended_type()) {
+	if (!pmu && legacy_core_type && perf_pmus__supports_extended_type(pmus)) {
 		type = attr->config >> PERF_PMU_TYPE_SHIFT;
 
-		pmu = perf_pmus__find_by_type(type);
+		pmu = pmu_find_by_type(pmus, type);
 	}
 	if (!pmu && (legacy_core_type || type == PERF_TYPE_RAW)) {
 		/*
@@ -867,7 +932,7 @@ struct perf_pmu *perf_pmus__find_by_attr(const struct perf_event_attr *attr)
 		 * PERF_TYPE_RAW, assume the RAW events are going to be handled
 		 * by the first core PMU.
 		 */
-		pmu = perf_pmus__find_core_pmu();
+		pmu = pmu_scan_core(pmus, NULL);
 	}
 	return pmu;
 }
@@ -875,11 +940,13 @@ struct perf_pmu *perf_pmus__find_by_attr(const struct perf_event_attr *attr)
 struct perf_pmu *evsel__find_pmu(const struct evsel *evsel)
 {
 	struct perf_pmu *pmu = evsel->pmu;
+	struct perf_env *env;
 
 	if (pmu)
 		return pmu;
 
-	pmu = perf_pmus__find_by_attr(&evsel->core.attr);
+	env = evsel__env((struct evsel *)evsel) ?: host_env();
+	pmu = perf_pmus__find_by_attr(perf_env__pmus(env), &evsel->core.attr);
 	((struct evsel *)evsel)->pmu = pmu;
 	return pmu;
 }
@@ -889,21 +956,37 @@ struct perf_pmu *perf_pmus__find_core_pmu(void)
 	return perf_pmus__scan_core(NULL);
 }
 
-struct perf_pmu *perf_pmus__add_test_pmu(int test_sysfs_dirfd, const char *name)
+struct perf_pmu *perf_pmus__add_test_pmu(struct perf_pmus *pmus,
+					 int test_sysfs_dirfd, const char *name)
 {
+	struct perf_pmu *pmu;
+
+	if (!pmus->core_pmus.next || !pmus->other_pmus.next)
+		perf_pmus__init(pmus);
 	/*
 	 * Some PMU functions read from the sysfs mount point, so care is
 	 * needed, hence passing the eager_load flag to load things like the
 	 * format files.
 	 */
-	return perf_pmu__lookup(&other_pmus, test_sysfs_dirfd, name, /*eager_load=*/true);
+	pmu = perf_pmu__lookup(&pmus->other_pmus, test_sysfs_dirfd, name, /*eager_load=*/true);
+	if (pmu)
+		pmu->pmus = pmus;
+	return pmu;
 }
 
-struct perf_pmu *perf_pmus__add_test_hwmon_pmu(const char *hwmon_dir,
+struct perf_pmu *perf_pmus__add_test_hwmon_pmu(struct perf_pmus *pmus,
+					       const char *hwmon_dir,
 					       const char *sysfs_name,
 					       const char *name)
 {
-	return hwmon_pmu__new(&other_pmus, hwmon_dir, sysfs_name, name);
+	struct perf_pmu *pmu;
+
+	if (!pmus->core_pmus.next || !pmus->other_pmus.next)
+		perf_pmus__init(pmus);
+	pmu = hwmon_pmu__new(&pmus->other_pmus, hwmon_dir, sysfs_name, name);
+	if (pmu)
+		pmu->pmus = pmus;
+	return pmu;
 }
 
 struct perf_pmu *perf_pmus__fake_pmu(void)
diff --git a/tools/perf/util/pmus.h b/tools/perf/util/pmus.h
index 0d55edb3f2fc..45f4bd8b6aa6 100644
--- a/tools/perf/util/pmus.h
+++ b/tools/perf/util/pmus.h
@@ -2,41 +2,220 @@
 #ifndef __PMUS_H
 #define __PMUS_H
 
+#include <linux/list.h>
 #include <stdbool.h>
 #include <stddef.h>
+#include "mutex.h"
 
 struct perf_event_attr;
 struct perf_pmu;
 struct print_callbacks;
 
+/**
+ * enum perf_pmus_extended_type - Tri-state cache for extended PMU type support.
+ * @PERF_PMUS_EXTENDED_TYPE_UNKNOWN: Support has not been checked yet.
+ * @PERF_PMUS_EXTENDED_TYPE_SUPPORTED: Multiple core PMUs / extended type supported.
+ * @PERF_PMUS_EXTENDED_TYPE_NOT_SUPPORTED: Extended type not supported.
+ */
+enum perf_pmus_extended_type {
+	PERF_PMUS_EXTENDED_TYPE_UNKNOWN = 0,
+	PERF_PMUS_EXTENDED_TYPE_SUPPORTED,
+	PERF_PMUS_EXTENDED_TYPE_NOT_SUPPORTED,
+};
+
+/**
+ * struct perf_pmus - Collection of core and non-core PMUs for an environment.
+ */
+struct perf_pmus {
+	/**
+	 * @core_pmus: In-order list of core PMUs (e.g. "cpu", "cpu_core",
+	 * "cpu_atom").
+	 */
+	struct list_head core_pmus;
+	/**
+	 * @other_pmus: In-order list of non-core PMUs (uncore, software,
+	 * tracepoint, tool, hwmon, drm, etc.).
+	 */
+	struct list_head other_pmus;
+	/**
+	 * @read_pmu_types: Bitmask of enum perf_tool_pmu_type values whose PMU
+	 * lists have been populated.
+	 */
+	unsigned int read_pmu_types;
+	/** @num_core_pmus: Cached count of entries in @core_pmus. */
+	int num_core_pmus;
+	/**
+	 * @extended_type: Cached result of checking whether extended PMU type
+	 * encoding in perf_event_attr::config is supported.
+	 */
+	enum perf_pmus_extended_type extended_type;
+	/** @lock: Protects lazy initialization of @extended_type. */
+	struct mutex lock;
+};
+
+#define PERF_PMUS_INIT(name) {					\
+	.core_pmus = LIST_HEAD_INIT((name).core_pmus),		\
+	.other_pmus = LIST_HEAD_INIT((name).other_pmus),	\
+	.lock = { .lock = __PERF_MUTEX_INITIALIZER },		\
+}
+
 size_t pmu_name_len_no_suffix(const char *str);
 /* Exposed for testing only. */
 int pmu_name_cmp(const char *lhs_pmu_name, const char *rhs_pmu_name);
 
-void perf_pmus__destroy(void);
+/**
+ * perf_pmus__init() - Initialize an empty struct perf_pmus.
+ * @pmus: The PMU collection to initialize.
+ */
+void perf_pmus__init(struct perf_pmus *pmus);
+
+/**
+ * perf_pmus__exit() - Free all PMUs in @pmus and reset its state.
+ * @pmus: The PMU collection to clean up.
+ */
+void perf_pmus__exit(struct perf_pmus *pmus);
 
 struct perf_pmu *perf_pmus__find(const char *name);
 struct perf_pmu *perf_pmus__find_by_type(unsigned int type);
-struct perf_pmu *perf_pmus__find_by_attr(const struct perf_event_attr *attr);
+
+/**
+ * perf_pmus__find_by_attr() - Find the PMU corresponding to @attr.
+ * @pmus: The PMU collection to search.
+ * @attr: The perf_event_attr whose type/config identifies the PMU.
+ *
+ * Return: Pointer to the matching struct perf_pmu, or NULL if not found.
+ */
+struct perf_pmu *perf_pmus__find_by_attr(struct perf_pmus *pmus,
+					 const struct perf_event_attr *attr);
 
 struct perf_pmu *perf_pmus__scan(struct perf_pmu *pmu);
 struct perf_pmu *perf_pmus__scan_core(struct perf_pmu *pmu);
-struct perf_pmu *perf_pmus__scan_for_event(struct perf_pmu *pmu, const char *event);
-struct perf_pmu *perf_pmus__scan_matching_wildcard(struct perf_pmu *pmu, const char *wildcard);
-struct perf_pmu *perf_pmus__scan_for_uncore_id(struct perf_pmu *pmu, const char *compat);
 
-const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str);
+/**
+ * perf_pmus__scan_for_event() - Iterate over PMUs in @pmus that have @event.
+ * @pmus: The PMU collection to scan.
+ * @pmu: Previous matching PMU returned, or NULL to start.
+ * @event: Event name to search for.
+ *
+ * Return: Next struct perf_pmu supporting @event, or NULL when done.
+ */
+struct perf_pmu *perf_pmus__scan_for_event(struct perf_pmus *pmus,
+					   struct perf_pmu *pmu, const char *event);
+
+/**
+ * perf_pmus__scan_matching_wildcard() - Iterate over PMUs matching @wildcard.
+ * @pmus: The PMU collection to scan.
+ * @pmu: Previous matching PMU returned, or NULL to start.
+ * @wildcard: Wildcard/prefix pattern to match against PMU names.
+ *
+ * Return: Next matching struct perf_pmu, or NULL when done.
+ */
+struct perf_pmu *perf_pmus__scan_matching_wildcard(struct perf_pmus *pmus,
+						   struct perf_pmu *pmu, const char *wildcard);
+
+/**
+ * perf_pmus__scan_for_uncore_id() - Iterate over uncore PMUs matching @compat.
+ * @pmus: The PMU collection to scan.
+ * @pmu: Previous matching PMU returned, or NULL to start.
+ * @compat: Uncore identifier string to match.
+ *
+ * Return: Next matching uncore struct perf_pmu, or NULL when done.
+ */
+struct perf_pmu *perf_pmus__scan_for_uncore_id(struct perf_pmus *pmus,
+					       struct perf_pmu *pmu, const char *compat);
+
+/**
+ * perf_pmus__pmu_for_pmu_filter() - Find PMU matching a pmu_filter string.
+ * @pmus: The PMU collection to search.
+ * @str: Filter string to match against PMU names.
+ *
+ * Return: Matching struct perf_pmu, or NULL if none matches.
+ */
+const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(struct perf_pmus *pmus, const char *str);
 
-void perf_pmus__print_pmu_events(const struct print_callbacks *print_cb, void *print_state);
-void perf_pmus__print_raw_pmu_events(const struct print_callbacks *print_cb, void *print_state);
-bool perf_pmus__have_event(const char *pname, const char *name);
-int perf_pmus__num_core_pmus(void);
-bool perf_pmus__supports_extended_type(void);
+/**
+ * perf_pmus__print_pmu_events() - Print formatted PMU events via @print_cb.
+ * @pmus: The PMU collection whose events to print.
+ * @print_cb: Callbacks for printing event entries.
+ * @print_state: Caller state passed to @print_cb.
+ */
+void perf_pmus__print_pmu_events(struct perf_pmus *pmus,
+				 const struct print_callbacks *print_cb, void *print_state);
 
-struct perf_pmu *perf_pmus__add_test_pmu(int test_sysfs_dirfd, const char *name);
-struct perf_pmu *perf_pmus__add_test_hwmon_pmu(const char *hwmon_dir,
+/**
+ * perf_pmus__print_raw_pmu_events() - Print raw PMU event descriptors.
+ * @pmus: The PMU collection whose raw events to print.
+ * @print_cb: Callbacks for printing event entries.
+ * @print_state: Caller state passed to @print_cb.
+ */
+void perf_pmus__print_raw_pmu_events(struct perf_pmus *pmus,
+				     const struct print_callbacks *print_cb, void *print_state);
+
+/**
+ * perf_pmus__have_event() - Check whether @pname in @pmus has event @name.
+ * @pmus: The PMU collection to search.
+ * @pname: PMU name.
+ * @name: Event name.
+ *
+ * Return: true if the PMU exists and has @name, false otherwise.
+ */
+bool perf_pmus__have_event(struct perf_pmus *pmus, const char *pname, const char *name);
+
+/**
+ * perf_pmus__num_core_pmus() - Return the number of core PMUs in @pmus.
+ * @pmus: The PMU collection to query.
+ *
+ * Return: Number of core PMUs.
+ */
+int perf_pmus__num_core_pmus(struct perf_pmus *pmus);
+
+/**
+ * perf_pmus__supports_extended_type() - Check whether extended PMU type
+ *                                       encoding is supported in @pmus.
+ * @pmus: The PMU collection to query.
+ *
+ * Return: true if extended type encoding is supported, false otherwise.
+ */
+bool perf_pmus__supports_extended_type(struct perf_pmus *pmus);
+
+/**
+ * perf_pmus__host_supports_extended_type() - Check whether extended PMU type
+ *                                            encoding is supported on the host.
+ *
+ * Return: true if extended type encoding is supported on the live host.
+ */
+bool perf_pmus__host_supports_extended_type(void);
+
+/**
+ * perf_pmus__add_test_pmu() - Add a sysfs-backed test PMU to @pmus.
+ * @pmus: The PMU collection to add the test PMU to.
+ * @test_sysfs_dirfd: Directory fd of the synthetic sysfs root.
+ * @name: Name of the test PMU.
+ *
+ * Return: Pointer to the created struct perf_pmu, or NULL on failure.
+ */
+struct perf_pmu *perf_pmus__add_test_pmu(struct perf_pmus *pmus,
+					 int test_sysfs_dirfd, const char *name);
+
+/**
+ * perf_pmus__add_test_hwmon_pmu() - Add a synthetic hwmon test PMU to @pmus.
+ * @pmus: The PMU collection to add the test hwmon PMU to.
+ * @hwmon_dir: Path to the synthetic hwmon directory.
+ * @sysfs_name: Directory entry name (e.g. "hwmon0").
+ * @name: Logical name of the hwmon device.
+ *
+ * Return: Pointer to the created struct perf_pmu, or NULL on failure.
+ */
+struct perf_pmu *perf_pmus__add_test_hwmon_pmu(struct perf_pmus *pmus,
+					       const char *hwmon_dir,
 					       const char *sysfs_name,
 					       const char *name);
+
+/**
+ * perf_pmus__fake_pmu() - Return the singleton fake PMU used for metric tests.
+ *
+ * Return: Pointer to the fake struct perf_pmu.
+ */
 struct perf_pmu *perf_pmus__fake_pmu(void);
 struct perf_pmu *perf_pmus__find_core_pmu(void);
 
diff --git a/tools/perf/util/print-events.c b/tools/perf/util/print-events.c
index 77428d9e73c4..fe48efe9aefb 100644
--- a/tools/perf/util/print-events.c
+++ b/tools/perf/util/print-events.c
@@ -17,6 +17,7 @@
 
 #include "build-id.h"
 #include "debug.h"
+#include "env.h"
 #include "evsel.h"
 #include "metricgroup.h"
 #include "parse-events.h"
@@ -320,7 +321,9 @@ void metricgroup__print(const struct print_callbacks *print_cb, void *print_stat
  */
 void print_events(const struct print_callbacks *print_cb, void *print_state)
 {
-	perf_pmus__print_pmu_events(print_cb, print_state);
+	struct perf_pmus *pmus = perf_env__pmus(host_env());
+
+	perf_pmus__print_pmu_events(pmus, print_cb, print_state);
 
 	print_cb->print_event(print_state,
 			/*topic=*/NULL,
@@ -335,7 +338,7 @@ void print_events(const struct print_callbacks *print_cb, void *print_state)
 			/*long_desc=*/NULL,
 			/*encoding_desc=*/NULL);
 
-	perf_pmus__print_raw_pmu_events(print_cb, print_state);
+	perf_pmus__print_raw_pmu_events(pmus, print_cb, print_state);
 
 	print_cb->print_event(print_state,
 			/*topic=*/NULL,
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 6ff8d97dcf7d..d22f6e8e504a 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -26,6 +26,7 @@
 #include "env.h"
 #include "evlist.h"
 #include "evsel.h"
+#include "pmu.h"
 #include "memswap.h"
 #include "map.h"
 #include "symbol.h"
@@ -264,6 +265,16 @@ void perf_session__delete(struct perf_session *session)
 	debuginfo_cache__delete();
 	perf_session__destroy_kernel_maps(session);
 	perf_decomp__release_events(session->decomp_data.decomp);
+	if (session->data && perf_data__is_read(session->data) && session->evlist) {
+		struct evsel *evsel;
+
+		evlist__for_each_entry(session->evlist, evsel) {
+			if (evsel->pmu && evsel->pmu->pmus == &session->header.env.pmus)
+				evsel->pmu = NULL;
+		}
+		if (evlist__session(session->evlist) == session)
+			evlist__set_session(session->evlist, NULL);
+	}
 	perf_env__exit(&session->header.env);
 	machines__exit(&session->machines);
 	if (session->data) {
diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c
index b337cc23f413..288f98a323e9 100644
--- a/tools/perf/util/stat-display.c
+++ b/tools/perf/util/stat-display.c
@@ -770,7 +770,9 @@ static bool evlist__has_hybrid_pmus(struct evlist *evlist)
 	struct evsel *evsel;
 	struct perf_pmu *last_core_pmu = NULL;
 
-	if (perf_pmus__num_core_pmus() == 1)
+	struct perf_pmus *pmus = perf_env__pmus(evlist__env(evlist) ?: host_env());
+
+	if (perf_pmus__num_core_pmus(pmus) == 1)
 		return false;
 
 	evlist__for_each_entry(evlist, evsel) {
diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c
index 525a3fe4a46e..f5ae399bf336 100644
--- a/tools/perf/util/stat-shadow.c
+++ b/tools/perf/util/stat-shadow.c
@@ -236,7 +236,8 @@ static void perf_stat__print_metricgroup_header(struct perf_stat_config *config,
 						const char *name,
 						struct perf_stat_output_ctx *out)
 {
-	bool need_full_name = perf_pmus__num_core_pmus() > 1;
+	bool need_full_name =
+		perf_pmus__num_core_pmus(perf_env__pmus(evsel__env(evsel) ?: host_env())) > 1;
 	static const char *last_name;
 	static const struct perf_pmu *last_pmu;
 	char full_name[64];
-- 
2.56.0.385.gac671c819a-goog


  parent reply	other threads:[~2026-11-03 23:45 UTC|newest]

Thread overview: 83+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-03 23:44 [PATCH v1 00/41] perf: Per-env PMUs, recorded event metrics, and perf sysinfo Ian Rogers
2026-11-03 23:44 ` [PATCH v1 01/41] perf env: Make lazy perf_env initialization thread-safe Ian Rogers
2026-11-03 23:53   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 02/41] perf env: Add host_env() singleton Ian Rogers
2026-11-04  0:18   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 03/41] perf pmu: Add from_env parameter to perf_pmu__init() Ian Rogers
2026-11-04  0:31   ` sashiko-bot
2026-11-03 23:44 ` Ian Rogers [this message]
2026-11-04  0:48   ` [PATCH v1 04/41] perf pmus: Introduce struct perf_pmus and perf_env__pmus() sashiko-bot
2026-11-03 23:44 ` [PATCH v1 05/41] perf pmus: Pass struct perf_pmus to find helpers Ian Rogers
2026-11-04  0:56   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 06/41] perf pmus: Pass struct perf_pmus to scan helpers Ian Rogers
2026-11-04  1:01   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 07/41] perf pmu-events: Add e_machine to struct pmu_events_map Ian Rogers
2026-11-04  1:06   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 08/41] perf pmu-events: Pass e_machine to find_core_*_table() Ian Rogers
2026-11-04  1:14   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 09/41] perf header: Pass e_machine to strcmp_cpuid_str() Ian Rogers
2026-11-04  1:18   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 10/41] perf header: Support CPUID matching across all architectures Ian Rogers
2026-11-04  1:23   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 11/41] perf test: Make the CPUID matching test architecture independent Ian Rogers
2026-11-04  1:32   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 12/41] perf test: Add CPUID matching tests for other architectures Ian Rogers
2026-11-04  1:36   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 13/41] perf pmus: Add perf_pmus__read_from_env() Ian Rogers
2026-11-04  2:05   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 14/41] perf metricgroup: Pass struct perf_pmus to metricgroup__for_each_metric() Ian Rogers
2026-11-04  2:17   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 15/41] perf metricgroup: Move matching of metrics to recorded events from perf script Ian Rogers
2026-11-04  2:36   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 16/41] perf parse-events: Decode metric-id terms when adding events Ian Rogers
2026-11-04  2:44   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 17/41] perf evsel: Generalize metric_id to a set of metric_ids Ian Rogers
2026-11-04  3:17   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 18/41] perf pmu: Add perf_pmu__for_each_name_from_config() Ian Rogers
2026-11-04  3:23   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 19/41] perf metricgroup: Improve matching of metrics to recorded events Ian Rogers
2026-11-04  3:46   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 20/41] perf metricgroup: Add metricgroup__add_recorded_metrics() Ian Rogers
2026-11-04  4:10   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 21/41] perf script: Fix metrics computed from samples Ian Rogers
2026-11-04  4:16   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 22/41] perf script: Don't drop metric samples for CPUs missing from the evsel Ian Rogers
2026-11-04  4:22   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 23/41] perf stat report: Display metrics for recorded events Ian Rogers
2026-11-04  4:39   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 24/41] perf script: Display metrics for perf stat record data Ian Rogers
2026-11-04  4:47   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 25/41] perf test: Add stat record metrics test Ian Rogers
2026-11-04  5:01   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 26/41] perf pmus: Expose duplicate uncore PMU scanning helpers Ian Rogers
2026-11-04  5:08   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 27/41] perf mem-events: Expose PMU init and name formatting helpers Ian Rogers
2026-11-04  5:20   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 28/41] perf pmu: Add perf_pmu__probe() for basic open, counting, and sampling Ian Rogers
2026-11-04  5:39   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 29/41] perf pmu: Probe supported sample_type flags Ian Rogers
2026-11-04  5:49   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 30/41] perf pmu: Probe precise_ip levels Ian Rogers
2026-11-04  5:59   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 31/41] perf pmu: Probe privilege exclusion modifiers Ian Rogers
2026-11-04  6:04   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 32/41] perf pmu: Probe execution and AUX area features Ian Rogers
2026-11-04  6:19   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 33/41] perf pmu: Probe event group size limits Ian Rogers
2026-11-04  6:22   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 34/41] perf pmu: Probe user and interrupt sample register masks Ian Rogers
2026-11-04  6:44   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 35/41] perf pmu: Probe interrupt registers that require precise sampling Ian Rogers
2026-11-04  6:53   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 36/41] perf pmu: Probe memory profiling events and sample formats Ian Rogers
2026-11-04  7:04   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 37/41] perf pmu: Probe user-space mmap capabilities Ian Rogers
2026-11-04  7:27   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 38/41] perf pmu: Probe branch instruction skid Ian Rogers
2026-11-04  7:32   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 39/41] perf sysinfo: Add sysinfo subcommand Ian Rogers
2026-11-04  7:50   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 40/41] perf docs: Add perf-sysinfo man page Ian Rogers
2026-11-04  7:51   ` sashiko-bot
2026-11-03 23:44 ` [PATCH v1 41/41] perf test: Add perf sysinfo shell test Ian Rogers
2026-11-04  7:59   ` 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=20261103234502.1008382-5-irogers@google.com \
    --to=irogers@google.com \
    --cc=acme@kernel.org \
    --cc=adrian.hunter@intel.com \
    --cc=atrajeev@linux.ibm.com \
    --cc=dapeng1.mi@linux.intel.com \
    --cc=james.clark@linaro.org \
    --cc=john.garry@linux.dev \
    --cc=jolsa@kernel.org \
    --cc=kan.liang@linux.intel.com \
    --cc=leo.yan@arm.com \
    --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 \
    --cc=ravi.bangoria@amd.com \
    --cc=sandipan.das@amd.com \
    --cc=thomas.falcon@intel.com \
    --cc=tmricht@linux.ibm.com \
    --cc=will@kernel.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