perf pmu-events: Add extra underscore to function names
[platform/kernel/linux-starfive.git] / tools / perf / util / pmu.c
index 7f984a7..d913671 100644 (file)
@@ -19,8 +19,8 @@
 #include "evsel.h"
 #include "pmu.h"
 #include "pmus.h"
-#include "pmu-bison.h"
-#include "pmu-flex.h"
+#include <util/pmu-bison.h>
+#include <util/pmu-flex.h>
 #include "parse-events.h"
 #include "print-events.h"
 #include "header.h"
 
 struct perf_pmu perf_pmu__fake;
 
+#define UNIT_MAX_LEN   31 /* max length for event unit name */
+
+/**
+ * struct perf_pmu_alias - An event either read from sysfs or builtin in
+ * pmu-events.c, created by parsing the pmu-events json files.
+ */
+struct perf_pmu_alias {
+       /** @name: Name of the event like "mem-loads". */
+       char *name;
+       /** @desc: Optional short description of the event. */
+       char *desc;
+       /** @long_desc: Optional long description. */
+       char *long_desc;
+       /**
+        * @topic: Optional topic such as cache or pipeline, particularly for
+        * json events.
+        */
+       char *topic;
+       /**
+        * @str: Comma separated parameter list like
+        * "event=0xcd,umask=0x1,ldlat=0x3".
+        */
+       char *str;
+       /** @terms: Owned list of the original parsed parameters. */
+       struct list_head terms;
+       /** @list: List element of struct perf_pmu aliases. */
+       struct list_head list;
+       /** @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. */
+       double scale;
+       /**
+        * @per_pkg: Does the file
+        * <sysfs>/bus/event_source/devices/<pmu_name>/events/<name>.per-pkg or
+        * equivalent json value exist and have the value 1.
+        */
+       bool per_pkg;
+       /**
+        * @snapshot: Does the file
+        * <sysfs>/bus/event_source/devices/<pmu_name>/events/<name>.snapshot
+        * exist and have the value 1.
+        */
+       bool snapshot;
+       /**
+        * @deprecated: Is the event hidden and so not shown in perf list by
+        * default.
+        */
+       bool deprecated;
+       /**
+        * @pmu_name: The name copied from the json struct pmu_event. This can
+        * differ from the PMU name as it won't have suffixes.
+        */
+       char *pmu_name;
+};
+
 /**
  * struct perf_pmu_format - Values from a format file read from
  * <sysfs>/devices/cpu/format/ held in struct perf_pmu.
@@ -40,6 +95,10 @@ struct perf_pmu perf_pmu__fake;
  * value=PERF_PMU_FORMAT_VALUE_CONFIG and bits 0 to 7 will be set.
  */
 struct perf_pmu_format {
+       /** @list: Element on list within struct perf_pmu. */
+       struct list_head list;
+       /** @bits: Which config bits are set by this format value. */
+       DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS);
        /** @name: The modifier/file name. */
        char *name;
        /**
@@ -47,18 +106,79 @@ struct perf_pmu_format {
         * are from PERF_PMU_FORMAT_VALUE_CONFIG to
         * PERF_PMU_FORMAT_VALUE_CONFIG_END.
         */
-       int value;
-       /** @bits: Which config bits are set by this format value. */
-       DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS);
-       /** @list: Element on list within struct perf_pmu. */
-       struct list_head list;
+       u16 value;
+       /** @loaded: Has the contents been loaded/parsed. */
+       bool loaded;
 };
 
+static struct perf_pmu_format *perf_pmu__new_format(struct list_head *list, char *name)
+{
+       struct perf_pmu_format *format;
+
+       format = zalloc(sizeof(*format));
+       if (!format)
+               return NULL;
+
+       format->name = strdup(name);
+       if (!format->name) {
+               free(format);
+               return NULL;
+       }
+       list_add_tail(&format->list, list);
+       return format;
+}
+
+/* Called at the end of parsing a format. */
+void perf_pmu_format__set_value(void *vformat, int config, unsigned long *bits)
+{
+       struct perf_pmu_format *format = vformat;
+
+       format->value = config;
+       memcpy(format->bits, bits, sizeof(format->bits));
+}
+
+static void __perf_pmu_format__load(struct perf_pmu_format *format, FILE *file)
+{
+       void *scanner;
+       int ret;
+
+       ret = perf_pmu_lex_init(&scanner);
+       if (ret)
+               return;
+
+       perf_pmu_set_in(file, scanner);
+       ret = perf_pmu_parse(format, scanner);
+       perf_pmu_lex_destroy(scanner);
+       format->loaded = true;
+}
+
+static void perf_pmu_format__load(struct perf_pmu *pmu, struct perf_pmu_format *format)
+{
+       char path[PATH_MAX];
+       FILE *file = NULL;
+
+       if (format->loaded)
+               return;
+
+       if (!perf_pmu__pathname_scnprintf(path, sizeof(path), pmu->name, "format"))
+               return;
+
+       assert(strlen(path) + strlen(format->name) + 2 < sizeof(path));
+       strcat(path, "/");
+       strcat(path, format->name);
+
+       file = fopen(path, "r");
+       if (!file)
+               return;
+       __perf_pmu_format__load(format, file);
+       fclose(file);
+}
+
 /*
  * Parse & process all the sysfs attributes located under
  * the directory specified in 'dir' parameter.
  */
-int perf_pmu__format_parse(int dirfd, struct list_head *head)
+int perf_pmu__format_parse(struct perf_pmu *pmu, int dirfd, bool eager_load)
 {
        struct dirent *evt_ent;
        DIR *format_dir;
@@ -68,37 +188,35 @@ int perf_pmu__format_parse(int dirfd, struct list_head *head)
        if (!format_dir)
                return -EINVAL;
 
-       while (!ret && (evt_ent = readdir(format_dir))) {
+       while ((evt_ent = readdir(format_dir)) != NULL) {
+               struct perf_pmu_format *format;
                char *name = evt_ent->d_name;
-               int fd;
-               void *scanner;
-               FILE *file;
 
                if (!strcmp(name, ".") || !strcmp(name, ".."))
                        continue;
 
-
-               ret = -EINVAL;
-               fd = openat(dirfd, name, O_RDONLY);
-               if (fd < 0)
-                       break;
-
-               file = fdopen(fd, "r");
-               if (!file) {
-                       close(fd);
+               format = perf_pmu__new_format(&pmu->format, name);
+               if (!format) {
+                       ret = -ENOMEM;
                        break;
                }
 
-               ret = perf_pmu_lex_init(&scanner);
-               if (ret) {
+               if (eager_load) {
+                       FILE *file;
+                       int fd = openat(dirfd, name, O_RDONLY);
+
+                       if (fd < 0) {
+                               ret = -errno;
+                               break;
+                       }
+                       file = fdopen(fd, "r");
+                       if (!file) {
+                               close(fd);
+                               break;
+                       }
+                       __perf_pmu_format__load(format, file);
                        fclose(file);
-                       break;
                }
-
-               perf_pmu_set_in(file, scanner);
-               ret = perf_pmu_parse(head, name, scanner);
-               perf_pmu_lex_destroy(scanner);
-               fclose(file);
        }
 
        closedir(format_dir);
@@ -110,7 +228,7 @@ int perf_pmu__format_parse(int dirfd, struct list_head *head)
  * located at:
  * /sys/bus/event_source/devices/<dev>/format as sysfs group attributes.
  */
-static int pmu_format(int dirfd, const char *name, struct list_head *format)
+static int pmu_format(struct perf_pmu *pmu, int dirfd, const char *name)
 {
        int fd;
 
@@ -119,7 +237,7 @@ static int pmu_format(int dirfd, const char *name, struct list_head *format)
                return 0;
 
        /* it'll close the fd */
-       if (perf_pmu__format_parse(fd, format))
+       if (perf_pmu__format_parse(pmu, fd, /*eager_load=*/false))
                return -1;
 
        return 0;
@@ -292,7 +410,7 @@ static void perf_pmu_update_alias(struct perf_pmu_alias *old,
 }
 
 /* Delete an alias entry. */
-void perf_pmu_free_alias(struct perf_pmu_alias *newalias)
+static void perf_pmu_free_alias(struct perf_pmu_alias *newalias)
 {
        zfree(&newalias->name);
        zfree(&newalias->desc);
@@ -508,7 +626,7 @@ static int pmu_aliases_parse(int dirfd, struct list_head *head)
  * Reading the pmu event aliases definition, which should be located at:
  * /sys/bus/event_source/devices/<dev>/events as sysfs group attributes.
  */
-static int pmu_aliases(int dirfd, const char *name, struct list_head *head)
+static int pmu_aliases(struct perf_pmu *pmu, int dirfd, const char *name)
 {
        int fd;
 
@@ -517,7 +635,7 @@ static int pmu_aliases(int dirfd, const char *name, struct list_head *head)
                return 0;
 
        /* it'll close the fd */
-       if (pmu_aliases_parse(fd, head))
+       if (pmu_aliases_parse(fd, &pmu->aliases))
                return -1;
 
        return 0;
@@ -770,20 +888,19 @@ static int pmu_add_cpu_aliases_map_callback(const struct pmu_event *pe,
  * From the pmu_events_table, find the events that correspond to the given
  * PMU and add them to the list 'head'.
  */
-void pmu_add_cpu_aliases_table(struct list_head *head, struct perf_pmu *pmu,
-                       const struct pmu_events_table *table)
+void pmu_add_cpu_aliases_table(struct perf_pmu *pmu, const struct pmu_events_table *table)
 {
        struct pmu_add_cpu_aliases_map_data data = {
-               .head = head,
+               .head = &pmu->aliases,
                .default_pmu_name = perf_pmus__default_pmu_name(),
                .pmu = pmu,
        };
 
-       pmu_events_table_for_each_event(table, pmu_add_cpu_aliases_map_callback, &data);
+       pmu_events_table__for_each_event(table, pmu_add_cpu_aliases_map_callback, &data);
        free(data.default_pmu_name);
 }
 
-static void pmu_add_cpu_aliases(struct list_head *head, struct perf_pmu *pmu)
+static void pmu_add_cpu_aliases(struct perf_pmu *pmu)
 {
        const struct pmu_events_table *table;
 
@@ -791,7 +908,7 @@ static void pmu_add_cpu_aliases(struct list_head *head, struct perf_pmu *pmu)
        if (!table)
                return;
 
-       pmu_add_cpu_aliases_table(head, pmu, table);
+       pmu_add_cpu_aliases_table(pmu, table);
 }
 
 struct pmu_sys_event_iter_data {
@@ -821,10 +938,10 @@ static int pmu_add_sys_aliases_iter_fn(const struct pmu_event *pe,
        return 0;
 }
 
-void pmu_add_sys_aliases(struct list_head *head, struct perf_pmu *pmu)
+void pmu_add_sys_aliases(struct perf_pmu *pmu)
 {
        struct pmu_sys_event_iter_data idata = {
-               .head = head,
+               .head = &pmu->aliases,
                .pmu = pmu,
        };
 
@@ -863,30 +980,33 @@ static int pmu_max_precise(int dirfd, struct perf_pmu *pmu)
 struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char *lookup_name)
 {
        struct perf_pmu *pmu;
-       LIST_HEAD(format);
-       LIST_HEAD(aliases);
        __u32 type;
        char *name = pmu_find_real_name(lookup_name);
        char *alias_name;
 
+       pmu = zalloc(sizeof(*pmu));
+       if (!pmu)
+               return NULL;
+
+       INIT_LIST_HEAD(&pmu->format);
+       INIT_LIST_HEAD(&pmu->aliases);
+       INIT_LIST_HEAD(&pmu->caps);
        /*
         * The pmu data we store & need consists of the pmu
         * type value and format definitions. Load both right
         * now.
         */
-       if (pmu_format(dirfd, name, &format))
+       if (pmu_format(pmu, dirfd, name)) {
+               free(pmu);
                return NULL;
-
+       }
        /*
         * Check the aliases first to avoid unnecessary work.
         */
-       if (pmu_aliases(dirfd, name, &aliases))
-               return NULL;
-
-       pmu = zalloc(sizeof(*pmu));
-       if (!pmu)
+       if (pmu_aliases(pmu, dirfd, name)) {
+               free(pmu);
                return NULL;
-
+       }
        pmu->is_core = is_pmu_core(name);
        pmu->cpus = pmu_cpumask(dirfd, name, pmu->is_core);
        pmu->name = strdup(name);
@@ -909,14 +1029,8 @@ struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char
        if (pmu->is_uncore)
                pmu->id = pmu_id(name);
        pmu->max_precise = pmu_max_precise(dirfd, pmu);
-       pmu_add_cpu_aliases(&aliases, pmu);
-       pmu_add_sys_aliases(&aliases, pmu);
-
-       INIT_LIST_HEAD(&pmu->format);
-       INIT_LIST_HEAD(&pmu->aliases);
-       INIT_LIST_HEAD(&pmu->caps);
-       list_splice(&format, &pmu->format);
-       list_splice(&aliases, &pmu->aliases);
+       pmu_add_cpu_aliases(pmu);
+       pmu_add_sys_aliases(pmu);
        list_add_tail(&pmu->list, pmus);
 
        pmu->default_config = perf_pmu__get_default_config(pmu);
@@ -966,13 +1080,15 @@ void perf_pmu__warn_invalid_formats(struct perf_pmu *pmu)
        if (pmu == &perf_pmu__fake)
                return;
 
-       list_for_each_entry(format, &pmu->format, list)
+       list_for_each_entry(format, &pmu->format, list) {
+               perf_pmu_format__load(pmu, format);
                if (format->value >= PERF_PMU_FORMAT_VALUE_CONFIG_END) {
                        pr_warning("WARNING: '%s' format '%s' requires 'perf_event_attr::config%d'"
                                   "which is not supported by this version of perf!\n",
                                   pmu->name, format->name, format->value);
                        return;
                }
+       }
 }
 
 bool evsel__is_aux_event(const struct evsel *evsel)
@@ -1000,7 +1116,7 @@ void evsel__set_config_if_unset(struct perf_pmu *pmu, struct evsel *evsel,
        if (term)
                user_bits = term->val.cfg_chg;
 
-       bits = perf_pmu__format_bits(&pmu->format, config_name);
+       bits = perf_pmu__format_bits(pmu, config_name);
 
        /* Do nothing if the user changed the value */
        if (bits & user_bits)
@@ -1023,9 +1139,9 @@ pmu_find_format(struct list_head *formats, const char *name)
        return NULL;
 }
 
-__u64 perf_pmu__format_bits(struct list_head *formats, const char *name)
+__u64 perf_pmu__format_bits(struct perf_pmu *pmu, const char *name)
 {
-       struct perf_pmu_format *format = pmu_find_format(formats, name);
+       struct perf_pmu_format *format = pmu_find_format(&pmu->format, name);
        __u64 bits = 0;
        int fbit;
 
@@ -1038,13 +1154,14 @@ __u64 perf_pmu__format_bits(struct list_head *formats, const char *name)
        return bits;
 }
 
-int perf_pmu__format_type(struct list_head *formats, const char *name)
+int perf_pmu__format_type(struct perf_pmu *pmu, const char *name)
 {
-       struct perf_pmu_format *format = pmu_find_format(formats, name);
+       struct perf_pmu_format *format = pmu_find_format(&pmu->format, name);
 
        if (!format)
                return -1;
 
+       perf_pmu_format__load(pmu, format);
        return format->value;
 }
 
@@ -1135,8 +1252,7 @@ error:
  * Setup one of config[12] attr members based on the
  * user input data - term parameter.
  */
-static int pmu_config_term(const char *pmu_name,
-                          struct list_head *formats,
+static int pmu_config_term(struct perf_pmu *pmu,
                           struct perf_event_attr *attr,
                           struct parse_events_term *term,
                           struct list_head *head_terms,
@@ -1160,15 +1276,15 @@ static int pmu_config_term(const char *pmu_name,
        if (parse_events__is_hardcoded_term(term))
                return 0;
 
-       format = pmu_find_format(formats, term->config);
+       format = pmu_find_format(&pmu->format, term->config);
        if (!format) {
-               char *pmu_term = pmu_formats_string(formats);
+               char *pmu_term = pmu_formats_string(&pmu->format);
                char *unknown_term;
                char *help_msg;
 
                if (asprintf(&unknown_term,
                                "unknown term '%s' for pmu '%s'",
-                               term->config, pmu_name) < 0)
+                               term->config, pmu->name) < 0)
                        unknown_term = NULL;
                help_msg = parse_events_formats_error_string(pmu_term);
                if (err) {
@@ -1182,7 +1298,7 @@ static int pmu_config_term(const char *pmu_name,
                free(pmu_term);
                return -EINVAL;
        }
-
+       perf_pmu_format__load(pmu, format);
        switch (format->value) {
        case PERF_PMU_FORMAT_VALUE_CONFIG:
                vp = &attr->config;
@@ -1259,7 +1375,7 @@ static int pmu_config_term(const char *pmu_name,
        return 0;
 }
 
-int perf_pmu__config_terms(const char *pmu_name, struct list_head *formats,
+int perf_pmu__config_terms(struct perf_pmu *pmu,
                           struct perf_event_attr *attr,
                           struct list_head *head_terms,
                           bool zero, struct parse_events_error *err)
@@ -1267,8 +1383,7 @@ int perf_pmu__config_terms(const char *pmu_name, struct list_head *formats,
        struct parse_events_term *term;
 
        list_for_each_entry(term, head_terms, list) {
-               if (pmu_config_term(pmu_name, formats, attr, term, head_terms,
-                                   zero, err))
+               if (pmu_config_term(pmu, attr, term, head_terms, zero, err))
                        return -EINVAL;
        }
 
@@ -1286,14 +1401,23 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
 {
        bool zero = !!pmu->default_config;
 
-       return perf_pmu__config_terms(pmu->name, &pmu->format, attr,
-                                     head_terms, zero, err);
+       return perf_pmu__config_terms(pmu, attr, head_terms, zero, err);
+}
+
+static struct perf_pmu_alias *perf_pmu__find_alias(const struct perf_pmu *pmu, const char *str)
+{
+       struct perf_pmu_alias *alias;
+
+       list_for_each_entry(alias, &pmu->aliases, list) {
+               if (!strcasecmp(alias->name, str))
+                       return alias;
+       }
+       return NULL;
 }
 
 static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
                                             struct parse_events_term *term)
 {
-       struct perf_pmu_alias *alias;
        char *name;
 
        if (parse_events__is_hardcoded_term(term))
@@ -1305,6 +1429,7 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
                if (pmu_find_format(&pmu->format, term->config))
                        return NULL;
                name = term->config;
+
        } else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) {
                if (strcasecmp(term->config, "event"))
                        return NULL;
@@ -1313,11 +1438,7 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
                return NULL;
        }
 
-       list_for_each_entry(alias, &pmu->aliases, list) {
-               if (!strcasecmp(alias->name, name))
-                       return alias;
-       }
-       return NULL;
+       return perf_pmu__find_alias(pmu, name);
 }
 
 
@@ -1400,36 +1521,34 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
        return 0;
 }
 
-int perf_pmu__new_format(struct list_head *list, char *name,
-                        int config, unsigned long *bits)
-{
-       struct perf_pmu_format *format;
+struct find_event_args {
+       const char *event;
+       void *state;
+       pmu_event_callback cb;
+};
 
-       format = zalloc(sizeof(*format));
-       if (!format)
-               return -ENOMEM;
+static int find_event_callback(void *state, struct pmu_event_info *info)
+{
+       struct find_event_args *args = state;
 
-       format->name = strdup(name);
-       format->value = config;
-       memcpy(format->bits, bits, sizeof(format->bits));
+       if (!strcmp(args->event, info->name))
+               return args->cb(args->state, info);
 
-       list_add_tail(&format->list, list);
        return 0;
 }
 
-void perf_pmu__set_format(unsigned long *bits, long from, long to)
+int perf_pmu__find_event(struct perf_pmu *pmu, const char *event, void *state, pmu_event_callback cb)
 {
-       long b;
-
-       if (!to)
-               to = from;
+       struct find_event_args args = {
+               .event = event,
+               .state = state,
+               .cb = cb,
+       };
 
-       memset(bits, 0, BITS_TO_BYTES(PERF_PMU_FORMAT_BITS));
-       for (b = from; b <= to; b++)
-               __set_bit(b, bits);
+       return perf_pmu__for_each_event(pmu, &args, find_event_callback);
 }
 
-void perf_pmu__del_formats(struct list_head *formats)
+static void perf_pmu__del_formats(struct list_head *formats)
 {
        struct perf_pmu_format *fmt, *tmp;
 
@@ -1440,6 +1559,17 @@ void perf_pmu__del_formats(struct list_head *formats)
        }
 }
 
+bool perf_pmu__has_format(const struct perf_pmu *pmu, const char *name)
+{
+       struct perf_pmu_format *format;
+
+       list_for_each_entry(format, &pmu->format, list) {
+               if (!strcmp(format->name, name))
+                       return true;
+       }
+       return false;
+}
+
 bool is_pmu_core(const char *name)
 {
        return !strcmp(name, "cpu") || !strcmp(name, "cpum_cf") || is_sysfs_pmu_core(name);
@@ -1457,13 +1587,110 @@ bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu)
 
 bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name)
 {
-       struct perf_pmu_alias *alias;
+       return perf_pmu__find_alias(pmu, name) != NULL;
+}
 
-       list_for_each_entry(alias, &pmu->aliases, list) {
-               if (!strcmp(alias->name, name))
-                       return true;
+size_t perf_pmu__num_events(const struct perf_pmu *pmu)
+{
+       struct list_head *list;
+       size_t nr = 0;
+
+       list_for_each(list, &pmu->aliases)
+               nr++;
+
+       return pmu->selectable ? nr + 1 : nr;
+}
+
+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);
        }
-       return false;
+
+       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;
+}
+
+int perf_pmu__for_each_event(const struct perf_pmu *pmu, void *state, pmu_event_callback cb)
+{
+       char buf[1024];
+       struct perf_pmu_alias *event;
+       struct pmu_event_info info = {
+               .pmu = pmu,
+       };
+       int ret = 0;
+
+       list_for_each_entry(event, &pmu->aliases, list) {
+               size_t buf_used;
+
+               info.pmu_name = event->pmu_name ?: pmu->name;
+               info.alias = NULL;
+               if (event->desc) {
+                       info.name = event->name;
+                       buf_used = 0;
+               } else {
+                       info.name = format_alias(buf, sizeof(buf), pmu, event);
+                       if (pmu->is_core) {
+                               info.alias = info.name;
+                               info.name = event->name;
+                       }
+                       buf_used = strlen(buf) + 1;
+               }
+               info.scale_unit = NULL;
+               if (strlen(event->unit) || event->scale != 1.0) {
+                       info.scale_unit = buf + buf_used;
+                       buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
+                                       "%G%s", event->scale, event->unit) + 1;
+               }
+               info.desc = event->desc;
+               info.long_desc = event->long_desc;
+               info.encoding_desc = buf + buf_used;
+               buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
+                               "%s/%s/", info.pmu_name, event->str) + 1;
+               info.topic = event->topic;
+               info.str = event->str;
+               info.deprecated = event->deprecated;
+               ret = cb(state, &info);
+               if (ret)
+                       return ret;
+       }
+       if (pmu->selectable) {
+               info.name = buf;
+               snprintf(buf, sizeof(buf), "%s//", pmu->name);
+               info.alias = NULL;
+               info.scale_unit = NULL;
+               info.desc = NULL;
+               info.long_desc = NULL;
+               info.encoding_desc = NULL;
+               info.topic = NULL;
+               info.pmu_name = pmu->name;
+               info.deprecated = false;
+               ret = cb(state, &info);
+       }
+       return ret;
 }
 
 bool perf_pmu__is_software(const struct perf_pmu *pmu)
@@ -1745,17 +1972,19 @@ int perf_pmu__event_source_devices_fd(void)
  * then pathname will be filled with
  * "/sys/bus/event_source/devices/cs_etm/format"
  *
- * Return 0 if the sysfs mountpoint couldn't be found or if no
- * characters were written.
+ * Return 0 if the sysfs mountpoint couldn't be found, if no characters were
+ * written or if the buffer size is exceeded.
  */
 int perf_pmu__pathname_scnprintf(char *buf, size_t size,
                                 const char *pmu_name, const char *filename)
 {
-       char base_path[PATH_MAX];
+       size_t len;
 
-       if (!perf_pmu__event_source_devices_scnprintf(base_path, sizeof(base_path)))
+       len = perf_pmu__event_source_devices_scnprintf(buf, size);
+       if (!len || (len + strlen(pmu_name) + strlen(filename) + 1)  >= size)
                return 0;
-       return scnprintf(buf, size, "%s%s/%s", base_path, pmu_name, filename);
+
+       return scnprintf(buf + len, size - len, "%s/%s", pmu_name, filename);
 }
 
 int perf_pmu__pathname_fd(int dirfd, const char *pmu_name, const char *filename, int flags)
@@ -1779,3 +2008,20 @@ void perf_pmu__delete(struct perf_pmu *pmu)
        zfree(&pmu->alias_name);
        free(pmu);
 }
+
+struct perf_pmu *pmu__find_core_pmu(void)
+{
+       struct perf_pmu *pmu = NULL;
+
+       while ((pmu = perf_pmus__scan_core(pmu))) {
+               /*
+                * The cpumap should cover all CPUs. Otherwise, some CPUs may
+                * not support some events or have different event IDs.
+                */
+               if (RC_CHK_ACCESS(pmu->cpus)->nr != cpu__max_cpu().cpu)
+                       return NULL;
+
+               return pmu;
+       }
+       return NULL;
+}