* [PATCH v1 1/6] perf pmu: Merge boolean sysfs event option parsing
2024-07-17 22:47 [PATCH v1 0/6] Add support for sysfs event.cpus and cpu event term Ian Rogers
@ 2024-07-17 22:47 ` Ian Rogers
2024-07-17 22:47 ` [PATCH v1 2/6] perf parse-events: Pass cpu_list as a perf_cpu_map in __add_event Ian Rogers
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Ian Rogers @ 2024-07-17 22:47 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, Bjorn Helgaas,
Jonathan Corbet, James Clark, Ravi Bangoria, Dominique Martinet,
linux-kernel, linux-perf-users, Dhananjay Ugwekar, ananth.narayan,
gautham.shenoy, kprateek.nayak, sandipan.das
Merge perf_pmu__parse_per_pkg and perf_pmu__parse_snapshot that do the
same parsing except for the file suffix used.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/pmu.c | 47 +++++++++++++++++++++----------------------
1 file changed, 23 insertions(+), 24 deletions(-)
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 986166bc7c78..5148b6639dd3 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -367,8 +367,8 @@ static int perf_pmu__parse_unit(struct perf_pmu *pmu, struct perf_pmu_alias *ali
return -1;
}
-static int
-perf_pmu__parse_per_pkg(struct perf_pmu *pmu, struct perf_pmu_alias *alias)
+static bool perf_pmu__parse_event_source_bool(const char *pmu_name, const char *event_name,
+ const char *suffix)
{
char path[PATH_MAX];
size_t len;
@@ -376,37 +376,36 @@ perf_pmu__parse_per_pkg(struct perf_pmu *pmu, struct perf_pmu_alias *alias)
len = perf_pmu__event_source_devices_scnprintf(path, sizeof(path));
if (!len)
- return 0;
- scnprintf(path + len, sizeof(path) - len, "%s/events/%s.per-pkg", pmu->name, alias->name);
+ return false;
+
+ scnprintf(path + len, sizeof(path) - len, "%s/events/%s.%s", pmu_name, event_name, suffix);
fd = open(path, O_RDONLY);
if (fd == -1)
- return -1;
+ return false;
- close(fd);
+#ifndef NDEBUG
+ {
+ char buf[8];
- alias->per_pkg = true;
- return 0;
+ len = read(fd, buf, sizeof(buf));
+ assert(len == 1 || len == 2);
+ assert(buf[0] == '1');
+ }
+#endif
+
+ close(fd);
+ return true;
}
-static int perf_pmu__parse_snapshot(struct perf_pmu *pmu, struct perf_pmu_alias *alias)
+static void perf_pmu__parse_per_pkg(struct perf_pmu *pmu, struct perf_pmu_alias *alias)
{
- char path[PATH_MAX];
- size_t len;
- int fd;
-
- len = perf_pmu__event_source_devices_scnprintf(path, sizeof(path));
- if (!len)
- return 0;
- scnprintf(path + len, sizeof(path) - len, "%s/events/%s.snapshot", pmu->name, alias->name);
-
- fd = open(path, O_RDONLY);
- if (fd == -1)
- return -1;
+ alias->per_pkg = perf_pmu__parse_event_source_bool(pmu->name, alias->name, "per-pkg");
+}
- alias->snapshot = true;
- close(fd);
- return 0;
+static void perf_pmu__parse_snapshot(struct perf_pmu *pmu, struct perf_pmu_alias *alias)
+{
+ alias->snapshot = perf_pmu__parse_event_source_bool(pmu->name, alias->name, "snapshot");
}
/* Delete an alias entry. */
--
2.45.2.1089.g2a221341d9-goog
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v1 2/6] perf parse-events: Pass cpu_list as a perf_cpu_map in __add_event
2024-07-17 22:47 [PATCH v1 0/6] Add support for sysfs event.cpus and cpu event term Ian Rogers
2024-07-17 22:47 ` [PATCH v1 1/6] perf pmu: Merge boolean sysfs event option parsing Ian Rogers
@ 2024-07-17 22:47 ` Ian Rogers
2024-07-17 22:47 ` [PATCH v1 3/6] perf pmu: Add support for event.cpus files in sysfs Ian Rogers
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Ian Rogers @ 2024-07-17 22:47 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, Bjorn Helgaas,
Jonathan Corbet, James Clark, Ravi Bangoria, Dominique Martinet,
linux-kernel, linux-perf-users, Dhananjay Ugwekar, ananth.narayan,
gautham.shenoy, kprateek.nayak, sandipan.das
Previously the cpu_list is a string and typically no cpu_list is
passed to __add_event. Wanting to make events have their cpus distinct
from the PMU means that in more occassions we want to pass a
cpu_list. If we're reading this from sysfs it is easier to read a
perf_cpu_map than allocate and pass around strings that will later be
parsed.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/parse-events.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 321586fb5556..43501eb56336 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -227,12 +227,12 @@ __add_event(struct list_head *list, int *idx,
bool init_attr,
const char *name, const char *metric_id, struct perf_pmu *pmu,
struct list_head *config_terms, bool auto_merge_stats,
- const char *cpu_list)
+ struct perf_cpu_map *cpu_list)
{
struct evsel *evsel;
- struct perf_cpu_map *cpus = pmu ? perf_cpu_map__get(pmu->cpus) :
- cpu_list ? perf_cpu_map__new(cpu_list) : NULL;
+ struct perf_cpu_map *cpus = perf_cpu_map__is_empty(cpu_list) && pmu ? pmu->cpus : cpu_list;
+ cpus = perf_cpu_map__get(cpus);
if (pmu)
perf_pmu__warn_invalid_formats(pmu);
@@ -305,16 +305,17 @@ static int add_event_tool(struct list_head *list, int *idx,
.type = PERF_TYPE_SOFTWARE,
.config = PERF_COUNT_SW_DUMMY,
};
- const char *cpu_list = NULL;
+ struct perf_cpu_map *cpu_list = NULL;
if (tool_event == PERF_TOOL_DURATION_TIME) {
/* Duration time is gathered globally, pretend it is only on CPU0. */
- cpu_list = "0";
+ cpu_list = perf_cpu_map__new("0");
}
evsel = __add_event(list, idx, &attr, /*init_attr=*/true, /*name=*/NULL,
/*metric_id=*/NULL, /*pmu=*/NULL,
/*config_terms=*/NULL, /*auto_merge_stats=*/false,
cpu_list);
+ perf_cpu_map__put(cpu_list);
if (!evsel)
return -ENOMEM;
evsel->tool_event = tool_event;
--
2.45.2.1089.g2a221341d9-goog
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v1 3/6] perf pmu: Add support for event.cpus files in sysfs
2024-07-17 22:47 [PATCH v1 0/6] Add support for sysfs event.cpus and cpu event term Ian Rogers
2024-07-17 22:47 ` [PATCH v1 1/6] perf pmu: Merge boolean sysfs event option parsing Ian Rogers
2024-07-17 22:47 ` [PATCH v1 2/6] perf parse-events: Pass cpu_list as a perf_cpu_map in __add_event Ian Rogers
@ 2024-07-17 22:47 ` Ian Rogers
2024-07-17 22:47 ` [PATCH v1 4/6] libperf cpumap: Add ability to create CPU from a single CPU number Ian Rogers
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Ian Rogers @ 2024-07-17 22:47 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, Bjorn Helgaas,
Jonathan Corbet, James Clark, Ravi Bangoria, Dominique Martinet,
linux-kernel, linux-perf-users, Dhananjay Ugwekar, ananth.narayan,
gautham.shenoy, kprateek.nayak, sandipan.das
If an event file exists in sysfs, check if a event.cpus file exists
and read a perf_cpu_map from it if it does. This allows particular
events to have a different set of CPUs compared to the PMU.
One scenario where this could be useful is when a PMU is set up with a
cpumask/events per SMT thread but some events record for all SMT
threads. Programming an event on each SMT thread will cause
unnecessary counters to be programmed and the aggregate value to be
too large.
Another scenario where this could be useful if when a PMU has
historically had a cpumask at the package level, but now newer per
die, core or CPU information is available.
Additional context for the motivation is in these patches and
conversation:
https://lore.kernel.org/lkml/20240711102436.4432-1-Dhananjay.Ugwekar@amd.com/
Signed-off-by: Ian Rogers <irogers@google.com>
---
.../sysfs-bus-event_source-devices-events | 14 ++++++
tools/perf/util/parse-events.c | 45 ++++++++++---------
tools/perf/util/pmu.c | 44 +++++++++++++++++-
tools/perf/util/pmu.h | 1 +
4 files changed, 82 insertions(+), 22 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-event_source-devices-events b/Documentation/ABI/testing/sysfs-bus-event_source-devices-events
index e7efeab2ee83..d8e3a4dd3ba7 100644
--- a/Documentation/ABI/testing/sysfs-bus-event_source-devices-events
+++ b/Documentation/ABI/testing/sysfs-bus-event_source-devices-events
@@ -70,6 +70,20 @@ Description: Per-pmu performance monitoring events specific to the running syste
This is referred to as "event parameterization". Event
parameters have the format 'param=?'.
+What: /sys/bus/event_source/devices/<pmu>/events/<event>.cpus
+Date: 2024/07/17
+Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
+Description: Perf event CPUs
+
+ A list of CPUs on which a the perf event should be
+ opened by default. This is an event specific variant
+ of /sys/bus/event_source/devices/<pmu>/cpumask.
+
+ Examples (each of these lines would be in a separate file):
+
+ 0,2,4,6
+ 0-7
+
What: /sys/bus/event_source/devices/<pmu>/events/<event>.unit
Date: 2014/02/24
Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 43501eb56336..b181f83c9678 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -1425,12 +1425,13 @@ static int parse_events_add_pmu(struct parse_events_state *parse_state,
bool auto_merge_stats)
{
struct perf_event_attr attr;
- struct perf_pmu_info info;
+ struct perf_pmu_info info = {};
struct evsel *evsel;
struct parse_events_error *err = parse_state->error;
LIST_HEAD(config_terms);
struct parse_events_terms parsed_terms;
bool alias_rewrote_terms = false;
+ int ret = 0;
if (verbose > 1) {
struct strbuf sb;
@@ -1465,8 +1466,7 @@ static int parse_events_add_pmu(struct parse_events_state *parse_state,
parse_events_terms__init(&parsed_terms);
if (const_parsed_terms) {
- int ret = parse_events_terms__copy(const_parsed_terms, &parsed_terms);
-
+ ret = parse_events_terms__copy(const_parsed_terms, &parsed_terms);
if (ret)
return ret;
}
@@ -1474,15 +1474,15 @@ static int parse_events_add_pmu(struct parse_events_state *parse_state,
/* Configure attr/terms with a known PMU, this will set hardcoded terms. */
if (config_attr(&attr, &parsed_terms, parse_state->error, config_term_pmu)) {
- parse_events_terms__exit(&parsed_terms);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out_err;
}
/* Look for event names in the terms and rewrite into format based terms. */
if (!parse_state->fake_pmu && perf_pmu__check_alias(pmu, &parsed_terms,
&info, &alias_rewrote_terms, err)) {
- parse_events_terms__exit(&parsed_terms);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out_err;
}
if (verbose > 1) {
@@ -1497,13 +1497,13 @@ static int parse_events_add_pmu(struct parse_events_state *parse_state,
/* Configure attr/terms again if an alias was expanded. */
if (alias_rewrote_terms &&
config_attr(&attr, &parsed_terms, parse_state->error, config_term_pmu)) {
- parse_events_terms__exit(&parsed_terms);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out_err;
}
if (get_config_terms(&parsed_terms, &config_terms)) {
- parse_events_terms__exit(&parsed_terms);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto out_err;
}
/*
@@ -1512,24 +1512,23 @@ static int parse_events_add_pmu(struct parse_events_state *parse_state,
*/
if (pmu->perf_event_attr_init_default &&
get_config_chgs(pmu, &parsed_terms, &config_terms)) {
- parse_events_terms__exit(&parsed_terms);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto out_err;
}
if (!parse_state->fake_pmu &&
perf_pmu__config(pmu, &attr, &parsed_terms, parse_state->error)) {
- free_config_terms(&config_terms);
- parse_events_terms__exit(&parsed_terms);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out_err;
}
evsel = __add_event(list, &parse_state->idx, &attr, /*init_attr=*/true,
get_config_name(&parsed_terms),
get_config_metric_id(&parsed_terms), pmu,
- &config_terms, auto_merge_stats, /*cpu_list=*/NULL);
+ &config_terms, auto_merge_stats, info.cpus);
if (!evsel) {
- parse_events_terms__exit(&parsed_terms);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto out_err;
}
if (evsel->name)
@@ -1542,13 +1541,17 @@ static int parse_events_add_pmu(struct parse_events_state *parse_state,
return 0;
}
- parse_events_terms__exit(&parsed_terms);
free((char *)evsel->unit);
evsel->unit = strdup(info.unit);
evsel->scale = info.scale;
evsel->per_pkg = info.per_pkg;
evsel->snapshot = info.snapshot;
- return 0;
+out_err:
+ parse_events_terms__exit(&parsed_terms);
+ if (ret)
+ free_config_terms(&config_terms);
+ perf_cpu_map__put(info.cpus);
+ return ret;
}
int parse_events_multi_pmu_add(struct parse_events_state *parse_state,
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 5148b6639dd3..280b2499c861 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -73,6 +73,11 @@ struct perf_pmu_alias {
* differ from the PMU name as it won't have suffixes.
*/
char *pmu_name;
+ /**
+ * @cpus: A possible per-event cpumap that overrides that given for the
+ * PMU.
+ */
+ struct perf_cpu_map *cpus;
/** @unit: Units for the event, such as bytes or cache lines. */
char unit[UNIT_MAX_LEN+1];
/** @scale: Value to scale read counter values by. */
@@ -332,6 +337,32 @@ static int perf_pmu__parse_scale(struct perf_pmu *pmu, struct perf_pmu_alias *al
return ret;
}
+static void perf_pmu__parse_cpus(struct perf_pmu *pmu, struct perf_pmu_alias *alias)
+{
+ char path[PATH_MAX];
+ size_t len;
+ FILE *file;
+ int fd;
+
+ len = perf_pmu__event_source_devices_scnprintf(path, sizeof(path));
+ if (!len)
+ return;
+ scnprintf(path + len, sizeof(path) - len, "%s/events/%s.cpus", pmu->name, alias->name);
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return; /* Expected common case. */
+
+ file = fdopen(fd, "r");
+ if (!file) {
+ close(fd);
+ return;
+ }
+
+ alias->cpus = perf_cpu_map__read(file);
+ fclose(file);
+}
+
static int perf_pmu__parse_unit(struct perf_pmu *pmu, struct perf_pmu_alias *alias)
{
char path[PATH_MAX];
@@ -493,6 +524,7 @@ static void read_alias_info(struct perf_pmu *pmu, struct perf_pmu_alias *alias)
/*
* load unit name and scale if available
*/
+ perf_pmu__parse_cpus(pmu, alias);
perf_pmu__parse_unit(pmu, alias);
perf_pmu__parse_scale(pmu, alias);
perf_pmu__parse_per_pkg(pmu, alias);
@@ -618,7 +650,7 @@ static inline bool pmu_alias_info_file(const char *name)
size_t len;
len = strlen(name);
- if (len > 5 && !strcmp(name + len - 5, ".unit"))
+ if (len > 5 && (!strcmp(name + len - 5, ".cpus") || !strcmp(name + len - 5, ".unit")))
return true;
if (len > 6 && !strcmp(name + len - 6, ".scale"))
return true;
@@ -1560,6 +1592,12 @@ static int check_info_data(struct perf_pmu *pmu,
* define unit, scale and snapshot, fail
* if there's more than one.
*/
+ if (!perf_cpu_map__is_empty(info->cpus) && !perf_cpu_map__is_empty(alias->cpus)) {
+ parse_events_error__handle(err, column,
+ strdup("Attempt to set event's cpus twice"),
+ NULL);
+ return -EINVAL;
+ }
if (info->unit && alias->unit[0]) {
parse_events_error__handle(err, column,
strdup("Attempt to set event's unit twice"),
@@ -1579,6 +1617,9 @@ static int check_info_data(struct perf_pmu *pmu,
return -EINVAL;
}
+ if (!perf_cpu_map__is_empty(alias->cpus))
+ info->cpus = perf_cpu_map__get(alias->cpus);
+
if (alias->unit[0])
info->unit = alias->unit;
@@ -1610,6 +1651,7 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct parse_events_terms *head_
* Mark unit and scale as not set
* (different from default values, see below)
*/
+ info->cpus = NULL;
info->unit = NULL;
info->scale = 0.0;
info->snapshot = false;
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index b2d3fd291f02..b1ccfe8d3df4 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -177,6 +177,7 @@ struct perf_pmu {
extern struct perf_pmu perf_pmu__fake;
struct perf_pmu_info {
+ struct perf_cpu_map *cpus;
const char *unit;
double scale;
bool per_pkg;
--
2.45.2.1089.g2a221341d9-goog
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v1 4/6] libperf cpumap: Add ability to create CPU from a single CPU number
2024-07-17 22:47 [PATCH v1 0/6] Add support for sysfs event.cpus and cpu event term Ian Rogers
` (2 preceding siblings ...)
2024-07-17 22:47 ` [PATCH v1 3/6] perf pmu: Add support for event.cpus files in sysfs Ian Rogers
@ 2024-07-17 22:47 ` Ian Rogers
2024-07-17 22:47 ` [PATCH v1 5/6] perf parse-events: Set is_pmu_core for legacy hardware events Ian Rogers
2024-07-17 22:47 ` [PATCH v1 6/6] perf parse-events: Add "cpu" term to set the CPU an event is recorded on Ian Rogers
5 siblings, 0 replies; 7+ messages in thread
From: Ian Rogers @ 2024-07-17 22:47 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, Bjorn Helgaas,
Jonathan Corbet, James Clark, Ravi Bangoria, Dominique Martinet,
linux-kernel, linux-perf-users, Dhananjay Ugwekar, ananth.narayan,
gautham.shenoy, kprateek.nayak, sandipan.das
Add perf_cpu_map__new_int so that a CPU map can be created from a
single integer.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/lib/perf/cpumap.c | 10 ++++++++++
tools/lib/perf/include/perf/cpumap.h | 2 ++
2 files changed, 12 insertions(+)
diff --git a/tools/lib/perf/cpumap.c b/tools/lib/perf/cpumap.c
index cae799ad44e1..2c8e36d0efaa 100644
--- a/tools/lib/perf/cpumap.c
+++ b/tools/lib/perf/cpumap.c
@@ -293,6 +293,16 @@ struct perf_cpu_map *perf_cpu_map__new(const char *cpu_list)
return cpus;
}
+struct perf_cpu_map *perf_cpu_map__new_int(int cpu)
+{
+ struct perf_cpu_map *cpus = perf_cpu_map__alloc(1);
+
+ if (cpus)
+ RC_CHK_ACCESS(cpus)->map[0].cpu = cpu;
+
+ return cpus;
+}
+
static int __perf_cpu_map__nr(const struct perf_cpu_map *cpus)
{
return RC_CHK_ACCESS(cpus)->nr;
diff --git a/tools/lib/perf/include/perf/cpumap.h b/tools/lib/perf/include/perf/cpumap.h
index 90457d17fb2f..79ed3449d288 100644
--- a/tools/lib/perf/include/perf/cpumap.h
+++ b/tools/lib/perf/include/perf/cpumap.h
@@ -37,6 +37,8 @@ LIBPERF_API struct perf_cpu_map *perf_cpu_map__new_online_cpus(void);
* perf_cpu_map__new_online_cpus is returned.
*/
LIBPERF_API struct perf_cpu_map *perf_cpu_map__new(const char *cpu_list);
+/** perf_cpu_map__new_int - create a map with the one given cpu. */
+LIBPERF_API struct perf_cpu_map *perf_cpu_map__new_int(int cpu);
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,
--
2.45.2.1089.g2a221341d9-goog
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v1 5/6] perf parse-events: Set is_pmu_core for legacy hardware events
2024-07-17 22:47 [PATCH v1 0/6] Add support for sysfs event.cpus and cpu event term Ian Rogers
` (3 preceding siblings ...)
2024-07-17 22:47 ` [PATCH v1 4/6] libperf cpumap: Add ability to create CPU from a single CPU number Ian Rogers
@ 2024-07-17 22:47 ` Ian Rogers
2024-07-17 22:47 ` [PATCH v1 6/6] perf parse-events: Add "cpu" term to set the CPU an event is recorded on Ian Rogers
5 siblings, 0 replies; 7+ messages in thread
From: Ian Rogers @ 2024-07-17 22:47 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, Bjorn Helgaas,
Jonathan Corbet, James Clark, Ravi Bangoria, Dominique Martinet,
linux-kernel, linux-perf-users, Dhananjay Ugwekar, ananth.narayan,
gautham.shenoy, kprateek.nayak, sandipan.das
Also set the CPU map to all online CPU maps. This is done so the
behavior of legacy hardware and hardware cache events better matches
that of sysfs and json events during __perf_evlist__propagate_maps.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/parse-events.c | 37 +++++++++++++++++++++-------------
1 file changed, 23 insertions(+), 14 deletions(-)
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index b181f83c9678..8c0c33361c5e 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -230,21 +230,30 @@ __add_event(struct list_head *list, int *idx,
struct perf_cpu_map *cpu_list)
{
struct evsel *evsel;
- struct perf_cpu_map *cpus = perf_cpu_map__is_empty(cpu_list) && pmu ? pmu->cpus : cpu_list;
+ bool is_pmu_core;
+ struct perf_cpu_map *cpus;
- cpus = perf_cpu_map__get(cpus);
- if (pmu)
+ if (pmu) {
+ is_pmu_core = pmu->is_core;
+ cpus = perf_cpu_map__get(perf_cpu_map__is_empty(cpu_list) ? pmu->cpus : cpu_list);
perf_pmu__warn_invalid_formats(pmu);
-
- if (pmu && (attr->type == PERF_TYPE_RAW || attr->type >= PERF_TYPE_MAX)) {
- perf_pmu__warn_invalid_config(pmu, attr->config, name,
- PERF_PMU_FORMAT_VALUE_CONFIG, "config");
- perf_pmu__warn_invalid_config(pmu, attr->config1, name,
- PERF_PMU_FORMAT_VALUE_CONFIG1, "config1");
- perf_pmu__warn_invalid_config(pmu, attr->config2, name,
- PERF_PMU_FORMAT_VALUE_CONFIG2, "config2");
- perf_pmu__warn_invalid_config(pmu, attr->config3, name,
- PERF_PMU_FORMAT_VALUE_CONFIG3, "config3");
+ if (attr->type == PERF_TYPE_RAW || attr->type >= PERF_TYPE_MAX) {
+ perf_pmu__warn_invalid_config(pmu, attr->config, name,
+ PERF_PMU_FORMAT_VALUE_CONFIG, "config");
+ perf_pmu__warn_invalid_config(pmu, attr->config1, name,
+ PERF_PMU_FORMAT_VALUE_CONFIG1, "config1");
+ perf_pmu__warn_invalid_config(pmu, attr->config2, name,
+ PERF_PMU_FORMAT_VALUE_CONFIG2, "config2");
+ perf_pmu__warn_invalid_config(pmu, attr->config3, name,
+ PERF_PMU_FORMAT_VALUE_CONFIG3, "config3");
+ }
+ } else {
+ is_pmu_core = (attr->type == PERF_TYPE_HARDWARE ||
+ attr->type == PERF_TYPE_HW_CACHE);
+ if (perf_cpu_map__is_empty(cpu_list))
+ cpus = is_pmu_core ? perf_cpu_map__new_online_cpus() : NULL;
+ else
+ cpus = perf_cpu_map__get(cpu_list);
}
if (init_attr)
event_attr_init(attr);
@@ -259,7 +268,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->core.is_pmu_core = is_pmu_core;
evsel->auto_merge_stats = auto_merge_stats;
evsel->pmu = pmu;
evsel->pmu_name = pmu ? strdup(pmu->name) : NULL;
--
2.45.2.1089.g2a221341d9-goog
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v1 6/6] perf parse-events: Add "cpu" term to set the CPU an event is recorded on
2024-07-17 22:47 [PATCH v1 0/6] Add support for sysfs event.cpus and cpu event term Ian Rogers
` (4 preceding siblings ...)
2024-07-17 22:47 ` [PATCH v1 5/6] perf parse-events: Set is_pmu_core for legacy hardware events Ian Rogers
@ 2024-07-17 22:47 ` Ian Rogers
5 siblings, 0 replies; 7+ messages in thread
From: Ian Rogers @ 2024-07-17 22:47 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, Bjorn Helgaas,
Jonathan Corbet, James Clark, Ravi Bangoria, Dominique Martinet,
linux-kernel, linux-perf-users, Dhananjay Ugwekar, ananth.narayan,
gautham.shenoy, kprateek.nayak, sandipan.das
The -C option allows the CPUs for a list of events to be specified but
its not possible to set the CPU for a single event. Add a term to
allow this. The term isn't a general CPU list due to ',' already being
a special character in event parsing.
An example of mixing different types of events counted on different CPUs:
```
$ perf stat -A -C 0,4,8 -e "instructions/cpu=0/,l1d-misses/cpu=4/,inst_retired.any/cpu=8/,cycles" -a sleep 0.1
Performance counter stats for 'system wide':
CPU0 419,426 instructions/cpu=0/ # 0.25 insn per cycle
CPU4 <not counted> instructions/cpu=0/
CPU8 <not counted> instructions/cpu=0/
CPU0 <not counted> l1d-misses [cpu]
CPU4 45,574 l1d-misses [cpu]
CPU8 <not counted> l1d-misses [cpu]
CPU0 <not counted> cpu/cpu=8/
CPU4 <not counted> cpu/cpu=8/
CPU8 164,073 cpu/cpu=8/
CPU0 1,689,993 cycles
CPU4 5,204,403 cycles
CPU8 668,986 cycles
```
An example of spreading uncore overhead across two CPUs:
```
$ perf stat -A -e "data_read/cpu=0/,data_write/cpu=1/" -a sleep 0.1
Performance counter stats for 'system wide':
CPU0 223.65 MiB uncore_imc_free_running_0/cpu=0/
CPU0 223.66 MiB uncore_imc_free_running_1/cpu=0/
CPU0 <not counted> MiB uncore_imc_free_running_0/cpu=1/
CPU1 5.78 MiB uncore_imc_free_running_0/cpu=1/
CPU0 <not counted> MiB uncore_imc_free_running_1/cpu=1/
CPU1 5.74 MiB uncore_imc_free_running_1/cpu=1/
```
Note, the event names are missing as there are unmerged uniquify fixes
like:
https://lore.kernel.org/lkml/20240510053705.2462258-3-irogers@google.com/
Manually fixing the output should be:
```
CPU0 223.65 MiB uncore_imc_free_running_0/data_read,cpu=0/
CPU0 223.66 MiB uncore_imc_free_running_1/data_read,cpu=0/
CPU1 5.78 MiB uncore_imc_free_running_0/data_write,cpu=1/
CPU1 5.74 MiB uncore_imc_free_running_1/data_write,cpu=1/
```
That is data_read from 2 PMUs was read on CPU0 and data_write was read
on CPU1.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/Documentation/perf-list.txt | 7 +++
tools/perf/util/evsel_config.h | 1 +
tools/perf/util/parse-events.c | 67 ++++++++++++++++++++++----
tools/perf/util/parse-events.h | 3 +-
tools/perf/util/parse-events.l | 1 +
tools/perf/util/pmu.c | 1 +
6 files changed, 69 insertions(+), 11 deletions(-)
diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt
index 6bf2468f59d3..abbff01665eb 100644
--- a/tools/perf/Documentation/perf-list.txt
+++ b/tools/perf/Documentation/perf-list.txt
@@ -273,6 +273,13 @@ Sums up the event counts for all hardware threads in a core, e.g.:
perf stat -e cpu/event=0,umask=0x3,percore=1/
+cpu:
+
+Specifies the CPU to open the event upon:
+
+
+ perf stat -e data_read/cpu=0/,data_write/cpu=1/ -a sleep 1
+
EVENT GROUPS
------------
diff --git a/tools/perf/util/evsel_config.h b/tools/perf/util/evsel_config.h
index aee6f808b512..9630c4a24721 100644
--- a/tools/perf/util/evsel_config.h
+++ b/tools/perf/util/evsel_config.h
@@ -47,6 +47,7 @@ struct evsel_config_term {
u32 aux_sample_size;
u64 cfg_chg;
char *str;
+ int cpu;
} val;
bool weak;
};
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 8c0c33361c5e..86d074aa6c9a 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -7,6 +7,7 @@
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/param.h>
+#include "cpumap.h"
#include "term.h"
#include "evlist.h"
#include "evsel.h"
@@ -177,6 +178,20 @@ static char *get_config_name(const struct parse_events_terms *head_terms)
return get_config_str(head_terms, PARSE_EVENTS__TERM_TYPE_NAME);
}
+static struct perf_cpu_map *get_config_cpu(const struct parse_events_terms *head_terms)
+{
+ struct parse_events_term *term;
+
+ if (!head_terms)
+ return NULL;
+
+ list_for_each_entry(term, &head_terms->terms, list)
+ if (term->type_term == PARSE_EVENTS__TERM_TYPE_CPU)
+ return perf_cpu_map__new_int(term->val.num);
+
+ return NULL;
+}
+
/**
* fix_raw - For each raw term see if there is an event (aka alias) in pmu that
* matches the raw's string value. If the string value matches an
@@ -468,11 +483,12 @@ int parse_events_add_cache(struct list_head *list, int *idx, const char *name,
bool found_supported = false;
const char *config_name = get_config_name(parsed_terms);
const char *metric_id = get_config_metric_id(parsed_terms);
+ struct perf_cpu_map *cpus = get_config_cpu(parsed_terms);
+ int ret = 0;
while ((pmu = perf_pmus__scan(pmu)) != NULL) {
LIST_HEAD(config_terms);
struct perf_event_attr attr;
- int ret;
if (parse_events__filter_pmu(parse_state, pmu))
continue;
@@ -486,7 +502,7 @@ int parse_events_add_cache(struct list_head *list, int *idx, const char *name,
parsed_terms,
perf_pmu__auto_merge_stats(pmu));
if (ret)
- return ret;
+ goto out_err;
continue;
}
@@ -506,20 +522,27 @@ int parse_events_add_cache(struct list_head *list, int *idx, const char *name,
if (parsed_terms) {
if (config_attr(&attr, parsed_terms, parse_state->error,
- config_term_common))
- return -EINVAL;
-
- if (get_config_terms(parsed_terms, &config_terms))
- return -ENOMEM;
+ config_term_common)) {
+ ret = -EINVAL;
+ goto out_err;
+ }
+ if (get_config_terms(parsed_terms, &config_terms)) {
+ ret = -ENOMEM;
+ goto out_err;
+ }
}
if (__add_event(list, idx, &attr, /*init_attr*/true, config_name ?: name,
metric_id, pmu, &config_terms, /*auto_merge_stats=*/false,
- /*cpu_list=*/NULL) == NULL)
- return -ENOMEM;
+ cpus) == NULL)
+ ret = -ENOMEM;
free_config_terms(&config_terms);
+ if (ret)
+ goto out_err;
}
+out_err:
+ perf_cpu_map__put(cpus);
return found_supported ? 0 : -EINVAL;
}
@@ -814,6 +837,7 @@ static const char *config_term_name(enum parse_events__term_type term_type)
[PARSE_EVENTS__TERM_TYPE_RAW] = "raw",
[PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE] = "legacy-cache",
[PARSE_EVENTS__TERM_TYPE_HARDWARE] = "hardware",
+ [PARSE_EVENTS__TERM_TYPE_CPU] = "cpu",
};
if ((unsigned int)term_type >= __PARSE_EVENTS__TERM_TYPE_NR)
return "unknown term";
@@ -843,6 +867,7 @@ config_term_avail(enum parse_events__term_type term_type, struct parse_events_er
case PARSE_EVENTS__TERM_TYPE_METRIC_ID:
case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
case PARSE_EVENTS__TERM_TYPE_PERCORE:
+ case PARSE_EVENTS__TERM_TYPE_CPU:
return true;
case PARSE_EVENTS__TERM_TYPE_USER:
case PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ:
@@ -986,6 +1011,15 @@ do { \
return -EINVAL;
}
break;
+ case PARSE_EVENTS__TERM_TYPE_CPU:
+ CHECK_TYPE_VAL(NUM);
+ if (term->val.num >= (u64)cpu__max_present_cpu().cpu) {
+ parse_events_error__handle(err, term->err_val,
+ strdup("too big"),
+ NULL);
+ return -EINVAL;
+ }
+ break;
case PARSE_EVENTS__TERM_TYPE_DRV_CFG:
case PARSE_EVENTS__TERM_TYPE_USER:
case PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE:
@@ -1112,6 +1146,7 @@ static int config_term_tracepoint(struct perf_event_attr *attr,
case PARSE_EVENTS__TERM_TYPE_RAW:
case PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE:
case PARSE_EVENTS__TERM_TYPE_HARDWARE:
+ case PARSE_EVENTS__TERM_TYPE_CPU:
default:
if (err) {
parse_events_error__handle(err, term->err_term,
@@ -1243,6 +1278,7 @@ do { \
case PARSE_EVENTS__TERM_TYPE_RAW:
case PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE:
case PARSE_EVENTS__TERM_TYPE_HARDWARE:
+ case PARSE_EVENTS__TERM_TYPE_CPU:
default:
break;
}
@@ -1296,6 +1332,7 @@ static int get_config_chgs(struct perf_pmu *pmu, struct parse_events_terms *head
case PARSE_EVENTS__TERM_TYPE_RAW:
case PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE:
case PARSE_EVENTS__TERM_TYPE_HARDWARE:
+ case PARSE_EVENTS__TERM_TYPE_CPU:
default:
break;
}
@@ -1350,6 +1387,7 @@ static int __parse_events_add_numeric(struct parse_events_state *parse_state,
struct perf_event_attr attr;
LIST_HEAD(config_terms);
const char *name, *metric_id;
+ struct perf_cpu_map *cpus;
int ret;
memset(&attr, 0, sizeof(attr));
@@ -1371,9 +1409,11 @@ static int __parse_events_add_numeric(struct parse_events_state *parse_state,
name = get_config_name(head_config);
metric_id = get_config_metric_id(head_config);
+ cpus = get_config_cpu(head_config);
ret = __add_event(list, &parse_state->idx, &attr, /*init_attr*/true, name,
metric_id, pmu, &config_terms, /*auto_merge_stats=*/false,
- /*cpu_list=*/NULL) ? 0 : -ENOMEM;
+ cpus) ? 0 : -ENOMEM;
+ perf_cpu_map__put(cpus);
free_config_terms(&config_terms);
return ret;
}
@@ -1440,6 +1480,7 @@ static int parse_events_add_pmu(struct parse_events_state *parse_state,
LIST_HEAD(config_terms);
struct parse_events_terms parsed_terms;
bool alias_rewrote_terms = false;
+ struct perf_cpu_map *term_cpu = NULL;
int ret = 0;
if (verbose > 1) {
@@ -1531,6 +1572,12 @@ static int parse_events_add_pmu(struct parse_events_state *parse_state,
goto out_err;
}
+ term_cpu = get_config_cpu(&parsed_terms);
+ if (!perf_cpu_map__is_empty(term_cpu)) {
+ perf_cpu_map__put(info.cpus);
+ info.cpus = term_cpu;
+ term_cpu = NULL;
+ }
evsel = __add_event(list, &parse_state->idx, &attr, /*init_attr=*/true,
get_config_name(&parsed_terms),
get_config_metric_id(&parsed_terms), pmu,
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index e13de2c8b706..b03857499030 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -79,7 +79,8 @@ enum parse_events__term_type {
PARSE_EVENTS__TERM_TYPE_RAW,
PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE,
PARSE_EVENTS__TERM_TYPE_HARDWARE,
-#define __PARSE_EVENTS__TERM_TYPE_NR (PARSE_EVENTS__TERM_TYPE_HARDWARE + 1)
+ PARSE_EVENTS__TERM_TYPE_CPU,
+#define __PARSE_EVENTS__TERM_TYPE_NR (PARSE_EVENTS__TERM_TYPE_CPU + 1)
};
struct parse_events_term {
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
index 16045c383ada..e06097a62796 100644
--- a/tools/perf/util/parse-events.l
+++ b/tools/perf/util/parse-events.l
@@ -330,6 +330,7 @@ percore { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_PERCORE); }
aux-output { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT); }
aux-sample-size { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE); }
metric-id { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_METRIC_ID); }
+cpu { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CPU); }
cpu-cycles|cycles { return hw_term(yyscanner, PERF_COUNT_HW_CPU_CYCLES); }
stalled-cycles-frontend|idle-cycles-frontend { return hw_term(yyscanner, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); }
stalled-cycles-backend|idle-cycles-backend { return hw_term(yyscanner, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); }
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 280b2499c861..27e2ff23799e 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -1767,6 +1767,7 @@ int perf_pmu__for_each_format(struct perf_pmu *pmu, void *state, pmu_format_call
"percore",
"aux-output",
"aux-sample-size=number",
+ "cpu=number",
};
struct perf_pmu_format *format;
int ret;
--
2.45.2.1089.g2a221341d9-goog
^ permalink raw reply related [flat|nested] 7+ messages in thread