Message ID | 20171017132909.23401-1-mark.rutland@arm.com |
---|---|
State | Superseded |
Headers | show |
Series | [v4.13.y] perf pmu: Unbreak perf record for arm/arm64 with events with explicit PMU | expand |
Sorry all, I messed up, and this was generated from my v4.12 backports branch. There's a small difference in the context, so I will send the correct patch shortly. Sorry for the noise. Thanks, Mark. On Tue, Oct 17, 2017 at 02:29:09PM +0100, Mark Rutland wrote: > Commit 66ec11919a0f96e936bb731fdbc2851316077d26 upstream. > > Currently, perf record is broken on arm/arm64 systems when the PMU is > specified explicitly as part of the event, e.g. > > $ ./perf record -e armv8_cortex_a53/cpu_cycles/u true > > In such cases, perf record fails to open events unless > perf_event_paranoid is set to -1, even if the PMU in question supports > mode exclusion. Further, even when perf_event_paranoid is toggled, no > samples are recorded. > > This is an unintended side effect of commit: > > e3ba76deef23064f ("perf tools: Force uncore events to system wide monitoring) > > ... which assumes that if a PMU has an associated cpu_map, it is an > uncore PMU, and forces events for such PMUs to be system-wide. > > This is not true for arm/arm64 systems, which can have heterogeneous > CPUs. To account for this, multiple CPU PMUs are exposed, each with a > "cpus" field under sysfs, which the perf tool parses into a cpu_map. ARM > PMUs do not have a "cpumask" file, and only have a "cpus" file. For the > gory details as to why, see commit: > > 7e3fcffe95544010 ("perf pmu: Support alternative sysfs cpumask") > > Given all of this, we can instead identify uncore PMUs by explicitly > checking for a "cpumask" file, and restore arm/arm64 PMU support back to > a working state. This patch does so, adding a new perf_pmu::is_uncore > field, and splitting the existing cpumask parsing so that it can be > reused. > > Signed-off-by: Mark Rutland <mark.rutland@arm.com> > Tested-by Will Deacon <will.deacon@arm.com> > Acked-by: Jiri Olsa <jolsa@kernel.org> > Cc: Adrian Hunter <adrian.hunter@intel.com> > Cc: Borislav Petkov <bp@alien8.de> > Cc: David Ahern <dsahern@gmail.com> > Cc: Namhyung Kim <namhyung@kernel.org> > Cc: Peter Zijlstra <peterz@infradead.org> > Cc: 4.12+ <stable@vger.kernel.org> > Fixes: e3ba76deef23064f ("perf tools: Force uncore events to system wide monitoring) > Link: http://lkml.kernel.org/r/1507315102-5942-1-git-send-email-mark.rutland@arm.com > Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> > --- > tools/perf/util/parse-events.c | 9 ++++--- > tools/perf/util/pmu.c | 56 +++++++++++++++++++++++++++++++----------- > tools/perf/util/pmu.h | 1 + > 3 files changed, 47 insertions(+), 19 deletions(-) > > diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c > index 01e779b91c8e..2e3ffc3bc483 100644 > --- a/tools/perf/util/parse-events.c > +++ b/tools/perf/util/parse-events.c > @@ -309,10 +309,11 @@ static char *get_config_name(struct list_head *head_terms) > static struct perf_evsel * > __add_event(struct list_head *list, int *idx, > struct perf_event_attr *attr, > - char *name, struct cpu_map *cpus, > + char *name, struct perf_pmu *pmu, > struct list_head *config_terms) > { > struct perf_evsel *evsel; > + struct cpu_map *cpus = pmu ? pmu->cpus : NULL; > > event_attr_init(attr); > > @@ -323,7 +324,7 @@ __add_event(struct list_head *list, int *idx, > (*idx)++; > evsel->cpus = cpu_map__get(cpus); > evsel->own_cpus = cpu_map__get(cpus); > - evsel->system_wide = !!cpus; > + evsel->system_wide = pmu ? pmu->is_uncore : false; > > if (name) > evsel->name = strdup(name); > @@ -1232,7 +1233,7 @@ int parse_events_add_pmu(struct parse_events_evlist *data, > > if (!head_config) { > attr.type = pmu->type; > - evsel = __add_event(list, &data->idx, &attr, NULL, pmu->cpus, NULL); > + evsel = __add_event(list, &data->idx, &attr, NULL, pmu, NULL); > return evsel ? 0 : -ENOMEM; > } > > @@ -1253,7 +1254,7 @@ int parse_events_add_pmu(struct parse_events_evlist *data, > return -EINVAL; > > evsel = __add_event(list, &data->idx, &attr, > - get_config_name(head_config), pmu->cpus, > + get_config_name(head_config), pmu, > &config_terms); > if (evsel) { > evsel->unit = info.unit; > diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c > index ac16a9db1fb5..1c4d7b4e4fb5 100644 > --- a/tools/perf/util/pmu.c > +++ b/tools/perf/util/pmu.c > @@ -470,17 +470,36 @@ static void pmu_read_sysfs(void) > closedir(dir); > } > > +static struct cpu_map *__pmu_cpumask(const char *path) > +{ > + FILE *file; > + struct cpu_map *cpus; > + > + file = fopen(path, "r"); > + if (!file) > + return NULL; > + > + cpus = cpu_map__read(file); > + fclose(file); > + return cpus; > +} > + > +/* > + * Uncore PMUs have a "cpumask" file under sysfs. CPU PMUs (e.g. on arm/arm64) > + * may have a "cpus" file. > + */ > +#define CPUS_TEMPLATE_UNCORE "%s/bus/event_source/devices/%s/cpumask" > +#define CPUS_TEMPLATE_CPU "%s/bus/event_source/devices/%s/cpus" > + > static struct cpu_map *pmu_cpumask(const char *name) > { > - struct stat st; > char path[PATH_MAX]; > - FILE *file; > struct cpu_map *cpus; > const char *sysfs = sysfs__mountpoint(); > const char *templates[] = { > - "%s/bus/event_source/devices/%s/cpumask", > - "%s/bus/event_source/devices/%s/cpus", > - NULL > + CPUS_TEMPLATE_UNCORE, > + CPUS_TEMPLATE_CPU, > + NULL > }; > const char **template; > > @@ -489,20 +508,25 @@ static struct cpu_map *pmu_cpumask(const char *name) > > for (template = templates; *template; template++) { > snprintf(path, PATH_MAX, *template, sysfs, name); > - if (stat(path, &st) == 0) > - break; > + cpus = __pmu_cpumask(path); > + if (cpus) > + return cpus; > } > > - if (!*template) > - return NULL; > + return NULL; > +} > > - file = fopen(path, "r"); > - if (!file) > - return NULL; > +static bool pmu_is_uncore(const char *name) > +{ > + char path[PATH_MAX]; > + struct cpu_map *cpus; > + const char *sysfs = sysfs__mountpoint(); > > - cpus = cpu_map__read(file); > - fclose(file); > - return cpus; > + snprintf(path, PATH_MAX, CPUS_TEMPLATE_UNCORE, sysfs, name); > + cpus = __pmu_cpumask(path); > + cpu_map__put(cpus); > + > + return !!cpus; > } > > /* > @@ -617,6 +641,8 @@ static struct perf_pmu *pmu_lookup(const char *name) > > pmu->cpus = pmu_cpumask(name); > > + pmu->is_uncore = pmu_is_uncore(name); > + > INIT_LIST_HEAD(&pmu->format); > INIT_LIST_HEAD(&pmu->aliases); > list_splice(&format, &pmu->format); > diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h > index ea7f450dc609..50abef2eb689 100644 > --- a/tools/perf/util/pmu.h > +++ b/tools/perf/util/pmu.h > @@ -21,6 +21,7 @@ struct perf_pmu { > char *name; > __u32 type; > bool selectable; > + bool is_uncore; > struct perf_event_attr *default_config; > struct cpu_map *cpus; > struct list_head format; /* HEAD struct perf_pmu_format -> list */ > -- > 2.11.0 >
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 01e779b91c8e..2e3ffc3bc483 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -309,10 +309,11 @@ static char *get_config_name(struct list_head *head_terms) static struct perf_evsel * __add_event(struct list_head *list, int *idx, struct perf_event_attr *attr, - char *name, struct cpu_map *cpus, + char *name, struct perf_pmu *pmu, struct list_head *config_terms) { struct perf_evsel *evsel; + struct cpu_map *cpus = pmu ? pmu->cpus : NULL; event_attr_init(attr); @@ -323,7 +324,7 @@ __add_event(struct list_head *list, int *idx, (*idx)++; evsel->cpus = cpu_map__get(cpus); evsel->own_cpus = cpu_map__get(cpus); - evsel->system_wide = !!cpus; + evsel->system_wide = pmu ? pmu->is_uncore : false; if (name) evsel->name = strdup(name); @@ -1232,7 +1233,7 @@ int parse_events_add_pmu(struct parse_events_evlist *data, if (!head_config) { attr.type = pmu->type; - evsel = __add_event(list, &data->idx, &attr, NULL, pmu->cpus, NULL); + evsel = __add_event(list, &data->idx, &attr, NULL, pmu, NULL); return evsel ? 0 : -ENOMEM; } @@ -1253,7 +1254,7 @@ int parse_events_add_pmu(struct parse_events_evlist *data, return -EINVAL; evsel = __add_event(list, &data->idx, &attr, - get_config_name(head_config), pmu->cpus, + get_config_name(head_config), pmu, &config_terms); if (evsel) { evsel->unit = info.unit; diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index ac16a9db1fb5..1c4d7b4e4fb5 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -470,17 +470,36 @@ static void pmu_read_sysfs(void) closedir(dir); } +static struct cpu_map *__pmu_cpumask(const char *path) +{ + FILE *file; + struct cpu_map *cpus; + + file = fopen(path, "r"); + if (!file) + return NULL; + + cpus = cpu_map__read(file); + fclose(file); + return cpus; +} + +/* + * Uncore PMUs have a "cpumask" file under sysfs. CPU PMUs (e.g. on arm/arm64) + * may have a "cpus" file. + */ +#define CPUS_TEMPLATE_UNCORE "%s/bus/event_source/devices/%s/cpumask" +#define CPUS_TEMPLATE_CPU "%s/bus/event_source/devices/%s/cpus" + static struct cpu_map *pmu_cpumask(const char *name) { - struct stat st; char path[PATH_MAX]; - FILE *file; struct cpu_map *cpus; const char *sysfs = sysfs__mountpoint(); const char *templates[] = { - "%s/bus/event_source/devices/%s/cpumask", - "%s/bus/event_source/devices/%s/cpus", - NULL + CPUS_TEMPLATE_UNCORE, + CPUS_TEMPLATE_CPU, + NULL }; const char **template; @@ -489,20 +508,25 @@ static struct cpu_map *pmu_cpumask(const char *name) for (template = templates; *template; template++) { snprintf(path, PATH_MAX, *template, sysfs, name); - if (stat(path, &st) == 0) - break; + cpus = __pmu_cpumask(path); + if (cpus) + return cpus; } - if (!*template) - return NULL; + return NULL; +} - file = fopen(path, "r"); - if (!file) - return NULL; +static bool pmu_is_uncore(const char *name) +{ + char path[PATH_MAX]; + struct cpu_map *cpus; + const char *sysfs = sysfs__mountpoint(); - cpus = cpu_map__read(file); - fclose(file); - return cpus; + snprintf(path, PATH_MAX, CPUS_TEMPLATE_UNCORE, sysfs, name); + cpus = __pmu_cpumask(path); + cpu_map__put(cpus); + + return !!cpus; } /* @@ -617,6 +641,8 @@ static struct perf_pmu *pmu_lookup(const char *name) pmu->cpus = pmu_cpumask(name); + pmu->is_uncore = pmu_is_uncore(name); + INIT_LIST_HEAD(&pmu->format); INIT_LIST_HEAD(&pmu->aliases); list_splice(&format, &pmu->format); diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index ea7f450dc609..50abef2eb689 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -21,6 +21,7 @@ struct perf_pmu { char *name; __u32 type; bool selectable; + bool is_uncore; struct perf_event_attr *default_config; struct cpu_map *cpus; struct list_head format; /* HEAD struct perf_pmu_format -> list */