Re: [RFC][PATCH 4/4] perf: Create aliases for PMU events
From: Vineet Gupta
Date: Sat May 02 2015 - 03:04:29 EST
On Friday 01 May 2015 12:35 PM, Sukadev Bhattiprolu wrote:
> Using the tables of Power7 and Power8 events, create aliases for the
> Power PMU events. This would allow us to specify all Power events by
> name rather than by raw code:
>
> $ /tmp/perf stat -e PM_1PLUS_PPC_CMPL sleep 1
>
> Performance counter stats for 'sleep 1':
>
> 757,661 PM_1PLUS_PPC_CMPL
>
> 1.001620145 seconds time elapsed
>
> The perf binary built on Power8 can be copied to Power7 and it will use
> the Power7 events (if arch/powerpc/util/pmu-events.h knows the CPU string).
>
> Hopefully other architecutres can also implement arch_get_events_table()
> and take advantage of this.
>
> Signed-off-by: Sukadev Bhattiprolu <sukadev@xxxxxxxxxxxxxxxxxx>
> ---
> tools/perf/arch/powerpc/util/Build | 2 +-
> tools/perf/arch/powerpc/util/pmu-events.c | 52 +++++++++++++++++++
> tools/perf/arch/powerpc/util/pmu-events.h | 17 +++++++
> tools/perf/util/pmu.c | 77 +++++++++++++++++++++++++++++
> tools/perf/util/pmu.h | 10 ++++
> 5 files changed, 157 insertions(+), 1 deletion(-)
> create mode 100644 tools/perf/arch/powerpc/util/pmu-events.c
> create mode 100644 tools/perf/arch/powerpc/util/pmu-events.h
>
> diff --git a/tools/perf/arch/powerpc/util/Build b/tools/perf/arch/powerpc/util/Build
> index 0af6e9b..52fbc7f 100644
> --- a/tools/perf/arch/powerpc/util/Build
> +++ b/tools/perf/arch/powerpc/util/Build
> @@ -1,4 +1,4 @@
> -libperf-y += header.o
> +libperf-y += header.o pmu-events.o
>
> libperf-$(CONFIG_DWARF) += dwarf-regs.o
> libperf-$(CONFIG_DWARF) += skip-callchain-idx.o
> diff --git a/tools/perf/arch/powerpc/util/pmu-events.c b/tools/perf/arch/powerpc/util/pmu-events.c
> new file mode 100644
> index 0000000..7036f6d
> --- /dev/null
> +++ b/tools/perf/arch/powerpc/util/pmu-events.c
> @@ -0,0 +1,52 @@
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <sys/types.h>
> +#include "pmu.h"
> +#include "pmu-events.h"
> +#include "../../util/debug.h" /* verbose */
> +#include "header.h" /* mfspr */
> +
> +static char *get_cpu_str(void)
> +{
> + char *bufp;
> +
> + if (asprintf(&bufp, "%.8lx-core", mfspr(SPRN_PVR)) < 0)
> + bufp = NULL;
> +
> + return bufp;
> +}
> +
> +struct perf_pmu_event *arch_get_events_table(char *cpustr)
> +{
> + int i, nmaps, must_free;
> + struct perf_pmu_event *table;
> +
> + must_free = 0;
> + if (!cpustr) {
> + cpustr = get_cpu_str();
> + if (!cpustr)
> + return NULL;
> + must_free = 1;
> + }
> +
> + nmaps = sizeof(pvr_events_map) / sizeof(struct pvr_events_map_entry);
> +
> + for (i = 0; i < nmaps; i++) {
> + if (!strcmp(pvr_events_map[i].pvr, cpustr))
> + break;
> + }
> +
> + table = NULL;
> + if (i < nmaps) {
> + /* pvr_events_map is a const; cast to override */
> + table = (struct perf_pmu_event *)pvr_events_map[i].pmu_events;
> + } else if (verbose) {
> + printf("Unknown CPU %s, ignoring aliases\n", cpustr);
> + }
> +
> + if (must_free)
> + free(cpustr);
> +
> + return table;
> +}
> +
> diff --git a/tools/perf/arch/powerpc/util/pmu-events.h b/tools/perf/arch/powerpc/util/pmu-events.h
> new file mode 100644
> index 0000000..1daf8e5
> --- /dev/null
> +++ b/tools/perf/arch/powerpc/util/pmu-events.h
> @@ -0,0 +1,17 @@
> +/*
> + * Include all Power processor tables that we care about.
> + */
> +#include "power7-events.h"
> +#include "power8-events.h"
> +
> +/*
> + * Map a processor version (PVR) to its table of events.
> + */
> +struct pvr_events_map_entry {
> + const char *pvr;
> + const struct perf_pmu_event *pmu_events;
> +} pvr_events_map[] = {
> + { .pvr = "004d0100-core", .pmu_events = power8_pmu_events },
> + { .pvr = "003f0201-core", .pmu_events = power7_pmu_events }
> +};
Do u really need the header - this could go in the .c file ?
> +
> diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
> index 4841167..f998d91 100644
> --- a/tools/perf/util/pmu.c
> +++ b/tools/perf/util/pmu.c
> @@ -435,6 +435,80 @@ perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
> return NULL;
> }
>
> +/*
> + * Default arch_get_events_table() is empty.
> + *
> + * Actual implementation is in arch/$(ARCH)/util/pmu-events.c. This
> + * allows architectures could choose what set(s) of events to a) include
> + * in perf binary b) consider for _this_ invocation of perf.
> + *
> + * Eg: For Power, we include both Power7 and Power8 event tables in the
> + * perf binary. But depending on the processor where perf is executed,
> + * either the Power7 or Power8 table is returned.
> + */
> +struct perf_pmu_event * __attribute__ ((weak))
> +arch_get_events_table(char *cpustr __maybe_unused)
> +{
> + return NULL;
> +}
> +
> +static int pmu_add_cpu_aliases(char *cpustr, void *data)
> +{
> + struct list_head *head = (struct list_head *)data;
> + struct perf_pmu_alias *alias;
> + int i;
> + struct perf_pmu_event *events_table, *event;
> + struct parse_events_term *term;
> +
> + events_table = arch_get_events_table(cpustr);
> + if (!events_table)
> + return 0;
> +
> + for (i = 0; events_table[i].name != NULL; i++) {
> + event = &events_table[i];
> +
> + alias = malloc(sizeof(*alias));
> + if (!alias)
> + return -ENOMEM;
> +
> + term = malloc(sizeof(*term));
> + if (!term) {
> + /*
> + * TODO: cleanup aliases allocated so far?
> + */
> + free(alias);
> + return -ENOMEM;
> + }
> +
> + /* ->config is not const; cast to override */
> + term->config = (char *)"event";
> + term->val.num = event->code;
> + term->type_val = PARSE_EVENTS__TERM_TYPE_NUM;
> + term->type_term = PARSE_EVENTS__TERM_TYPE_USER;
> + INIT_LIST_HEAD(&term->list);
> + term->used = 0;
> +
> + INIT_LIST_HEAD(&alias->terms);
> + list_add_tail(&alias->terms, &term->list);
> +
> + alias->scale = 1.0;
> + alias->unit[0] = '\0';
> + alias->per_pkg = false;
> +
> + alias->name = strdup(event->name);
> +#if 0
> + /*
> + * TODO: Need Andi Kleen's patch for ->desc
> + */
> + alias->desc = event->short_desc ?
> + strdup(event->short_desc) : NULL;
> +#endif
> + list_add_tail(&alias->list, head);
> + }
> +
> + return 0;
> +}
> +
> static struct perf_pmu *pmu_lookup(const char *name)
> {
> struct perf_pmu *pmu;
> @@ -453,6 +527,9 @@ static struct perf_pmu *pmu_lookup(const char *name)
> if (pmu_aliases(name, &aliases))
> return NULL;
>
> + if (!strcmp(name, "cpu"))
> + (void)pmu_add_cpu_aliases(NULL, &aliases);
> +
> if (pmu_type(name, &type))
> return NULL;
>
> diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
> index 6b1249f..ca3e7a0 100644
> --- a/tools/perf/util/pmu.h
> +++ b/tools/perf/util/pmu.h
> @@ -45,6 +45,14 @@ struct perf_pmu_alias {
> bool snapshot;
> };
>
> +struct perf_pmu_event {
> + const char *name;
> + const unsigned long code;
> + const char *short_desc;
> + const char *long_desc;
> + /* add unit, mask etc as needed here */
> +};
> +
> struct perf_pmu *perf_pmu__find(const char *name);
> int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
> struct list_head *head_terms);
> @@ -76,4 +84,6 @@ int perf_pmu__test(void);
>
> struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu);
>
> +struct perf_pmu_event *arch_get_events_table(char *cpustr);
> +
> #endif /* __PMU_H */
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/