perf pmu: Abstract alias/event struct
authorIan Rogers <irogers@google.com>
Thu, 24 Aug 2023 04:13:14 +0000 (21:13 -0700)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Thu, 24 Aug 2023 13:42:46 +0000 (10:42 -0300)
In order to be able to lazily compute aliases/events for a PMU, move
the struct perf_pmu_alias into pmu.c.

Add perf_pmu__find_event and perf_pmu__for_each_event that take a
callback that is called for the found event or for each event.

The layout of struct pmu and the event/alias list is unchanged but the
API is altered so that aliases are no longer directly accessed, allowing
for later changes.

Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Gaosheng Cui <cuigaosheng1@huawei.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jing Zhang <renyu.zj@linux.alibaba.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: John Garry <john.g.garry@oracle.com>
Cc: Kajol Jain <kjain@linux.ibm.com>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ravi Bangoria <ravi.bangoria@amd.com>
Cc: Rob Herring <robh@kernel.org>
Link: https://lore.kernel.org/r/20230824041330.266337-3-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/bench/pmu-scan.c
tools/perf/tests/pmu-events.c
tools/perf/util/parse-events.c
tools/perf/util/pmu.c
tools/perf/util/pmu.h
tools/perf/util/pmus.c

index c7d207f..9e4d364 100644 (file)
@@ -57,9 +57,7 @@ static int save_result(void)
                r->is_core = pmu->is_core;
                r->nr_caps = pmu->nr_caps;
 
-               r->nr_aliases = 0;
-               list_for_each(list, &pmu->aliases)
-                       r->nr_aliases++;
+               r->nr_aliases = perf_pmu__num_events(pmu);
 
                r->nr_formats = 0;
                list_for_each(list, &pmu->format)
@@ -98,9 +96,7 @@ static int check_result(bool core_only)
                        return -1;
                }
 
-               nr = 0;
-               list_for_each(list, &pmu->aliases)
-                       nr++;
+               nr = perf_pmu__num_events(pmu);
                if (nr != r->nr_aliases) {
                        pr_err("Unmatched number of event aliases in %s: expect %d vs got %d\n",
                                pmu->name, r->nr_aliases, nr);
index 05d6e6e..dc87e66 100644 (file)
@@ -341,7 +341,7 @@ static int compare_pmu_events(const struct pmu_event *e1, const struct pmu_event
        return 0;
 }
 
-static int compare_alias_to_test_event(struct perf_pmu_alias *alias,
+static int compare_alias_to_test_event(struct pmu_event_info *alias,
                                struct perf_pmu_test_event const *test_event,
                                char const *pmu_name)
 {
@@ -496,6 +496,23 @@ static int test__pmu_event_table(struct test_suite *test __maybe_unused,
        return 0;
 }
 
+struct test_core_pmu_event_aliases_cb_args {
+       struct perf_pmu_test_event const *test_event;
+       int *count;
+};
+
+static int test_core_pmu_event_aliases_cb(void *state, struct pmu_event_info *alias)
+{
+       struct test_core_pmu_event_aliases_cb_args *args = state;
+
+       if (compare_alias_to_test_event(alias, args->test_event, alias->pmu->name))
+               return -1;
+       (*args->count)++;
+       pr_debug2("testing aliases core PMU %s: matched event %s\n",
+               alias->pmu_name, alias->name);
+       return 0;
+}
+
 /* Verify aliases are as expected */
 static int __test_core_pmu_event_aliases(char *pmu_name, int *count)
 {
@@ -522,25 +539,19 @@ static int __test_core_pmu_event_aliases(char *pmu_name, int *count)
        pmu_add_cpu_aliases_table(pmu, table);
 
        for (; *test_event_table; test_event_table++) {
-               struct perf_pmu_test_event const *test_event = *test_event_table;
-               struct pmu_event const *event = &test_event->event;
-               struct perf_pmu_alias *alias = perf_pmu__find_alias(pmu, event->name);
-
-               if (!alias) {
-                       pr_debug("testing aliases core PMU %s: no alias, alias_table->name=%s\n",
-                                 pmu_name, event->name);
-                       res = -1;
-                       break;
-               }
-
-               if (compare_alias_to_test_event(alias, test_event, pmu_name)) {
-                       res = -1;
-                       break;
-               }
-
-               (*count)++;
-               pr_debug2("testing aliases core PMU %s: matched event %s\n",
-                         pmu_name, alias->name);
+               struct perf_pmu_test_event test_event = **test_event_table;
+               struct pmu_event const *event = &test_event.event;
+               struct test_core_pmu_event_aliases_cb_args args = {
+                       .test_event = &test_event,
+                       .count = count,
+               };
+               int err;
+
+               test_event.event.pmu = pmu_name;
+               err = perf_pmu__find_event(pmu, event->name, &args,
+                                          test_core_pmu_event_aliases_cb);
+               if (err)
+                       res = err;
        }
        perf_pmu__delete(pmu);
 
@@ -553,7 +564,6 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu)
        struct perf_pmu_test_event const **table;
        struct perf_pmu *pmu = &test_pmu->pmu;
        const char *pmu_name = pmu->name;
-       struct perf_pmu_alias *a, *tmp, *alias;
        const struct pmu_events_table *events_table;
        int res = 0;
 
@@ -564,8 +574,7 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu)
        pmu_add_sys_aliases(pmu);
 
        /* Count how many aliases we generated */
-       list_for_each_entry(alias, &pmu->aliases, list)
-               alias_count++;
+       alias_count = perf_pmu__num_events(pmu);
 
        /* Count how many aliases we expect from the known table */
        for (table = &test_pmu->aliases[0]; *table; table++)
@@ -574,33 +583,25 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu)
        if (alias_count != to_match_count) {
                pr_debug("testing aliases uncore PMU %s: mismatch expected aliases (%d) vs found (%d)\n",
                         pmu_name, to_match_count, alias_count);
-               res = -1;
-               goto out;
+               return -1;
        }
 
-       list_for_each_entry(alias, &pmu->aliases, list) {
-               bool matched = false;
-
-               for (table = &test_pmu->aliases[0]; *table; table++) {
-                       struct perf_pmu_test_event const *test_event = *table;
-                       struct pmu_event const *event = &test_event->event;
-
-                       if (!strcmp(event->name, alias->name)) {
-                               if (compare_alias_to_test_event(alias,
-                                                       test_event,
-                                                       pmu_name)) {
-                                       continue;
-                               }
-                               matched = true;
-                               matched_count++;
-                       }
-               }
-
-               if (matched == false) {
+       for (table = &test_pmu->aliases[0]; *table; table++) {
+               struct perf_pmu_test_event test_event = **table;
+               struct pmu_event const *event = &test_event.event;
+               int err;
+               struct test_core_pmu_event_aliases_cb_args args = {
+                       .test_event = &test_event,
+                       .count = &matched_count,
+               };
+
+               err = perf_pmu__find_event(pmu, event->name, &args,
+                                          test_core_pmu_event_aliases_cb);
+               if (err) {
+                       res = err;
                        pr_debug("testing aliases uncore PMU %s: could not match alias %s\n",
-                                pmu_name, alias->name);
-                       res = -1;
-                       goto out;
+                                pmu_name, event->name);
+                       return -1;
                }
        }
 
@@ -609,12 +610,6 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu)
                         pmu_name, matched_count, alias_count);
                res = -1;
        }
-
-out:
-       list_for_each_entry_safe(a, tmp, &pmu->aliases, list) {
-               list_del(&a->list);
-               perf_pmu_free_alias(a);
-       }
        return res;
 }
 
index 7d9d687..7cad82a 100644 (file)
@@ -193,38 +193,31 @@ static void fix_raw(struct list_head *config_terms, struct perf_pmu *pmu)
        struct parse_events_term *term;
 
        list_for_each_entry(term, config_terms, list) {
-               struct perf_pmu_alias *alias;
-               bool matched = false;
+               u64 num;
 
                if (term->type_term != PARSE_EVENTS__TERM_TYPE_RAW)
                        continue;
 
-               list_for_each_entry(alias, &pmu->aliases, list) {
-                       if (!strcmp(alias->name, term->val.str)) {
-                               free(term->config);
-                               term->config = term->val.str;
-                               term->type_val = PARSE_EVENTS__TERM_TYPE_NUM;
-                               term->type_term = PARSE_EVENTS__TERM_TYPE_USER;
-                               term->val.num = 1;
-                               term->no_value = true;
-                               matched = true;
-                               break;
-                       }
-               }
-               if (!matched) {
-                       u64 num;
-
+               if (perf_pmu__have_event(pmu, term->val.str)) {
                        free(term->config);
-                       term->config = strdup("config");
-                       errno = 0;
-                       num = strtoull(term->val.str + 1, NULL, 16);
-                       assert(errno == 0);
-                       free(term->val.str);
+                       term->config = term->val.str;
                        term->type_val = PARSE_EVENTS__TERM_TYPE_NUM;
-                       term->type_term = PARSE_EVENTS__TERM_TYPE_CONFIG;
-                       term->val.num = num;
-                       term->no_value = false;
+                       term->type_term = PARSE_EVENTS__TERM_TYPE_USER;
+                       term->val.num = 1;
+                       term->no_value = true;
+                       continue;
                }
+
+               free(term->config);
+               term->config = strdup("config");
+               errno = 0;
+               num = strtoull(term->val.str + 1, NULL, 16);
+               assert(errno == 0);
+               free(term->val.str);
+               term->type_val = PARSE_EVENTS__TERM_TYPE_NUM;
+               term->type_term = PARSE_EVENTS__TERM_TYPE_CONFIG;
+               term->val.num = num;
+               term->no_value = false;
        }
 }
 
@@ -1458,28 +1451,22 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state,
        INIT_LIST_HEAD(list);
 
        while ((pmu = perf_pmus__scan(pmu)) != NULL) {
-               struct perf_pmu_alias *alias;
                bool auto_merge_stats;
 
                if (parse_events__filter_pmu(parse_state, pmu))
                        continue;
 
-               auto_merge_stats = perf_pmu__auto_merge_stats(pmu);
+               if (!perf_pmu__have_event(pmu, str))
+                       continue;
 
-               list_for_each_entry(alias, &pmu->aliases, list) {
-                       if (!strcasecmp(alias->name, str)) {
-                               parse_events_copy_term_list(head, &orig_head);
-                               if (!parse_events_add_pmu(parse_state, list,
-                                                         pmu->name, orig_head,
-                                                         auto_merge_stats, loc)) {
-                                       pr_debug("%s -> %s/%s/\n", str,
-                                                pmu->name, alias->str);
-                                       parse_state->wild_card_pmus = true;
-                                       ok++;
-                               }
-                               parse_events_terms__delete(orig_head);
-                       }
+               auto_merge_stats = perf_pmu__auto_merge_stats(pmu);
+               parse_events_copy_term_list(head, &orig_head);
+               if (!parse_events_add_pmu(parse_state, list, pmu->name,
+                                         orig_head, auto_merge_stats, loc)) {
+                       pr_debug("%s -> %s/%s/\n", str, pmu->name, str);
+                       ok++;
                }
+               parse_events_terms__delete(orig_head);
        }
 
        if (parse_state->fake_pmu) {
index 3cfd3e5..268aaeb 100644 (file)
 
 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.
@@ -355,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);
@@ -1349,10 +1404,20 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
        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))
@@ -1364,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;
@@ -1372,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);
 }
 
 
@@ -1459,16 +1521,33 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
        return 0;
 }
 
-struct perf_pmu_alias *perf_pmu__find_alias(struct perf_pmu *pmu, const char *event)
+struct find_event_args {
+       const char *event;
+       void *state;
+       pmu_event_callback cb;
+};
+
+static int find_event_callback(void *state, struct pmu_event_info *info)
 {
-       struct perf_pmu_alias *alias;
+       struct find_event_args *args = state;
 
-       list_for_each_entry(alias, &pmu->aliases, list)
-               if (!strcmp(event, alias->name))
-                       return alias;
+       if (!strcmp(args->event, info->name))
+               return args->cb(args->state, info);
 
-       return NULL;
+       return 0;
 }
+
+int perf_pmu__find_event(struct perf_pmu *pmu, const char *event, void *state, pmu_event_callback cb)
+{
+       struct find_event_args args = {
+               .event = event,
+               .state = state,
+               .cb = cb,
+       };
+
+       return perf_pmu__for_each_event(pmu, &args, find_event_callback);
+}
+
 static void perf_pmu__del_formats(struct list_head *formats)
 {
        struct perf_pmu_format *fmt, *tmp;
@@ -1508,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)
index 675c9b9..f37e3d7 100644 (file)
@@ -158,61 +158,22 @@ struct perf_pmu_info {
        bool snapshot;
 };
 
-#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.
-        */
+struct pmu_event_info {
+       const struct perf_pmu *pmu;
+       const char *name;
+       const char* alias;
+       const char *scale_unit;
+       const char *desc;
+       const char *long_desc;
+       const char *encoding_desc;
+       const char *topic;
+       const char *pmu_name;
+       const char *str;
        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;
 };
 
+typedef int (*pmu_event_callback)(void *state, struct pmu_event_info *info);
+
 void pmu_add_sys_aliases(struct perf_pmu *pmu);
 int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
                     struct list_head *head_terms,
@@ -225,7 +186,7 @@ __u64 perf_pmu__format_bits(struct perf_pmu *pmu, const char *name);
 int perf_pmu__format_type(struct perf_pmu *pmu, const char *name);
 int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
                          struct perf_pmu_info *info);
-struct perf_pmu_alias *perf_pmu__find_alias(struct perf_pmu *pmu, const char *event);
+int perf_pmu__find_event(struct perf_pmu *pmu, const char *event, void *state, pmu_event_callback cb);
 
 int perf_pmu__format_parse(struct perf_pmu *pmu, int dirfd, bool eager_load);
 void perf_pmu_format__set_value(void *format, int config, unsigned long *bits);
@@ -235,6 +196,9 @@ bool is_pmu_core(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);
+size_t perf_pmu__num_events(const struct perf_pmu *pmu);
+int perf_pmu__for_each_event(const struct perf_pmu *pmu, void *state, pmu_event_callback cb);
+
 /**
  * perf_pmu_is_software - is the PMU a software PMU as in it uses the
  *                        perf_sw_context in the kernel?
@@ -259,7 +223,6 @@ void pmu_add_cpu_aliases_table(struct perf_pmu *pmu,
 char *perf_pmu__getcpuid(struct perf_pmu *pmu);
 const struct pmu_events_table *pmu_events_table__find(void);
 const struct pmu_metrics_table *pmu_metrics_table__find(void);
-void perf_pmu_free_alias(struct perf_pmu_alias *alias);
 
 int perf_pmu__convert_scale(const char *scale, char **end, double *sval);
 
index c58ba9f..4dd5912 100644 (file)
@@ -258,219 +258,145 @@ int __weak perf_pmus__num_mem_pmus(void)
 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;
+       const char *name;
+       const char* alias;
+       const char *scale_unit;
+       const char *desc;
+       const char *long_desc;
+       const char *encoding_desc;
+       const char *topic;
+       const char *pmu_name;
+       bool deprecated;
 };
 
 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 = "";
+       bool a_iscpu, b_iscpu;
        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;
+       if (!!as->desc != !!bs->desc)
+               return !!as->desc - !!bs->desc;
 
        /* Order by topics. */
-       ret = strcmp(a_topic, b_topic);
+       ret = strcmp(as->topic ?: "", bs->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;
+       a_iscpu = as->pmu ? as->pmu->is_core : true;
+       b_iscpu = bs->pmu ? bs->pmu->is_core : true;
+       if (a_iscpu != b_iscpu)
+               return a_iscpu ? -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);
+               ret = strcmp(as->pmu_name ?: "", bs->pmu_name ?: "");
                if (ret)
                        return ret;
        }
 
        /* Order by event name. */
-       return strcmp(a_name, b_name);
+       return strcmp(as->name, bs->name);
 }
 
-static bool pmu_alias_is_duplicate(struct sevent *alias_a,
-                                  struct sevent *alias_b)
+static bool pmu_alias_is_duplicate(struct sevent *a, struct sevent *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))
+       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;
+       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;
-}
+struct events_callback_state {
+       struct sevent *aliases;
+       size_t aliases_len;
+       size_t index;
+};
 
-static char *format_alias(char *buf, int len, const struct perf_pmu *pmu,
-                         const struct perf_pmu_alias *alias)
+static int perf_pmus__print_pmu_events__callback(void *vstate,
+                                               struct pmu_event_info *info)
 {
-       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);
-       }
+       struct events_callback_state *state = vstate;
+       struct sevent *s;
 
-       if (sub_non_neg(len, used) > 0) {
-               buf[used] = '/';
-               used++;
+       if (state->index >= state->aliases_len) {
+               pr_err("Unexpected event %s/%s/\n", info->pmu->name, info->name);
+               return 1;
        }
-       if (sub_non_neg(len, used) > 0) {
-               buf[used] = '\0';
-               used++;
-       } else
-               buf[len - 1] = '\0';
-
-       return buf;
+       s = &state->aliases[state->index];
+       s->pmu = info->pmu;
+#define COPY_STR(str) s->str = info->str ? strdup(info->str) : NULL
+       COPY_STR(name);
+       COPY_STR(alias);
+       COPY_STR(scale_unit);
+       COPY_STR(desc);
+       COPY_STR(long_desc);
+       COPY_STR(encoding_desc);
+       COPY_STR(topic);
+       COPY_STR(pmu_name);
+#undef COPY_STR
+       s->deprecated = info->deprecated;
+       state->index++;
+       return 0;
 }
 
 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;
+       int len;
        struct sevent *aliases;
+       struct events_callback_state state;
 
        pmu = NULL;
        len = 0;
-       while ((pmu = perf_pmus__scan(pmu)) != NULL) {
-               list_for_each_entry(event, &pmu->aliases, list)
-                       len++;
-               if (pmu->selectable)
-                       len++;
-       }
+       while ((pmu = perf_pmus__scan(pmu)) != NULL)
+               len += perf_pmu__num_events(pmu);
+
        aliases = zalloc(sizeof(struct sevent) * len);
        if (!aliases) {
                pr_err("FATAL: not enough memory to print PMU events\n");
                return;
        }
        pmu = NULL;
-       j = 0;
+       state = (struct events_callback_state) {
+               .aliases = aliases,
+               .aliases_len = len,
+               .index = 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++;
-               }
+               perf_pmu__for_each_event(pmu, &state, perf_pmus__print_pmu_events__callback);
        }
-       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;
-
+       for (int j = 0; j < len; j++) {
                /* 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,
+                               aliases[j].pmu_name,
+                               aliases[j].topic,
+                               aliases[j].name,
+                               aliases[j].alias,
+                               aliases[j].scale_unit,
+                               aliases[j].deprecated,
                                "Kernel PMU event",
-                               desc,
-                               long_desc,
-                               encoding_desc);
+                               aliases[j].desc,
+                               aliases[j].long_desc,
+                               aliases[j].encoding_desc);
+               zfree(&aliases[j].name);
+               zfree(&aliases[j].alias);
+               zfree(&aliases[j].scale_unit);
+               zfree(&aliases[j].desc);
+               zfree(&aliases[j].long_desc);
+               zfree(&aliases[j].encoding_desc);
+               zfree(&aliases[j].topic);
+               zfree(&aliases[j].pmu_name);
        }
        if (printed && pager_in_use())
                printf("\n");