2023-05-24 22:46:00

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 00/35] PMU refactoring and improvements

Separate the code in pmu.[ch] into the set/list of PMUs and the code
for a particular PMU. Move the set/list of PMUs code into
pmus.[ch]. Clean up hybrid code and remove hybrid PMU list, it is
sufficient to scan PMUs looking for core ones. Add core PMU list and
perf_pmus__scan_core that just reads core PMUs. Switch code that skips
non-core PMUs during a perf_pmus__scan, to use the
perf_pmus__scan_core variant. Don't scan sysfs for PMUs if all such
PMUs have been previously scanned/loaded. Scanning just core PMUs, for
the cases it is applicable, can improve the sysfs reading time by more
than 4 fold on my laptop, as servers generally have many more uncore
PMUs the improvement there should be larger:

```
$ perf bench internals pmu-scan -i 1000
Computing performance of sysfs PMU event scan for 1000 times
Average core PMU scanning took: 989.231 usec (+- 1.535 usec)
Average PMU scanning took: 4309.425 usec (+- 74.322 usec)
```

The patch "perf pmu: Separate pmu and pmus" moves and renames a lot of
functions, and is consequently large. The changes are trivial, but
kept together to keep the overall number of patches more reasonable.

v3. Address fixing hybrid user specified CPU maps by doing it in
propagate maps. Remove nearly all references to cpu_core/cpu_atom
in particular by removing is_pmu_hybrid - hybrid is now >1 core
PMU. Addresses comments by Kan and Namhyung.
v2. Address Kan's review comments wrt "cycles" -> "cycles:P" and
"uncore_pmus" -> "other_pmus".

Ian Rogers (35):
perf cpumap: Add intersect function
perf tests: Organize cpu_map tests into a single suite
perf cpumap: Add equal function
libperf cpumap: Add "any CPU"/dummy test function
perf pmu: Detect ARM and hybrid PMUs with sysfs
perf pmu: Add is_core to pmu
perf evsel: Add is_pmu_core inorder to interpret own_cpus
perf pmu: Add CPU map for "cpu" PMUs
perf evlist: Propagate user CPU maps intersecting core PMU maps
perf evlist: Allow has_user_cpus to be set on hybrid
perf target: Remove unused hybrid value
perf tools: Warn if no user requested CPUs match PMU's CPUs
perf evlist: Remove evlist__warn_hybrid_group
perf evlist: Remove __evlist__add_default
perf evlist: Reduce scope of evlist__has_hybrid
perf pmu: Remove perf_pmu__hybrid_mounted
perf pmu: Rewrite perf_pmu__has_hybrid to avoid list
perf x86: Iterate hybrid PMUs as core PMUs
perf topology: Avoid hybrid list for hybrid topology
perf evsel: Compute is_hybrid from PMU being core
perf header: Avoid hybrid PMU list in write_pmu_caps
perf metrics: Remove perf_pmu__is_hybrid use
perf stat: Avoid hybrid PMU list
perf mem: Avoid hybrid PMU list
perf pmu: Remove perf_pmu__hybrid_pmus list
perf pmus: Prefer perf_pmu__scan over perf_pmus__for_each_pmu
perf x86 mem: minor refactor to is_mem_loads_aux_event
perf pmu: Separate pmu and pmus
perf pmus: Split pmus list into core and other
perf pmus: Allow just core PMU scanning
perf pmus: Avoid repeated sysfs scanning
perf pmus: Ensure all PMUs are read for find_by_type
perf pmus: Add function to return count of core PMUs
perf pmus: Remove perf_pmus__has_hybrid
perf pmu: Remove is_pmu_hybrid

tools/lib/perf/cpumap.c | 61 +++
tools/lib/perf/evlist.c | 25 +-
tools/lib/perf/include/internal/evsel.h | 9 +
tools/lib/perf/include/perf/cpumap.h | 14 +
tools/perf/arch/arm/util/auxtrace.c | 7 +-
tools/perf/arch/arm/util/cs-etm.c | 4 +-
tools/perf/arch/arm64/util/pmu.c | 6 +-
tools/perf/arch/x86/tests/hybrid.c | 7 +-
tools/perf/arch/x86/util/auxtrace.c | 5 +-
tools/perf/arch/x86/util/evlist.c | 25 +-
tools/perf/arch/x86/util/evsel.c | 27 +-
tools/perf/arch/x86/util/intel-bts.c | 4 +-
tools/perf/arch/x86/util/intel-pt.c | 4 +-
tools/perf/arch/x86/util/mem-events.c | 17 +-
tools/perf/arch/x86/util/perf_regs.c | 15 +-
tools/perf/arch/x86/util/topdown.c | 5 +-
tools/perf/bench/pmu-scan.c | 60 +--
tools/perf/builtin-c2c.c | 9 +-
tools/perf/builtin-list.c | 4 +-
tools/perf/builtin-mem.c | 9 +-
tools/perf/builtin-record.c | 29 +-
tools/perf/builtin-stat.c | 14 +-
tools/perf/builtin-top.c | 10 +-
tools/perf/tests/attr.c | 11 +-
tools/perf/tests/builtin-test.c | 4 +-
tools/perf/tests/cpumap.c | 92 ++++-
tools/perf/tests/event_groups.c | 7 +-
tools/perf/tests/parse-events.c | 15 +-
tools/perf/tests/parse-metric.c | 9 +-
tools/perf/tests/pmu-events.c | 6 +-
tools/perf/tests/switch-tracking.c | 14 +-
tools/perf/tests/tests.h | 4 +-
tools/perf/tests/topology.c | 16 +-
tools/perf/util/Build | 2 -
tools/perf/util/cpumap.c | 4 +-
tools/perf/util/cpumap.h | 4 +-
tools/perf/util/cputopo.c | 12 +-
tools/perf/util/env.c | 5 +-
tools/perf/util/evlist-hybrid.c | 162 --------
tools/perf/util/evlist-hybrid.h | 15 -
tools/perf/util/evlist.c | 64 +++-
tools/perf/util/evlist.h | 9 +-
tools/perf/util/evsel.c | 60 +--
tools/perf/util/evsel.h | 3 -
tools/perf/util/header.c | 27 +-
tools/perf/util/mem-events.c | 25 +-
tools/perf/util/metricgroup.c | 9 +-
tools/perf/util/parse-events.c | 25 +-
tools/perf/util/parse-events.y | 3 +-
tools/perf/util/pfm.c | 6 +-
tools/perf/util/pmu-hybrid.c | 52 ---
tools/perf/util/pmu-hybrid.h | 32 --
tools/perf/util/pmu.c | 483 ++----------------------
tools/perf/util/pmu.h | 25 +-
tools/perf/util/pmus.c | 465 ++++++++++++++++++++++-
tools/perf/util/pmus.h | 15 +-
tools/perf/util/print-events.c | 15 +-
tools/perf/util/python-ext-sources | 1 -
tools/perf/util/stat-display.c | 19 +-
tools/perf/util/target.h | 1 -
60 files changed, 1002 insertions(+), 1089 deletions(-)
delete mode 100644 tools/perf/util/evlist-hybrid.c
delete mode 100644 tools/perf/util/evlist-hybrid.h
delete mode 100644 tools/perf/util/pmu-hybrid.c
delete mode 100644 tools/perf/util/pmu-hybrid.h

--
2.40.1.698.g37aff9b760-goog



2023-05-24 22:46:04

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 19/35] perf topology: Avoid hybrid list for hybrid topology

Avoid perf_pmu__for_each_hybrid_pmu in hybrid_topology__new by
scanning all PMUs and processing the is_core ones. Add early exit for
non-hybrid.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/util/cputopo.c | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/tools/perf/util/cputopo.c b/tools/perf/util/cputopo.c
index ca1d833a0c26..a5c259bd5cc0 100644
--- a/tools/perf/util/cputopo.c
+++ b/tools/perf/util/cputopo.c
@@ -12,7 +12,7 @@
#include "cpumap.h"
#include "debug.h"
#include "env.h"
-#include "pmu-hybrid.h"
+#include "pmu.h"

#define PACKAGE_CPUS_FMT \
"%s/devices/system/cpu/cpu%d/topology/package_cpus_list"
@@ -469,11 +469,17 @@ static int load_hybrid_node(struct hybrid_topology_node *node,

struct hybrid_topology *hybrid_topology__new(void)
{
- struct perf_pmu *pmu;
+ struct perf_pmu *pmu = NULL;
struct hybrid_topology *tp = NULL;
- u32 nr, i = 0;
+ u32 nr = 0, i = 0;

- nr = perf_pmu__hybrid_pmu_num();
+ if (!perf_pmu__has_hybrid())
+ return NULL;
+
+ while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ if (pmu->is_core)
+ nr++;
+ }
if (nr == 0)
return NULL;

@@ -482,7 +488,10 @@ struct hybrid_topology *hybrid_topology__new(void)
return NULL;

tp->nr = nr;
- perf_pmu__for_each_hybrid_pmu(pmu) {
+ while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ if (!pmu->is_core)
+ continue;
+
if (load_hybrid_node(&tp->nodes[i], pmu)) {
hybrid_topology__delete(tp);
return NULL;
--
2.40.1.698.g37aff9b760-goog


2023-05-24 22:46:28

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 31/35] perf pmus: Avoid repeated sysfs scanning

perf_pmus__scan will process every directory in sysfs to see if it is
a PMU, attempting to add it if not already in the pmus list. Add two
booleans to record whether this scanning has been done for core or all
PMUs. Skip scanning in the event that scanning has already occurred.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/util/pmus.c | 33 +++++++++++++++++++++++++++++++--
1 file changed, 31 insertions(+), 2 deletions(-)

diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c
index de7fc36519c9..2c512345191d 100644
--- a/tools/perf/util/pmus.c
+++ b/tools/perf/util/pmus.c
@@ -14,6 +14,8 @@

static LIST_HEAD(core_pmus);
static LIST_HEAD(other_pmus);
+static bool read_sysfs_core_pmus;
+static bool read_sysfs_all_pmus;

void perf_pmus__destroy(void)
{
@@ -29,6 +31,8 @@ void perf_pmus__destroy(void)

perf_pmu__delete(pmu);
}
+ read_sysfs_core_pmus = false;
+ read_sysfs_all_pmus = false;
}

static struct perf_pmu *pmu_find(const char *name)
@@ -53,6 +57,7 @@ struct perf_pmu *perf_pmus__find(const char *name)
{
struct perf_pmu *pmu;
int dirfd;
+ bool core_pmu;

/*
* Once PMU is loaded it stays in the list,
@@ -63,8 +68,15 @@ struct perf_pmu *perf_pmus__find(const char *name)
if (pmu)
return pmu;

+ if (read_sysfs_all_pmus)
+ return NULL;
+
+ core_pmu = is_pmu_core(name);
+ if (core_pmu && read_sysfs_core_pmus)
+ return NULL;
+
dirfd = perf_pmu__event_source_devices_fd();
- pmu = perf_pmu__lookup(is_pmu_core(name) ? &core_pmus : &other_pmus, dirfd, name);
+ pmu = perf_pmu__lookup(core_pmu ? &core_pmus : &other_pmus, dirfd, name);
close(dirfd);

return pmu;
@@ -73,6 +85,7 @@ struct perf_pmu *perf_pmus__find(const char *name)
static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name)
{
struct perf_pmu *pmu;
+ bool core_pmu;

/*
* Once PMU is loaded it stays in the list,
@@ -83,7 +96,14 @@ static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name)
if (pmu)
return pmu;

- return perf_pmu__lookup(is_pmu_core(name) ? &core_pmus : &other_pmus, dirfd, name);
+ if (read_sysfs_all_pmus)
+ return NULL;
+
+ core_pmu = is_pmu_core(name);
+ if (core_pmu && read_sysfs_core_pmus)
+ return NULL;
+
+ return perf_pmu__lookup(core_pmu ? &core_pmus : &other_pmus, dirfd, name);
}

/* Add all pmus in sysfs to pmu list: */
@@ -93,6 +113,9 @@ static void pmu_read_sysfs(bool core_only)
DIR *dir;
struct dirent *dent;

+ if (read_sysfs_all_pmus || (core_only && read_sysfs_core_pmus))
+ return;
+
fd = perf_pmu__event_source_devices_fd();
if (fd < 0)
return;
@@ -111,6 +134,12 @@ static void pmu_read_sysfs(bool core_only)
}

closedir(dir);
+ if (core_only) {
+ read_sysfs_core_pmus = true;
+ } else {
+ read_sysfs_core_pmus = true;
+ read_sysfs_all_pmus = true;
+ }
}

struct perf_pmu *perf_pmus__find_by_type(unsigned int type)
--
2.40.1.698.g37aff9b760-goog


2023-05-24 22:46:47

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 33/35] perf pmus: Add function to return count of core PMUs

Add perf_pmus__num_core_pmus that will count core PMUs holding the
result in a static. Reuse for perf_pmus__num_mem_pmus.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/util/pmus.c | 21 ++++++++++++++-------
tools/perf/util/pmus.h | 1 +
2 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c
index 6ecccb5ad03e..bf927aed162e 100644
--- a/tools/perf/util/pmus.c
+++ b/tools/perf/util/pmus.c
@@ -229,14 +229,8 @@ const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str)

int perf_pmus__num_mem_pmus(void)
{
- struct perf_pmu *pmu = NULL;
- int count = 0;
-
/* All core PMUs are for mem events. */
- while ((pmu = perf_pmus__scan_core(pmu)) != NULL)
- count++;
-
- return count;
+ return perf_pmus__num_core_pmus();
}

/** Struct for ordering events as output in perf list. */
@@ -488,6 +482,19 @@ bool perf_pmus__has_hybrid(void)
return has_hybrid;
}

+int perf_pmus__num_core_pmus(void)
+{
+ static int count;
+
+ if (!count) {
+ struct perf_pmu *pmu = NULL;
+
+ while ((pmu = perf_pmus__scan_core(pmu)) != NULL)
+ count++;
+ }
+ return count;
+}
+
struct perf_pmu *evsel__find_pmu(const struct evsel *evsel)
{
struct perf_pmu *pmu = evsel->pmu;
diff --git a/tools/perf/util/pmus.h b/tools/perf/util/pmus.h
index 9de0222ed52b..27400a027d41 100644
--- a/tools/perf/util/pmus.h
+++ b/tools/perf/util/pmus.h
@@ -19,5 +19,6 @@ int perf_pmus__num_mem_pmus(void);
void perf_pmus__print_pmu_events(const struct print_callbacks *print_cb, void *print_state);
bool perf_pmus__have_event(const char *pname, const char *name);
bool perf_pmus__has_hybrid(void);
+int perf_pmus__num_core_pmus(void);

#endif /* __PMUS_H */
--
2.40.1.698.g37aff9b760-goog


2023-05-24 22:46:54

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 04/35] libperf cpumap: Add "any CPU"/dummy test function

It is common in the code currently to test a map for "empty" when in
fact the "any CPU"/dummy value of -1 is being sought. Add a new
function to enable this and document the behavior of two other
functions. The term "any CPU" comes from perf_event_open, where the
value is consumed, but it is more typical in the code to see this
value/map referred to as the dummy value. This could be misleading due
to the dummy event and also dummy not being intention revealing, so it
is hoped to migrate the code to referring to this as "any CPU".

Signed-off-by: Ian Rogers <[email protected]>
---
tools/lib/perf/cpumap.c | 5 +++++
tools/lib/perf/include/perf/cpumap.h | 10 ++++++++++
2 files changed, 15 insertions(+)

diff --git a/tools/lib/perf/cpumap.c b/tools/lib/perf/cpumap.c
index 48595a3ad69c..7c8237a9e902 100644
--- a/tools/lib/perf/cpumap.c
+++ b/tools/lib/perf/cpumap.c
@@ -342,6 +342,11 @@ bool perf_cpu_map__equal(const struct perf_cpu_map *lhs, const struct perf_cpu_m
return true;
}

+bool perf_cpu_map__has_any_cpu(const struct perf_cpu_map *map)
+{
+ return map && perf_cpu_map__cpu(map, 0).cpu == -1;
+}
+
struct perf_cpu perf_cpu_map__max(const struct perf_cpu_map *map)
{
struct perf_cpu result = {
diff --git a/tools/lib/perf/include/perf/cpumap.h b/tools/lib/perf/include/perf/cpumap.h
index d0ae9552f8e2..3b7c965a17f2 100644
--- a/tools/lib/perf/include/perf/cpumap.h
+++ b/tools/lib/perf/include/perf/cpumap.h
@@ -13,6 +13,9 @@ struct perf_cpu {

struct perf_cpu_map;

+/**
+ * perf_cpu_map__dummy_new - a map with a singular "any CPU"/dummy -1 value.
+ */
LIBPERF_API struct perf_cpu_map *perf_cpu_map__dummy_new(void);
LIBPERF_API struct perf_cpu_map *perf_cpu_map__default_new(void);
LIBPERF_API struct perf_cpu_map *perf_cpu_map__new(const char *cpu_list);
@@ -25,11 +28,18 @@ LIBPERF_API struct perf_cpu_map *perf_cpu_map__intersect(struct perf_cpu_map *or
LIBPERF_API void perf_cpu_map__put(struct perf_cpu_map *map);
LIBPERF_API struct perf_cpu perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx);
LIBPERF_API int perf_cpu_map__nr(const struct perf_cpu_map *cpus);
+/**
+ * perf_cpu_map__empty - is map either empty or the "any CPU"/dummy value.
+ */
LIBPERF_API bool perf_cpu_map__empty(const struct perf_cpu_map *map);
LIBPERF_API struct perf_cpu perf_cpu_map__max(const struct perf_cpu_map *map);
LIBPERF_API bool perf_cpu_map__has(const struct perf_cpu_map *map, struct perf_cpu cpu);
LIBPERF_API bool perf_cpu_map__equal(const struct perf_cpu_map *lhs,
const struct perf_cpu_map *rhs);
+/**
+ * perf_cpu_map__any_cpu - Does the map contain the "any CPU"/dummy -1 value?
+ */
+LIBPERF_API bool perf_cpu_map__has_any_cpu(const struct perf_cpu_map *map);

#define perf_cpu_map__for_each_cpu(cpu, idx, cpus) \
for ((idx) = 0, (cpu) = perf_cpu_map__cpu(cpus, idx); \
--
2.40.1.698.g37aff9b760-goog


2023-05-24 22:46:57

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 18/35] perf x86: Iterate hybrid PMUs as core PMUs

Rather than iterating over a separate hybrid list, iterate all PMUs
with the hybrid ones having is_core as true.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/arch/x86/tests/hybrid.c | 2 +-
tools/perf/arch/x86/util/evlist.c | 25 +++++++++++++++++--------
tools/perf/arch/x86/util/perf_regs.c | 14 ++++++++++----
3 files changed, 28 insertions(+), 13 deletions(-)

diff --git a/tools/perf/arch/x86/tests/hybrid.c b/tools/perf/arch/x86/tests/hybrid.c
index 941a9edfed4e..944bd1b4bab6 100644
--- a/tools/perf/arch/x86/tests/hybrid.c
+++ b/tools/perf/arch/x86/tests/hybrid.c
@@ -3,7 +3,7 @@
#include "debug.h"
#include "evlist.h"
#include "evsel.h"
-#include "pmu-hybrid.h"
+#include "pmu.h"
#include "tests/tests.h"

static bool test_config(const struct evsel *evsel, __u64 expected_config)
diff --git a/tools/perf/arch/x86/util/evlist.c b/tools/perf/arch/x86/util/evlist.c
index 1b6065841fb0..03f7eb4cf0a4 100644
--- a/tools/perf/arch/x86/util/evlist.c
+++ b/tools/perf/arch/x86/util/evlist.c
@@ -4,7 +4,6 @@
#include "util/evlist.h"
#include "util/parse-events.h"
#include "util/event.h"
-#include "util/pmu-hybrid.h"
#include "topdown.h"
#include "evsel.h"

@@ -12,9 +11,6 @@ static int ___evlist__add_default_attrs(struct evlist *evlist,
struct perf_event_attr *attrs,
size_t nr_attrs)
{
- struct perf_cpu_map *cpus;
- struct evsel *evsel, *n;
- struct perf_pmu *pmu;
LIST_HEAD(head);
size_t i = 0;

@@ -25,15 +21,24 @@ static int ___evlist__add_default_attrs(struct evlist *evlist,
return evlist__add_attrs(evlist, attrs, nr_attrs);

for (i = 0; i < nr_attrs; i++) {
+ struct perf_pmu *pmu = NULL;
+
if (attrs[i].type == PERF_TYPE_SOFTWARE) {
- evsel = evsel__new(attrs + i);
+ struct evsel *evsel = evsel__new(attrs + i);
+
if (evsel == NULL)
goto out_delete_partial_list;
list_add_tail(&evsel->core.node, &head);
continue;
}

- perf_pmu__for_each_hybrid_pmu(pmu) {
+ while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ struct perf_cpu_map *cpus;
+ struct evsel *evsel;
+
+ if (!pmu->is_core)
+ continue;
+
evsel = evsel__new(attrs + i);
if (evsel == NULL)
goto out_delete_partial_list;
@@ -51,8 +56,12 @@ static int ___evlist__add_default_attrs(struct evlist *evlist,
return 0;

out_delete_partial_list:
- __evlist__for_each_entry_safe(&head, n, evsel)
- evsel__delete(evsel);
+ {
+ struct evsel *evsel, *n;
+
+ __evlist__for_each_entry_safe(&head, n, evsel)
+ evsel__delete(evsel);
+ }
return -1;
}

diff --git a/tools/perf/arch/x86/util/perf_regs.c b/tools/perf/arch/x86/util/perf_regs.c
index 0ed177991ad0..26abc159fc0e 100644
--- a/tools/perf/arch/x86/util/perf_regs.c
+++ b/tools/perf/arch/x86/util/perf_regs.c
@@ -10,7 +10,6 @@
#include "../../../util/debug.h"
#include "../../../util/event.h"
#include "../../../util/pmu.h"
-#include "../../../util/pmu-hybrid.h"

const struct sample_reg sample_reg_masks[] = {
SMPL_REG(AX, PERF_REG_X86_AX),
@@ -286,7 +285,6 @@ uint64_t arch__intr_reg_mask(void)
.disabled = 1,
.exclude_kernel = 1,
};
- struct perf_pmu *pmu;
int fd;
/*
* In an unnamed union, init it here to build on older gcc versions
@@ -294,12 +292,20 @@ uint64_t arch__intr_reg_mask(void)
attr.sample_period = 1;

if (perf_pmu__has_hybrid()) {
+ struct perf_pmu *pmu = NULL;
+ __u64 type = PERF_TYPE_RAW;
+
/*
* The same register set is supported among different hybrid PMUs.
* Only check the first available one.
*/
- pmu = list_first_entry(&perf_pmu__hybrid_pmus, typeof(*pmu), hybrid_list);
- attr.config |= (__u64)pmu->type << PERF_PMU_TYPE_SHIFT;
+ while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ if (pmu->is_core) {
+ type = pmu->type;
+ break;
+ }
+ }
+ attr.config |= type << PERF_PMU_TYPE_SHIFT;
}

event_attr_init(&attr);
--
2.40.1.698.g37aff9b760-goog


2023-05-24 22:48:23

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 35/35] perf pmu: Remove is_pmu_hybrid

Users have been removed or switched to using pmu->is_core with
perf_pmus__num_core_pmus() > 1.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/util/pmu.c | 7 +------
tools/perf/util/pmu.h | 1 -
2 files changed, 1 insertion(+), 7 deletions(-)

diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 4dd40a38f6bf..a548d38f2a07 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -1430,11 +1430,6 @@ bool is_pmu_core(const char *name)
return !strcmp(name, "cpu") || is_sysfs_pmu_core(name);
}

-bool is_pmu_hybrid(const char *name)
-{
- return !strcmp(name, "cpu_atom") || !strcmp(name, "cpu_core");
-}
-
bool perf_pmu__supports_legacy_cache(const struct perf_pmu *pmu)
{
return pmu->is_core;
@@ -1442,7 +1437,7 @@ bool perf_pmu__supports_legacy_cache(const struct perf_pmu *pmu)

bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu)
{
- return !is_pmu_hybrid(pmu->name);
+ return pmu->is_core && perf_pmus__num_core_pmus() > 1;
}

bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name)
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 02fec0a7d4c8..287f593b15c7 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -221,7 +221,6 @@ int perf_pmu__format_parse(int dirfd, struct list_head *head);
void perf_pmu__del_formats(struct list_head *formats);

bool is_pmu_core(const char *name);
-bool is_pmu_hybrid(const char *name);
bool perf_pmu__supports_legacy_cache(const struct perf_pmu *pmu);
bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu);
bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name);
--
2.40.1.698.g37aff9b760-goog


2023-05-24 22:48:58

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 09/35] perf evlist: Propagate user CPU maps intersecting core PMU maps

The CPU map for a non-core PMU gives a default CPU value for
perf_event_open. For core PMUs the CPU map lists all CPUs the evsel
may be opened on. If there are >1 core PMU, the CPU maps will list the
CPUs for that core PMU, but the user_requested_cpus may contain CPUs
that are invalid for the PMU and cause perf_event_open to fail. To
avoid this, when propagating the CPU map for core PMUs intersect it
with the CPU map of the PMU (the evsel's "own_cpus").

Add comments to __perf_evlist__propagate_maps to explain its somewhat
complex behavior.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/lib/perf/evlist.c | 25 ++++++++++++++++++++-----
1 file changed, 20 insertions(+), 5 deletions(-)

diff --git a/tools/lib/perf/evlist.c b/tools/lib/perf/evlist.c
index 81e8b5fcd8ba..b8b066d0dc5e 100644
--- a/tools/lib/perf/evlist.c
+++ b/tools/lib/perf/evlist.c
@@ -36,18 +36,33 @@ void perf_evlist__init(struct perf_evlist *evlist)
static void __perf_evlist__propagate_maps(struct perf_evlist *evlist,
struct perf_evsel *evsel)
{
- /*
- * We already have cpus for evsel (via PMU sysfs) so
- * keep it, if there's no target cpu list defined.
- */
if (evsel->system_wide) {
+ /* System wide: set the cpu map of the evsel to all online CPUs. */
perf_cpu_map__put(evsel->cpus);
evsel->cpus = perf_cpu_map__new(NULL);
+ } else if (evlist->has_user_cpus && evsel->is_pmu_core) {
+ /*
+ * User requested CPUs on a core PMU, ensure the requested CPUs
+ * are valid by intersecting with those of the PMU.
+ */
+ perf_cpu_map__put(evsel->cpus);
+ evsel->cpus = perf_cpu_map__intersect(evlist->user_requested_cpus, evsel->own_cpus);
} else if (!evsel->own_cpus || evlist->has_user_cpus ||
- (!evsel->requires_cpu && perf_cpu_map__empty(evlist->user_requested_cpus))) {
+ (!evsel->requires_cpu && perf_cpu_map__has_any_cpu(evlist->user_requested_cpus))) {
+ /*
+ * The PMU didn't specify a default cpu map, this isn't a core
+ * event and the user requested CPUs or the evlist user
+ * requested CPUs have the "any CPU" (aka dummy) CPU value. In
+ * which case use the user requested CPUs rather than the PMU
+ * ones.
+ */
perf_cpu_map__put(evsel->cpus);
evsel->cpus = perf_cpu_map__get(evlist->user_requested_cpus);
} else if (evsel->cpus != evsel->own_cpus) {
+ /*
+ * No user requested cpu map but the PMU cpu map doesn't match
+ * the evsel's. Reset it back to the PMU cpu map.
+ */
perf_cpu_map__put(evsel->cpus);
evsel->cpus = perf_cpu_map__get(evsel->own_cpus);
}
--
2.40.1.698.g37aff9b760-goog


2023-05-24 22:49:19

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 32/35] perf pmus: Ensure all PMUs are read for find_by_type

perf_pmus__find_by_type may be called for something like a raw event,
in which case the PMU isn't guaranteed to have been looked up. Add a
second check to make sure all PMUs are loaded.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/util/pmus.c | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c
index 2c512345191d..6ecccb5ad03e 100644
--- a/tools/perf/util/pmus.c
+++ b/tools/perf/util/pmus.c
@@ -142,7 +142,7 @@ static void pmu_read_sysfs(bool core_only)
}
}

-struct perf_pmu *perf_pmus__find_by_type(unsigned int type)
+static struct perf_pmu *__perf_pmus__find_by_type(unsigned int type)
{
struct perf_pmu *pmu;

@@ -150,6 +150,7 @@ struct perf_pmu *perf_pmus__find_by_type(unsigned int type)
if (pmu->type == type)
return pmu;
}
+
list_for_each_entry(pmu, &other_pmus, list) {
if (pmu->type == type)
return pmu;
@@ -157,6 +158,18 @@ struct perf_pmu *perf_pmus__find_by_type(unsigned int type)
return NULL;
}

+struct perf_pmu *perf_pmus__find_by_type(unsigned int type)
+{
+ struct perf_pmu *pmu = __perf_pmus__find_by_type(type);
+
+ if (pmu || read_sysfs_all_pmus)
+ return pmu;
+
+ pmu_read_sysfs(/*core_only=*/false);
+ pmu = __perf_pmus__find_by_type(type);
+ return pmu;
+}
+
/*
* pmu iterator: If pmu is NULL, we start at the begin, otherwise return the
* next pmu. Returns NULL on end.
--
2.40.1.698.g37aff9b760-goog


2023-05-24 22:50:05

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 07/35] perf evsel: Add is_pmu_core inorder to interpret own_cpus

The behaviour of handling cpu maps varies for core and other PMUs. For
core PMUs the cpu map lists all valid CPUs, whereas for other PMUs the
map is the default CPU. Add a flag in the evsel to indicate if a PMU
is core to help with later interpreting of the cpu maps and populate
it when the evsel is created during parsing. When propagating cpu
maps, core PMUs should intersect the cpu map of the PMU with the user
requested one.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/lib/perf/include/internal/evsel.h | 9 +++++++++
tools/perf/util/evsel.c | 1 +
tools/perf/util/parse-events.c | 1 +
3 files changed, 11 insertions(+)

diff --git a/tools/lib/perf/include/internal/evsel.h b/tools/lib/perf/include/internal/evsel.h
index a99a75d9e78f..4d6f2a032f45 100644
--- a/tools/lib/perf/include/internal/evsel.h
+++ b/tools/lib/perf/include/internal/evsel.h
@@ -41,7 +41,14 @@ struct perf_sample_id {
struct perf_evsel {
struct list_head node;
struct perf_event_attr attr;
+ /** The commonly used cpu map of CPUs the event should be opened upon, etc. */
struct perf_cpu_map *cpus;
+ /**
+ * The cpu map read from the PMU. For core PMUs this is the list of all
+ * CPUs the event can be opened upon. For other PMUs this is the default
+ * cpu map for opening the event on, for example, the first CPU on a
+ * socket for an uncore event.
+ */
struct perf_cpu_map *own_cpus;
struct perf_thread_map *threads;
struct xyarray *fd;
@@ -65,6 +72,8 @@ struct perf_evsel {
* i.e. it cannot be the 'any CPU' value of -1.
*/
bool requires_cpu;
+ /** Is the PMU for the event a core one? Effects the handling of own_cpus. */
+ bool is_pmu_core;
int idx;
};

diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 2f5910b31fa9..8c8f371ea2b5 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -415,6 +415,7 @@ struct evsel *evsel__clone(struct evsel *orig)
evsel->core.nr_members = orig->core.nr_members;
evsel->core.system_wide = orig->core.system_wide;
evsel->core.requires_cpu = orig->core.requires_cpu;
+ evsel->core.is_pmu_core = orig->core.is_pmu_core;

if (orig->name) {
evsel->name = strdup(orig->name);
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index b93264f8a37c..1a0be395c887 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -260,6 +260,7 @@ __add_event(struct list_head *list, int *idx,
evsel->core.cpus = cpus;
evsel->core.own_cpus = perf_cpu_map__get(cpus);
evsel->core.requires_cpu = pmu ? pmu->is_uncore : false;
+ evsel->core.is_pmu_core = pmu ? pmu->is_core : false;
evsel->auto_merge_stats = auto_merge_stats;
evsel->pmu = pmu;
evsel->pmu_name = pmu && pmu->name ? strdup(pmu->name) : NULL;
--
2.40.1.698.g37aff9b760-goog


2023-05-24 22:50:03

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 28/35] perf pmu: Separate pmu and pmus

Separate and hide the pmus list in pmus.[ch]. Move pmus functionality
out of pmu.[ch] into pmus.[ch] renaming pmus functions which were
prefixed perf_pmu__ to perf_pmus__.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/arch/arm/util/auxtrace.c | 7 +-
tools/perf/arch/arm/util/cs-etm.c | 4 +-
tools/perf/arch/arm64/util/pmu.c | 3 +-
tools/perf/arch/x86/tests/hybrid.c | 5 +-
tools/perf/arch/x86/util/auxtrace.c | 5 +-
tools/perf/arch/x86/util/evlist.c | 5 +-
tools/perf/arch/x86/util/evsel.c | 7 +-
tools/perf/arch/x86/util/intel-bts.c | 4 +-
tools/perf/arch/x86/util/intel-pt.c | 4 +-
tools/perf/arch/x86/util/mem-events.c | 9 +-
tools/perf/arch/x86/util/perf_regs.c | 5 +-
tools/perf/arch/x86/util/topdown.c | 5 +-
tools/perf/bench/pmu-scan.c | 10 +-
tools/perf/builtin-c2c.c | 4 +-
tools/perf/builtin-list.c | 4 +-
tools/perf/builtin-mem.c | 4 +-
tools/perf/builtin-record.c | 6 +-
tools/perf/builtin-stat.c | 4 +-
tools/perf/tests/attr.c | 4 +-
tools/perf/tests/event_groups.c | 2 +-
tools/perf/tests/parse-events.c | 8 +-
tools/perf/tests/parse-metric.c | 4 +-
tools/perf/tests/pmu-events.c | 3 +-
tools/perf/tests/switch-tracking.c | 4 +-
tools/perf/tests/topology.c | 4 +-
tools/perf/util/cputopo.c | 7 +-
tools/perf/util/env.c | 5 +-
tools/perf/util/evsel.c | 3 +-
tools/perf/util/header.c | 15 +-
tools/perf/util/mem-events.c | 11 +-
tools/perf/util/metricgroup.c | 5 +-
tools/perf/util/parse-events.c | 15 +-
tools/perf/util/parse-events.y | 3 +-
tools/perf/util/pfm.c | 6 +-
tools/perf/util/pmu.c | 411 +-------------------------
tools/perf/util/pmu.h | 13 +-
tools/perf/util/pmus.c | 396 ++++++++++++++++++++++++-
tools/perf/util/pmus.h | 14 +-
tools/perf/util/print-events.c | 5 +-
tools/perf/util/stat-display.c | 3 +-
40 files changed, 531 insertions(+), 505 deletions(-)

diff --git a/tools/perf/arch/arm/util/auxtrace.c b/tools/perf/arch/arm/util/auxtrace.c
index adec6c9ee11d..3b8eca0ffb17 100644
--- a/tools/perf/arch/arm/util/auxtrace.c
+++ b/tools/perf/arch/arm/util/auxtrace.c
@@ -14,6 +14,7 @@
#include "../../../util/debug.h"
#include "../../../util/evlist.h"
#include "../../../util/pmu.h"
+#include "../../../util/pmus.h"
#include "cs-etm.h"
#include "arm-spe.h"
#include "hisi-ptt.h"
@@ -40,7 +41,7 @@ static struct perf_pmu **find_all_arm_spe_pmus(int *nr_spes, int *err)
return NULL;
}

- arm_spe_pmus[*nr_spes] = perf_pmu__find(arm_spe_pmu_name);
+ arm_spe_pmus[*nr_spes] = perf_pmus__find(arm_spe_pmu_name);
if (arm_spe_pmus[*nr_spes]) {
pr_debug2("%s %d: arm_spe_pmu %d type %d name %s\n",
__func__, __LINE__, *nr_spes,
@@ -87,7 +88,7 @@ static struct perf_pmu **find_all_hisi_ptt_pmus(int *nr_ptts, int *err)
rewinddir(dir);
while ((dent = readdir(dir))) {
if (strstr(dent->d_name, HISI_PTT_PMU_NAME) && idx < *nr_ptts) {
- hisi_ptt_pmus[idx] = perf_pmu__find(dent->d_name);
+ hisi_ptt_pmus[idx] = perf_pmus__find(dent->d_name);
if (hisi_ptt_pmus[idx])
idx++;
}
@@ -131,7 +132,7 @@ struct auxtrace_record
if (!evlist)
return NULL;

- cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
+ cs_etm_pmu = perf_pmus__find(CORESIGHT_ETM_PMU_NAME);
arm_spe_pmus = find_all_arm_spe_pmus(&nr_spes, err);
hisi_ptt_pmus = find_all_hisi_ptt_pmus(&nr_ptts, err);

diff --git a/tools/perf/arch/arm/util/cs-etm.c b/tools/perf/arch/arm/util/cs-etm.c
index 9ca040bfb1aa..7c51fa182b51 100644
--- a/tools/perf/arch/arm/util/cs-etm.c
+++ b/tools/perf/arch/arm/util/cs-etm.c
@@ -25,7 +25,7 @@
#include "../../../util/evsel.h"
#include "../../../util/perf_api_probe.h"
#include "../../../util/evsel_config.h"
-#include "../../../util/pmu.h"
+#include "../../../util/pmus.h"
#include "../../../util/cs-etm.h"
#include <internal/lib.h> // page_size
#include "../../../util/session.h"
@@ -881,7 +881,7 @@ struct auxtrace_record *cs_etm_record_init(int *err)
struct perf_pmu *cs_etm_pmu;
struct cs_etm_recording *ptr;

- cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
+ cs_etm_pmu = perf_pmus__find(CORESIGHT_ETM_PMU_NAME);

if (!cs_etm_pmu) {
*err = -EINVAL;
diff --git a/tools/perf/arch/arm64/util/pmu.c b/tools/perf/arch/arm64/util/pmu.c
index ef1ed645097c..2504d43a39a7 100644
--- a/tools/perf/arch/arm64/util/pmu.c
+++ b/tools/perf/arch/arm64/util/pmu.c
@@ -3,6 +3,7 @@
#include <internal/cpumap.h>
#include "../../../util/cpumap.h"
#include "../../../util/pmu.h"
+#include "../../../util/pmus.h"
#include <api/fs/fs.h>
#include <math.h>

@@ -10,7 +11,7 @@ static struct perf_pmu *pmu__find_core_pmu(void)
{
struct perf_pmu *pmu = NULL;

- while ((pmu = perf_pmu__scan(pmu))) {
+ while ((pmu = perf_pmus__scan(pmu))) {
if (!is_pmu_core(pmu->name))
continue;

diff --git a/tools/perf/arch/x86/tests/hybrid.c b/tools/perf/arch/x86/tests/hybrid.c
index 944bd1b4bab6..e466735d68d5 100644
--- a/tools/perf/arch/x86/tests/hybrid.c
+++ b/tools/perf/arch/x86/tests/hybrid.c
@@ -4,6 +4,7 @@
#include "evlist.h"
#include "evsel.h"
#include "pmu.h"
+#include "pmus.h"
#include "tests/tests.h"

static bool test_config(const struct evsel *evsel, __u64 expected_config)
@@ -113,7 +114,7 @@ static int test__hybrid_raw1(struct evlist *evlist)
struct perf_evsel *evsel;

perf_evlist__for_each_evsel(&evlist->core, evsel) {
- struct perf_pmu *pmu = perf_pmu__find_by_type(evsel->attr.type);
+ struct perf_pmu *pmu = perf_pmus__find_by_type(evsel->attr.type);

TEST_ASSERT_VAL("missing pmu", pmu);
TEST_ASSERT_VAL("unexpected pmu", !strncmp(pmu->name, "cpu_", 4));
@@ -280,7 +281,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_pmu__has_hybrid())
+ if (!perf_pmus__has_hybrid())
return TEST_SKIP;

return test_events(test__hybrid_events, ARRAY_SIZE(test__hybrid_events));
diff --git a/tools/perf/arch/x86/util/auxtrace.c b/tools/perf/arch/x86/util/auxtrace.c
index 330d03216b0e..354780ff1605 100644
--- a/tools/perf/arch/x86/util/auxtrace.c
+++ b/tools/perf/arch/x86/util/auxtrace.c
@@ -10,6 +10,7 @@
#include "../../../util/header.h"
#include "../../../util/debug.h"
#include "../../../util/pmu.h"
+#include "../../../util/pmus.h"
#include "../../../util/auxtrace.h"
#include "../../../util/intel-pt.h"
#include "../../../util/intel-bts.h"
@@ -25,8 +26,8 @@ struct auxtrace_record *auxtrace_record__init_intel(struct evlist *evlist,
bool found_pt = false;
bool found_bts = false;

- intel_pt_pmu = perf_pmu__find(INTEL_PT_PMU_NAME);
- intel_bts_pmu = perf_pmu__find(INTEL_BTS_PMU_NAME);
+ intel_pt_pmu = perf_pmus__find(INTEL_PT_PMU_NAME);
+ intel_bts_pmu = perf_pmus__find(INTEL_BTS_PMU_NAME);

evlist__for_each_entry(evlist, evsel) {
if (intel_pt_pmu && evsel->core.attr.type == intel_pt_pmu->type)
diff --git a/tools/perf/arch/x86/util/evlist.c b/tools/perf/arch/x86/util/evlist.c
index 03f7eb4cf0a4..03240c640c7f 100644
--- a/tools/perf/arch/x86/util/evlist.c
+++ b/tools/perf/arch/x86/util/evlist.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
#include "util/pmu.h"
+#include "util/pmus.h"
#include "util/evlist.h"
#include "util/parse-events.h"
#include "util/event.h"
@@ -17,7 +18,7 @@ static int ___evlist__add_default_attrs(struct evlist *evlist,
for (i = 0; i < nr_attrs; i++)
event_attr_init(attrs + i);

- if (!perf_pmu__has_hybrid())
+ if (!perf_pmus__has_hybrid())
return evlist__add_attrs(evlist, attrs, nr_attrs);

for (i = 0; i < nr_attrs; i++) {
@@ -32,7 +33,7 @@ static int ___evlist__add_default_attrs(struct evlist *evlist,
continue;
}

- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ while ((pmu = perf_pmus__scan(pmu)) != NULL) {
struct perf_cpu_map *cpus;
struct evsel *evsel;

diff --git a/tools/perf/arch/x86/util/evsel.c b/tools/perf/arch/x86/util/evsel.c
index 153cdca94cd4..25da46c8cca9 100644
--- a/tools/perf/arch/x86/util/evsel.c
+++ b/tools/perf/arch/x86/util/evsel.c
@@ -4,6 +4,7 @@
#include "util/evsel.h"
#include "util/env.h"
#include "util/pmu.h"
+#include "util/pmus.h"
#include "linux/string.h"
#include "evsel.h"
#include "util/debug.h"
@@ -30,7 +31,7 @@ bool evsel__sys_has_perf_metrics(const struct evsel *evsel)
* should be good enough to detect the perf metrics feature.
*/
if ((evsel->core.attr.type == PERF_TYPE_RAW) &&
- pmu_have_event(pmu_name, "slots"))
+ perf_pmus__have_event(pmu_name, "slots"))
return true;

return false;
@@ -98,8 +99,8 @@ void arch__post_evsel_config(struct evsel *evsel, struct perf_event_attr *attr)
if (!evsel_pmu)
return;

- ibs_fetch_pmu = perf_pmu__find("ibs_fetch");
- ibs_op_pmu = perf_pmu__find("ibs_op");
+ ibs_fetch_pmu = perf_pmus__find("ibs_fetch");
+ ibs_op_pmu = perf_pmus__find("ibs_op");

if (ibs_fetch_pmu && ibs_fetch_pmu->type == evsel_pmu->type) {
if (attr->config & IBS_FETCH_L3MISSONLY) {
diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c
index 439c2956f3e7..d2c8cac11470 100644
--- a/tools/perf/arch/x86/util/intel-bts.c
+++ b/tools/perf/arch/x86/util/intel-bts.c
@@ -17,7 +17,7 @@
#include "../../../util/evlist.h"
#include "../../../util/mmap.h"
#include "../../../util/session.h"
-#include "../../../util/pmu.h"
+#include "../../../util/pmus.h"
#include "../../../util/debug.h"
#include "../../../util/record.h"
#include "../../../util/tsc.h"
@@ -416,7 +416,7 @@ static int intel_bts_find_snapshot(struct auxtrace_record *itr, int idx,

struct auxtrace_record *intel_bts_recording_init(int *err)
{
- struct perf_pmu *intel_bts_pmu = perf_pmu__find(INTEL_BTS_PMU_NAME);
+ struct perf_pmu *intel_bts_pmu = perf_pmus__find(INTEL_BTS_PMU_NAME);
struct intel_bts_recording *btsr;

if (!intel_bts_pmu)
diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c
index 17336da08b58..74b70fd379df 100644
--- a/tools/perf/arch/x86/util/intel-pt.c
+++ b/tools/perf/arch/x86/util/intel-pt.c
@@ -23,7 +23,7 @@
#include "../../../util/mmap.h"
#include <subcmd/parse-options.h>
#include "../../../util/parse-events.h"
-#include "../../../util/pmu.h"
+#include "../../../util/pmus.h"
#include "../../../util/debug.h"
#include "../../../util/auxtrace.h"
#include "../../../util/perf_api_probe.h"
@@ -1185,7 +1185,7 @@ static u64 intel_pt_reference(struct auxtrace_record *itr __maybe_unused)

struct auxtrace_record *intel_pt_recording_init(int *err)
{
- struct perf_pmu *intel_pt_pmu = perf_pmu__find(INTEL_PT_PMU_NAME);
+ struct perf_pmu *intel_pt_pmu = perf_pmus__find(INTEL_PT_PMU_NAME);
struct intel_pt_recording *ptr;

if (!intel_pt_pmu)
diff --git a/tools/perf/arch/x86/util/mem-events.c b/tools/perf/arch/x86/util/mem-events.c
index 02d65e446f46..32879d12a8d5 100644
--- a/tools/perf/arch/x86/util/mem-events.c
+++ b/tools/perf/arch/x86/util/mem-events.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include "util/pmu.h"
+#include "util/pmus.h"
#include "util/env.h"
#include "map_symbol.h"
#include "mem-events.h"
@@ -55,12 +56,12 @@ struct perf_mem_event *perf_mem_events__ptr(int i)

bool is_mem_loads_aux_event(struct evsel *leader)
{
- struct perf_pmu *pmu = perf_pmu__find("cpu");
+ struct perf_pmu *pmu = perf_pmus__find("cpu");

if (!pmu)
- pmu = perf_pmu__find("cpu_core");
+ pmu = perf_pmus__find("cpu_core");

- if (pmu && !pmu_have_event(pmu->name, "mem-loads-aux"))
+ if (pmu && !perf_pmu__have_event(pmu, "mem-loads-aux"))
return false;

return leader->core.attr.config == MEM_LOADS_AUX;
@@ -82,7 +83,7 @@ char *perf_mem_events__name(int i, char *pmu_name)
pmu_name = (char *)"cpu";
}

- if (pmu_have_event(pmu_name, "mem-loads-aux")) {
+ if (perf_pmus__have_event(pmu_name, "mem-loads-aux")) {
scnprintf(mem_loads_name, sizeof(mem_loads_name),
MEM_LOADS_AUX_NAME, pmu_name, pmu_name,
perf_mem_events__loads_ldlat);
diff --git a/tools/perf/arch/x86/util/perf_regs.c b/tools/perf/arch/x86/util/perf_regs.c
index 26abc159fc0e..befa7f3659b9 100644
--- a/tools/perf/arch/x86/util/perf_regs.c
+++ b/tools/perf/arch/x86/util/perf_regs.c
@@ -10,6 +10,7 @@
#include "../../../util/debug.h"
#include "../../../util/event.h"
#include "../../../util/pmu.h"
+#include "../../../util/pmus.h"

const struct sample_reg sample_reg_masks[] = {
SMPL_REG(AX, PERF_REG_X86_AX),
@@ -291,7 +292,7 @@ uint64_t arch__intr_reg_mask(void)
*/
attr.sample_period = 1;

- if (perf_pmu__has_hybrid()) {
+ if (perf_pmus__has_hybrid()) {
struct perf_pmu *pmu = NULL;
__u64 type = PERF_TYPE_RAW;

@@ -299,7 +300,7 @@ uint64_t arch__intr_reg_mask(void)
* The same register set is supported among different hybrid PMUs.
* Only check the first available one.
*/
- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ while ((pmu = perf_pmus__scan(pmu)) != NULL) {
if (pmu->is_core) {
type = pmu->type;
break;
diff --git a/tools/perf/arch/x86/util/topdown.c b/tools/perf/arch/x86/util/topdown.c
index 9ad5e5c7bd27..3f9a267d4501 100644
--- a/tools/perf/arch/x86/util/topdown.c
+++ b/tools/perf/arch/x86/util/topdown.c
@@ -2,6 +2,7 @@
#include "api/fs/fs.h"
#include "util/evsel.h"
#include "util/pmu.h"
+#include "util/pmus.h"
#include "util/topdown.h"
#include "topdown.h"
#include "evsel.h"
@@ -22,8 +23,8 @@ bool topdown_sys_has_perf_metrics(void)
* The slots event is only available when the core PMU
* supports the perf metrics feature.
*/
- pmu = perf_pmu__find_by_type(PERF_TYPE_RAW);
- if (pmu && pmu_have_event(pmu->name, "slots"))
+ pmu = perf_pmus__find_by_type(PERF_TYPE_RAW);
+ if (pmu && perf_pmu__have_event(pmu, "slots"))
has_perf_metrics = true;

cached = true;
diff --git a/tools/perf/bench/pmu-scan.c b/tools/perf/bench/pmu-scan.c
index f4a6c37cbe27..51cae2d03353 100644
--- a/tools/perf/bench/pmu-scan.c
+++ b/tools/perf/bench/pmu-scan.c
@@ -44,7 +44,7 @@ static int save_result(void)
struct list_head *list;
struct pmu_scan_result *r;

- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ while ((pmu = perf_pmus__scan(pmu)) != NULL) {
r = realloc(results, (nr_pmus + 1) * sizeof(*r));
if (r == NULL)
return -ENOMEM;
@@ -68,7 +68,7 @@ static int save_result(void)
nr_pmus++;
}

- perf_pmu__destroy();
+ perf_pmus__destroy();
return 0;
}

@@ -81,7 +81,7 @@ static int check_result(void)

for (int i = 0; i < nr_pmus; i++) {
r = &results[i];
- pmu = perf_pmu__find(r->name);
+ pmu = perf_pmus__find(r->name);
if (pmu == NULL) {
pr_err("Cannot find PMU %s\n", r->name);
return -1;
@@ -144,7 +144,7 @@ static int run_pmu_scan(void)

for (i = 0; i < iterations; i++) {
gettimeofday(&start, NULL);
- perf_pmu__scan(NULL);
+ perf_pmus__scan(NULL);
gettimeofday(&end, NULL);

timersub(&end, &start, &diff);
@@ -152,7 +152,7 @@ static int run_pmu_scan(void)
update_stats(&stats, runtime_us);

ret = check_result();
- perf_pmu__destroy();
+ perf_pmus__destroy();
if (ret < 0)
break;
}
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index 2757ccc19c5e..05dfd98af170 100644
--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -41,7 +41,7 @@
#include "symbol.h"
#include "ui/ui.h"
#include "ui/progress.h"
-#include "pmu.h"
+#include "pmus.h"
#include "string2.h"
#include "util/util.h"

@@ -3259,7 +3259,7 @@ static int perf_c2c__record(int argc, const char **argv)
PARSE_OPT_KEEP_UNKNOWN);

/* Max number of arguments multiplied by number of PMUs that can support them. */
- rec_argc = argc + 11 * perf_pmu__num_mem_pmus();
+ rec_argc = argc + 11 * perf_pmus__num_mem_pmus();

rec_argv = calloc(rec_argc + 1, sizeof(char *));
if (!rec_argv)
diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
index c6bd0aa4a56e..6a2e74bdb1db 100644
--- a/tools/perf/builtin-list.c
+++ b/tools/perf/builtin-list.c
@@ -522,7 +522,7 @@ int cmd_list(int argc, const char **argv)
strcmp(argv[i], "hwcache") == 0)
print_hwcache_events(&print_cb, ps);
else if (strcmp(argv[i], "pmu") == 0)
- print_pmu_events(&print_cb, ps);
+ perf_pmus__print_pmu_events(&print_cb, ps);
else if (strcmp(argv[i], "sdt") == 0)
print_sdt_events(&print_cb, ps);
else if (strcmp(argv[i], "metric") == 0 || strcmp(argv[i], "metrics") == 0) {
@@ -562,7 +562,7 @@ int cmd_list(int argc, const char **argv)
event_symbols_sw, PERF_COUNT_SW_MAX);
print_tool_events(&print_cb, ps);
print_hwcache_events(&print_cb, ps);
- print_pmu_events(&print_cb, ps);
+ perf_pmus__print_pmu_events(&print_cb, ps);
print_tracepoint_events(&print_cb, ps);
print_sdt_events(&print_cb, ps);
default_ps.metrics = true;
diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c
index f4f1ff76d49d..960bfd4b732a 100644
--- a/tools/perf/builtin-mem.c
+++ b/tools/perf/builtin-mem.c
@@ -17,7 +17,7 @@
#include "util/dso.h"
#include "util/map.h"
#include "util/symbol.h"
-#include "util/pmu.h"
+#include "util/pmus.h"
#include "util/sample.h"
#include "util/string2.h"
#include "util/util.h"
@@ -93,7 +93,7 @@ static int __cmd_record(int argc, const char **argv, struct perf_mem *mem)
PARSE_OPT_KEEP_UNKNOWN);

/* Max number of arguments multiplied by number of PMUs that can support them. */
- rec_argc = argc + 9 * perf_pmu__num_mem_pmus();
+ rec_argc = argc + 9 * perf_pmus__num_mem_pmus();

if (mem->cpu_list)
rec_argc += 2;
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 2abcad2998f6..4b9212f75493 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -48,6 +48,8 @@
#include "util/bpf-event.h"
#include "util/util.h"
#include "util/pfm.h"
+#include "util/pmu.h"
+#include "util/pmus.h"
#include "util/clockid.h"
#include "util/off_cpu.h"
#include "util/bpf-filter.h"
@@ -1292,7 +1294,7 @@ static int record__open(struct record *rec)
* of waiting or event synthesis.
*/
if (opts->target.initial_delay || target__has_cpu(&opts->target) ||
- perf_pmu__has_hybrid()) {
+ perf_pmus__has_hybrid()) {
pos = evlist__get_tracking_event(evlist);
if (!evsel__is_dummy_event(pos)) {
/* Set up dummy event. */
@@ -2191,7 +2193,7 @@ static void record__uniquify_name(struct record *rec)
char *new_name;
int ret;

- if (!perf_pmu__has_hybrid())
+ if (!perf_pmus__has_hybrid())
return;

evlist__for_each_entry(evlist, pos) {
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 3b90d742b023..0736225d8015 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -1882,11 +1882,11 @@ static int add_default_attributes(void)

if (evlist__add_default_attrs(evsel_list, default_attrs0) < 0)
return -1;
- if (pmu_have_event("cpu", "stalled-cycles-frontend")) {
+ if (perf_pmus__have_event("cpu", "stalled-cycles-frontend")) {
if (evlist__add_default_attrs(evsel_list, frontend_attrs) < 0)
return -1;
}
- if (pmu_have_event("cpu", "stalled-cycles-backend")) {
+ if (perf_pmus__have_event("cpu", "stalled-cycles-backend")) {
if (evlist__add_default_attrs(evsel_list, backend_attrs) < 0)
return -1;
}
diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c
index 56fba08a3037..674876e6c8e6 100644
--- a/tools/perf/tests/attr.c
+++ b/tools/perf/tests/attr.c
@@ -34,7 +34,7 @@
#include "event.h"
#include "util.h"
#include "tests.h"
-#include "pmu.h"
+#include "pmus.h"

#define ENV "PERF_TEST_ATTR"

@@ -185,7 +185,7 @@ static int test__attr(struct test_suite *test __maybe_unused, int subtest __mayb
char path_dir[PATH_MAX];
char *exec_path;

- if (perf_pmu__has_hybrid())
+ if (perf_pmus__has_hybrid())
return TEST_SKIP;

/* First try development tree tests. */
diff --git a/tools/perf/tests/event_groups.c b/tools/perf/tests/event_groups.c
index 3d9a2b524bba..ccd9d8b2903f 100644
--- a/tools/perf/tests/event_groups.c
+++ b/tools/perf/tests/event_groups.c
@@ -53,7 +53,7 @@ static int setup_uncore_event(void)
struct perf_pmu *pmu = NULL;
int i, fd;

- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ while ((pmu = perf_pmus__scan(pmu)) != NULL) {
for (i = 0; i < NR_UNCORE_PMUS; i++) {
if (!strcmp(uncore_pmus[i].name, pmu->name)) {
pr_debug("Using %s for uncore pmu event\n", pmu->name);
diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c
index 277607ede060..9d05bc551791 100644
--- a/tools/perf/tests/parse-events.c
+++ b/tools/perf/tests/parse-events.c
@@ -112,7 +112,7 @@ static int test__checkevent_raw(struct evlist *evlist)
bool type_matched = false;

TEST_ASSERT_VAL("wrong config", test_perf_config(evsel, 0x1a));
- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ while ((pmu = perf_pmus__scan(pmu)) != NULL) {
if (pmu->type == evsel->attr.type) {
TEST_ASSERT_VAL("PMU type expected once", !type_matched);
type_matched = true;
@@ -1443,12 +1443,12 @@ static int test__checkevent_config_cache(struct evlist *evlist)

static bool test__pmu_cpu_valid(void)
{
- return !!perf_pmu__find("cpu");
+ return !!perf_pmus__find("cpu");
}

static bool test__intel_pt_valid(void)
{
- return !!perf_pmu__find("intel_pt");
+ return !!perf_pmus__find("intel_pt");
}

static int test__intel_pt(struct evlist *evlist)
@@ -2246,7 +2246,7 @@ static int test__pmu_events(struct test_suite *test __maybe_unused, int subtest
struct perf_pmu *pmu = NULL;
int ret = TEST_OK;

- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ while ((pmu = perf_pmus__scan(pmu)) != NULL) {
struct stat st;
char path[PATH_MAX];
struct dirent *ent;
diff --git a/tools/perf/tests/parse-metric.c b/tools/perf/tests/parse-metric.c
index c05148ea400c..1d6493a5a956 100644
--- a/tools/perf/tests/parse-metric.c
+++ b/tools/perf/tests/parse-metric.c
@@ -11,7 +11,7 @@
#include "debug.h"
#include "expr.h"
#include "stat.h"
-#include "pmu.h"
+#include "pmus.h"

struct value {
const char *event;
@@ -303,7 +303,7 @@ static int test__parse_metric(struct test_suite *test __maybe_unused, int subtes
TEST_ASSERT_VAL("recursion fail failed", test_recursion_fail() == 0);
TEST_ASSERT_VAL("Memory bandwidth", test_memory_bandwidth() == 0);

- if (!perf_pmu__has_hybrid()) {
+ if (!perf_pmus__has_hybrid()) {
TEST_ASSERT_VAL("cache_miss_cycles failed", test_cache_miss_cycles() == 0);
TEST_ASSERT_VAL("test metric group", test_metric_group() == 0);
}
diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c
index 734004f1a37d..64ecb7845af4 100644
--- a/tools/perf/tests/pmu-events.c
+++ b/tools/perf/tests/pmu-events.c
@@ -2,6 +2,7 @@
#include "math.h"
#include "parse-events.h"
#include "pmu.h"
+#include "pmus.h"
#include "tests.h"
#include <errno.h>
#include <stdio.h>
@@ -708,7 +709,7 @@ static int test__aliases(struct test_suite *test __maybe_unused,
struct perf_pmu *pmu = NULL;
unsigned long i;

- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ while ((pmu = perf_pmus__scan(pmu)) != NULL) {
int count = 0;

if (!is_pmu_core(pmu->name))
diff --git a/tools/perf/tests/switch-tracking.c b/tools/perf/tests/switch-tracking.c
index b3bd14b025a8..cff6ab87b2f6 100644
--- a/tools/perf/tests/switch-tracking.c
+++ b/tools/perf/tests/switch-tracking.c
@@ -20,7 +20,7 @@
#include "tests.h"
#include "util/mmap.h"
#include "util/sample.h"
-#include "pmu.h"
+#include "pmus.h"

static int spin_sleep(void)
{
@@ -375,7 +375,7 @@ static int test__switch_tracking(struct test_suite *test __maybe_unused, int sub
cpu_clocks_evsel = evlist__last(evlist);

/* Second event */
- if (perf_pmu__has_hybrid()) {
+ if (perf_pmus__has_hybrid()) {
cycles = "cpu_core/cycles/u";
err = parse_event(evlist, cycles);
if (err) {
diff --git a/tools/perf/tests/topology.c b/tools/perf/tests/topology.c
index c4630cfc80ea..49e80d15420b 100644
--- a/tools/perf/tests/topology.c
+++ b/tools/perf/tests/topology.c
@@ -8,7 +8,7 @@
#include "session.h"
#include "evlist.h"
#include "debug.h"
-#include "pmu.h"
+#include "pmus.h"
#include <linux/err.h>

#define TEMPL "/tmp/perf-test-XXXXXX"
@@ -41,7 +41,7 @@ static int session_write_header(char *path)
session = perf_session__new(&data, NULL);
TEST_ASSERT_VAL("can't get session", !IS_ERR(session));

- if (!perf_pmu__has_hybrid()) {
+ if (!perf_pmus__has_hybrid()) {
session->evlist = evlist__new_default();
TEST_ASSERT_VAL("can't get evlist", session->evlist);
} else {
diff --git a/tools/perf/util/cputopo.c b/tools/perf/util/cputopo.c
index a5c259bd5cc0..4578c26747e1 100644
--- a/tools/perf/util/cputopo.c
+++ b/tools/perf/util/cputopo.c
@@ -13,6 +13,7 @@
#include "debug.h"
#include "env.h"
#include "pmu.h"
+#include "pmus.h"

#define PACKAGE_CPUS_FMT \
"%s/devices/system/cpu/cpu%d/topology/package_cpus_list"
@@ -473,10 +474,10 @@ struct hybrid_topology *hybrid_topology__new(void)
struct hybrid_topology *tp = NULL;
u32 nr = 0, i = 0;

- if (!perf_pmu__has_hybrid())
+ if (!perf_pmus__has_hybrid())
return NULL;

- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ while ((pmu = perf_pmus__scan(pmu)) != NULL) {
if (pmu->is_core)
nr++;
}
@@ -488,7 +489,7 @@ struct hybrid_topology *hybrid_topology__new(void)
return NULL;

tp->nr = nr;
- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ while ((pmu = perf_pmus__scan(pmu)) != NULL) {
if (!pmu->is_core)
continue;

diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
index 4a4fdad820d6..9eabf3ec56e9 100644
--- a/tools/perf/util/env.c
+++ b/tools/perf/util/env.c
@@ -10,6 +10,7 @@
#include <sys/utsname.h>
#include <stdlib.h>
#include <string.h>
+#include "pmus.h"
#include "strbuf.h"

struct perf_env perf_env;
@@ -323,7 +324,7 @@ int perf_env__read_pmu_mappings(struct perf_env *env)
u32 pmu_num = 0;
struct strbuf sb;

- while ((pmu = perf_pmu__scan(pmu))) {
+ while ((pmu = perf_pmus__scan(pmu))) {
if (!pmu->name)
continue;
pmu_num++;
@@ -337,7 +338,7 @@ int perf_env__read_pmu_mappings(struct perf_env *env)
if (strbuf_init(&sb, 128 * pmu_num) < 0)
return -ENOMEM;

- while ((pmu = perf_pmu__scan(pmu))) {
+ while ((pmu = perf_pmus__scan(pmu))) {
if (!pmu->name)
continue;
if (strbuf_addf(&sb, "%u:%s", pmu->type, pmu->name) < 0)
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 1c6e22e3f345..b4237fc713d5 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -48,6 +48,7 @@
#include "util/hashmap.h"
#include "off_cpu.h"
#include "pmu.h"
+#include "pmus.h"
#include "../perf-sys.h"
#include "util/parse-branch-options.h"
#include "util/bpf-filter.h"
@@ -3139,7 +3140,7 @@ void evsel__zero_per_pkg(struct evsel *evsel)
*/
bool evsel__is_hybrid(const struct evsel *evsel)
{
- if (!perf_pmu__has_hybrid())
+ if (!perf_pmus__has_hybrid())
return false;

return evsel->core.is_pmu_core;
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index e24cc8f316cd..fa3f7dbbd90e 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -37,6 +37,7 @@
#include "debug.h"
#include "cpumap.h"
#include "pmu.h"
+#include "pmus.h"
#include "vdso.h"
#include "strbuf.h"
#include "build-id.h"
@@ -744,7 +745,7 @@ static int write_pmu_mappings(struct feat_fd *ff,
* Do a first pass to count number of pmu to avoid lseek so this
* works in pipe mode as well.
*/
- while ((pmu = perf_pmu__scan(pmu))) {
+ while ((pmu = perf_pmus__scan(pmu))) {
if (!pmu->name)
continue;
pmu_num++;
@@ -754,7 +755,7 @@ static int write_pmu_mappings(struct feat_fd *ff,
if (ret < 0)
return ret;

- while ((pmu = perf_pmu__scan(pmu))) {
+ while ((pmu = perf_pmus__scan(pmu))) {
if (!pmu->name)
continue;

@@ -1550,7 +1551,7 @@ static int __write_pmu_caps(struct feat_fd *ff, struct perf_pmu *pmu,
static int write_cpu_pmu_caps(struct feat_fd *ff,
struct evlist *evlist __maybe_unused)
{
- struct perf_pmu *cpu_pmu = perf_pmu__find("cpu");
+ struct perf_pmu *cpu_pmu = perf_pmus__find("cpu");
int ret;

if (!cpu_pmu)
@@ -1570,7 +1571,7 @@ static int write_pmu_caps(struct feat_fd *ff,
int nr_pmu = 0;
int ret;

- while ((pmu = perf_pmu__scan(pmu))) {
+ while ((pmu = perf_pmus__scan(pmu))) {
if (!pmu->name || !strcmp(pmu->name, "cpu") ||
perf_pmu__caps_parse(pmu) <= 0)
continue;
@@ -1588,9 +1589,9 @@ static int write_pmu_caps(struct feat_fd *ff,
* Write hybrid pmu caps first to maintain compatibility with
* older perf tool.
*/
- if (perf_pmu__has_hybrid()) {
+ if (perf_pmus__has_hybrid()) {
pmu = NULL;
- while ((pmu = perf_pmu__scan(pmu))) {
+ while ((pmu = perf_pmus__scan(pmu))) {
if (!pmu->is_core)
continue;

@@ -1601,7 +1602,7 @@ static int write_pmu_caps(struct feat_fd *ff,
}

pmu = NULL;
- while ((pmu = perf_pmu__scan(pmu))) {
+ while ((pmu = perf_pmus__scan(pmu))) {
if (pmu->is_core || !pmu->nr_caps)
continue;

diff --git a/tools/perf/util/mem-events.c b/tools/perf/util/mem-events.c
index c9e422a38258..08ac3ea2e366 100644
--- a/tools/perf/util/mem-events.c
+++ b/tools/perf/util/mem-events.c
@@ -13,6 +13,7 @@
#include "debug.h"
#include "symbol.h"
#include "pmu.h"
+#include "pmus.h"

unsigned int perf_mem_events__loads_ldlat = 30;

@@ -128,14 +129,14 @@ int perf_mem_events__init(void)
if (!e->tag)
continue;

- if (!perf_pmu__has_hybrid()) {
+ if (!perf_pmus__has_hybrid()) {
scnprintf(sysfs_name, sizeof(sysfs_name),
e->sysfs_name, "cpu");
e->supported = perf_mem_event__supported(mnt, sysfs_name);
} else {
struct perf_pmu *pmu = NULL;

- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ while ((pmu = perf_pmus__scan(pmu)) != NULL) {
if (!pmu->is_core)
continue;

@@ -175,7 +176,7 @@ static void perf_mem_events__print_unsupport_hybrid(struct perf_mem_event *e,
char sysfs_name[100];
struct perf_pmu *pmu = NULL;

- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ while ((pmu = perf_pmus__scan(pmu)) != NULL) {
if (!pmu->is_core)
continue;

@@ -201,7 +202,7 @@ int perf_mem_events__record_args(const char **rec_argv, int *argv_nr,
if (!e->record)
continue;

- if (!perf_pmu__has_hybrid()) {
+ if (!perf_pmus__has_hybrid()) {
if (!e->supported) {
pr_err("failed: event '%s' not supported\n",
perf_mem_events__name(j, NULL));
@@ -216,7 +217,7 @@ int perf_mem_events__record_args(const char **rec_argv, int *argv_nr,
return -1;
}

- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ while ((pmu = perf_pmus__scan(pmu)) != NULL) {
if (!pmu->is_core)
continue;
rec_argv[i++] = "-e";
diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index 72583f62eda8..27310eff49ab 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -11,6 +11,7 @@
#include "evsel.h"
#include "strbuf.h"
#include "pmu.h"
+#include "pmus.h"
#include "print-events.h"
#include "smt.h"
#include "expr.h"
@@ -273,7 +274,7 @@ 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;
- bool all_pmus = !strcmp(pmu, "all") || !perf_pmu__has_hybrid() || !is_pmu_hybrid(pmu);
+ bool all_pmus = !strcmp(pmu, "all") || !perf_pmus__has_hybrid() || !is_pmu_hybrid(pmu);

*out_metric_events = NULL;
ids_size = hashmap__size(ids);
@@ -488,7 +489,7 @@ static int metricgroup__sys_event_iter(const struct pmu_metric *pm,
if (!pm->metric_expr || !pm->compat)
return 0;

- while ((pmu = perf_pmu__scan(pmu))) {
+ while ((pmu = perf_pmus__scan(pmu))) {

if (!pmu->id || strcmp(pmu->id, pm->compat))
continue;
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 1a0be395c887..be544f948be2 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -21,6 +21,7 @@
#include "parse-events-bison.h"
#include "parse-events-flex.h"
#include "pmu.h"
+#include "pmus.h"
#include "asm/bug.h"
#include "util/parse-branch-options.h"
#include "util/evsel_config.h"
@@ -452,7 +453,7 @@ int parse_events_add_cache(struct list_head *list, int *idx, const char *name,
const char *config_name = get_config_name(head_config);
const char *metric_id = get_config_metric_id(head_config);

- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ while ((pmu = perf_pmus__scan(pmu)) != NULL) {
LIST_HEAD(config_terms);
struct perf_event_attr attr;
int ret;
@@ -1193,7 +1194,7 @@ static int config_term_pmu(struct perf_event_attr *attr,
struct parse_events_error *err)
{
if (term->type_term == PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE) {
- const struct perf_pmu *pmu = perf_pmu__find_by_type(attr->type);
+ const struct perf_pmu *pmu = perf_pmus__find_by_type(attr->type);

if (perf_pmu__supports_legacy_cache(pmu)) {
attr->type = PERF_TYPE_HW_CACHE;
@@ -1203,7 +1204,7 @@ static int config_term_pmu(struct perf_event_attr *attr,
term->type_term = PARSE_EVENTS__TERM_TYPE_USER;
}
if (term->type_term == PARSE_EVENTS__TERM_TYPE_HARDWARE) {
- const struct perf_pmu *pmu = perf_pmu__find_by_type(attr->type);
+ const struct perf_pmu *pmu = perf_pmus__find_by_type(attr->type);

if (!pmu) {
pr_debug("Failed to find PMU for type %d", attr->type);
@@ -1480,7 +1481,7 @@ int parse_events_add_numeric(struct parse_events_state *parse_state,
return __parse_events_add_numeric(parse_state, list, /*pmu=*/NULL,
type, config, head_config);

- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ while ((pmu = perf_pmus__scan(pmu)) != NULL) {
int ret;

if (!perf_pmu__supports_wildcard_numeric(pmu))
@@ -1529,7 +1530,7 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
struct parse_events_error *err = parse_state->error;
LIST_HEAD(config_terms);

- pmu = parse_state->fake_pmu ?: perf_pmu__find(name);
+ pmu = parse_state->fake_pmu ?: perf_pmus__find(name);

if (verbose > 1 && !(pmu && pmu->selectable)) {
fprintf(stderr, "Attempting to add event pmu '%s' with '",
@@ -1674,7 +1675,7 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state,

INIT_LIST_HEAD(list);

- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ while ((pmu = perf_pmus__scan(pmu)) != NULL) {
struct perf_pmu_alias *alias;
bool auto_merge_stats;

@@ -2410,7 +2411,7 @@ static int set_filter(struct evsel *evsel, const void *arg)
return 0;
}

- while ((pmu = perf_pmu__scan(pmu)) != NULL)
+ while ((pmu = perf_pmus__scan(pmu)) != NULL)
if (pmu->type == evsel->core.attr.type) {
found = true;
break;
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index 4e1f5de35be8..abd6ab460e12 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -15,6 +15,7 @@
#include <linux/types.h>
#include <linux/zalloc.h>
#include "pmu.h"
+#include "pmus.h"
#include "evsel.h"
#include "parse-events.h"
#include "parse-events-bison.h"
@@ -316,7 +317,7 @@ PE_NAME opt_pmu_config
if (asprintf(&pattern, "%s*", $1) < 0)
CLEANUP_YYABORT;

- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ while ((pmu = perf_pmus__scan(pmu)) != NULL) {
char *name = pmu->name;

if (parse_events__filter_pmu(parse_state, pmu))
diff --git a/tools/perf/util/pfm.c b/tools/perf/util/pfm.c
index 6c11914c179f..076aecc22c16 100644
--- a/tools/perf/util/pfm.c
+++ b/tools/perf/util/pfm.c
@@ -10,7 +10,7 @@
#include "util/evlist.h"
#include "util/evsel.h"
#include "util/parse-events.h"
-#include "util/pmu.h"
+#include "util/pmus.h"
#include "util/pfm.h"
#include "util/strbuf.h"

@@ -49,7 +49,7 @@ int parse_libpfm_events_option(const struct option *opt, const char *str,
/*
* force loading of the PMU list
*/
- perf_pmu__scan(NULL);
+ perf_pmus__scan(NULL);

for (q = p; strsep(&p, ",{}"); q = p) {
sep = p ? str + (p - p_orig - 1) : "";
@@ -86,7 +86,7 @@ int parse_libpfm_events_option(const struct option *opt, const char *str,
goto error;
}

- pmu = perf_pmu__find_by_type((unsigned int)attr.type);
+ pmu = perf_pmus__find_by_type((unsigned int)attr.type);
evsel = parse_events__add_event(evlist->core.nr_entries,
&attr, q, /*metric_id=*/NULL,
pmu);
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 62cd795e2fd3..044f31f9d0ff 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -4,20 +4,15 @@
#include <linux/string.h>
#include <linux/zalloc.h>
#include <linux/ctype.h>
-#include <subcmd/pager.h>
#include <sys/types.h>
-#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdbool.h>
-#include <stdarg.h>
#include <dirent.h>
#include <api/fs/fs.h>
#include <locale.h>
-#include <regex.h>
-#include <perf/cpumap.h>
#include <fnmatch.h>
#include <math.h>
#include "debug.h"
@@ -59,8 +54,6 @@ struct perf_pmu_format {
struct list_head list;
};

-static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name);
-
/*
* Parse & process all the sysfs attributes located under
* the directory specified in 'dir' parameter.
@@ -554,31 +547,6 @@ static int pmu_alias_terms(struct perf_pmu_alias *alias,
return 0;
}

-/* Add all pmus in sysfs to pmu list: */
-static void pmu_read_sysfs(void)
-{
- int fd;
- DIR *dir;
- struct dirent *dent;
-
- fd = perf_pmu__event_source_devices_fd();
- if (fd < 0)
- return;
-
- dir = fdopendir(fd);
- if (!dir)
- return;
-
- while ((dent = readdir(dir))) {
- if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
- continue;
- /* add to static LIST_HEAD(pmus): */
- perf_pmu__find2(fd, dent->d_name);
- }
-
- closedir(dir);
-}
-
/*
* Uncore PMUs have a "cpumask" file under sysfs. CPU PMUs (e.g. on arm/arm64)
* may have a "cpus" file.
@@ -904,7 +872,7 @@ static bool perf_pmu__skip_empty_cpus(const char *name)
return !strcmp(name, "cpu_core") || !strcmp(name, "cpu_atom");
}

-static struct perf_pmu *pmu_lookup(int dirfd, const char *lookup_name)
+struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char *lookup_name)
{
struct perf_pmu *pmu;
LIST_HEAD(format);
@@ -964,7 +932,7 @@ static struct perf_pmu *pmu_lookup(int dirfd, const char *lookup_name)
INIT_LIST_HEAD(&pmu->caps);
list_splice(&format, &pmu->format);
list_splice(&aliases, &pmu->aliases);
- list_add_tail(&pmu->list, &pmus);
+ list_add_tail(&pmu->list, pmus);

pmu->default_config = perf_pmu__get_default_config(pmu);

@@ -992,61 +960,6 @@ void perf_pmu__warn_invalid_formats(struct perf_pmu *pmu)
}
}

-static struct perf_pmu *pmu_find(const char *name)
-{
- struct perf_pmu *pmu;
-
- list_for_each_entry(pmu, &pmus, list) {
- if (!strcmp(pmu->name, name) ||
- (pmu->alias_name && !strcmp(pmu->alias_name, name)))
- return pmu;
- }
-
- return NULL;
-}
-
-struct perf_pmu *perf_pmu__find_by_type(unsigned int type)
-{
- struct perf_pmu *pmu;
-
- list_for_each_entry(pmu, &pmus, list)
- if (pmu->type == type)
- return pmu;
-
- return NULL;
-}
-
-struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu)
-{
- /*
- * pmu iterator: If pmu is NULL, we start at the begin,
- * otherwise return the next pmu. Returns NULL on end.
- */
- if (!pmu) {
- pmu_read_sysfs();
- pmu = list_prepare_entry(pmu, &pmus, list);
- }
- list_for_each_entry_continue(pmu, &pmus, list)
- return pmu;
- return NULL;
-}
-
-struct perf_pmu *evsel__find_pmu(const struct evsel *evsel)
-{
- struct perf_pmu *pmu = NULL;
-
- if (evsel->pmu)
- return evsel->pmu;
-
- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
- if (pmu->type == evsel->core.attr.type)
- break;
- }
-
- ((struct evsel *)evsel)->pmu = pmu;
- return pmu;
-}
-
bool evsel__is_aux_event(const struct evsel *evsel)
{
struct perf_pmu *pmu = evsel__find_pmu(evsel);
@@ -1083,43 +996,6 @@ void evsel__set_config_if_unset(struct perf_pmu *pmu, struct evsel *evsel,
evsel->core.attr.config |= field_prep(bits, val);
}

-struct perf_pmu *perf_pmu__find(const char *name)
-{
- struct perf_pmu *pmu;
- int dirfd;
-
- /*
- * Once PMU is loaded it stays in the list,
- * so we keep us from multiple reading/parsing
- * the pmu format definitions.
- */
- pmu = pmu_find(name);
- if (pmu)
- return pmu;
-
- dirfd = perf_pmu__event_source_devices_fd();
- pmu = pmu_lookup(dirfd, name);
- close(dirfd);
-
- return pmu;
-}
-
-static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name)
-{
- struct perf_pmu *pmu;
-
- /*
- * Once PMU is loaded it stays in the list,
- * so we keep us from multiple reading/parsing
- * the pmu format definitions.
- */
- pmu = pmu_find(name);
- if (pmu)
- return pmu;
-
- return pmu_lookup(dirfd, name);
-}
-
static struct perf_pmu_format *
pmu_find_format(struct list_head *formats, const char *name)
{
@@ -1549,99 +1425,6 @@ void perf_pmu__del_formats(struct list_head *formats)
}
}

-static int sub_non_neg(int a, int b)
-{
- if (b > a)
- return 0;
- return a - b;
-}
-
-static char *format_alias(char *buf, int len, const struct perf_pmu *pmu,
- const struct perf_pmu_alias *alias)
-{
- struct parse_events_term *term;
- int used = snprintf(buf, len, "%s/%s", pmu->name, alias->name);
-
- list_for_each_entry(term, &alias->terms, list) {
- if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR)
- used += snprintf(buf + used, sub_non_neg(len, used),
- ",%s=%s", term->config,
- term->val.str);
- }
-
- if (sub_non_neg(len, used) > 0) {
- buf[used] = '/';
- used++;
- }
- if (sub_non_neg(len, used) > 0) {
- buf[used] = '\0';
- used++;
- } else
- buf[len - 1] = '\0';
-
- return buf;
-}
-
-/** Struct for ordering events as output in perf list. */
-struct sevent {
- /** PMU for event. */
- const struct perf_pmu *pmu;
- /**
- * Optional event for name, desc, etc. If not present then this is a
- * selectable PMU and the event name is shown as "//".
- */
- const struct perf_pmu_alias *event;
- /** Is the PMU for the CPU? */
- bool is_cpu;
-};
-
-static int cmp_sevent(const void *a, const void *b)
-{
- const struct sevent *as = a;
- const struct sevent *bs = b;
- const char *a_pmu_name = NULL, *b_pmu_name = NULL;
- const char *a_name = "//", *a_desc = NULL, *a_topic = "";
- const char *b_name = "//", *b_desc = NULL, *b_topic = "";
- int ret;
-
- if (as->event) {
- a_name = as->event->name;
- a_desc = as->event->desc;
- a_topic = as->event->topic ?: "";
- a_pmu_name = as->event->pmu_name;
- }
- if (bs->event) {
- b_name = bs->event->name;
- b_desc = bs->event->desc;
- b_topic = bs->event->topic ?: "";
- b_pmu_name = bs->event->pmu_name;
- }
- /* Put extra events last. */
- if (!!a_desc != !!b_desc)
- return !!a_desc - !!b_desc;
-
- /* Order by topics. */
- ret = strcmp(a_topic, b_topic);
- if (ret)
- return ret;
-
- /* Order CPU core events to be first */
- if (as->is_cpu != bs->is_cpu)
- return as->is_cpu ? -1 : 1;
-
- /* Order by PMU name. */
- if (as->pmu != bs->pmu) {
- a_pmu_name = a_pmu_name ?: (as->pmu->name ?: "");
- b_pmu_name = b_pmu_name ?: (bs->pmu->name ?: "");
- ret = strcmp(a_pmu_name, b_pmu_name);
- if (ret)
- return ret;
- }
-
- /* Order by event name. */
- return strcmp(a_name, b_name);
-}
-
bool is_pmu_core(const char *name)
{
return !strcmp(name, "cpu") || is_sysfs_pmu_core(name);
@@ -1667,167 +1450,18 @@ bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu)
return !is_pmu_hybrid(pmu->name);
}

-static bool perf_pmu__is_mem_pmu(const struct perf_pmu *pmu)
+bool perf_pmu__is_mem_pmu(const struct perf_pmu *pmu)
{
return pmu->is_core;
}

-int perf_pmu__num_mem_pmus(void)
-{
- struct perf_pmu *pmu = NULL;
- int count = 0;
-
- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
- if (perf_pmu__is_mem_pmu(pmu))
- count++;
- }
- return count;
-}
-
-static bool pmu_alias_is_duplicate(struct sevent *alias_a,
- struct sevent *alias_b)
-{
- const char *a_pmu_name = NULL, *b_pmu_name = NULL;
- const char *a_name = "//", *b_name = "//";
-
-
- if (alias_a->event) {
- a_name = alias_a->event->name;
- a_pmu_name = alias_a->event->pmu_name;
- }
- if (alias_b->event) {
- b_name = alias_b->event->name;
- b_pmu_name = alias_b->event->pmu_name;
- }
-
- /* Different names -> never duplicates */
- if (strcmp(a_name, b_name))
- return false;
-
- /* Don't remove duplicates for different PMUs */
- a_pmu_name = a_pmu_name ?: (alias_a->pmu->name ?: "");
- b_pmu_name = b_pmu_name ?: (alias_b->pmu->name ?: "");
- return strcmp(a_pmu_name, b_pmu_name) == 0;
-}
-
-void print_pmu_events(const struct print_callbacks *print_cb, void *print_state)
-{
- struct perf_pmu *pmu;
- struct perf_pmu_alias *event;
- char buf[1024];
- int printed = 0;
- int len, j;
- struct sevent *aliases;
-
- pmu = NULL;
- len = 0;
- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
- list_for_each_entry(event, &pmu->aliases, list)
- len++;
- if (pmu->selectable)
- len++;
- }
- aliases = zalloc(sizeof(struct sevent) * len);
- if (!aliases) {
- pr_err("FATAL: not enough memory to print PMU events\n");
- return;
- }
- pmu = NULL;
- j = 0;
- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
- bool is_cpu = pmu->is_core;
-
- list_for_each_entry(event, &pmu->aliases, list) {
- aliases[j].event = event;
- aliases[j].pmu = pmu;
- aliases[j].is_cpu = is_cpu;
- j++;
- }
- if (pmu->selectable) {
- aliases[j].event = NULL;
- aliases[j].pmu = pmu;
- aliases[j].is_cpu = is_cpu;
- j++;
- }
- }
- len = j;
- qsort(aliases, len, sizeof(struct sevent), cmp_sevent);
- for (j = 0; j < len; j++) {
- const char *name, *alias = NULL, *scale_unit = NULL,
- *desc = NULL, *long_desc = NULL,
- *encoding_desc = NULL, *topic = NULL,
- *pmu_name = NULL;
- bool deprecated = false;
- size_t buf_used;
-
- /* Skip duplicates */
- if (j > 0 && pmu_alias_is_duplicate(&aliases[j], &aliases[j - 1]))
- continue;
-
- if (!aliases[j].event) {
- /* A selectable event. */
- pmu_name = aliases[j].pmu->name;
- buf_used = snprintf(buf, sizeof(buf), "%s//", pmu_name) + 1;
- name = buf;
- } else {
- if (aliases[j].event->desc) {
- name = aliases[j].event->name;
- buf_used = 0;
- } else {
- name = format_alias(buf, sizeof(buf), aliases[j].pmu,
- aliases[j].event);
- if (aliases[j].is_cpu) {
- alias = name;
- name = aliases[j].event->name;
- }
- buf_used = strlen(buf) + 1;
- }
- pmu_name = aliases[j].event->pmu_name ?: (aliases[j].pmu->name ?: "");
- if (strlen(aliases[j].event->unit) || aliases[j].event->scale != 1.0) {
- scale_unit = buf + buf_used;
- buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
- "%G%s", aliases[j].event->scale,
- aliases[j].event->unit) + 1;
- }
- desc = aliases[j].event->desc;
- long_desc = aliases[j].event->long_desc;
- topic = aliases[j].event->topic;
- encoding_desc = buf + buf_used;
- buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
- "%s/%s/", pmu_name, aliases[j].event->str) + 1;
- deprecated = aliases[j].event->deprecated;
- }
- print_cb->print_event(print_state,
- pmu_name,
- topic,
- name,
- alias,
- scale_unit,
- deprecated,
- "Kernel PMU event",
- desc,
- long_desc,
- encoding_desc);
- }
- if (printed && pager_in_use())
- printf("\n");
-
- zfree(&aliases);
- return;
-}
-
-bool pmu_have_event(const char *pname, const char *name)
+bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name)
{
- struct perf_pmu *pmu;
struct perf_pmu_alias *alias;

- pmu = NULL;
- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
- if (strcmp(pname, pmu->name))
- continue;
- list_for_each_entry(alias, &pmu->aliases, list)
- if (!strcmp(alias->name, name))
- return true;
+ list_for_each_entry(alias, &pmu->aliases, list) {
+ if (!strcmp(alias->name, name))
+ return true;
}
return false;
}
@@ -2033,24 +1667,6 @@ void perf_pmu__warn_invalid_config(struct perf_pmu *pmu, __u64 config,
name ?: "N/A", buf, config);
}

-bool perf_pmu__has_hybrid(void)
-{
- static bool hybrid_scanned, has_hybrid;
-
- if (!hybrid_scanned) {
- struct perf_pmu *pmu = NULL;
-
- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
- if (pmu->is_core && is_pmu_hybrid(pmu->name)) {
- has_hybrid = true;
- break;
- }
- }
- hybrid_scanned = true;
- }
- return has_hybrid;
-}
-
int perf_pmu__match(char *pattern, char *name, char *tok)
{
if (!name)
@@ -2118,7 +1734,7 @@ int perf_pmu__pathname_fd(int dirfd, const char *pmu_name, const char *filename,
return openat(dirfd, path, flags);
}

-static void perf_pmu__delete(struct perf_pmu *pmu)
+void perf_pmu__delete(struct perf_pmu *pmu)
{
perf_pmu__del_formats(&pmu->format);
perf_pmu__del_aliases(pmu);
@@ -2131,14 +1747,3 @@ static void perf_pmu__delete(struct perf_pmu *pmu)
zfree(&pmu->alias_name);
free(pmu);
}
-
-void perf_pmu__destroy(void)
-{
- struct perf_pmu *pmu, *tmp;
-
- list_for_each_entry_safe(pmu, tmp, &pmus, list) {
- list_del(&pmu->list);
-
- perf_pmu__delete(pmu);
- }
-}
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index cb51ad6e40fa..f1f3e8a2e00e 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -198,8 +198,6 @@ struct perf_pmu_alias {
char *pmu_name;
};

-struct perf_pmu *perf_pmu__find(const char *name);
-struct perf_pmu *perf_pmu__find_by_type(unsigned int type);
void pmu_add_sys_aliases(struct list_head *head, struct perf_pmu *pmu);
int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
struct list_head *head_terms,
@@ -222,16 +220,13 @@ void perf_pmu__set_format(unsigned long *bits, long from, long to);
int perf_pmu__format_parse(int dirfd, struct list_head *head);
void perf_pmu__del_formats(struct list_head *formats);

-struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu);
-
bool is_pmu_core(const char *name);
bool is_pmu_hybrid(const char *name);
bool perf_pmu__supports_legacy_cache(const struct perf_pmu *pmu);
bool perf_pmu__supports_wildcard_numeric(const struct perf_pmu *pmu);
bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu);
-int perf_pmu__num_mem_pmus(void);
-void print_pmu_events(const struct print_callbacks *print_cb, void *print_state);
-bool pmu_have_event(const char *pname, const char *name);
+bool perf_pmu__is_mem_pmu(const struct perf_pmu *pmu);
+bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name);

FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name);
FILE *perf_pmu__open_file_at(struct perf_pmu *pmu, int dirfd, const char *name);
@@ -261,7 +256,6 @@ void perf_pmu__warn_invalid_config(struct perf_pmu *pmu, __u64 config,
const char *name);
void perf_pmu__warn_invalid_formats(struct perf_pmu *pmu);

-bool perf_pmu__has_hybrid(void);
int perf_pmu__match(char *pattern, char *name, char *tok);

char *pmu_find_real_name(const char *name);
@@ -273,6 +267,7 @@ int perf_pmu__pathname_scnprintf(char *buf, size_t size,
int perf_pmu__event_source_devices_fd(void);
int perf_pmu__pathname_fd(int dirfd, const char *pmu_name, const char *filename, int flags);

-void perf_pmu__destroy(void);
+struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char *lookup_name);
+void perf_pmu__delete(struct perf_pmu *pmu);

#endif /* __PMU_H */
diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c
index 140e11f00b29..58ff7937e9b7 100644
--- a/tools/perf/util/pmus.c
+++ b/tools/perf/util/pmus.c
@@ -1,16 +1,136 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/list.h>
+#include <linux/zalloc.h>
+#include <subcmd/pager.h>
+#include <sys/types.h>
+#include <dirent.h>
#include <string.h>
+#include <unistd.h>
+#include "debug.h"
+#include "evsel.h"
#include "pmus.h"
#include "pmu.h"
+#include "print-events.h"

-LIST_HEAD(pmus);
+static LIST_HEAD(pmus);
+
+void perf_pmus__destroy(void)
+{
+ struct perf_pmu *pmu, *tmp;
+
+ list_for_each_entry_safe(pmu, tmp, &pmus, list) {
+ list_del(&pmu->list);
+
+ perf_pmu__delete(pmu);
+ }
+}
+
+static struct perf_pmu *pmu_find(const char *name)
+{
+ struct perf_pmu *pmu;
+
+ list_for_each_entry(pmu, &pmus, list) {
+ if (!strcmp(pmu->name, name) ||
+ (pmu->alias_name && !strcmp(pmu->alias_name, name)))
+ return pmu;
+ }
+
+ return NULL;
+}
+
+struct perf_pmu *perf_pmus__find(const char *name)
+{
+ struct perf_pmu *pmu;
+ int dirfd;
+
+ /*
+ * Once PMU is loaded it stays in the list,
+ * so we keep us from multiple reading/parsing
+ * the pmu format definitions.
+ */
+ pmu = pmu_find(name);
+ if (pmu)
+ return pmu;
+
+ dirfd = perf_pmu__event_source_devices_fd();
+ pmu = perf_pmu__lookup(&pmus, dirfd, name);
+ close(dirfd);
+
+ return pmu;
+}
+
+static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name)
+{
+ struct perf_pmu *pmu;
+
+ /*
+ * Once PMU is loaded it stays in the list,
+ * so we keep us from multiple reading/parsing
+ * the pmu format definitions.
+ */
+ pmu = pmu_find(name);
+ if (pmu)
+ return pmu;
+
+ return perf_pmu__lookup(&pmus, dirfd, name);
+}
+
+/* Add all pmus in sysfs to pmu list: */
+static void pmu_read_sysfs(void)
+{
+ int fd;
+ DIR *dir;
+ struct dirent *dent;
+
+ fd = perf_pmu__event_source_devices_fd();
+ if (fd < 0)
+ return;
+
+ dir = fdopendir(fd);
+ if (!dir)
+ return;
+
+ while ((dent = readdir(dir))) {
+ if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
+ continue;
+ /* add to static LIST_HEAD(pmus): */
+ perf_pmu__find2(fd, dent->d_name);
+ }
+
+ closedir(dir);
+}
+
+struct perf_pmu *perf_pmus__find_by_type(unsigned int type)
+{
+ struct perf_pmu *pmu;
+
+ list_for_each_entry(pmu, &pmus, list)
+ if (pmu->type == type)
+ return pmu;
+
+ return NULL;
+}
+
+struct perf_pmu *perf_pmus__scan(struct perf_pmu *pmu)
+{
+ /*
+ * pmu iterator: If pmu is NULL, we start at the begin,
+ * otherwise return the next pmu. Returns NULL on end.
+ */
+ if (!pmu) {
+ pmu_read_sysfs();
+ pmu = list_prepare_entry(pmu, &pmus, list);
+ }
+ list_for_each_entry_continue(pmu, &pmus, list)
+ return pmu;
+ return NULL;
+}

const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str)
{
struct perf_pmu *pmu = NULL;

- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ while ((pmu = perf_pmus__scan(pmu)) != NULL) {
if (!strcmp(pmu->name, str))
return pmu;
/* Ignore "uncore_" prefix. */
@@ -26,3 +146,275 @@ const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str)
}
return NULL;
}
+
+int perf_pmus__num_mem_pmus(void)
+{
+ struct perf_pmu *pmu = NULL;
+ int count = 0;
+
+ while ((pmu = perf_pmus__scan(pmu)) != NULL) {
+ if (perf_pmu__is_mem_pmu(pmu))
+ count++;
+ }
+ return count;
+}
+
+/** Struct for ordering events as output in perf list. */
+struct sevent {
+ /** PMU for event. */
+ const struct perf_pmu *pmu;
+ /**
+ * Optional event for name, desc, etc. If not present then this is a
+ * selectable PMU and the event name is shown as "//".
+ */
+ const struct perf_pmu_alias *event;
+ /** Is the PMU for the CPU? */
+ bool is_cpu;
+};
+
+static int cmp_sevent(const void *a, const void *b)
+{
+ const struct sevent *as = a;
+ const struct sevent *bs = b;
+ const char *a_pmu_name = NULL, *b_pmu_name = NULL;
+ const char *a_name = "//", *a_desc = NULL, *a_topic = "";
+ const char *b_name = "//", *b_desc = NULL, *b_topic = "";
+ int ret;
+
+ if (as->event) {
+ a_name = as->event->name;
+ a_desc = as->event->desc;
+ a_topic = as->event->topic ?: "";
+ a_pmu_name = as->event->pmu_name;
+ }
+ if (bs->event) {
+ b_name = bs->event->name;
+ b_desc = bs->event->desc;
+ b_topic = bs->event->topic ?: "";
+ b_pmu_name = bs->event->pmu_name;
+ }
+ /* Put extra events last. */
+ if (!!a_desc != !!b_desc)
+ return !!a_desc - !!b_desc;
+
+ /* Order by topics. */
+ ret = strcmp(a_topic, b_topic);
+ if (ret)
+ return ret;
+
+ /* Order CPU core events to be first */
+ if (as->is_cpu != bs->is_cpu)
+ return as->is_cpu ? -1 : 1;
+
+ /* Order by PMU name. */
+ if (as->pmu != bs->pmu) {
+ a_pmu_name = a_pmu_name ?: (as->pmu->name ?: "");
+ b_pmu_name = b_pmu_name ?: (bs->pmu->name ?: "");
+ ret = strcmp(a_pmu_name, b_pmu_name);
+ if (ret)
+ return ret;
+ }
+
+ /* Order by event name. */
+ return strcmp(a_name, b_name);
+}
+
+static bool pmu_alias_is_duplicate(struct sevent *alias_a,
+ struct sevent *alias_b)
+{
+ const char *a_pmu_name = NULL, *b_pmu_name = NULL;
+ const char *a_name = "//", *b_name = "//";
+
+
+ if (alias_a->event) {
+ a_name = alias_a->event->name;
+ a_pmu_name = alias_a->event->pmu_name;
+ }
+ if (alias_b->event) {
+ b_name = alias_b->event->name;
+ b_pmu_name = alias_b->event->pmu_name;
+ }
+
+ /* Different names -> never duplicates */
+ if (strcmp(a_name, b_name))
+ return false;
+
+ /* Don't remove duplicates for different PMUs */
+ a_pmu_name = a_pmu_name ?: (alias_a->pmu->name ?: "");
+ b_pmu_name = b_pmu_name ?: (alias_b->pmu->name ?: "");
+ return strcmp(a_pmu_name, b_pmu_name) == 0;
+}
+
+static int sub_non_neg(int a, int b)
+{
+ if (b > a)
+ return 0;
+ return a - b;
+}
+
+static char *format_alias(char *buf, int len, const struct perf_pmu *pmu,
+ const struct perf_pmu_alias *alias)
+{
+ struct parse_events_term *term;
+ int used = snprintf(buf, len, "%s/%s", pmu->name, alias->name);
+
+ list_for_each_entry(term, &alias->terms, list) {
+ if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR)
+ used += snprintf(buf + used, sub_non_neg(len, used),
+ ",%s=%s", term->config,
+ term->val.str);
+ }
+
+ if (sub_non_neg(len, used) > 0) {
+ buf[used] = '/';
+ used++;
+ }
+ if (sub_non_neg(len, used) > 0) {
+ buf[used] = '\0';
+ used++;
+ } else
+ buf[len - 1] = '\0';
+
+ return buf;
+}
+
+void perf_pmus__print_pmu_events(const struct print_callbacks *print_cb, void *print_state)
+{
+ struct perf_pmu *pmu;
+ struct perf_pmu_alias *event;
+ char buf[1024];
+ int printed = 0;
+ int len, j;
+ struct sevent *aliases;
+
+ pmu = NULL;
+ len = 0;
+ while ((pmu = perf_pmus__scan(pmu)) != NULL) {
+ list_for_each_entry(event, &pmu->aliases, list)
+ len++;
+ if (pmu->selectable)
+ len++;
+ }
+ aliases = zalloc(sizeof(struct sevent) * len);
+ if (!aliases) {
+ pr_err("FATAL: not enough memory to print PMU events\n");
+ return;
+ }
+ pmu = NULL;
+ j = 0;
+ while ((pmu = perf_pmus__scan(pmu)) != NULL) {
+ bool is_cpu = pmu->is_core;
+
+ list_for_each_entry(event, &pmu->aliases, list) {
+ aliases[j].event = event;
+ aliases[j].pmu = pmu;
+ aliases[j].is_cpu = is_cpu;
+ j++;
+ }
+ if (pmu->selectable) {
+ aliases[j].event = NULL;
+ aliases[j].pmu = pmu;
+ aliases[j].is_cpu = is_cpu;
+ j++;
+ }
+ }
+ len = j;
+ qsort(aliases, len, sizeof(struct sevent), cmp_sevent);
+ for (j = 0; j < len; j++) {
+ const char *name, *alias = NULL, *scale_unit = NULL,
+ *desc = NULL, *long_desc = NULL,
+ *encoding_desc = NULL, *topic = NULL,
+ *pmu_name = NULL;
+ bool deprecated = false;
+ size_t buf_used;
+
+ /* Skip duplicates */
+ if (j > 0 && pmu_alias_is_duplicate(&aliases[j], &aliases[j - 1]))
+ continue;
+
+ if (!aliases[j].event) {
+ /* A selectable event. */
+ pmu_name = aliases[j].pmu->name;
+ buf_used = snprintf(buf, sizeof(buf), "%s//", pmu_name) + 1;
+ name = buf;
+ } else {
+ if (aliases[j].event->desc) {
+ name = aliases[j].event->name;
+ buf_used = 0;
+ } else {
+ name = format_alias(buf, sizeof(buf), aliases[j].pmu,
+ aliases[j].event);
+ if (aliases[j].is_cpu) {
+ alias = name;
+ name = aliases[j].event->name;
+ }
+ buf_used = strlen(buf) + 1;
+ }
+ pmu_name = aliases[j].event->pmu_name ?: (aliases[j].pmu->name ?: "");
+ if (strlen(aliases[j].event->unit) || aliases[j].event->scale != 1.0) {
+ scale_unit = buf + buf_used;
+ buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
+ "%G%s", aliases[j].event->scale,
+ aliases[j].event->unit) + 1;
+ }
+ desc = aliases[j].event->desc;
+ long_desc = aliases[j].event->long_desc;
+ topic = aliases[j].event->topic;
+ encoding_desc = buf + buf_used;
+ buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
+ "%s/%s/", pmu_name, aliases[j].event->str) + 1;
+ deprecated = aliases[j].event->deprecated;
+ }
+ print_cb->print_event(print_state,
+ pmu_name,
+ topic,
+ name,
+ alias,
+ scale_unit,
+ deprecated,
+ "Kernel PMU event",
+ desc,
+ long_desc,
+ encoding_desc);
+ }
+ if (printed && pager_in_use())
+ printf("\n");
+
+ zfree(&aliases);
+}
+
+bool perf_pmus__have_event(const char *pname, const char *name)
+{
+ struct perf_pmu *pmu = perf_pmus__find(pname);
+
+ return pmu && perf_pmu__have_event(pmu, name);
+}
+
+bool perf_pmus__has_hybrid(void)
+{
+ static bool hybrid_scanned, has_hybrid;
+
+ if (!hybrid_scanned) {
+ struct perf_pmu *pmu = NULL;
+
+ while ((pmu = perf_pmus__scan(pmu)) != NULL) {
+ if (pmu->is_core && is_pmu_hybrid(pmu->name)) {
+ has_hybrid = true;
+ break;
+ }
+ }
+ hybrid_scanned = true;
+ }
+ return has_hybrid;
+}
+
+struct perf_pmu *evsel__find_pmu(const struct evsel *evsel)
+{
+ struct perf_pmu *pmu = evsel->pmu;
+
+ if (!pmu) {
+ pmu = perf_pmus__find_by_type(evsel->core.attr.type);
+ ((struct evsel *)evsel)->pmu = pmu;
+ }
+ return pmu;
+}
diff --git a/tools/perf/util/pmus.h b/tools/perf/util/pmus.h
index 257de10788e8..2a771d9f8da7 100644
--- a/tools/perf/util/pmus.h
+++ b/tools/perf/util/pmus.h
@@ -2,9 +2,21 @@
#ifndef __PMUS_H
#define __PMUS_H

-extern struct list_head pmus;
struct perf_pmu;
+struct print_callbacks;
+
+void perf_pmus__destroy(void);
+
+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__scan(struct perf_pmu *pmu);

const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str);

+int perf_pmus__num_mem_pmus(void);
+void perf_pmus__print_pmu_events(const struct print_callbacks *print_cb, void *print_state);
+bool perf_pmus__have_event(const char *pname, const char *name);
+bool perf_pmus__has_hybrid(void);
+
#endif /* __PMUS_H */
diff --git a/tools/perf/util/print-events.c b/tools/perf/util/print-events.c
index 8d823bc906e6..9cee7bb7a561 100644
--- a/tools/perf/util/print-events.c
+++ b/tools/perf/util/print-events.c
@@ -20,6 +20,7 @@
#include "metricgroup.h"
#include "parse-events.h"
#include "pmu.h"
+#include "pmus.h"
#include "print-events.h"
#include "probe-file.h"
#include "string2.h"
@@ -271,7 +272,7 @@ int print_hwcache_events(const struct print_callbacks *print_cb, void *print_sta
struct perf_pmu *pmu = NULL;
const char *event_type_descriptor = event_type_descriptors[PERF_TYPE_HW_CACHE];

- while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ while ((pmu = perf_pmus__scan(pmu)) != NULL) {
/*
* Skip uncore PMUs for performance. PERF_TYPE_HW_CACHE type
* attributes can accept software PMUs in the extended type, so
@@ -404,7 +405,7 @@ void print_events(const struct print_callbacks *print_cb, void *print_state)

print_hwcache_events(print_cb, print_state);

- print_pmu_events(print_cb, print_state);
+ perf_pmus__print_pmu_events(print_cb, print_state);

print_cb->print_event(print_state,
/*topic=*/NULL,
diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c
index 78ce7095dbcb..9355ddf7bca9 100644
--- a/tools/perf/util/stat-display.c
+++ b/tools/perf/util/stat-display.c
@@ -20,6 +20,7 @@
#include "util.h"
#include "iostat.h"
#include "pmu.h"
+#include "pmus.h"

#define CNTR_NOT_SUPPORTED "<not supported>"
#define CNTR_NOT_COUNTED "<not counted>"
@@ -680,7 +681,7 @@ static bool evlist__has_hybrid(struct evlist *evlist)
{
struct evsel *evsel;

- if (!perf_pmu__has_hybrid())
+ if (!perf_pmus__has_hybrid())
return false;

evlist__for_each_entry(evlist, evsel) {
--
2.40.1.698.g37aff9b760-goog


2023-05-24 22:52:54

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 29/35] perf pmus: Split pmus list into core and other

Split the pmus list into core and other. This will later allow for
the core and other pmus to be populated separately.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/util/pmus.c | 52 ++++++++++++++++++++++++++++++------------
1 file changed, 38 insertions(+), 14 deletions(-)

diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c
index 58ff7937e9b7..4ef4fecd335f 100644
--- a/tools/perf/util/pmus.c
+++ b/tools/perf/util/pmus.c
@@ -12,13 +12,19 @@
#include "pmu.h"
#include "print-events.h"

-static LIST_HEAD(pmus);
+static LIST_HEAD(core_pmus);
+static LIST_HEAD(other_pmus);

void perf_pmus__destroy(void)
{
struct perf_pmu *pmu, *tmp;

- list_for_each_entry_safe(pmu, tmp, &pmus, list) {
+ list_for_each_entry_safe(pmu, tmp, &core_pmus, list) {
+ list_del(&pmu->list);
+
+ perf_pmu__delete(pmu);
+ }
+ list_for_each_entry_safe(pmu, tmp, &other_pmus, list) {
list_del(&pmu->list);

perf_pmu__delete(pmu);
@@ -29,7 +35,12 @@ static struct perf_pmu *pmu_find(const char *name)
{
struct perf_pmu *pmu;

- list_for_each_entry(pmu, &pmus, list) {
+ list_for_each_entry(pmu, &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) {
if (!strcmp(pmu->name, name) ||
(pmu->alias_name && !strcmp(pmu->alias_name, name)))
return pmu;
@@ -53,7 +64,7 @@ struct perf_pmu *perf_pmus__find(const char *name)
return pmu;

dirfd = perf_pmu__event_source_devices_fd();
- pmu = perf_pmu__lookup(&pmus, dirfd, name);
+ pmu = perf_pmu__lookup(is_pmu_core(name) ? &core_pmus : &other_pmus, dirfd, name);
close(dirfd);

return pmu;
@@ -72,7 +83,7 @@ static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name)
if (pmu)
return pmu;

- return perf_pmu__lookup(&pmus, dirfd, name);
+ return perf_pmu__lookup(is_pmu_core(name) ? &core_pmus : &other_pmus, dirfd, name);
}

/* Add all pmus in sysfs to pmu list: */
@@ -93,7 +104,7 @@ static void pmu_read_sysfs(void)
while ((dent = readdir(dir))) {
if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
continue;
- /* add to static LIST_HEAD(pmus): */
+ /* add to static LIST_HEAD(core_pmus) or LIST_HEAD(other_pmus): */
perf_pmu__find2(fd, dent->d_name);
}

@@ -104,24 +115,37 @@ struct perf_pmu *perf_pmus__find_by_type(unsigned int type)
{
struct perf_pmu *pmu;

- list_for_each_entry(pmu, &pmus, list)
+ list_for_each_entry(pmu, &core_pmus, list) {
if (pmu->type == type)
return pmu;
-
+ }
+ list_for_each_entry(pmu, &other_pmus, list) {
+ if (pmu->type == type)
+ return pmu;
+ }
return NULL;
}

+/*
+ * 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)
{
- /*
- * pmu iterator: If pmu is NULL, we start at the begin,
- * otherwise return the next pmu. Returns NULL on end.
- */
+ bool use_core_pmus = !pmu || pmu->is_core;
+
if (!pmu) {
pmu_read_sysfs();
- pmu = list_prepare_entry(pmu, &pmus, list);
+ pmu = list_prepare_entry(pmu, &core_pmus, list);
+ }
+ if (use_core_pmus) {
+ list_for_each_entry_continue(pmu, &core_pmus, list)
+ return pmu;
+
+ pmu = NULL;
+ pmu = list_prepare_entry(pmu, &other_pmus, list);
}
- list_for_each_entry_continue(pmu, &pmus, list)
+ list_for_each_entry_continue(pmu, &other_pmus, list)
return pmu;
return NULL;
}
--
2.40.1.698.g37aff9b760-goog


2023-05-24 22:56:54

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 01/35] perf cpumap: Add intersect function

The merge function gives the union of two cpu maps. Add an intersect
function which is necessary, for example, when intersecting a PMUs
supported CPUs with user requested.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/lib/perf/cpumap.c | 35 ++++++++++++++++++++++++
tools/lib/perf/include/perf/cpumap.h | 2 ++
tools/perf/tests/builtin-test.c | 1 +
tools/perf/tests/cpumap.c | 41 ++++++++++++++++++++++++++++
tools/perf/tests/tests.h | 1 +
5 files changed, 80 insertions(+)

diff --git a/tools/lib/perf/cpumap.c b/tools/lib/perf/cpumap.c
index 1229b18bcdb1..d4f3a1a12522 100644
--- a/tools/lib/perf/cpumap.c
+++ b/tools/lib/perf/cpumap.c
@@ -402,3 +402,38 @@ struct perf_cpu_map *perf_cpu_map__merge(struct perf_cpu_map *orig,
perf_cpu_map__put(orig);
return merged;
}
+
+struct perf_cpu_map *perf_cpu_map__intersect(struct perf_cpu_map *orig,
+ struct perf_cpu_map *other)
+{
+ struct perf_cpu *tmp_cpus;
+ int tmp_len;
+ int i, j, k;
+ struct perf_cpu_map *merged = NULL;
+
+ if (perf_cpu_map__is_subset(other, orig))
+ return perf_cpu_map__get(orig);
+ if (perf_cpu_map__is_subset(orig, other))
+ return perf_cpu_map__get(other);
+
+ tmp_len = max(orig->nr, other->nr);
+ tmp_cpus = malloc(tmp_len * sizeof(struct perf_cpu));
+ if (!tmp_cpus)
+ return NULL;
+
+ i = j = k = 0;
+ while (i < orig->nr && j < other->nr) {
+ if (orig->map[i].cpu < other->map[j].cpu)
+ i++;
+ else if (orig->map[i].cpu > other->map[j].cpu)
+ j++;
+ else {
+ j++;
+ tmp_cpus[k++] = orig->map[i++];
+ }
+ }
+ if (k)
+ merged = cpu_map__trim_new(k, tmp_cpus);
+ free(tmp_cpus);
+ return merged;
+}
diff --git a/tools/lib/perf/include/perf/cpumap.h b/tools/lib/perf/include/perf/cpumap.h
index 3f43f770cdac..0466c4216fbb 100644
--- a/tools/lib/perf/include/perf/cpumap.h
+++ b/tools/lib/perf/include/perf/cpumap.h
@@ -20,6 +20,8 @@ LIBPERF_API struct perf_cpu_map *perf_cpu_map__read(FILE *file);
LIBPERF_API struct perf_cpu_map *perf_cpu_map__get(struct perf_cpu_map *map);
LIBPERF_API struct perf_cpu_map *perf_cpu_map__merge(struct perf_cpu_map *orig,
struct perf_cpu_map *other);
+LIBPERF_API struct perf_cpu_map *perf_cpu_map__intersect(struct perf_cpu_map *orig,
+ struct perf_cpu_map *other);
LIBPERF_API void perf_cpu_map__put(struct perf_cpu_map *map);
LIBPERF_API struct perf_cpu perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx);
LIBPERF_API int perf_cpu_map__nr(const struct perf_cpu_map *cpus);
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index b89d69afcef0..eef400025fca 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -97,6 +97,7 @@ static struct test_suite *generic_tests[] = {
&suite__backward_ring_buffer,
&suite__cpu_map_print,
&suite__cpu_map_merge,
+ &suite__cpu_map_intersect,
&suite__sdt_event,
&suite__is_printable_array,
&suite__bitmap_print,
diff --git a/tools/perf/tests/cpumap.c b/tools/perf/tests/cpumap.c
index b1a924314e09..92232978fe5e 100644
--- a/tools/perf/tests/cpumap.c
+++ b/tools/perf/tests/cpumap.c
@@ -171,6 +171,47 @@ static int test__cpu_map_merge(struct test_suite *test __maybe_unused, int subte
return 0;
}

+static int __test__cpu_map_intersect(const char *lhs, const char *rhs, int nr, const char *expected)
+{
+ struct perf_cpu_map *a = perf_cpu_map__new(lhs);
+ struct perf_cpu_map *b = perf_cpu_map__new(rhs);
+ struct perf_cpu_map *c = perf_cpu_map__intersect(a, b);
+ char buf[100];
+
+ TEST_ASSERT_EQUAL("failed to intersect map: bad nr", perf_cpu_map__nr(c), nr);
+ cpu_map__snprint(c, buf, sizeof(buf));
+ TEST_ASSERT_VAL("failed to intersect map: bad result", !strcmp(buf, expected));
+ perf_cpu_map__put(a);
+ perf_cpu_map__put(b);
+ perf_cpu_map__put(c);
+ return 0;
+}
+
+static int test__cpu_map_intersect(struct test_suite *test __maybe_unused,
+ int subtest __maybe_unused)
+{
+ int ret;
+
+ ret = __test__cpu_map_intersect("4,2,1", "4,5,7", 1, "4");
+ if (ret)
+ return ret;
+ ret = __test__cpu_map_intersect("1-8", "6-9", 3, "6-8");
+ if (ret)
+ return ret;
+ ret = __test__cpu_map_intersect("1-8,12-20", "6-9,15", 4, "6-8,15");
+ if (ret)
+ return ret;
+ ret = __test__cpu_map_intersect("4,2,1", "1", 1, "1");
+ if (ret)
+ return ret;
+ ret = __test__cpu_map_intersect("1", "4,2,1", 1, "1");
+ if (ret)
+ return ret;
+ ret = __test__cpu_map_intersect("1", "1", 1, "1");
+ return ret;
+}
+
DEFINE_SUITE("Synthesize cpu map", cpu_map_synthesize);
DEFINE_SUITE("Print cpu map", cpu_map_print);
DEFINE_SUITE("Merge cpu map", cpu_map_merge);
+DEFINE_SUITE("Intersect cpu map", cpu_map_intersect);
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index 9a0f3904e53d..b4e54f08bc39 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -127,6 +127,7 @@ DECLARE_SUITE(event_times);
DECLARE_SUITE(backward_ring_buffer);
DECLARE_SUITE(cpu_map_print);
DECLARE_SUITE(cpu_map_merge);
+DECLARE_SUITE(cpu_map_intersect);
DECLARE_SUITE(sdt_event);
DECLARE_SUITE(is_printable_array);
DECLARE_SUITE(bitmap_print);
--
2.40.1.698.g37aff9b760-goog


2023-05-24 22:57:05

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 11/35] perf target: Remove unused hybrid value

Previously this was used to modify CPU map propagation, but it is now
unnecessary as map propagation ensure core PMUs only have valid PMUs
in the CPU map from user requested CPUs.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/builtin-record.c | 2 --
tools/perf/builtin-stat.c | 1 -
tools/perf/util/target.h | 1 -
3 files changed, 4 deletions(-)

diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index ec0f2d5f189f..d152ab04a209 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -4204,8 +4204,6 @@ int cmd_record(int argc, const char **argv)
goto out;
}

- rec->opts.target.hybrid = perf_pmu__has_hybrid();
-
if (callchain_param.enabled && callchain_param.record_mode == CALLCHAIN_FP)
arch__add_leaf_frame_record_opts(&rec->opts);

diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index bc45cee3f77c..c70cb0990661 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -2467,7 +2467,6 @@ int cmd_stat(int argc, const char **argv)
goto out;
}

- target.hybrid = perf_pmu__has_hybrid();
if (evlist__create_maps(evsel_list, &target) < 0) {
if (target__has_task(&target)) {
pr_err("Problems finding threads of monitor\n");
diff --git a/tools/perf/util/target.h b/tools/perf/util/target.h
index 880f1af7f6ad..d582cae8e105 100644
--- a/tools/perf/util/target.h
+++ b/tools/perf/util/target.h
@@ -17,7 +17,6 @@ struct target {
bool default_per_cpu;
bool per_thread;
bool use_bpf;
- bool hybrid;
int initial_delay;
const char *attr_map;
};
--
2.40.1.698.g37aff9b760-goog


2023-05-24 22:57:29

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 23/35] perf stat: Avoid hybrid PMU list

perf_pmu__is_hybrid implicitly uses the hybrid PMU list. Instead
return false if hybrid isn't present, if it is then see if any evsel's
PMUs are core.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/util/stat-display.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c
index ede0477d958a..78ce7095dbcb 100644
--- a/tools/perf/util/stat-display.c
+++ b/tools/perf/util/stat-display.c
@@ -19,7 +19,7 @@
#include <api/fs/fs.h>
#include "util.h"
#include "iostat.h"
-#include "pmu-hybrid.h"
+#include "pmu.h"

#define CNTR_NOT_SUPPORTED "<not supported>"
#define CNTR_NOT_COUNTED "<not counted>"
@@ -680,11 +680,12 @@ static bool evlist__has_hybrid(struct evlist *evlist)
{
struct evsel *evsel;

+ if (!perf_pmu__has_hybrid())
+ return false;
+
evlist__for_each_entry(evlist, evsel) {
- if (evsel->pmu_name &&
- perf_pmu__is_hybrid(evsel->pmu_name)) {
+ if (evsel->core.is_pmu_core)
return true;
- }
}

return false;
--
2.40.1.698.g37aff9b760-goog


2023-05-24 22:58:44

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 27/35] perf x86 mem: minor refactor to is_mem_loads_aux_event

Find the PMU and then the event off of it.

Reviewed-by: Kan Liang <[email protected]>
Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/arch/x86/util/mem-events.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/tools/perf/arch/x86/util/mem-events.c b/tools/perf/arch/x86/util/mem-events.c
index f683ac702247..02d65e446f46 100644
--- a/tools/perf/arch/x86/util/mem-events.c
+++ b/tools/perf/arch/x86/util/mem-events.c
@@ -55,13 +55,13 @@ struct perf_mem_event *perf_mem_events__ptr(int i)

bool is_mem_loads_aux_event(struct evsel *leader)
{
- if (perf_pmu__find("cpu")) {
- if (!pmu_have_event("cpu", "mem-loads-aux"))
- return false;
- } else if (perf_pmu__find("cpu_core")) {
- if (!pmu_have_event("cpu_core", "mem-loads-aux"))
- return false;
- }
+ struct perf_pmu *pmu = perf_pmu__find("cpu");
+
+ if (!pmu)
+ pmu = perf_pmu__find("cpu_core");
+
+ if (pmu && !pmu_have_event(pmu->name, "mem-loads-aux"))
+ return false;

return leader->core.attr.config == MEM_LOADS_AUX;
}
--
2.40.1.698.g37aff9b760-goog


2023-05-24 22:58:47

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 26/35] perf pmus: Prefer perf_pmu__scan over perf_pmus__for_each_pmu

perf_pmus__for_each_pmu doesn't lazily initialize pmus making its use
error prone. Just use perf_pmu__scan as this only impacts
non-performance critical tests.

Reviewed-by: Kan Liang <[email protected]>
Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/bench/pmu-scan.c | 6 ++----
tools/perf/tests/event_groups.c | 7 ++-----
tools/perf/tests/parse-events.c | 11 ++++-------
tools/perf/util/pmus.h | 2 --
4 files changed, 8 insertions(+), 18 deletions(-)

diff --git a/tools/perf/bench/pmu-scan.c b/tools/perf/bench/pmu-scan.c
index f0f007843bb8..f4a6c37cbe27 100644
--- a/tools/perf/bench/pmu-scan.c
+++ b/tools/perf/bench/pmu-scan.c
@@ -40,13 +40,11 @@ static struct pmu_scan_result *results;

static int save_result(void)
{
- struct perf_pmu *pmu;
+ struct perf_pmu *pmu = NULL;
struct list_head *list;
struct pmu_scan_result *r;

- perf_pmu__scan(NULL);
-
- perf_pmus__for_each_pmu(pmu) {
+ while ((pmu = perf_pmu__scan(pmu)) != NULL) {
r = realloc(results, (nr_pmus + 1) * sizeof(*r));
if (r == NULL)
return -ENOMEM;
diff --git a/tools/perf/tests/event_groups.c b/tools/perf/tests/event_groups.c
index 029442b4e9c6..3d9a2b524bba 100644
--- a/tools/perf/tests/event_groups.c
+++ b/tools/perf/tests/event_groups.c
@@ -50,13 +50,10 @@ static int event_open(int type, unsigned long config, int group_fd)

static int setup_uncore_event(void)
{
- struct perf_pmu *pmu;
+ struct perf_pmu *pmu = NULL;
int i, fd;

- if (list_empty(&pmus))
- perf_pmu__scan(NULL);
-
- perf_pmus__for_each_pmu(pmu) {
+ while ((pmu = perf_pmu__scan(pmu)) != NULL) {
for (i = 0; i < NR_UNCORE_PMUS; i++) {
if (!strcmp(uncore_pmus[i].name, pmu->name)) {
pr_debug("Using %s for uncore pmu event\n", pmu->name);
diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c
index 72a10bed84fd..277607ede060 100644
--- a/tools/perf/tests/parse-events.c
+++ b/tools/perf/tests/parse-events.c
@@ -108,11 +108,11 @@ static int test__checkevent_raw(struct evlist *evlist)
TEST_ASSERT_VAL("wrong number of entries", 0 != evlist->core.nr_entries);

perf_evlist__for_each_evsel(&evlist->core, evsel) {
- struct perf_pmu *pmu;
+ struct perf_pmu *pmu = NULL;
bool type_matched = false;

TEST_ASSERT_VAL("wrong config", test_perf_config(evsel, 0x1a));
- perf_pmus__for_each_pmu(pmu) {
+ while ((pmu = perf_pmu__scan(pmu)) != NULL) {
if (pmu->type == evsel->attr.type) {
TEST_ASSERT_VAL("PMU type expected once", !type_matched);
type_matched = true;
@@ -2243,13 +2243,10 @@ static int test__terms2(struct test_suite *test __maybe_unused, int subtest __ma

static int test__pmu_events(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
{
- struct perf_pmu *pmu;
+ struct perf_pmu *pmu = NULL;
int ret = TEST_OK;

- if (list_empty(&pmus))
- perf_pmu__scan(NULL);
-
- perf_pmus__for_each_pmu(pmu) {
+ while ((pmu = perf_pmu__scan(pmu)) != NULL) {
struct stat st;
char path[PATH_MAX];
struct dirent *ent;
diff --git a/tools/perf/util/pmus.h b/tools/perf/util/pmus.h
index d475e2960c10..257de10788e8 100644
--- a/tools/perf/util/pmus.h
+++ b/tools/perf/util/pmus.h
@@ -5,8 +5,6 @@
extern struct list_head pmus;
struct perf_pmu;

-#define perf_pmus__for_each_pmu(pmu) list_for_each_entry(pmu, &pmus, list)
-
const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str);

#endif /* __PMUS_H */
--
2.40.1.698.g37aff9b760-goog


2023-05-24 22:59:43

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 22/35] perf metrics: Remove perf_pmu__is_hybrid use

Switch from perf_pmu__is_hybrid to avoid implicitly using the hybrid
PMU list.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/util/metricgroup.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index 7de721e9d895..72583f62eda8 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -11,7 +11,6 @@
#include "evsel.h"
#include "strbuf.h"
#include "pmu.h"
-#include "pmu-hybrid.h"
#include "print-events.h"
#include "smt.h"
#include "expr.h"
@@ -274,7 +273,7 @@ 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;
- bool all_pmus = !strcmp(pmu, "all") || !perf_pmu__is_hybrid(pmu);
+ bool all_pmus = !strcmp(pmu, "all") || !perf_pmu__has_hybrid() || !is_pmu_hybrid(pmu);

*out_metric_events = NULL;
ids_size = hashmap__size(ids);
@@ -288,8 +287,7 @@ static int setup_metric_events(const char *pmu, struct hashmap *ids,
struct expr_id_data *val_ptr;

/* Don't match events for the wrong hybrid PMU. */
- if (!all_pmus && ev->pmu_name &&
- perf_pmu__is_hybrid(ev->pmu_name) &&
+ if (!all_pmus && ev->pmu_name && evsel__is_hybrid(ev) &&
strcmp(ev->pmu_name, pmu))
continue;
/*
--
2.40.1.698.g37aff9b760-goog


2023-05-24 23:01:14

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 21/35] perf header: Avoid hybrid PMU list in write_pmu_caps

Avoid perf_pmu__for_each_hybrid_pmu by iterating all PMUs are dumping
the core ones. This will eventually allow removal of the hybrid PMU
list.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/util/header.c | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 276870221ce0..e24cc8f316cd 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -51,7 +51,6 @@
#include "bpf-event.h"
#include "bpf-utils.h"
#include "clockid.h"
-#include "pmu-hybrid.h"

#include <linux/ctype.h>
#include <internal/lib.h>
@@ -1589,17 +1588,21 @@ static int write_pmu_caps(struct feat_fd *ff,
* Write hybrid pmu caps first to maintain compatibility with
* older perf tool.
*/
- pmu = NULL;
- perf_pmu__for_each_hybrid_pmu(pmu) {
- ret = __write_pmu_caps(ff, pmu, true);
- if (ret < 0)
- return ret;
+ if (perf_pmu__has_hybrid()) {
+ pmu = NULL;
+ while ((pmu = perf_pmu__scan(pmu))) {
+ if (!pmu->is_core)
+ continue;
+
+ ret = __write_pmu_caps(ff, pmu, true);
+ if (ret < 0)
+ return ret;
+ }
}

pmu = NULL;
while ((pmu = perf_pmu__scan(pmu))) {
- if (!pmu->name || !strcmp(pmu->name, "cpu") ||
- !pmu->nr_caps || perf_pmu__is_hybrid(pmu->name))
+ if (pmu->is_core || !pmu->nr_caps)
continue;

ret = __write_pmu_caps(ff, pmu, true);
--
2.40.1.698.g37aff9b760-goog


2023-05-24 23:01:14

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 25/35] perf pmu: Remove perf_pmu__hybrid_pmus list

Rather than iterate hybrid PMUs, inhererently Intel specific, iterate
all PMUs checking whether they are core. To only get hybrid cores,
first call perf_pmu__has_hybrid.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/builtin-record.c | 1 -
tools/perf/util/Build | 1 -
tools/perf/util/pmu-hybrid.c | 39 ------------------------------
tools/perf/util/pmu-hybrid.h | 29 ----------------------
tools/perf/util/pmu.c | 7 ------
tools/perf/util/pmu.h | 2 --
tools/perf/util/print-events.c | 1 -
tools/perf/util/python-ext-sources | 1 -
8 files changed, 81 deletions(-)
delete mode 100644 tools/perf/util/pmu-hybrid.c
delete mode 100644 tools/perf/util/pmu-hybrid.h

diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index e30e8d6a6575..2abcad2998f6 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -49,7 +49,6 @@
#include "util/util.h"
#include "util/pfm.h"
#include "util/clockid.h"
-#include "util/pmu-hybrid.h"
#include "util/off_cpu.h"
#include "util/bpf-filter.h"
#include "asm/bug.h"
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 21e4cdcba504..0d68be51a739 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -73,7 +73,6 @@ perf-y += pmu.o
perf-y += pmus.o
perf-y += pmu-flex.o
perf-y += pmu-bison.o
-perf-y += pmu-hybrid.o
perf-y += svghelper.o
perf-$(CONFIG_LIBTRACEEVENT) += trace-event-info.o
perf-y += trace-event-scripting.o
diff --git a/tools/perf/util/pmu-hybrid.c b/tools/perf/util/pmu-hybrid.c
deleted file mode 100644
index 7fe943dd3217..000000000000
--- a/tools/perf/util/pmu-hybrid.c
+++ /dev/null
@@ -1,39 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <linux/list.h>
-#include <linux/compiler.h>
-#include <linux/string.h>
-#include <linux/zalloc.h>
-#include <sys/types.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include <stdarg.h>
-#include <locale.h>
-#include <api/fs/fs.h>
-#include "fncache.h"
-#include "pmu-hybrid.h"
-
-LIST_HEAD(perf_pmu__hybrid_pmus);
-
-static struct perf_pmu *perf_pmu__find_hybrid_pmu(const char *name)
-{
- struct perf_pmu *pmu;
-
- if (!name)
- return NULL;
-
- perf_pmu__for_each_hybrid_pmu(pmu) {
- if (!strcmp(name, pmu->name))
- return pmu;
- }
-
- return NULL;
-}
-
-bool perf_pmu__is_hybrid(const char *name)
-{
- return perf_pmu__find_hybrid_pmu(name) != NULL;
-}
diff --git a/tools/perf/util/pmu-hybrid.h b/tools/perf/util/pmu-hybrid.h
deleted file mode 100644
index 8dbcae935020..000000000000
--- a/tools/perf/util/pmu-hybrid.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __PMU_HYBRID_H
-#define __PMU_HYBRID_H
-
-#include <linux/perf_event.h>
-#include <linux/compiler.h>
-#include <linux/list.h>
-#include <stdbool.h>
-#include "pmu.h"
-
-extern struct list_head perf_pmu__hybrid_pmus;
-
-#define perf_pmu__for_each_hybrid_pmu(pmu) \
- list_for_each_entry(pmu, &perf_pmu__hybrid_pmus, hybrid_list)
-
-bool perf_pmu__is_hybrid(const char *name);
-
-static inline int perf_pmu__hybrid_pmu_num(void)
-{
- struct perf_pmu *pmu;
- int num = 0;
-
- perf_pmu__for_each_hybrid_pmu(pmu)
- num++;
-
- return num;
-}
-
-#endif /* __PMU_HYBRID_H */
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 12446f63d1b3..62cd795e2fd3 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -32,7 +32,6 @@
#include "string2.h"
#include "strbuf.h"
#include "fncache.h"
-#include "pmu-hybrid.h"
#include "util/evsel_config.h"

struct perf_pmu perf_pmu__fake;
@@ -967,11 +966,6 @@ static struct perf_pmu *pmu_lookup(int dirfd, const char *lookup_name)
list_splice(&aliases, &pmu->aliases);
list_add_tail(&pmu->list, &pmus);

- if (!strcmp(name, "cpu_core") || !strcmp(name, "cpu_atom"))
- list_add_tail(&pmu->hybrid_list, &perf_pmu__hybrid_pmus);
- else
- INIT_LIST_HEAD(&pmu->hybrid_list);
-
pmu->default_config = perf_pmu__get_default_config(pmu);

return pmu;
@@ -2144,7 +2138,6 @@ void perf_pmu__destroy(void)

list_for_each_entry_safe(pmu, tmp, &pmus, list) {
list_del(&pmu->list);
- list_del(&pmu->hybrid_list);

perf_pmu__delete(pmu);
}
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 5f5de7c20ab6..cb51ad6e40fa 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -119,8 +119,6 @@ struct perf_pmu {
struct list_head caps;
/** @list: Element on pmus list in pmu.c. */
struct list_head list;
- /** @hybrid_list: Element on perf_pmu__hybrid_pmus. */
- struct list_head hybrid_list;

/**
* @missing_features: Features to inhibit when events on this PMU are
diff --git a/tools/perf/util/print-events.c b/tools/perf/util/print-events.c
index 69492cbd6921..8d823bc906e6 100644
--- a/tools/perf/util/print-events.c
+++ b/tools/perf/util/print-events.c
@@ -26,7 +26,6 @@
#include "strlist.h"
#include "tracepoint.h"
#include "pfm.h"
-#include "pmu-hybrid.h"
#include "thread_map.h"

#define MAX_NAME_LEN 100
diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources
index aa5156c2bcff..d4c9b4cd35ef 100644
--- a/tools/perf/util/python-ext-sources
+++ b/tools/perf/util/python-ext-sources
@@ -39,5 +39,4 @@ util/affinity.c
util/rwsem.c
util/hashmap.c
util/perf_regs.c
-util/pmu-hybrid.c
util/fncache.c
--
2.40.1.698.g37aff9b760-goog


2023-05-25 05:51:05

by Namhyung Kim

[permalink] [raw]
Subject: Re: [PATCH v3 09/35] perf evlist: Propagate user CPU maps intersecting core PMU maps

Hi Ian,

On Wed, May 24, 2023 at 3:19 PM Ian Rogers <[email protected]> wrote:
>
> The CPU map for a non-core PMU gives a default CPU value for
> perf_event_open. For core PMUs the CPU map lists all CPUs the evsel
> may be opened on. If there are >1 core PMU, the CPU maps will list the
> CPUs for that core PMU, but the user_requested_cpus may contain CPUs
> that are invalid for the PMU and cause perf_event_open to fail. To
> avoid this, when propagating the CPU map for core PMUs intersect it
> with the CPU map of the PMU (the evsel's "own_cpus").
>
> Add comments to __perf_evlist__propagate_maps to explain its somewhat
> complex behavior.

Thanks for tackling this. There are many assumptions on this code
which make this code hard to understand. I think we need to list
all possible cases and make the logic as simple as possible.

>
> Signed-off-by: Ian Rogers <[email protected]>
> ---
> tools/lib/perf/evlist.c | 25 ++++++++++++++++++++-----
> 1 file changed, 20 insertions(+), 5 deletions(-)
>
> diff --git a/tools/lib/perf/evlist.c b/tools/lib/perf/evlist.c
> index 81e8b5fcd8ba..b8b066d0dc5e 100644
> --- a/tools/lib/perf/evlist.c
> +++ b/tools/lib/perf/evlist.c
> @@ -36,18 +36,33 @@ void perf_evlist__init(struct perf_evlist *evlist)
> static void __perf_evlist__propagate_maps(struct perf_evlist *evlist,
> struct perf_evsel *evsel)
> {
> - /*
> - * We already have cpus for evsel (via PMU sysfs) so
> - * keep it, if there's no target cpu list defined.
> - */

So basically this code is only needed when the user specified a cpu list.
Otherwise evsels can use their own cpus. But it's a kind of sad that
libperf does not have a notion of PMU (with a cpu map) yet.

I think we have the following cases. Please tell me if I miss some.

1. non-hybrid core PMU: It used to not have a cpu map, but you added it
in this patchset to cover all (online) CPUs. So it'd be ok to treat them as
same as the hybrid PMUs.

2. hybrid core PMU: It has a cpu map to cover possible CPUs and the
user requested cpu map should be intersected with its map.

3. uncore PMU: It has a cpu map to indicate CPUs to handle event
settings but it's allowed to read the event from other CPUs (at least
for Intel CPUs). That means it can just use the user request cpu map.

4. dummy event: It can be marked as system-wide to get the sideband
events from all CPUs. Then it should ignore the user requested cpu
map. Otherwise it should be treated as other events.

5. tool event: It's used for perf stat and has a hardcoded cpu map for
CPU 0. Not sure if it can accept other CPUs but it seems we can ignore
the user requested cpu map.

6. other event: No restrictions. It can use the user requested cpu map.


> if (evsel->system_wide) {
> + /* System wide: set the cpu map of the evsel to all online CPUs. */
> perf_cpu_map__put(evsel->cpus);
> evsel->cpus = perf_cpu_map__new(NULL);
> + } else if (evlist->has_user_cpus && evsel->is_pmu_core) {
> + /*
> + * User requested CPUs on a core PMU, ensure the requested CPUs
> + * are valid by intersecting with those of the PMU.
> + */
> + perf_cpu_map__put(evsel->cpus);
> + evsel->cpus = perf_cpu_map__intersect(evlist->user_requested_cpus, evsel->own_cpus);
> } else if (!evsel->own_cpus || evlist->has_user_cpus ||
> - (!evsel->requires_cpu && perf_cpu_map__empty(evlist->user_requested_cpus))) {
> + (!evsel->requires_cpu && perf_cpu_map__has_any_cpu(evlist->user_requested_cpus))) {
> + /*
> + * The PMU didn't specify a default cpu map, this isn't a core
> + * event and the user requested CPUs or the evlist user
> + * requested CPUs have the "any CPU" (aka dummy) CPU value. In
> + * which case use the user requested CPUs rather than the PMU
> + * ones.
> + */
> perf_cpu_map__put(evsel->cpus);
> evsel->cpus = perf_cpu_map__get(evlist->user_requested_cpus);
> } else if (evsel->cpus != evsel->own_cpus) {
> + /*
> + * No user requested cpu map but the PMU cpu map doesn't match
> + * the evsel's. Reset it back to the PMU cpu map.
> + */

Not sure if it actually happens.

Thanks,
Namhyung


> perf_cpu_map__put(evsel->cpus);
> evsel->cpus = perf_cpu_map__get(evsel->own_cpus);
> }
> --
> 2.40.1.698.g37aff9b760-goog
>

2023-05-26 18:43:40

by Liang, Kan

[permalink] [raw]
Subject: Re: [PATCH v3 00/35] PMU refactoring and improvements



On 2023-05-24 6:17 p.m., Ian Rogers wrote:
> Separate the code in pmu.[ch] into the set/list of PMUs and the code
> for a particular PMU. Move the set/list of PMUs code into
> pmus.[ch]. Clean up hybrid code and remove hybrid PMU list, it is
> sufficient to scan PMUs looking for core ones. Add core PMU list and
> perf_pmus__scan_core that just reads core PMUs. Switch code that skips
> non-core PMUs during a perf_pmus__scan, to use the
> perf_pmus__scan_core variant. Don't scan sysfs for PMUs if all such
> PMUs have been previously scanned/loaded. Scanning just core PMUs, for
> the cases it is applicable, can improve the sysfs reading time by more
> than 4 fold on my laptop, as servers generally have many more uncore
> PMUs the improvement there should be larger:
>
> ```
> $ perf bench internals pmu-scan -i 1000
> Computing performance of sysfs PMU event scan for 1000 times
> Average core PMU scanning took: 989.231 usec (+- 1.535 usec)
> Average PMU scanning took: 4309.425 usec (+- 74.322 usec)
> ```
>
> The patch "perf pmu: Separate pmu and pmus" moves and renames a lot of
> functions, and is consequently large. The changes are trivial, but
> kept together to keep the overall number of patches more reasonable.
>

Other than the small suggestion in patch 16, the patch set looks good to
me. Thanks Ian!

Reviewed-by: Kan Liang <[email protected]>

Thanks,
Kan

> v3. Address fixing hybrid user specified CPU maps by doing it in
> propagate maps. Remove nearly all references to cpu_core/cpu_atom
> in particular by removing is_pmu_hybrid - hybrid is now >1 core
> PMU. Addresses comments by Kan and Namhyung.
> v2. Address Kan's review comments wrt "cycles" -> "cycles:P" and
> "uncore_pmus" -> "other_pmus".
>
> Ian Rogers (35):
> perf cpumap: Add intersect function
> perf tests: Organize cpu_map tests into a single suite
> perf cpumap: Add equal function
> libperf cpumap: Add "any CPU"/dummy test function
> perf pmu: Detect ARM and hybrid PMUs with sysfs
> perf pmu: Add is_core to pmu
> perf evsel: Add is_pmu_core inorder to interpret own_cpus
> perf pmu: Add CPU map for "cpu" PMUs
> perf evlist: Propagate user CPU maps intersecting core PMU maps
> perf evlist: Allow has_user_cpus to be set on hybrid
> perf target: Remove unused hybrid value
> perf tools: Warn if no user requested CPUs match PMU's CPUs
> perf evlist: Remove evlist__warn_hybrid_group
> perf evlist: Remove __evlist__add_default
> perf evlist: Reduce scope of evlist__has_hybrid
> perf pmu: Remove perf_pmu__hybrid_mounted
> perf pmu: Rewrite perf_pmu__has_hybrid to avoid list
> perf x86: Iterate hybrid PMUs as core PMUs
> perf topology: Avoid hybrid list for hybrid topology
> perf evsel: Compute is_hybrid from PMU being core
> perf header: Avoid hybrid PMU list in write_pmu_caps
> perf metrics: Remove perf_pmu__is_hybrid use
> perf stat: Avoid hybrid PMU list
> perf mem: Avoid hybrid PMU list
> perf pmu: Remove perf_pmu__hybrid_pmus list
> perf pmus: Prefer perf_pmu__scan over perf_pmus__for_each_pmu
> perf x86 mem: minor refactor to is_mem_loads_aux_event
> perf pmu: Separate pmu and pmus
> perf pmus: Split pmus list into core and other
> perf pmus: Allow just core PMU scanning
> perf pmus: Avoid repeated sysfs scanning
> perf pmus: Ensure all PMUs are read for find_by_type
> perf pmus: Add function to return count of core PMUs
> perf pmus: Remove perf_pmus__has_hybrid
> perf pmu: Remove is_pmu_hybrid
>
> tools/lib/perf/cpumap.c | 61 +++
> tools/lib/perf/evlist.c | 25 +-
> tools/lib/perf/include/internal/evsel.h | 9 +
> tools/lib/perf/include/perf/cpumap.h | 14 +
> tools/perf/arch/arm/util/auxtrace.c | 7 +-
> tools/perf/arch/arm/util/cs-etm.c | 4 +-
> tools/perf/arch/arm64/util/pmu.c | 6 +-
> tools/perf/arch/x86/tests/hybrid.c | 7 +-
> tools/perf/arch/x86/util/auxtrace.c | 5 +-
> tools/perf/arch/x86/util/evlist.c | 25 +-
> tools/perf/arch/x86/util/evsel.c | 27 +-
> tools/perf/arch/x86/util/intel-bts.c | 4 +-
> tools/perf/arch/x86/util/intel-pt.c | 4 +-
> tools/perf/arch/x86/util/mem-events.c | 17 +-
> tools/perf/arch/x86/util/perf_regs.c | 15 +-
> tools/perf/arch/x86/util/topdown.c | 5 +-
> tools/perf/bench/pmu-scan.c | 60 +--
> tools/perf/builtin-c2c.c | 9 +-
> tools/perf/builtin-list.c | 4 +-
> tools/perf/builtin-mem.c | 9 +-
> tools/perf/builtin-record.c | 29 +-
> tools/perf/builtin-stat.c | 14 +-
> tools/perf/builtin-top.c | 10 +-
> tools/perf/tests/attr.c | 11 +-
> tools/perf/tests/builtin-test.c | 4 +-
> tools/perf/tests/cpumap.c | 92 ++++-
> tools/perf/tests/event_groups.c | 7 +-
> tools/perf/tests/parse-events.c | 15 +-
> tools/perf/tests/parse-metric.c | 9 +-
> tools/perf/tests/pmu-events.c | 6 +-
> tools/perf/tests/switch-tracking.c | 14 +-
> tools/perf/tests/tests.h | 4 +-
> tools/perf/tests/topology.c | 16 +-
> tools/perf/util/Build | 2 -
> tools/perf/util/cpumap.c | 4 +-
> tools/perf/util/cpumap.h | 4 +-
> tools/perf/util/cputopo.c | 12 +-
> tools/perf/util/env.c | 5 +-
> tools/perf/util/evlist-hybrid.c | 162 --------
> tools/perf/util/evlist-hybrid.h | 15 -
> tools/perf/util/evlist.c | 64 +++-
> tools/perf/util/evlist.h | 9 +-
> tools/perf/util/evsel.c | 60 +--
> tools/perf/util/evsel.h | 3 -
> tools/perf/util/header.c | 27 +-
> tools/perf/util/mem-events.c | 25 +-
> tools/perf/util/metricgroup.c | 9 +-
> tools/perf/util/parse-events.c | 25 +-
> tools/perf/util/parse-events.y | 3 +-
> tools/perf/util/pfm.c | 6 +-
> tools/perf/util/pmu-hybrid.c | 52 ---
> tools/perf/util/pmu-hybrid.h | 32 --
> tools/perf/util/pmu.c | 483 ++----------------------
> tools/perf/util/pmu.h | 25 +-
> tools/perf/util/pmus.c | 465 ++++++++++++++++++++++-
> tools/perf/util/pmus.h | 15 +-
> tools/perf/util/print-events.c | 15 +-
> tools/perf/util/python-ext-sources | 1 -
> tools/perf/util/stat-display.c | 19 +-
> tools/perf/util/target.h | 1 -
> 60 files changed, 1002 insertions(+), 1089 deletions(-)
> delete mode 100644 tools/perf/util/evlist-hybrid.c
> delete mode 100644 tools/perf/util/evlist-hybrid.h
> delete mode 100644 tools/perf/util/pmu-hybrid.c
> delete mode 100644 tools/perf/util/pmu-hybrid.h
>

2023-05-26 22:17:41

by Ian Rogers

[permalink] [raw]
Subject: Re: [PATCH v3 09/35] perf evlist: Propagate user CPU maps intersecting core PMU maps

On Wed, May 24, 2023 at 10:30 PM Namhyung Kim <[email protected]> wrote:
>
> Hi Ian,
>
> On Wed, May 24, 2023 at 3:19 PM Ian Rogers <[email protected]> wrote:
> >
> > The CPU map for a non-core PMU gives a default CPU value for
> > perf_event_open. For core PMUs the CPU map lists all CPUs the evsel
> > may be opened on. If there are >1 core PMU, the CPU maps will list the
> > CPUs for that core PMU, but the user_requested_cpus may contain CPUs
> > that are invalid for the PMU and cause perf_event_open to fail. To
> > avoid this, when propagating the CPU map for core PMUs intersect it
> > with the CPU map of the PMU (the evsel's "own_cpus").
> >
> > Add comments to __perf_evlist__propagate_maps to explain its somewhat
> > complex behavior.
>
> Thanks for tackling this. There are many assumptions on this code
> which make this code hard to understand. I think we need to list
> all possible cases and make the logic as simple as possible.
>
> >
> > Signed-off-by: Ian Rogers <[email protected]>
> > ---
> > tools/lib/perf/evlist.c | 25 ++++++++++++++++++++-----
> > 1 file changed, 20 insertions(+), 5 deletions(-)
> >
> > diff --git a/tools/lib/perf/evlist.c b/tools/lib/perf/evlist.c
> > index 81e8b5fcd8ba..b8b066d0dc5e 100644
> > --- a/tools/lib/perf/evlist.c
> > +++ b/tools/lib/perf/evlist.c
> > @@ -36,18 +36,33 @@ void perf_evlist__init(struct perf_evlist *evlist)
> > static void __perf_evlist__propagate_maps(struct perf_evlist *evlist,
> > struct perf_evsel *evsel)
> > {
> > - /*
> > - * We already have cpus for evsel (via PMU sysfs) so
> > - * keep it, if there's no target cpu list defined.
> > - */
>
> So basically this code is only needed when the user specified a cpu list.
> Otherwise evsels can use their own cpus. But it's a kind of sad that
> libperf does not have a notion of PMU (with a cpu map) yet.
>
> I think we have the following cases. Please tell me if I miss some.
>
> 1. non-hybrid core PMU: It used to not have a cpu map, but you added it
> in this patchset to cover all (online) CPUs. So it'd be ok to treat them as
> same as the hybrid PMUs.
>
> 2. hybrid core PMU: It has a cpu map to cover possible CPUs and the
> user requested cpu map should be intersected with its map.

Right, and these two cases should be just considered as core PMU
cases, where there can be >1 core PMU.

> 3. uncore PMU: It has a cpu map to indicate CPUs to handle event
> settings but it's allowed to read the event from other CPUs (at least
> for Intel CPUs). That means it can just use the user request cpu map.

Yep, but uncore can be a confusing name as it means things not in the
core and doesn't include, say the interconnect, that Intel calls
offcore. In pmus.c at the end of this series we have a list of core
pmus and other pmus.

> 4. dummy event: It can be marked as system-wide to get the sideband
> events from all CPUs. Then it should ignore the user requested cpu
> map. Otherwise it should be treated as other events.

Agreed. Typically dummy is regarded as a software event but the PMU
for software events has an empty CPU map.

> 5. tool event: It's used for perf stat and has a hardcoded cpu map for
> CPU 0. Not sure if it can accept other CPUs but it seems we can ignore
> the user requested cpu map.

Tool events have their PMU type set to software but then we special
case things prior to say displaying the name or reading a counter. The
CPU maps are never used to my knowledge and the enable/running times
look questionable for user and system time.

> 6. other event: No restrictions. It can use the user requested cpu map.

Here there are software, tracepoint and breakpoint events and there is
no PMU provided CPU map. There are sysfs PMUs for these but they don't
provide a CPU map.

I think the thing that stems from this is the comment on evsel
system_wide is stale:

/*
* system_wide is for events that need to be on every CPU, irrespective
* of user requested CPUs or threads. Map propagation will set cpus to
* this event's own_cpus, whereby they will contribute to evlist
* all_cpus.
*/

If this were true then the empty software PMU's CPU map would be
copied to dummy events when instead every CPU is being requested. I'll
tweak the comment in v4.

Thanks,
Ian


> > if (evsel->system_wide) {
> > + /* System wide: set the cpu map of the evsel to all online CPUs. */
> > perf_cpu_map__put(evsel->cpus);
> > evsel->cpus = perf_cpu_map__new(NULL);
> > + } else if (evlist->has_user_cpus && evsel->is_pmu_core) {
> > + /*
> > + * User requested CPUs on a core PMU, ensure the requested CPUs
> > + * are valid by intersecting with those of the PMU.
> > + */
> > + perf_cpu_map__put(evsel->cpus);
> > + evsel->cpus = perf_cpu_map__intersect(evlist->user_requested_cpus, evsel->own_cpus);
> > } else if (!evsel->own_cpus || evlist->has_user_cpus ||
> > - (!evsel->requires_cpu && perf_cpu_map__empty(evlist->user_requested_cpus))) {
> > + (!evsel->requires_cpu && perf_cpu_map__has_any_cpu(evlist->user_requested_cpus))) {
> > + /*
> > + * The PMU didn't specify a default cpu map, this isn't a core
> > + * event and the user requested CPUs or the evlist user
> > + * requested CPUs have the "any CPU" (aka dummy) CPU value. In
> > + * which case use the user requested CPUs rather than the PMU
> > + * ones.
> > + */
> > perf_cpu_map__put(evsel->cpus);
> > evsel->cpus = perf_cpu_map__get(evlist->user_requested_cpus);
> > } else if (evsel->cpus != evsel->own_cpus) {
> > + /*
> > + * No user requested cpu map but the PMU cpu map doesn't match
> > + * the evsel's. Reset it back to the PMU cpu map.
> > + */
>
> Not sure if it actually happens.
>
> Thanks,
> Namhyung
>
>
> > perf_cpu_map__put(evsel->cpus);
> > evsel->cpus = perf_cpu_map__get(evsel->own_cpus);
> > }
> > --
> > 2.40.1.698.g37aff9b760-goog
> >