[PATCH v2 13/18] perf pmu: Lazily add json events

From: Ian Rogers
Date: Thu Aug 24 2023 - 00:15:36 EST


Rather than scanning all json events and adding them when a PMU is
created, add the alias when the json event is needed.

Average core PMU scanning run time reduced by 60.2%. Average PMU
scanning run time reduced by 15%. Page faults with no events reduced
by 74 page faults, 4% of total.

Signed-off-by: Ian Rogers <irogers@xxxxxxxxxx>
---
tools/perf/pmu-events/empty-pmu-events.c | 14 +++++++
tools/perf/pmu-events/jevents.py | 15 +++++++
tools/perf/pmu-events/pmu-events.h | 4 ++
tools/perf/tests/pmu-events.c | 2 +
tools/perf/util/pmu.c | 50 ++++++++++++++++++------
tools/perf/util/pmu.h | 15 +++++--
6 files changed, 85 insertions(+), 15 deletions(-)

diff --git a/tools/perf/pmu-events/empty-pmu-events.c b/tools/perf/pmu-events/empty-pmu-events.c
index ef18d403f25f..12bd043a05e3 100644
--- a/tools/perf/pmu-events/empty-pmu-events.c
+++ b/tools/perf/pmu-events/empty-pmu-events.c
@@ -298,6 +298,20 @@ int pmu_events_table__find_event(const struct pmu_events_table *table,
return -1000;
}

+size_t pmu_events_table__num_events(const struct pmu_events_table *table,
+ struct perf_pmu *pmu)
+{
+ size_t count = 0;
+
+ for (const struct pmu_event *pe = &table->entries[0]; pe->name; pe++) {
+ if (pmu && !pmu__name_match(pmu, pe->pmu))
+ continue;
+
+ count++;
+ }
+ return count;
+}
+
int pmu_metrics_table__for_each_metric(const struct pmu_metrics_table *table, pmu_metric_iter_fn fn,
void *data)
{
diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
index f07864fabd54..01335a452e70 100755
--- a/tools/perf/pmu-events/jevents.py
+++ b/tools/perf/pmu-events/jevents.py
@@ -909,6 +909,21 @@ int pmu_events_table__find_event(const struct pmu_events_table *table,
return -1000;
}

+size_t pmu_events_table__num_events(const struct pmu_events_table *table,
+ struct perf_pmu *pmu)
+{
+ size_t count = 0;
+
+ for (size_t i = 0; i < table->num_pmus; i++) {
+ const struct pmu_table_entry *table_pmu = &table->pmus[i];
+ const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset];
+
+ if (pmu__name_match(pmu, pmu_name))
+ count += table_pmu->num_entries;
+ }
+ return count;
+}
+
static int pmu_metrics_table__for_each_metric_pmu(const struct pmu_metrics_table *table,
const struct pmu_table_entry *pmu,
pmu_metric_iter_fn fn,
diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h
index 9882b7125761..f5aa96f1685c 100644
--- a/tools/perf/pmu-events/pmu-events.h
+++ b/tools/perf/pmu-events/pmu-events.h
@@ -3,6 +3,7 @@
#define PMU_EVENTS_H

#include <stdbool.h>
+#include <stddef.h>

struct perf_pmu;

@@ -86,6 +87,9 @@ int pmu_events_table__find_event(const struct pmu_events_table *table,
const char *name,
pmu_event_iter_fn fn,
void *data);
+size_t pmu_events_table__num_events(const struct pmu_events_table *table,
+ struct perf_pmu *pmu);
+
int pmu_metrics_table__for_each_metric(const struct pmu_metrics_table *table, pmu_metric_iter_fn fn,
void *data);

diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c
index 4fcb84fd1f65..9ac893ae5f0d 100644
--- a/tools/perf/tests/pmu-events.c
+++ b/tools/perf/tests/pmu-events.c
@@ -546,6 +546,7 @@ static int __test_core_pmu_event_aliases(char *pmu_name, int *count)

pmu->events_table = table;
pmu_add_cpu_aliases_table(pmu, table);
+ pmu->cpu_aliases_added = true;

res = pmu_events_table__find_event(table, pmu, "bp_l1_btb_correct", NULL, NULL);
if (res != 0) {
@@ -586,6 +587,7 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu)
return -1;
pmu->events_table = events_table;
pmu_add_cpu_aliases_table(pmu, events_table);
+ pmu->cpu_aliases_added = true;
pmu_add_sys_aliases(pmu);

/* Count how many aliases we generated */
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 685903c1970b..b6a118226541 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -523,8 +523,9 @@ static int perf_pmu__new_alias(struct perf_pmu *pmu, int dirfd, const char *name
if (!pe) {
/* Update an event from sysfs with json data. */
if (pmu->events_table) {
- pmu_events_table__find_event(pmu->events_table, pmu, name,
- update_alias, alias);
+ if (pmu_events_table__find_event(pmu->events_table, pmu, name,
+ update_alias, alias) == 0)
+ pmu->loaded_json_aliases++;
}
}

@@ -548,6 +549,10 @@ static int perf_pmu__new_alias(struct perf_pmu *pmu, int dirfd, const char *name
"%s=%s", term->config, term->val.str);
}
alias->str = strdup(newval);
+ if (!pe)
+ pmu->sysfs_aliases++;
+ else
+ pmu->loaded_json_aliases++;
list_add_tail(&alias->list, &pmu->aliases);
return 0;
}
@@ -878,7 +883,11 @@ static void pmu_add_cpu_aliases(struct perf_pmu *pmu)
if (!pmu->events_table)
return;

+ if (pmu->cpu_aliases_added)
+ return;
+
pmu_add_cpu_aliases_table(pmu, pmu->events_table);
+ pmu->cpu_aliases_added = true;
}

static int pmu_add_sys_aliases_iter_fn(const struct pmu_event *pe,
@@ -990,7 +999,6 @@ struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char
pmu->id = pmu_id(name);
pmu->max_precise = pmu_max_precise(dirfd, pmu);
pmu->events_table = perf_pmu__find_events_table(pmu);
- pmu_add_cpu_aliases(pmu);
pmu_add_sys_aliases(pmu);
list_add_tail(&pmu->list, pmus);

@@ -1368,6 +1376,7 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
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))
@@ -1388,7 +1397,18 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
return NULL;
}

- return perf_pmu__find_alias(pmu, name);
+ alias = perf_pmu__find_alias(pmu, name);
+ if (alias || pmu->cpu_aliases_added)
+ return alias;
+
+ /* Alias doesn't exist, try to get it from the json events. */
+ if (pmu->events_table &&
+ pmu_events_table__find_event(pmu->events_table, pmu, name,
+ pmu_add_cpu_aliases_map_callback,
+ pmu) == 0) {
+ alias = perf_pmu__find_alias(pmu, name);
+ }
+ return alias;
}


@@ -1555,18 +1575,23 @@ bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu)
return !pmu->is_core || perf_pmus__num_core_pmus() == 1;
}

-bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name)
+bool perf_pmu__have_event(struct perf_pmu *pmu, const char *name)
{
- return perf_pmu__find_alias(pmu, name) != NULL;
+ if (perf_pmu__find_alias(pmu, name) != NULL)
+ return true;
+ if (pmu->cpu_aliases_added || !pmu->events_table)
+ return false;
+ return pmu_events_table__find_event(pmu->events_table, pmu, name, NULL, NULL) == 0;
}

-size_t perf_pmu__num_events(const struct perf_pmu *pmu)
+size_t perf_pmu__num_events(struct perf_pmu *pmu)
{
- struct list_head *list;
- size_t nr = 0;
+ size_t nr = pmu->sysfs_aliases;

- list_for_each(list, &pmu->aliases)
- nr++;
+ if (pmu->cpu_aliases_added)
+ nr += pmu->loaded_json_aliases;
+ else if (pmu->events_table)
+ nr += pmu_events_table__num_events(pmu->events_table, pmu) - pmu->loaded_json_aliases;

return pmu->selectable ? nr + 1 : nr;
}
@@ -1604,7 +1629,7 @@ static char *format_alias(char *buf, int len, const struct perf_pmu *pmu,
return buf;
}

-int perf_pmu__for_each_event(const struct perf_pmu *pmu, void *state, pmu_event_callback cb)
+int perf_pmu__for_each_event(struct perf_pmu *pmu, void *state, pmu_event_callback cb)
{
char buf[1024];
struct perf_pmu_alias *event;
@@ -1613,6 +1638,7 @@ int perf_pmu__for_each_event(const struct perf_pmu *pmu, void *state, pmu_event_
};
int ret = 0;

+ pmu_add_cpu_aliases(pmu);
list_for_each_entry(event, &pmu->aliases, list) {
size_t buf_used;

diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 6bf0fbde4e85..288d2908382a 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -118,6 +118,15 @@ struct perf_pmu {
* @events_table: The events table for json events in pmu-events.c.
*/
const struct pmu_events_table *events_table;
+ /** @sysfs_aliases: Number of sysfs aliases loaded. */
+ uint32_t sysfs_aliases;
+ /** @sysfs_aliases: Number of json event aliases loaded. */
+ uint32_t loaded_json_aliases;
+ /**
+ * @cpu_aliases_added: Have all json events table entries for the PMU
+ * been added?
+ */
+ bool cpu_aliases_added;
/** @caps_initialized: Has the list caps been initialized? */
bool caps_initialized;
/** @nr_caps: The length of the list caps. */
@@ -199,9 +208,9 @@ bool perf_pmu__has_format(const struct perf_pmu *pmu, const char *name);
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);
+bool perf_pmu__have_event(struct perf_pmu *pmu, const char *name);
+size_t perf_pmu__num_events(struct perf_pmu *pmu);
+int perf_pmu__for_each_event(struct perf_pmu *pmu, void *state, pmu_event_callback cb);
bool pmu__name_match(const struct perf_pmu *pmu, const char *pmu_name);

/**
--
2.42.0.rc1.204.g551eb34607-goog