2022-07-29 07:46:04

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 00/17] Compress the pmu_event tables

jevents.py creates a number of large arrays from the json events. The
arrays contain pointers to strings that need relocating. The
relocations have file size, run time and memory costs. These changes
refactor the pmu_events API so that the storage of the pmu_event
struct isn't exposed. The format is then changed to an offset within a
combined big string, with adjacent pmu_event struct variables being
next to each other in the string separated by \0 - meaning only the
first variable of the struct needs its offset recording.

Some related fixes are contained with the patches. The architecture
jevents.py creates tables for can now be set by the JEVENTS_ARCH make
variable, with a new 'all' that generates the events and metrics for
all architectures.

An example of the improvement to the file size on x86 is:
no jevents - the same 19,788,464bytes
x86 jevents - ~16.7% file size saving 23,744,288bytes vs 28,502,632bytes
all jevents - ~19.5% file size saving 24,469,056bytes vs 30,379,920bytes
default build options plus NO_LIBBFD=1.

I originally suggested fixing this problem in:
https://lore.kernel.org/linux-perf-users/CAP-5=fVB8G4bdb9T=FncRTh9oBVKCS=+=eowAO+YSgAhab+Dtg@mail.gmail.com/

v3. Fix an ARM build issue with a missed weak symbol. Perform some
pytype clean up.
v2. Split the substring folding optimization to its own patch and
comment tweaks as suggested by Namhyung Kim
<[email protected]>. Recompute the file size savings with the
latest json events and metrics.

Ian Rogers (17):
perf jevents: Clean up pytype warnings
perf jevents: Simplify generation of C-string
perf jevents: Add JEVENTS_ARCH make option
perf jevent: Add an 'all' architecture argument
perf jevents: Remove the type/version variables
perf jevents: Provide path to json file on error
perf jevents: Sort json files entries
perf pmu-events: Hide pmu_sys_event_tables
perf pmu-events: Avoid passing pmu_events_map
perf pmu-events: Hide pmu_events_map
perf test: Use full metric resolution
perf pmu-events: Move test events/metrics to json
perf pmu-events: Don't assume pmu_event is an array
perf pmu-events: Hide the pmu_events
perf metrics: Copy entire pmu_event in find metric
perf jevents: Compress the pmu_events_table
perf jevents: Fold strings optimization

tools/perf/arch/arm64/util/pmu.c | 4 +-
tools/perf/pmu-events/Build | 6 +-
.../arch/test/test_soc/cpu/metrics.json | 64 +++
tools/perf/pmu-events/empty-pmu-events.c | 204 +++++++-
tools/perf/pmu-events/jevents.py | 495 ++++++++++++++----
tools/perf/pmu-events/pmu-events.h | 40 +-
tools/perf/tests/expand-cgroup.c | 25 +-
tools/perf/tests/parse-metric.c | 77 +--
tools/perf/tests/pmu-events.c | 466 +++++++----------
tools/perf/util/metricgroup.c | 275 ++++++----
tools/perf/util/metricgroup.h | 5 +-
tools/perf/util/pmu.c | 139 ++---
tools/perf/util/pmu.h | 8 +-
tools/perf/util/s390-sample-raw.c | 50 +-
14 files changed, 1140 insertions(+), 718 deletions(-)
create mode 100644 tools/perf/pmu-events/arch/test/test_soc/cpu/metrics.json

--
2.37.1.455.g008518b4e5-goog


2022-07-29 07:46:18

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 02/17] perf jevents: Simplify generation of C-string

Previous implementation wanted variable order and '(null)' string output
to match the C implementation. The '(null)' string output was a
quirk/bug and so there is no need to carry it forward.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/pmu-events/jevents.py | 14 ++++----------
1 file changed, 4 insertions(+), 10 deletions(-)

diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
index 5b72048d50da..cdfa4e0e7557 100755
--- a/tools/perf/pmu-events/jevents.py
+++ b/tools/perf/pmu-events/jevents.py
@@ -203,7 +203,7 @@ class JsonEvent:
"""Representation of the event as a C struct initializer."""

def attr_string(attr: str, value: str) -> str:
- return '\t.%s = \"%s\",\n' % (attr, value)
+ return f'\t.{attr} = \"{value}\",\n'

def str_if_present(self, attr: str) -> str:
if not getattr(self, attr):
@@ -211,17 +211,11 @@ class JsonEvent:
return attr_string(attr, getattr(self, attr))

s = '{\n'
- for attr in ['name', 'event']:
- s += str_if_present(self, attr)
- if self.desc is not None:
- s += attr_string('desc', self.desc)
- else:
- s += attr_string('desc', '(null)')
- s += str_if_present(self, 'compat')
s += f'\t.topic = "{topic_local}",\n'
for attr in [
- 'long_desc', 'pmu', 'unit', 'perpkg', 'aggr_mode', 'metric_expr',
- 'metric_name', 'metric_group', 'deprecated', 'metric_constraint'
+ 'aggr_mode', 'compat', 'deprecated', 'desc', 'event', 'long_desc',
+ 'metric_constraint', 'metric_expr', 'metric_group', 'metric_name',
+ 'name', 'perpkg', 'pmu', 'unit'
]:
s += str_if_present(self, attr)
s += '},\n'
--
2.37.1.455.g008518b4e5-goog

2022-07-29 07:46:43

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 06/17] perf jevents: Provide path to json file on error

If a JSONDecoderError or similar is raised then it is useful to know the
path. Print this and then raise the exception agan.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/pmu-events/jevents.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
index 98d18d5c3830..12d2daf3570c 100755
--- a/tools/perf/pmu-events/jevents.py
+++ b/tools/perf/pmu-events/jevents.py
@@ -224,7 +224,12 @@ class JsonEvent:

def read_json_events(path: str) -> Sequence[JsonEvent]:
"""Read json events from the specified file."""
- return json.load(open(path), object_hook=lambda d: JsonEvent(d))
+
+ try:
+ return json.load(open(path), object_hook=lambda d: JsonEvent(d))
+ except BaseException as err:
+ print(f"Exception processing {path}")
+ raise


def preprocess_arch_std_files(archpath: str) -> None:
--
2.37.1.455.g008518b4e5-goog

2022-07-29 07:46:51

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 07/17] perf jevents: Sort json files entries

Sort the json files entries on conversion to C. The sort order tries to
replicated cmp_sevent from pmu.c so that the input there is already
sorted except for sysfs events.

Add the topic to JsonEvent on reading to simplify. Remove an unnecessary
lambda in the json reading.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/pmu-events/jevents.py | 48 +++++++++++++++++++++++---------
1 file changed, 35 insertions(+), 13 deletions(-)

diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
index 12d2daf3570c..30e0e792221a 100755
--- a/tools/perf/pmu-events/jevents.py
+++ b/tools/perf/pmu-events/jevents.py
@@ -18,6 +18,8 @@ _sys_event_tables = []
_arch_std_events = {}
# Track whether an events table is currently being defined and needs closing.
_close_table = False
+# Events to write out when the table is closed
+_pending_events = []


def removesuffix(s: str, suffix: str) -> str:
@@ -127,6 +129,7 @@ class JsonEvent:
eventcode |= int(jd['ExtSel']) << 8
configcode = int(jd['ConfigCode'], 0) if 'ConfigCode' in jd else None
self.name = jd['EventName'].lower() if 'EventName' in jd else None
+ self.topic = ''
self.compat = jd.get('Compat')
self.desc = fixdesc(jd.get('BriefDescription'))
self.long_desc = fixdesc(jd.get('PublicDescription'))
@@ -199,7 +202,7 @@ class JsonEvent:
s += f'\t{attr} = {value},\n'
return s + '}'

- def to_c_string(self, topic_local: str) -> str:
+ def to_c_string(self) -> str:
"""Representation of the event as a C struct initializer."""

def attr_string(attr: str, value: str) -> str:
@@ -211,25 +214,27 @@ class JsonEvent:
return attr_string(attr, getattr(self, attr))

s = '{\n'
- s += f'\t.topic = "{topic_local}",\n'
for attr in [
'aggr_mode', 'compat', 'deprecated', 'desc', 'event', 'long_desc',
'metric_constraint', 'metric_expr', 'metric_group', 'metric_name',
- 'name', 'perpkg', 'pmu', 'unit'
+ 'name', 'perpkg', 'pmu', 'topic', 'unit'
]:
s += str_if_present(self, attr)
s += '},\n'
return s


-def read_json_events(path: str) -> Sequence[JsonEvent]:
+def read_json_events(path: str, topic: str) -> Sequence[JsonEvent]:
"""Read json events from the specified file."""

try:
- return json.load(open(path), object_hook=lambda d: JsonEvent(d))
+ result = json.load(open(path), object_hook=JsonEvent)
except BaseException as err:
print(f"Exception processing {path}")
raise
+ for event in result:
+ event.topic = topic
+ return result


def preprocess_arch_std_files(archpath: str) -> None:
@@ -237,7 +242,7 @@ def preprocess_arch_std_files(archpath: str) -> None:
global _arch_std_events
for item in os.scandir(archpath):
if item.is_file() and item.name.endswith('.json'):
- for event in read_json_events(item.path):
+ for event in read_json_events(item.path, topic=''):
if event.name:
_arch_std_events[event.name.lower()] = event

@@ -251,19 +256,36 @@ def print_events_table_prefix(tblname: str) -> None:
_close_table = True


-def print_events_table_entries(item: os.DirEntry, topic: str) -> None:
- """Create contents of an events table."""
+def add_events_table_entries(item: os.DirEntry, topic: str) -> None:
+ """Add contents of file to _pending_events table."""
if not _close_table:
raise IOError('Table entries missing prefix')
- for event in read_json_events(item.path):
- _args.output_file.write(event.to_c_string(topic))
+ for e in read_json_events(item.path, topic):
+ _pending_events.append(e)


def print_events_table_suffix() -> None:
"""Optionally close events table."""
+
+ def event_cmp_key(j: JsonEvent):
+ def fix_none(s: str):
+ if s is None:
+ return ''
+ return s
+
+ return (not j.desc is None, fix_none(j.topic), fix_none(j.name), fix_none(j.pmu),
+ fix_none(j.metric_name))
+
global _close_table
- if _close_table:
- _args.output_file.write("""{
+ if not _close_table:
+ return
+
+ global _pending_events
+ for event in sorted(_pending_events, key=event_cmp_key):
+ _args.output_file.write(event.to_c_string())
+ _pending_events = []
+
+ _args.output_file.write("""{
\t.name = 0,
\t.event = 0,
\t.desc = 0,
@@ -306,7 +328,7 @@ def process_one_file(parents: Sequence[str], item: os.DirEntry) -> None:
if not item.is_file() or not item.name.endswith('.json'):
return

- print_events_table_entries(item, get_topic(item.name))
+ add_events_table_entries(item, get_topic(item.name))


def print_mapping_table(archs: Sequence[str]) -> None:
--
2.37.1.455.g008518b4e5-goog

2022-07-29 07:46:54

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 08/17] perf pmu-events: Hide pmu_sys_event_tables

Move usage of the table to pmu-events.c so it may be hidden. By
abstracting the table the implementation can later be changed.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/pmu-events/empty-pmu-events.c | 37 ++++++++++++++++++-
tools/perf/pmu-events/jevents.py | 45 ++++++++++++++++++++++--
tools/perf/pmu-events/pmu-events.h | 11 +++---
tools/perf/tests/pmu-events.c | 14 +-------
tools/perf/util/pmu.c | 27 --------------
tools/perf/util/pmu.h | 2 --
6 files changed, 84 insertions(+), 52 deletions(-)

diff --git a/tools/perf/pmu-events/empty-pmu-events.c b/tools/perf/pmu-events/empty-pmu-events.c
index 4182a986f505..216ea0482c37 100644
--- a/tools/perf/pmu-events/empty-pmu-events.c
+++ b/tools/perf/pmu-events/empty-pmu-events.c
@@ -6,6 +6,8 @@
* The test cpu/soc is provided for testing.
*/
#include "pmu-events/pmu-events.h"
+#include <string.h>
+#include <stddef.h>

static const struct pmu_event pme_test_soc_cpu[] = {
{
@@ -145,7 +147,12 @@ static const struct pmu_event pme_test_soc_sys[] = {
},
};

-const struct pmu_sys_events pmu_sys_event_tables[] = {
+struct pmu_sys_events {
+ const char *name;
+ const struct pmu_event *table;
+};
+
+static const struct pmu_sys_events pmu_sys_event_tables[] = {
{
.table = pme_test_soc_sys,
.name = "pme_test_soc_sys",
@@ -154,3 +161,31 @@ const struct pmu_sys_events pmu_sys_event_tables[] = {
.table = 0
},
};
+
+const struct pmu_event *find_sys_events_table(const char *name)
+{
+ for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0];
+ tables->name;
+ tables++) {
+ if (!strcmp(tables->name, name))
+ return tables->table;
+ }
+ return NULL;
+}
+
+int pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data)
+{
+ for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0];
+ tables->name;
+ tables++) {
+ for (const struct pmu_event *pe = &tables->table[0];
+ pe->name || pe->metric_group || pe->metric_name;
+ pe++) {
+ int ret = fn(pe, data);
+
+ if (ret)
+ return ret;
+ }
+ }
+ return 0;
+}
diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
index 30e0e792221a..dd21bc9eeeed 100755
--- a/tools/perf/pmu-events/jevents.py
+++ b/tools/perf/pmu-events/jevents.py
@@ -370,8 +370,14 @@ def print_mapping_table(archs: Sequence[str]) -> None:

def print_system_mapping_table() -> None:
"""C struct mapping table array for tables from /sys directories."""
- _args.output_file.write(
- '\nconst struct pmu_sys_events pmu_sys_event_tables[] = {\n')
+ _args.output_file.write("""
+struct pmu_sys_events {
+\tconst char *name;
+\tconst struct pmu_event *table;
+};
+
+static const struct pmu_sys_events pmu_sys_event_tables[] = {
+""")
for tblname in _sys_event_tables:
_args.output_file.write(f"""\t{{
\t\t.table = {tblname},
@@ -382,6 +388,34 @@ def print_system_mapping_table() -> None:
\t\t.table = 0
\t},
};
+
+const struct pmu_event *find_sys_events_table(const char *name)
+{
+ for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0];
+ tables->name;
+ tables++) {
+ if (!strcmp(tables->name, name))
+ return tables->table;
+ }
+ return NULL;
+}
+
+int pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data)
+{
+ for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0];
+ tables->name;
+ tables++) {
+ for (const struct pmu_event *pe = &tables->table[0];
+ pe->name || pe->metric_group || pe->metric_name;
+ pe++) {
+ int ret = fn(pe, data);
+
+ if (ret)
+ return ret;
+ }
+ }
+ return 0;
+}
""")


@@ -413,7 +447,12 @@ def main() -> None:
'output_file', type=argparse.FileType('w'), nargs='?', default=sys.stdout)
_args = ap.parse_args()

- _args.output_file.write("#include \"pmu-events/pmu-events.h\"\n")
+ _args.output_file.write("""
+#include "pmu-events/pmu-events.h"
+#include <string.h>
+#include <stddef.h>
+
+""")
archs = []
for item in os.scandir(_args.starting_dir):
if not item.is_dir():
diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h
index a491b117c8ac..2386212b1df0 100644
--- a/tools/perf/pmu-events/pmu-events.h
+++ b/tools/perf/pmu-events/pmu-events.h
@@ -43,16 +43,15 @@ struct pmu_events_map {
const struct pmu_event *table;
};

-struct pmu_sys_events {
- const char *name;
- const struct pmu_event *table;
-};
-
/*
* Global table mapping each known CPU for the architecture to its
* table of PMU events.
*/
extern const struct pmu_events_map pmu_events_map[];
-extern const struct pmu_sys_events pmu_sys_event_tables[];
+
+const struct pmu_event *find_sys_events_table(const char *name);
+
+typedef int (*pmu_event_iter_fn)(const struct pmu_event *pe, void *data);
+int pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data);

#endif
diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c
index 82192f1a7bf7..a39a2c99ede6 100644
--- a/tools/perf/tests/pmu-events.c
+++ b/tools/perf/tests/pmu-events.c
@@ -286,18 +286,6 @@ static const struct pmu_events_map *__test_pmu_get_events_map(void)
return NULL;
}

-static const struct pmu_event *__test_pmu_get_sys_events_table(void)
-{
- const struct pmu_sys_events *tables = &pmu_sys_event_tables[0];
-
- for ( ; tables->name; tables++) {
- if (!strcmp("pme_test_soc_sys", tables->name))
- return tables->table;
- }
-
- return NULL;
-}
-
static int compare_pmu_events(const struct pmu_event *e1, const struct pmu_event *e2)
{
if (!is_same(e1->name, e2->name)) {
@@ -451,7 +439,7 @@ static int compare_alias_to_test_event(struct perf_pmu_alias *alias,
static int test__pmu_event_table(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
- const struct pmu_event *sys_event_tables = __test_pmu_get_sys_events_table();
+ const struct pmu_event *sys_event_tables = find_sys_events_table("pme_test_soc_sys");
const struct pmu_events_map *map = __test_pmu_get_events_map();
const struct pmu_event *table;
int map_events = 0, expected_events;
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 0112e1c36418..d8717c4548a4 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -868,33 +868,6 @@ static void pmu_add_cpu_aliases(struct list_head *head, struct perf_pmu *pmu)
pmu_add_cpu_aliases_map(head, pmu, map);
}

-void pmu_for_each_sys_event(pmu_sys_event_iter_fn fn, void *data)
-{
- int i = 0;
-
- while (1) {
- const struct pmu_sys_events *event_table;
- int j = 0;
-
- event_table = &pmu_sys_event_tables[i++];
-
- if (!event_table->table)
- break;
-
- while (1) {
- const struct pmu_event *pe = &event_table->table[j++];
- int ret;
-
- if (!pe->name && !pe->metric_group && !pe->metric_name)
- break;
-
- ret = fn(pe, data);
- if (ret)
- break;
- }
- }
-}
-
struct pmu_sys_event_iter_data {
struct list_head *head;
struct perf_pmu *pmu;
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 4b45fd8da5a3..7e667eec2a01 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -133,8 +133,6 @@ const struct pmu_events_map *pmu_events_map__find(void);
bool pmu_uncore_alias_match(const char *pmu_name, const char *name);
void perf_pmu_free_alias(struct perf_pmu_alias *alias);

-typedef int (*pmu_sys_event_iter_fn)(const struct pmu_event *pe, void *data);
-void pmu_for_each_sys_event(pmu_sys_event_iter_fn fn, void *data);
int perf_pmu__convert_scale(const char *scale, char **end, double *sval);

int perf_pmu__caps_parse(struct perf_pmu *pmu);
--
2.37.1.455.g008518b4e5-goog

2022-07-29 07:47:06

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 11/17] perf test: Use full metric resolution

The simple metric resolution doesn't handle recursion properly, switch
to use the full resolution as with the parse-metric tests which also
increases coverage. Don't set the values for the metric backward as
failures to generate a result are ignored.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/tests/pmu-events.c | 222 ++++++++++++----------------------
1 file changed, 77 insertions(+), 145 deletions(-)

diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c
index ea8b41b5c4e3..e8df6bc26ebd 100644
--- a/tools/perf/tests/pmu-events.c
+++ b/tools/perf/tests/pmu-events.c
@@ -9,10 +9,12 @@
#include <linux/zalloc.h>
#include "debug.h"
#include "../pmu-events/pmu-events.h"
+#include <perf/evlist.h>
#include "util/evlist.h"
#include "util/expr.h"
#include "util/parse-events.h"
#include "metricgroup.h"
+#include "stat.h"

struct perf_pmu_test_event {
/* used for matching against events from generated pmu-events.c */
@@ -801,27 +803,6 @@ static int check_parse_id(const char *id, struct parse_events_error *error,
return ret;
}

-static int check_parse_cpu(const char *id, bool same_cpu, const struct pmu_event *pe)
-{
- struct parse_events_error error;
- int ret;
-
- parse_events_error__init(&error);
- ret = check_parse_id(id, &error, NULL);
- if (ret && same_cpu) {
- pr_warning("Parse event failed metric '%s' id '%s' expr '%s'\n",
- pe->metric_name, id, pe->metric_expr);
- pr_warning("Error string '%s' help '%s'\n", error.str,
- error.help);
- } else if (ret) {
- pr_debug3("Parse event failed, but for an event that may not be supported by this CPU.\nid '%s' metric '%s' expr '%s'\n",
- id, pe->metric_name, pe->metric_expr);
- ret = 0;
- }
- parse_events_error__exit(&error);
- return ret;
-}
-
static int check_parse_fake(const char *id)
{
struct parse_events_error error;
@@ -838,160 +819,111 @@ struct metric {
struct metric_ref metric_ref;
};

-static int resolve_metric_simple(struct expr_parse_ctx *pctx,
- struct list_head *compound_list,
- const struct pmu_event *map,
- const char *metric_name)
-{
- struct hashmap_entry *cur, *cur_tmp;
- struct metric *metric, *tmp;
- size_t bkt;
- bool all;
- int rc;
-
- do {
- all = true;
- hashmap__for_each_entry_safe(pctx->ids, cur, cur_tmp, bkt) {
- struct metric_ref *ref;
- const struct pmu_event *pe;
-
- pe = metricgroup__find_metric(cur->key, map);
- if (!pe)
- continue;
-
- if (!strcmp(metric_name, (char *)cur->key)) {
- pr_warning("Recursion detected for metric %s\n", metric_name);
- rc = -1;
- goto out_err;
- }
-
- all = false;
-
- /* The metric key itself needs to go out.. */
- expr__del_id(pctx, cur->key);
-
- metric = malloc(sizeof(*metric));
- if (!metric) {
- rc = -ENOMEM;
- goto out_err;
- }
-
- ref = &metric->metric_ref;
- ref->metric_name = pe->metric_name;
- ref->metric_expr = pe->metric_expr;
- list_add_tail(&metric->list, compound_list);
-
- rc = expr__find_ids(pe->metric_expr, NULL, pctx);
- if (rc)
- goto out_err;
- break; /* The hashmap has been modified, so restart */
- }
- } while (!all);
-
- return 0;
-
-out_err:
- list_for_each_entry_safe(metric, tmp, compound_list, list)
- free(metric);
-
- return rc;
-
-}
-
-static void expr_failure(const char *msg, const struct pmu_event *pe)
-{
- pr_debug("%s\nOn metric %s\nOn expression %s\n", msg, pe->metric_name, pe->metric_expr);
-}
-
-
-struct test__parsing_data {
- const struct pmu_event *cpus_table;
- struct expr_parse_ctx *ctx;
- int failures;
-};
-
static int test__parsing_callback(const struct pmu_event *pe, const struct pmu_event *table,
- void *vdata)
+ void *data)
{
- struct test__parsing_data *data = vdata;
- struct metric *metric, *tmp;
- struct hashmap_entry *cur;
- LIST_HEAD(compound_list);
- size_t bkt;
+ int *failures = data;
int k;
- double result;
+ struct evlist *evlist;
+ struct perf_cpu_map *cpus;
+ struct runtime_stat st;
+ struct evsel *evsel;
+ struct rblist metric_events = {
+ .nr_entries = 0,
+ };
+ int err = 0;

if (!pe->metric_expr)
return 0;

pr_debug("Found metric '%s'\n", pe->metric_name);
+ (*failures)++;

- expr__ctx_clear(data->ctx);
- if (expr__find_ids(pe->metric_expr, NULL, data->ctx) < 0) {
- expr_failure("Parse find ids failed", pe);
- data->failures++;
- return 0;
+ /*
+ * We need to prepare evlist for stat mode running on CPU 0
+ * because that's where all the stats are going to be created.
+ */
+ evlist = evlist__new();
+ if (!evlist)
+ return -ENOMEM;
+
+ cpus = perf_cpu_map__new("0");
+ if (!cpus) {
+ evlist__delete(evlist);
+ return -ENOMEM;
}

- if (resolve_metric_simple(data->ctx, &compound_list, table,
- pe->metric_name)) {
- expr_failure("Could not resolve metrics", pe);
- data->failures++;
- return TEST_FAIL; /* Don't tolerate errors due to severity */
+ perf_evlist__set_maps(&evlist->core, cpus, NULL);
+ runtime_stat__init(&st);
+
+ err = metricgroup__parse_groups_test(evlist, table, pe->metric_name,
+ false, false,
+ &metric_events);
+ if (err) {
+ if (!strcmp(pe->metric_name, "M1") || !strcmp(pe->metric_name, "M2") ||
+ !strcmp(pe->metric_name, "M3")) {
+ (*failures)--;
+ pr_debug("Expected broken metric %s skipping\n", pe->metric_name);
+ err = 0;
+ }
+ goto out_err;
}

+ err = evlist__alloc_stats(evlist, false);
+ if (err)
+ goto out_err;
/*
* Add all ids with a made up value. The value may trigger divide by
* zero when subtracted and so try to make them unique.
*/
k = 1;
- hashmap__for_each_entry(data->ctx->ids, cur, bkt)
- expr__add_id_val(data->ctx, strdup(cur->key), k++);
-
- hashmap__for_each_entry(data->ctx->ids, cur, bkt) {
- if (check_parse_cpu(cur->key, table == data->cpus_table, pe))
- data->failures++;
+ perf_stat__reset_shadow_stats();
+ evlist__for_each_entry(evlist, evsel) {
+ perf_stat__update_shadow_stats(evsel, k, 0, &st);
+ if (!strcmp(evsel->name, "duration_time"))
+ update_stats(&walltime_nsecs_stats, k);
+ k++;
}
+ evlist__for_each_entry(evlist, evsel) {
+ struct metric_event *me = metricgroup__lookup(&metric_events, evsel, false);

- list_for_each_entry_safe(metric, tmp, &compound_list, list) {
- expr__add_ref(data->ctx, &metric->metric_ref);
- free(metric);
- }
+ if (me != NULL) {
+ struct metric_expr *mexp;

- if (expr__parse(&result, data->ctx, pe->metric_expr)) {
- /*
- * Parsing failed, make numbers go from large to small which can
- * resolve divide by zero issues.
- */
- k = 1024;
- hashmap__for_each_entry(data->ctx->ids, cur, bkt)
- expr__add_id_val(data->ctx, strdup(cur->key), k--);
- if (expr__parse(&result, data->ctx, pe->metric_expr)) {
- expr_failure("Parse failed", pe);
- data->failures++;
+ list_for_each_entry (mexp, &me->head, nd) {
+ if (strcmp(mexp->metric_name, pe->metric_name))
+ continue;
+ pr_debug("Result %f\n", test_generic_metric(mexp, 0, &st));
+ err = 0;
+ (*failures)--;
+ goto out_err;
+ }
}
}
- return 0;
+ pr_debug("Didn't find parsed metric %s", pe->metric_name);
+ err = 1;
+out_err:
+ if (err)
+ pr_debug("Broken metric %s\n", pe->metric_name);
+
+ /* ... cleanup. */
+ metricgroup__rblist_exit(&metric_events);
+ runtime_stat__exit(&st);
+ evlist__free_stats(evlist);
+ perf_cpu_map__put(cpus);
+ evlist__delete(evlist);
+ return err;
}

static int test__parsing(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
- struct test__parsing_data data = {
- .cpus_table = pmu_events_map__find(),
- .ctx = expr__ctx_new(),
- .failures = 0,
- };
+ int failures = 0;

- if (!data.ctx) {
- pr_debug("expr__ctx_new failed");
- return TEST_FAIL;
- }
- pmu_for_each_core_event(test__parsing_callback, &data);
- pmu_for_each_sys_event(test__parsing_callback, &data);
+ pmu_for_each_core_event(test__parsing_callback, &failures);
+ pmu_for_each_sys_event(test__parsing_callback, &failures);

- expr__ctx_free(data.ctx);
- return data.failures == 0 ? TEST_OK : TEST_FAIL;
+ return failures == 0 ? TEST_OK : TEST_FAIL;
}

struct test_metric {
--
2.37.1.455.g008518b4e5-goog

2022-07-29 07:47:09

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 12/17] perf pmu-events: Move test events/metrics to json

Move arrays of pmu_events into the json code so that it may be
regenerated and modified by the jevents.py script.

Signed-off-by: Ian Rogers <[email protected]>
---
.../arch/test/test_soc/cpu/metrics.json | 64 +++++++++++++++++
tools/perf/pmu-events/empty-pmu-events.c | 64 +++++++++++++++++
tools/perf/tests/expand-cgroup.c | 17 +----
tools/perf/tests/parse-metric.c | 68 +------------------
4 files changed, 132 insertions(+), 81 deletions(-)
create mode 100644 tools/perf/pmu-events/arch/test/test_soc/cpu/metrics.json

diff --git a/tools/perf/pmu-events/arch/test/test_soc/cpu/metrics.json b/tools/perf/pmu-events/arch/test/test_soc/cpu/metrics.json
new file mode 100644
index 000000000000..42d9b5242fd7
--- /dev/null
+++ b/tools/perf/pmu-events/arch/test/test_soc/cpu/metrics.json
@@ -0,0 +1,64 @@
+[
+ {
+ "MetricExpr": "1 / IPC",
+ "MetricName": "CPI"
+ },
+ {
+ "MetricExpr": "inst_retired.any / cpu_clk_unhalted.thread",
+ "MetricName": "IPC",
+ "MetricGroup": "group1"
+ },
+ {
+ "MetricExpr": "idq_uops_not_delivered.core / (4 * (( ( cpu_clk_unhalted.thread / 2 ) * ( 1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk ) )))",
+ "MetricName": "Frontend_Bound_SMT"
+ },
+ {
+ "MetricExpr": "l1d\\-loads\\-misses / inst_retired.any",
+ "MetricName": "dcache_miss_cpi"
+ },
+ {
+ "MetricExpr": "l1i\\-loads\\-misses / inst_retired.any",
+ "MetricName": "icache_miss_cycles"
+ },
+ {
+ "MetricExpr": "(dcache_miss_cpi + icache_miss_cycles)",
+ "MetricName": "cache_miss_cycles",
+ "MetricGroup": "group1"
+ },
+ {
+ "MetricExpr": "l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit",
+ "MetricName": "DCache_L2_All_Hits"
+ },
+ {
+ "MetricExpr": "max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + l2_rqsts.pf_miss + l2_rqsts.rfo_miss",
+ "MetricName": "DCache_L2_All_Miss"
+ },
+ {
+ "MetricExpr": "dcache_l2_all_hits + dcache_l2_all_miss",
+ "MetricName": "DCache_L2_All"
+ },
+ {
+ "MetricExpr": "d_ratio(dcache_l2_all_hits, dcache_l2_all)",
+ "MetricName": "DCache_L2_Hits"
+ },
+ {
+ "MetricExpr": "d_ratio(dcache_l2_all_miss, dcache_l2_all)",
+ "MetricName": "DCache_L2_Misses"
+ },
+ {
+ "MetricExpr": "ipc + M2",
+ "MetricName": "M1"
+ },
+ {
+ "MetricExpr": "ipc + M1",
+ "MetricName": "M2"
+ },
+ {
+ "MetricExpr": "1/M3",
+ "MetricName": "M3"
+ },
+ {
+ "MetricExpr": "64 * l1d.replacement / 1000000000 / duration_time",
+ "MetricName": "L1D_Cache_Fill_BW"
+ }
+]
diff --git a/tools/perf/pmu-events/empty-pmu-events.c b/tools/perf/pmu-events/empty-pmu-events.c
index 8ef75aff996c..028f44efe48d 100644
--- a/tools/perf/pmu-events/empty-pmu-events.c
+++ b/tools/perf/pmu-events/empty-pmu-events.c
@@ -105,6 +105,70 @@ static const struct pmu_event pme_test_soc_cpu[] = {
.desc = "L2 BTB Correction",
.topic = "branch",
},
+ {
+ .metric_expr = "1 / IPC",
+ .metric_name = "CPI",
+ },
+ {
+ .metric_expr = "inst_retired.any / cpu_clk_unhalted.thread",
+ .metric_name = "IPC",
+ .metric_group = "group1",
+ },
+ {
+ .metric_expr = "idq_uops_not_delivered.core / (4 * (( ( cpu_clk_unhalted.thread / 2 ) * "
+ "( 1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk ) )))",
+ .metric_name = "Frontend_Bound_SMT",
+ },
+ {
+ .metric_expr = "l1d\\-loads\\-misses / inst_retired.any",
+ .metric_name = "dcache_miss_cpi",
+ },
+ {
+ .metric_expr = "l1i\\-loads\\-misses / inst_retired.any",
+ .metric_name = "icache_miss_cycles",
+ },
+ {
+ .metric_expr = "(dcache_miss_cpi + icache_miss_cycles)",
+ .metric_name = "cache_miss_cycles",
+ .metric_group = "group1",
+ },
+ {
+ .metric_expr = "l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit",
+ .metric_name = "DCache_L2_All_Hits",
+ },
+ {
+ .metric_expr = "max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + "
+ "l2_rqsts.pf_miss + l2_rqsts.rfo_miss",
+ .metric_name = "DCache_L2_All_Miss",
+ },
+ {
+ .metric_expr = "dcache_l2_all_hits + dcache_l2_all_miss",
+ .metric_name = "DCache_L2_All",
+ },
+ {
+ .metric_expr = "d_ratio(dcache_l2_all_hits, dcache_l2_all)",
+ .metric_name = "DCache_L2_Hits",
+ },
+ {
+ .metric_expr = "d_ratio(dcache_l2_all_miss, dcache_l2_all)",
+ .metric_name = "DCache_L2_Misses",
+ },
+ {
+ .metric_expr = "ipc + M2",
+ .metric_name = "M1",
+ },
+ {
+ .metric_expr = "ipc + M1",
+ .metric_name = "M2",
+ },
+ {
+ .metric_expr = "1/M3",
+ .metric_name = "M3",
+ },
+ {
+ .metric_expr = "64 * l1d.replacement / 1000000000 / duration_time",
+ .metric_name = "L1D_Cache_Fill_BW",
+ },
{
.name = 0,
.event = 0,
diff --git a/tools/perf/tests/expand-cgroup.c b/tools/perf/tests/expand-cgroup.c
index 411fc578e5a4..e79ee8621a90 100644
--- a/tools/perf/tests/expand-cgroup.c
+++ b/tools/perf/tests/expand-cgroup.c
@@ -180,26 +180,13 @@ static int expand_metric_events(void)
struct evlist *evlist;
struct rblist metric_events;
const char metric_str[] = "CPI";
-
- struct pmu_event pme_test[] = {
- {
- .metric_expr = "instructions / cycles",
- .metric_name = "IPC",
- },
- {
- .metric_expr = "1 / IPC",
- .metric_name = "CPI",
- },
- {
- .metric_expr = NULL,
- .metric_name = NULL,
- },
- };
+ const struct pmu_event *pme_test;

evlist = evlist__new();
TEST_ASSERT_VAL("failed to get evlist", evlist);

rblist__init(&metric_events);
+ pme_test = find_core_events_table("testarch", "testcpu");
ret = metricgroup__parse_groups_test(evlist, pme_test, metric_str,
false, false, &metric_events);
if (ret < 0) {
diff --git a/tools/perf/tests/parse-metric.c b/tools/perf/tests/parse-metric.c
index 7aebde7c37ec..30c7091857b8 100644
--- a/tools/perf/tests/parse-metric.c
+++ b/tools/perf/tests/parse-metric.c
@@ -13,72 +13,6 @@
#include "stat.h"
#include "pmu.h"

-static struct pmu_event pme_test[] = {
-{
- .metric_expr = "inst_retired.any / cpu_clk_unhalted.thread",
- .metric_name = "IPC",
- .metric_group = "group1",
-},
-{
- .metric_expr = "idq_uops_not_delivered.core / (4 * (( ( cpu_clk_unhalted.thread / 2 ) * "
- "( 1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk ) )))",
- .metric_name = "Frontend_Bound_SMT",
-},
-{
- .metric_expr = "l1d\\-loads\\-misses / inst_retired.any",
- .metric_name = "dcache_miss_cpi",
-},
-{
- .metric_expr = "l1i\\-loads\\-misses / inst_retired.any",
- .metric_name = "icache_miss_cycles",
-},
-{
- .metric_expr = "(dcache_miss_cpi + icache_miss_cycles)",
- .metric_name = "cache_miss_cycles",
- .metric_group = "group1",
-},
-{
- .metric_expr = "l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit",
- .metric_name = "DCache_L2_All_Hits",
-},
-{
- .metric_expr = "max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + "
- "l2_rqsts.pf_miss + l2_rqsts.rfo_miss",
- .metric_name = "DCache_L2_All_Miss",
-},
-{
- .metric_expr = "dcache_l2_all_hits + dcache_l2_all_miss",
- .metric_name = "DCache_L2_All",
-},
-{
- .metric_expr = "d_ratio(dcache_l2_all_hits, dcache_l2_all)",
- .metric_name = "DCache_L2_Hits",
-},
-{
- .metric_expr = "d_ratio(dcache_l2_all_miss, dcache_l2_all)",
- .metric_name = "DCache_L2_Misses",
-},
-{
- .metric_expr = "ipc + m2",
- .metric_name = "M1",
-},
-{
- .metric_expr = "ipc + m1",
- .metric_name = "M2",
-},
-{
- .metric_expr = "1/m3",
- .metric_name = "M3",
-},
-{
- .metric_expr = "64 * l1d.replacement / 1000000000 / duration_time",
- .metric_name = "L1D_Cache_Fill_BW",
-},
-{
- .name = NULL,
-}
-};
-
struct value {
const char *event;
u64 val;
@@ -138,6 +72,7 @@ static int __compute_metric(const char *name, struct value *vals,
struct rblist metric_events = {
.nr_entries = 0,
};
+ const struct pmu_event *pme_test;
struct perf_cpu_map *cpus;
struct runtime_stat st;
struct evlist *evlist;
@@ -161,6 +96,7 @@ static int __compute_metric(const char *name, struct value *vals,
runtime_stat__init(&st);

/* Parse the metric into metric_events list. */
+ pme_test = find_core_events_table("testarch", "testcpu");
err = metricgroup__parse_groups_test(evlist, pme_test, name,
false, false,
&metric_events);
--
2.37.1.455.g008518b4e5-goog

2022-07-29 07:47:42

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 09/17] perf pmu-events: Avoid passing pmu_events_map

Preparation for hiding pmu_events_map as an implementation detail. While
the map is passed, the table of events is all that is normally wanted.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/arch/arm64/util/pmu.c | 4 +-
tools/perf/tests/expand-cgroup.c | 6 +--
tools/perf/tests/parse-metric.c | 7 +--
tools/perf/tests/pmu-events.c | 63 +++++++++++-------------
tools/perf/util/metricgroup.c | 82 +++++++++++++++----------------
tools/perf/util/metricgroup.h | 4 +-
tools/perf/util/pmu.c | 33 +++++++------
tools/perf/util/pmu.h | 6 +--
tools/perf/util/s390-sample-raw.c | 12 ++---
9 files changed, 99 insertions(+), 118 deletions(-)

diff --git a/tools/perf/arch/arm64/util/pmu.c b/tools/perf/arch/arm64/util/pmu.c
index 79124bba713e..646af8603227 100644
--- a/tools/perf/arch/arm64/util/pmu.c
+++ b/tools/perf/arch/arm64/util/pmu.c
@@ -3,7 +3,7 @@
#include "../../../util/cpumap.h"
#include "../../../util/pmu.h"

-const struct pmu_events_map *pmu_events_map__find(void)
+const struct pmu_event *pmu_events_map__find(void)
{
struct perf_pmu *pmu = NULL;

@@ -18,7 +18,7 @@ const struct pmu_events_map *pmu_events_map__find(void)
if (pmu->cpus->nr != cpu__max_cpu().cpu)
return NULL;

- return perf_pmu__find_map(pmu);
+ return perf_pmu__find_table(pmu);
}

return NULL;
diff --git a/tools/perf/tests/expand-cgroup.c b/tools/perf/tests/expand-cgroup.c
index dc4038f997d7..411fc578e5a4 100644
--- a/tools/perf/tests/expand-cgroup.c
+++ b/tools/perf/tests/expand-cgroup.c
@@ -195,16 +195,12 @@ static int expand_metric_events(void)
.metric_name = NULL,
},
};
- const struct pmu_events_map ev_map = {
- .cpuid = "test",
- .table = pme_test,
- };

evlist = evlist__new();
TEST_ASSERT_VAL("failed to get evlist", evlist);

rblist__init(&metric_events);
- ret = metricgroup__parse_groups_test(evlist, &ev_map, metric_str,
+ ret = metricgroup__parse_groups_test(evlist, pme_test, metric_str,
false, false, &metric_events);
if (ret < 0) {
pr_debug("failed to parse '%s' metric\n", metric_str);
diff --git a/tools/perf/tests/parse-metric.c b/tools/perf/tests/parse-metric.c
index 1b811a26f4ee..7aebde7c37ec 100644
--- a/tools/perf/tests/parse-metric.c
+++ b/tools/perf/tests/parse-metric.c
@@ -79,11 +79,6 @@ static struct pmu_event pme_test[] = {
}
};

-static const struct pmu_events_map map = {
- .cpuid = "test",
- .table = pme_test,
-};
-
struct value {
const char *event;
u64 val;
@@ -166,7 +161,7 @@ static int __compute_metric(const char *name, struct value *vals,
runtime_stat__init(&st);

/* Parse the metric into metric_events list. */
- err = metricgroup__parse_groups_test(evlist, &map, name,
+ err = metricgroup__parse_groups_test(evlist, pme_test, name,
false, false,
&metric_events);
if (err)
diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c
index a39a2c99ede6..b3cde5f98982 100644
--- a/tools/perf/tests/pmu-events.c
+++ b/tools/perf/tests/pmu-events.c
@@ -272,13 +272,11 @@ static bool is_same(const char *reference, const char *test)
return !strcmp(reference, test);
}

-static const struct pmu_events_map *__test_pmu_get_events_map(void)
+static const struct pmu_event *__test_pmu_get_events_table(void)
{
- const struct pmu_events_map *map;
-
- for (map = &pmu_events_map[0]; map->cpuid; map++) {
+ for (const struct pmu_events_map *map = &pmu_events_map[0]; map->cpuid; map++) {
if (!strcmp(map->cpuid, "testcpu"))
- return map;
+ return map->table;
}

pr_err("could not find test events map\n");
@@ -440,8 +438,7 @@ static int test__pmu_event_table(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
const struct pmu_event *sys_event_tables = find_sys_events_table("pme_test_soc_sys");
- const struct pmu_events_map *map = __test_pmu_get_events_map();
- const struct pmu_event *table;
+ const struct pmu_event *table = __test_pmu_get_events_table();
int map_events = 0, expected_events;

/* ignore 3x sentinels */
@@ -449,10 +446,10 @@ static int test__pmu_event_table(struct test_suite *test __maybe_unused,
ARRAY_SIZE(uncore_events) +
ARRAY_SIZE(sys_events) - 3;

- if (!map || !sys_event_tables)
+ if (!table || !sys_event_tables)
return -1;

- for (table = map->table; table->name; table++) {
+ for (; table->name; table++) {
struct perf_pmu_test_event const **test_event_table;
bool found = false;

@@ -537,10 +534,10 @@ static int __test_core_pmu_event_aliases(char *pmu_name, int *count)
struct perf_pmu *pmu;
LIST_HEAD(aliases);
int res = 0;
- const struct pmu_events_map *map = __test_pmu_get_events_map();
+ const struct pmu_event *table = __test_pmu_get_events_table();
struct perf_pmu_alias *a, *tmp;

- if (!map)
+ if (!table)
return -1;

test_event_table = &core_events[0];
@@ -551,7 +548,7 @@ static int __test_core_pmu_event_aliases(char *pmu_name, int *count)

pmu->name = pmu_name;

- pmu_add_cpu_aliases_map(&aliases, pmu, map);
+ pmu_add_cpu_aliases_map(&aliases, pmu, table);

for (; *test_event_table; test_event_table++) {
struct perf_pmu_test_event const *test_event = *test_event_table;
@@ -590,14 +587,14 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu)
struct perf_pmu *pmu = &test_pmu->pmu;
const char *pmu_name = pmu->name;
struct perf_pmu_alias *a, *tmp, *alias;
- const struct pmu_events_map *map;
+ const struct pmu_event *events_table;
LIST_HEAD(aliases);
int res = 0;

- map = __test_pmu_get_events_map();
- if (!map)
+ events_table = __test_pmu_get_events_table();
+ if (!events_table)
return -1;
- pmu_add_cpu_aliases_map(&aliases, pmu, map);
+ pmu_add_cpu_aliases_map(&aliases, pmu, events_table);
pmu_add_sys_aliases(&aliases, pmu);

/* Count how many aliases we generated */
@@ -848,13 +845,9 @@ static int check_parse_fake(const char *id)
return ret;
}

-static void expr_failure(const char *msg,
- const struct pmu_events_map *map,
- const struct pmu_event *pe)
+static void expr_failure(const char *msg, const struct pmu_event *pe)
{
- pr_debug("%s for map %s %s\n", msg, map->arch, map->cpuid);
- pr_debug("On metric %s\n", pe->metric_name);
- pr_debug("On expression %s\n", pe->metric_expr);
+ pr_debug("%s\nOn metric %s\nOn expression %s\n", msg, pe->metric_name, pe->metric_expr);
}

struct metric {
@@ -864,7 +857,7 @@ struct metric {

static int resolve_metric_simple(struct expr_parse_ctx *pctx,
struct list_head *compound_list,
- const struct pmu_events_map *map,
+ const struct pmu_event *map,
const char *metric_name)
{
struct hashmap_entry *cur, *cur_tmp;
@@ -925,8 +918,7 @@ static int resolve_metric_simple(struct expr_parse_ctx *pctx,
static int test__parsing(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
- const struct pmu_events_map *cpus_map = pmu_events_map__find();
- const struct pmu_events_map *map;
+ const struct pmu_event *cpus_table = pmu_events_map__find();
const struct pmu_event *pe;
int i, j, k;
int ret = 0;
@@ -940,7 +932,8 @@ static int test__parsing(struct test_suite *test __maybe_unused,
}
i = 0;
for (;;) {
- map = &pmu_events_map[i++];
+ const struct pmu_events_map *map = &pmu_events_map[i++];
+
if (!map->table)
break;
j = 0;
@@ -957,14 +950,14 @@ static int test__parsing(struct test_suite *test __maybe_unused,
continue;
expr__ctx_clear(ctx);
if (expr__find_ids(pe->metric_expr, NULL, ctx) < 0) {
- expr_failure("Parse find ids failed", map, pe);
+ expr_failure("Parse find ids failed", pe);
ret++;
continue;
}

- if (resolve_metric_simple(ctx, &compound_list, map,
+ if (resolve_metric_simple(ctx, &compound_list, map->table,
pe->metric_name)) {
- expr_failure("Could not resolve metrics", map, pe);
+ expr_failure("Could not resolve metrics", pe);
ret++;
goto exit; /* Don't tolerate errors due to severity */
}
@@ -979,7 +972,7 @@ static int test__parsing(struct test_suite *test __maybe_unused,
expr__add_id_val(ctx, strdup(cur->key), k++);

hashmap__for_each_entry(ctx->ids, cur, bkt) {
- if (check_parse_cpu(cur->key, map == cpus_map,
+ if (check_parse_cpu(cur->key, map->table == cpus_table,
pe))
ret++;
}
@@ -999,7 +992,7 @@ static int test__parsing(struct test_suite *test __maybe_unused,
hashmap__for_each_entry(ctx->ids, cur, bkt)
expr__add_id_val(ctx, strdup(cur->key), k--);
if (expr__parse(&result, ctx, pe->metric_expr)) {
- expr_failure("Parse failed", map, pe);
+ expr_failure("Parse failed", pe);
ret++;
}
}
@@ -1088,8 +1081,6 @@ static int metric_parse_fake(const char *str)
static int test__parsing_fake(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
- const struct pmu_events_map *map;
- const struct pmu_event *pe;
unsigned int i, j;
int err = 0;

@@ -1101,12 +1092,14 @@ static int test__parsing_fake(struct test_suite *test __maybe_unused,

i = 0;
for (;;) {
- map = &pmu_events_map[i++];
+ const struct pmu_events_map *map = &pmu_events_map[i++];
+
if (!map->table)
break;
j = 0;
for (;;) {
- pe = &map->table[j++];
+ const struct pmu_event *pe = &map->table[j++];
+
if (!pe->name && !pe->metric_group && !pe->metric_name)
break;
if (!pe->metric_expr)
diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index 8f7baeabc5cf..4d32b4fbf67d 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -539,9 +539,6 @@ static int metricgroup__print_sys_event_iter(const struct pmu_event *pe, void *d
void metricgroup__print(bool metrics, bool metricgroups, char *filter,
bool raw, bool details, const char *pmu_name)
{
- const struct pmu_events_map *map = pmu_events_map__find();
- const struct pmu_event *pe;
- int i;
struct rblist groups;
struct rb_node *node, *next;
struct strlist *metriclist = NULL;
@@ -556,8 +553,7 @@ void metricgroup__print(bool metrics, bool metricgroups, char *filter,
groups.node_new = mep_new;
groups.node_cmp = mep_cmp;
groups.node_delete = mep_delete;
- for (i = 0; map; i++) {
- pe = &map->table[i];
+ for (const struct pmu_event *pe = pmu_events_map__find(); pe; pe++) {

if (!pe->name && !pe->metric_group && !pe->metric_name)
break;
@@ -850,7 +846,7 @@ struct metricgroup_add_iter_data {
bool metric_no_group;
struct metric *root_metric;
const struct visited_metric *visited;
- const struct pmu_events_map *map;
+ const struct pmu_event *table;
};

static int add_metric(struct list_head *metric_list,
@@ -859,7 +855,7 @@ static int add_metric(struct list_head *metric_list,
bool metric_no_group,
struct metric *root_metric,
const struct visited_metric *visited,
- const struct pmu_events_map *map);
+ const struct pmu_event *table);

/**
* resolve_metric - Locate metrics within the root metric and recursively add
@@ -874,7 +870,7 @@ static int add_metric(struct list_head *metric_list,
* metrics. When adding a root this argument is NULL.
* @visited: A singly linked list of metric names being added that is used to
* detect recursion.
- * @map: The map that is searched for metrics, most commonly the table for the
+ * @table: The table that is searched for metrics, most commonly the table for the
* architecture perf is running upon.
*/
static int resolve_metric(struct list_head *metric_list,
@@ -882,7 +878,7 @@ static int resolve_metric(struct list_head *metric_list,
bool metric_no_group,
struct metric *root_metric,
const struct visited_metric *visited,
- const struct pmu_events_map *map)
+ const struct pmu_event *table)
{
struct hashmap_entry *cur;
size_t bkt;
@@ -904,7 +900,7 @@ static int resolve_metric(struct list_head *metric_list,
hashmap__for_each_entry(root_metric->pctx->ids, cur, bkt) {
const struct pmu_event *pe;

- pe = metricgroup__find_metric(cur->key, map);
+ pe = metricgroup__find_metric(cur->key, table);
if (pe) {
pending = realloc(pending,
(pending_cnt + 1) * sizeof(struct to_resolve));
@@ -927,7 +923,7 @@ static int resolve_metric(struct list_head *metric_list,
*/
for (i = 0; i < pending_cnt; i++) {
ret = add_metric(metric_list, pending[i].pe, modifier, metric_no_group,
- root_metric, visited, map);
+ root_metric, visited, table);
if (ret)
break;
}
@@ -950,7 +946,7 @@ static int resolve_metric(struct list_head *metric_list,
* metrics. When adding a root this argument is NULL.
* @visited: A singly linked list of metric names being added that is used to
* detect recursion.
- * @map: The map that is searched for metrics, most commonly the table for the
+ * @table: The table that is searched for metrics, most commonly the table for the
* architecture perf is running upon.
*/
static int __add_metric(struct list_head *metric_list,
@@ -960,7 +956,7 @@ static int __add_metric(struct list_head *metric_list,
int runtime,
struct metric *root_metric,
const struct visited_metric *visited,
- const struct pmu_events_map *map)
+ const struct pmu_event *table)
{
const struct visited_metric *vm;
int ret;
@@ -1032,7 +1028,7 @@ static int __add_metric(struct list_head *metric_list,
} else {
/* Resolve referenced metrics. */
ret = resolve_metric(metric_list, modifier, metric_no_group, root_metric,
- &visited_node, map);
+ &visited_node, table);
}

if (ret) {
@@ -1045,25 +1041,25 @@ static int __add_metric(struct list_head *metric_list,
return ret;
}

-#define map_for_each_event(__pe, __idx, __map) \
- if (__map) \
- for (__idx = 0, __pe = &__map->table[__idx]; \
+#define table_for_each_event(__pe, __idx, __table) \
+ if (__table) \
+ for (__idx = 0, __pe = &__table[__idx]; \
__pe->name || __pe->metric_group || __pe->metric_name; \
- __pe = &__map->table[++__idx])
+ __pe = &__table[++__idx])

-#define map_for_each_metric(__pe, __idx, __map, __metric) \
- map_for_each_event(__pe, __idx, __map) \
+#define table_for_each_metric(__pe, __idx, __table, __metric) \
+ table_for_each_event(__pe, __idx, __table) \
if (__pe->metric_expr && \
(match_metric(__pe->metric_group, __metric) || \
match_metric(__pe->metric_name, __metric)))

const struct pmu_event *metricgroup__find_metric(const char *metric,
- const struct pmu_events_map *map)
+ const struct pmu_event *table)
{
const struct pmu_event *pe;
int i;

- map_for_each_event(pe, i, map) {
+ table_for_each_event(pe, i, table) {
if (match_metric(pe->metric_name, metric))
return pe;
}
@@ -1077,7 +1073,7 @@ static int add_metric(struct list_head *metric_list,
bool metric_no_group,
struct metric *root_metric,
const struct visited_metric *visited,
- const struct pmu_events_map *map)
+ const struct pmu_event *table)
{
int ret = 0;

@@ -1085,7 +1081,7 @@ static int add_metric(struct list_head *metric_list,

if (!strstr(pe->metric_expr, "?")) {
ret = __add_metric(metric_list, pe, modifier, metric_no_group, 0,
- root_metric, visited, map);
+ root_metric, visited, table);
} else {
int j, count;

@@ -1098,7 +1094,7 @@ static int add_metric(struct list_head *metric_list,

for (j = 0; j < count && !ret; j++)
ret = __add_metric(metric_list, pe, modifier, metric_no_group, j,
- root_metric, visited, map);
+ root_metric, visited, table);
}

return ret;
@@ -1114,7 +1110,7 @@ static int metricgroup__add_metric_sys_event_iter(const struct pmu_event *pe,
return 0;

ret = add_metric(d->metric_list, pe, d->modifier, d->metric_no_group,
- d->root_metric, d->visited, d->map);
+ d->root_metric, d->visited, d->table);
if (ret)
goto out;

@@ -1162,13 +1158,13 @@ static int metric_list_cmp(void *priv __maybe_unused, const struct list_head *l,
* global. Grouping is the default but due to multiplexing the
* user may override.
* @metric_list: The list that the metric or metric group are added to.
- * @map: The map that is searched for metrics, most commonly the table for the
+ * @table: The table that is searched for metrics, most commonly the table for the
* architecture perf is running upon.
*/
static int metricgroup__add_metric(const char *metric_name, const char *modifier,
bool metric_no_group,
struct list_head *metric_list,
- const struct pmu_events_map *map)
+ const struct pmu_event *table)
{
const struct pmu_event *pe;
LIST_HEAD(list);
@@ -1179,11 +1175,11 @@ static int metricgroup__add_metric(const char *metric_name, const char *modifier
* Iterate over all metrics seeing if metric matches either the name or
* group. When it does add the metric to the list.
*/
- map_for_each_metric(pe, i, map, metric_name) {
+ table_for_each_metric(pe, i, table, metric_name) {
has_match = true;
ret = add_metric(&list, pe, modifier, metric_no_group,
/*root_metric=*/NULL,
- /*visited_metrics=*/NULL, map);
+ /*visited_metrics=*/NULL, table);
if (ret)
goto out;
}
@@ -1198,7 +1194,7 @@ static int metricgroup__add_metric(const char *metric_name, const char *modifier
.metric_no_group = metric_no_group,
.has_match = &has_match,
.ret = &ret,
- .map = map,
+ .table = table,
},
};

@@ -1227,12 +1223,12 @@ static int metricgroup__add_metric(const char *metric_name, const char *modifier
* global. Grouping is the default but due to multiplexing the
* user may override.
* @metric_list: The list that metrics are added to.
- * @map: The map that is searched for metrics, most commonly the table for the
+ * @table: The table that is searched for metrics, most commonly the table for the
* architecture perf is running upon.
*/
static int metricgroup__add_metric_list(const char *list, bool metric_no_group,
struct list_head *metric_list,
- const struct pmu_events_map *map)
+ const struct pmu_event *table)
{
char *list_itr, *list_copy, *metric_name, *modifier;
int ret, count = 0;
@@ -1249,7 +1245,7 @@ static int metricgroup__add_metric_list(const char *list, bool metric_no_group,

ret = metricgroup__add_metric(metric_name, modifier,
metric_no_group, metric_list,
- map);
+ table);
if (ret == -EINVAL)
pr_err("Cannot find metric or group `%s'\n", metric_name);

@@ -1440,7 +1436,7 @@ static int parse_groups(struct evlist *perf_evlist, const char *str,
bool metric_no_merge,
struct perf_pmu *fake_pmu,
struct rblist *metric_events_list,
- const struct pmu_events_map *map)
+ const struct pmu_event *table)
{
struct evlist *combined_evlist = NULL;
LIST_HEAD(metric_list);
@@ -1451,7 +1447,7 @@ static int parse_groups(struct evlist *perf_evlist, const char *str,
if (metric_events_list->nr_entries == 0)
metricgroup__rblist_init(metric_events_list);
ret = metricgroup__add_metric_list(str, metric_no_group,
- &metric_list, map);
+ &metric_list, table);
if (ret)
goto out;

@@ -1586,34 +1582,34 @@ int metricgroup__parse_groups(const struct option *opt,
struct rblist *metric_events)
{
struct evlist *perf_evlist = *(struct evlist **)opt->value;
- const struct pmu_events_map *map = pmu_events_map__find();
+ const struct pmu_event *table = pmu_events_map__find();

return parse_groups(perf_evlist, str, metric_no_group,
- metric_no_merge, NULL, metric_events, map);
+ metric_no_merge, NULL, metric_events, table);
}

int metricgroup__parse_groups_test(struct evlist *evlist,
- const struct pmu_events_map *map,
+ const struct pmu_event *table,
const char *str,
bool metric_no_group,
bool metric_no_merge,
struct rblist *metric_events)
{
return parse_groups(evlist, str, metric_no_group,
- metric_no_merge, &perf_pmu__fake, metric_events, map);
+ metric_no_merge, &perf_pmu__fake, metric_events, table);
}

bool metricgroup__has_metric(const char *metric)
{
- const struct pmu_events_map *map = pmu_events_map__find();
+ const struct pmu_event *table = pmu_events_map__find();
const struct pmu_event *pe;
int i;

- if (!map)
+ if (!table)
return false;

for (i = 0; ; i++) {
- pe = &map->table[i];
+ pe = &table[i];

if (!pe->name && !pe->metric_group && !pe->metric_name)
break;
diff --git a/tools/perf/util/metricgroup.h b/tools/perf/util/metricgroup.h
index 2b42b778d1bf..5a1390e73d25 100644
--- a/tools/perf/util/metricgroup.h
+++ b/tools/perf/util/metricgroup.h
@@ -71,9 +71,9 @@ int metricgroup__parse_groups(const struct option *opt,
bool metric_no_merge,
struct rblist *metric_events);
const struct pmu_event *metricgroup__find_metric(const char *metric,
- const struct pmu_events_map *map);
+ const struct pmu_event *table);
int metricgroup__parse_groups_test(struct evlist *evlist,
- const struct pmu_events_map *map,
+ const struct pmu_event *table,
const char *str,
bool metric_no_group,
bool metric_no_merge,
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index d8717c4548a4..f3e3c4a147e9 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -710,9 +710,9 @@ static char *perf_pmu__getcpuid(struct perf_pmu *pmu)
return cpuid;
}

-const struct pmu_events_map *perf_pmu__find_map(struct perf_pmu *pmu)
+const struct pmu_event *perf_pmu__find_table(struct perf_pmu *pmu)
{
- const struct pmu_events_map *map;
+ const struct pmu_event *table = NULL;
char *cpuid = perf_pmu__getcpuid(pmu);
int i;

@@ -724,22 +724,23 @@ const struct pmu_events_map *perf_pmu__find_map(struct perf_pmu *pmu)

i = 0;
for (;;) {
- map = &pmu_events_map[i++];
- if (!map->table) {
- map = NULL;
+ const struct pmu_events_map *map = &pmu_events_map[i++];
+
+ if (!map->table)
break;
- }

- if (!strcmp_cpuid_str(map->cpuid, cpuid))
+ if (!strcmp_cpuid_str(map->cpuid, cpuid)) {
+ table = map->table;
break;
+ }
}
free(cpuid);
- return map;
+ return table;
}

-const struct pmu_events_map *__weak pmu_events_map__find(void)
+__weak const struct pmu_event *pmu_events_map__find(void)
{
- return perf_pmu__find_map(NULL);
+ return perf_pmu__find_table(NULL);
}

/*
@@ -824,7 +825,7 @@ bool pmu_uncore_alias_match(const char *pmu_name, const char *name)
* as aliases.
*/
void pmu_add_cpu_aliases_map(struct list_head *head, struct perf_pmu *pmu,
- const struct pmu_events_map *map)
+ const struct pmu_event *table)
{
int i;
const char *name = pmu->name;
@@ -834,7 +835,7 @@ void pmu_add_cpu_aliases_map(struct list_head *head, struct perf_pmu *pmu,
i = 0;
while (1) {
const char *cpu_name = is_arm_pmu_core(name) ? name : "cpu";
- const struct pmu_event *pe = &map->table[i++];
+ const struct pmu_event *pe = &table[i++];
const char *pname = pe->pmu ? pe->pmu : cpu_name;

if (!pe->name) {
@@ -859,13 +860,13 @@ void pmu_add_cpu_aliases_map(struct list_head *head, struct perf_pmu *pmu,

static void pmu_add_cpu_aliases(struct list_head *head, struct perf_pmu *pmu)
{
- const struct pmu_events_map *map;
+ const struct pmu_event *table;

- map = perf_pmu__find_map(pmu);
- if (!map)
+ table = perf_pmu__find_table(pmu);
+ if (!table)
return;

- pmu_add_cpu_aliases_map(head, pmu, map);
+ pmu_add_cpu_aliases_map(head, pmu, table);
}

struct pmu_sys_event_iter_data {
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 7e667eec2a01..015242c83698 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -126,10 +126,10 @@ int perf_pmu__test(void);

struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu);
void pmu_add_cpu_aliases_map(struct list_head *head, struct perf_pmu *pmu,
- const struct pmu_events_map *map);
+ const struct pmu_event *map);

-const struct pmu_events_map *perf_pmu__find_map(struct perf_pmu *pmu);
-const struct pmu_events_map *pmu_events_map__find(void);
+const struct pmu_event *perf_pmu__find_table(struct perf_pmu *pmu);
+const struct pmu_event *pmu_events_map__find(void);
bool pmu_uncore_alias_match(const char *pmu_name, const char *name);
void perf_pmu_free_alias(struct perf_pmu_alias *alias);

diff --git a/tools/perf/util/s390-sample-raw.c b/tools/perf/util/s390-sample-raw.c
index cd3a34840389..1ecb718fc0eb 100644
--- a/tools/perf/util/s390-sample-raw.c
+++ b/tools/perf/util/s390-sample-raw.c
@@ -135,12 +135,12 @@ static int get_counterset_start(int setnr)
* the name of this counter.
* If no match is found a NULL pointer is returned.
*/
-static const char *get_counter_name(int set, int nr, const struct pmu_events_map *map)
+static const char *get_counter_name(int set, int nr, const struct pmu_event *table)
{
int rc, event_nr, wanted = get_counterset_start(set) + nr;

- if (map) {
- const struct pmu_event *evp = map->table;
+ if (table) {
+ const struct pmu_event *evp = table;

for (; evp->name || evp->event || evp->desc; ++evp) {
if (evp->name == NULL || evp->event == NULL)
@@ -159,10 +159,10 @@ static void s390_cpumcfdg_dump(struct perf_sample *sample)
unsigned char *buf = sample->raw_data;
const char *color = PERF_COLOR_BLUE;
struct cf_ctrset_entry *cep, ce;
- const struct pmu_events_map *map;
+ const struct pmu_event *table;
u64 *p;

- map = pmu_events_map__find();
+ table = pmu_events_map__find();
while (offset < len) {
cep = (struct cf_ctrset_entry *)(buf + offset);

@@ -180,7 +180,7 @@ static void s390_cpumcfdg_dump(struct perf_sample *sample)
color_fprintf(stdout, color, " [%#08zx] Counterset:%d"
" Counters:%d\n", offset, ce.set, ce.ctr);
for (i = 0, p = (u64 *)(cep + 1); i < ce.ctr; ++i, ++p) {
- const char *ev_name = get_counter_name(ce.set, i, map);
+ const char *ev_name = get_counter_name(ce.set, i, table);

color_fprintf(stdout, color,
"\tCounter:%03d %s Value:%#018lx\n", i,
--
2.37.1.455.g008518b4e5-goog

2022-07-29 07:47:44

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 10/17] perf pmu-events: Hide pmu_events_map

Move usage of the table to pmu-events.c so it may be hidden. By
abstracting the table the implementation can later be changed.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/pmu-events/empty-pmu-events.c | 81 ++++++++-
tools/perf/pmu-events/jevents.py | 81 ++++++++-
tools/perf/pmu-events/pmu-events.h | 29 +--
tools/perf/tests/pmu-events.c | 218 +++++++++++------------
tools/perf/util/metricgroup.c | 15 +-
tools/perf/util/pmu.c | 34 +---
tools/perf/util/pmu.h | 2 +-
7 files changed, 280 insertions(+), 180 deletions(-)

diff --git a/tools/perf/pmu-events/empty-pmu-events.c b/tools/perf/pmu-events/empty-pmu-events.c
index 216ea0482c37..8ef75aff996c 100644
--- a/tools/perf/pmu-events/empty-pmu-events.c
+++ b/tools/perf/pmu-events/empty-pmu-events.c
@@ -6,6 +6,8 @@
* The test cpu/soc is provided for testing.
*/
#include "pmu-events/pmu-events.h"
+#include "util/header.h"
+#include "util/pmu.h"
#include <string.h>
#include <stddef.h>

@@ -110,7 +112,26 @@ static const struct pmu_event pme_test_soc_cpu[] = {
},
};

-const struct pmu_events_map pmu_events_map[] = {
+
+/*
+ * Map a CPU to its table of PMU events. The CPU is identified by the
+ * cpuid field, which is an arch-specific identifier for the CPU.
+ * The identifier specified in tools/perf/pmu-events/arch/xxx/mapfile
+ * must match the get_cpuid_str() in tools/perf/arch/xxx/util/header.c)
+ *
+ * The cpuid can contain any character other than the comma.
+ */
+struct pmu_events_map {
+ const char *arch;
+ const char *cpuid;
+ const struct pmu_event *table;
+};
+
+/*
+ * Global table mapping each known CPU for the architecture to its
+ * table of PMU events.
+ */
+static const struct pmu_events_map pmu_events_map[] = {
{
.arch = "testarch",
.cpuid = "testcpu",
@@ -162,6 +183,62 @@ static const struct pmu_sys_events pmu_sys_event_tables[] = {
},
};

+const struct pmu_event *perf_pmu__find_table(struct perf_pmu *pmu)
+{
+ const struct pmu_event *table = NULL;
+ char *cpuid = perf_pmu__getcpuid(pmu);
+ int i;
+
+ /* on some platforms which uses cpus map, cpuid can be NULL for
+ * PMUs other than CORE PMUs.
+ */
+ if (!cpuid)
+ return NULL;
+
+ i = 0;
+ for (;;) {
+ const struct pmu_events_map *map = &pmu_events_map[i++];
+
+ if (!map->table)
+ break;
+
+ if (!strcmp_cpuid_str(map->cpuid, cpuid)) {
+ table = map->table;
+ break;
+ }
+ }
+ free(cpuid);
+ return table;
+}
+
+const struct pmu_event *find_core_events_table(const char *arch, const char *cpuid)
+{
+ for (const struct pmu_events_map *tables = &pmu_events_map[0];
+ tables->table;
+ tables++) {
+ if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid))
+ return tables->table;
+ }
+ return NULL;
+}
+
+int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data)
+{
+ for (const struct pmu_events_map *tables = &pmu_events_map[0];
+ tables->table;
+ tables++) {
+ for (const struct pmu_event *pe = &tables->table[0];
+ pe->name || pe->metric_group || pe->metric_name;
+ pe++) {
+ int ret = fn(pe, &tables->table[0], data);
+
+ if (ret)
+ return ret;
+ }
+ }
+ return 0;
+}
+
const struct pmu_event *find_sys_events_table(const char *name)
{
for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0];
@@ -181,7 +258,7 @@ int pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data)
for (const struct pmu_event *pe = &tables->table[0];
pe->name || pe->metric_group || pe->metric_name;
pe++) {
- int ret = fn(pe, data);
+ int ret = fn(pe, &tables->table[0], data);

if (ret)
return ret;
diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
index dd21bc9eeeed..e976c5e8e80b 100755
--- a/tools/perf/pmu-events/jevents.py
+++ b/tools/perf/pmu-events/jevents.py
@@ -333,7 +333,27 @@ def process_one_file(parents: Sequence[str], item: os.DirEntry) -> None:

def print_mapping_table(archs: Sequence[str]) -> None:
"""Read the mapfile and generate the struct from cpuid string to event table."""
- _args.output_file.write('const struct pmu_events_map pmu_events_map[] = {\n')
+ _args.output_file.write("""
+/*
+ * Map a CPU to its table of PMU events. The CPU is identified by the
+ * cpuid field, which is an arch-specific identifier for the CPU.
+ * The identifier specified in tools/perf/pmu-events/arch/xxx/mapfile
+ * must match the get_cpuid_str() in tools/perf/arch/xxx/util/header.c)
+ *
+ * The cpuid can contain any character other than the comma.
+ */
+struct pmu_events_map {
+ const char *arch;
+ const char *cpuid;
+ const struct pmu_event *table;
+};
+
+/*
+ * Global table mapping each known CPU for the architecture to its
+ * table of PMU events.
+ */
+const struct pmu_events_map pmu_events_map[] = {
+""")
for arch in archs:
if arch == 'test':
_args.output_file.write("""{
@@ -389,6 +409,61 @@ static const struct pmu_sys_events pmu_sys_event_tables[] = {
\t},
};

+const struct pmu_event *perf_pmu__find_table(struct perf_pmu *pmu)
+{
+ const struct pmu_event *table = NULL;
+ char *cpuid = perf_pmu__getcpuid(pmu);
+ int i;
+
+ /* on some platforms which uses cpus map, cpuid can be NULL for
+ * PMUs other than CORE PMUs.
+ */
+ if (!cpuid)
+ return NULL;
+
+ i = 0;
+ for (;;) {
+ const struct pmu_events_map *map = &pmu_events_map[i++];
+ if (!map->table)
+ break;
+
+ if (!strcmp_cpuid_str(map->cpuid, cpuid)) {
+ table = map->table;
+ break;
+ }
+ }
+ free(cpuid);
+ return table;
+}
+
+const struct pmu_event *find_core_events_table(const char *arch, const char *cpuid)
+{
+ for (const struct pmu_events_map *tables = &pmu_events_map[0];
+ tables->table;
+ tables++) {
+ if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid))
+ return tables->table;
+ }
+ return NULL;
+}
+
+int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data)
+{
+ for (const struct pmu_events_map *tables = &pmu_events_map[0];
+ tables->table;
+ tables++) {
+ for (const struct pmu_event *pe = &tables->table[0];
+ pe->name || pe->metric_group || pe->metric_name;
+ pe++) {
+ int ret = fn(pe, &tables->table[0], data);
+
+ if (ret)
+ return ret;
+ }
+ }
+ return 0;
+}
+
const struct pmu_event *find_sys_events_table(const char *name)
{
for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0];
@@ -408,7 +483,7 @@ int pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data)
for (const struct pmu_event *pe = &tables->table[0];
pe->name || pe->metric_group || pe->metric_name;
pe++) {
- int ret = fn(pe, data);
+ int ret = fn(pe, &tables->table[0], data);

if (ret)
return ret;
@@ -449,6 +524,8 @@ def main() -> None:

_args.output_file.write("""
#include "pmu-events/pmu-events.h"
+#include "util/header.h"
+#include "util/pmu.h"
#include <string.h>
#include <stddef.h>

diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h
index 2386212b1df0..485e730f9922 100644
--- a/tools/perf/pmu-events/pmu-events.h
+++ b/tools/perf/pmu-events/pmu-events.h
@@ -2,6 +2,8 @@
#ifndef PMU_EVENTS_H
#define PMU_EVENTS_H

+struct perf_pmu;
+
enum aggr_mode_class {
PerChip = 1,
PerCore
@@ -28,30 +30,15 @@ struct pmu_event {
const char *metric_constraint;
};

-/*
- *
- * Map a CPU to its table of PMU events. The CPU is identified by the
- * cpuid field, which is an arch-specific identifier for the CPU.
- * The identifier specified in tools/perf/pmu-events/arch/xxx/mapfile
- * must match the get_cpuid_str() in tools/perf/arch/xxx/util/header.c)
- *
- * The cpuid can contain any character other than the comma.
- */
-struct pmu_events_map {
- const char *arch;
- const char *cpuid;
- const struct pmu_event *table;
-};
+typedef int (*pmu_event_iter_fn)(const struct pmu_event *pe,
+ const struct pmu_event *table,
+ void *data);

-/*
- * Global table mapping each known CPU for the architecture to its
- * table of PMU events.
- */
-extern const struct pmu_events_map pmu_events_map[];
+const struct pmu_event *perf_pmu__find_table(struct perf_pmu *pmu);
+const struct pmu_event *find_core_events_table(const char *arch, const char *cpuid);
+int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data);

const struct pmu_event *find_sys_events_table(const char *name);
-
-typedef int (*pmu_event_iter_fn)(const struct pmu_event *pe, void *data);
int pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data);

#endif
diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c
index b3cde5f98982..ea8b41b5c4e3 100644
--- a/tools/perf/tests/pmu-events.c
+++ b/tools/perf/tests/pmu-events.c
@@ -272,18 +272,6 @@ static bool is_same(const char *reference, const char *test)
return !strcmp(reference, test);
}

-static const struct pmu_event *__test_pmu_get_events_table(void)
-{
- for (const struct pmu_events_map *map = &pmu_events_map[0]; map->cpuid; map++) {
- if (!strcmp(map->cpuid, "testcpu"))
- return map->table;
- }
-
- pr_err("could not find test events map\n");
-
- return NULL;
-}
-
static int compare_pmu_events(const struct pmu_event *e1, const struct pmu_event *e2)
{
if (!is_same(e1->name, e2->name)) {
@@ -438,7 +426,7 @@ static int test__pmu_event_table(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
const struct pmu_event *sys_event_tables = find_sys_events_table("pme_test_soc_sys");
- const struct pmu_event *table = __test_pmu_get_events_table();
+ const struct pmu_event *table = find_core_events_table("testarch", "testcpu");
int map_events = 0, expected_events;

/* ignore 3x sentinels */
@@ -534,7 +522,7 @@ static int __test_core_pmu_event_aliases(char *pmu_name, int *count)
struct perf_pmu *pmu;
LIST_HEAD(aliases);
int res = 0;
- const struct pmu_event *table = __test_pmu_get_events_table();
+ const struct pmu_event *table = find_core_events_table("testarch", "testcpu");
struct perf_pmu_alias *a, *tmp;

if (!table)
@@ -591,7 +579,7 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu)
LIST_HEAD(aliases);
int res = 0;

- events_table = __test_pmu_get_events_table();
+ events_table = find_core_events_table("testarch", "testcpu");
if (!events_table)
return -1;
pmu_add_cpu_aliases_map(&aliases, pmu, events_table);
@@ -845,11 +833,6 @@ static int check_parse_fake(const char *id)
return ret;
}

-static void expr_failure(const char *msg, const struct pmu_event *pe)
-{
- pr_debug("%s\nOn metric %s\nOn expression %s\n", msg, pe->metric_name, pe->metric_expr);
-}
-
struct metric {
struct list_head list;
struct metric_ref metric_ref;
@@ -915,93 +898,100 @@ static int resolve_metric_simple(struct expr_parse_ctx *pctx,

}

-static int test__parsing(struct test_suite *test __maybe_unused,
- int subtest __maybe_unused)
+static void expr_failure(const char *msg, const struct pmu_event *pe)
{
- const struct pmu_event *cpus_table = pmu_events_map__find();
- const struct pmu_event *pe;
- int i, j, k;
- int ret = 0;
+ pr_debug("%s\nOn metric %s\nOn expression %s\n", msg, pe->metric_name, pe->metric_expr);
+}
+
+
+struct test__parsing_data {
+ const struct pmu_event *cpus_table;
struct expr_parse_ctx *ctx;
+ int failures;
+};
+
+static int test__parsing_callback(const struct pmu_event *pe, const struct pmu_event *table,
+ void *vdata)
+{
+ struct test__parsing_data *data = vdata;
+ struct metric *metric, *tmp;
+ struct hashmap_entry *cur;
+ LIST_HEAD(compound_list);
+ size_t bkt;
+ int k;
double result;

- ctx = expr__ctx_new();
- if (!ctx) {
- pr_debug("expr__ctx_new failed");
- return TEST_FAIL;
+ if (!pe->metric_expr)
+ return 0;
+
+ pr_debug("Found metric '%s'\n", pe->metric_name);
+
+ expr__ctx_clear(data->ctx);
+ if (expr__find_ids(pe->metric_expr, NULL, data->ctx) < 0) {
+ expr_failure("Parse find ids failed", pe);
+ data->failures++;
+ return 0;
}
- i = 0;
- for (;;) {
- const struct pmu_events_map *map = &pmu_events_map[i++];

- if (!map->table)
- break;
- j = 0;
- for (;;) {
- struct metric *metric, *tmp;
- struct hashmap_entry *cur;
- LIST_HEAD(compound_list);
- size_t bkt;
-
- pe = &map->table[j++];
- if (!pe->name && !pe->metric_group && !pe->metric_name)
- break;
- if (!pe->metric_expr)
- continue;
- expr__ctx_clear(ctx);
- if (expr__find_ids(pe->metric_expr, NULL, ctx) < 0) {
- expr_failure("Parse find ids failed", pe);
- ret++;
- continue;
- }
+ if (resolve_metric_simple(data->ctx, &compound_list, table,
+ pe->metric_name)) {
+ expr_failure("Could not resolve metrics", pe);
+ data->failures++;
+ return TEST_FAIL; /* Don't tolerate errors due to severity */
+ }

- if (resolve_metric_simple(ctx, &compound_list, map->table,
- pe->metric_name)) {
- expr_failure("Could not resolve metrics", pe);
- ret++;
- goto exit; /* Don't tolerate errors due to severity */
- }
+ /*
+ * Add all ids with a made up value. The value may trigger divide by
+ * zero when subtracted and so try to make them unique.
+ */
+ k = 1;
+ hashmap__for_each_entry(data->ctx->ids, cur, bkt)
+ expr__add_id_val(data->ctx, strdup(cur->key), k++);

- /*
- * Add all ids with a made up value. The value may
- * trigger divide by zero when subtracted and so try to
- * make them unique.
- */
- k = 1;
- hashmap__for_each_entry(ctx->ids, cur, bkt)
- expr__add_id_val(ctx, strdup(cur->key), k++);
-
- hashmap__for_each_entry(ctx->ids, cur, bkt) {
- if (check_parse_cpu(cur->key, map->table == cpus_table,
- pe))
- ret++;
- }
+ hashmap__for_each_entry(data->ctx->ids, cur, bkt) {
+ if (check_parse_cpu(cur->key, table == data->cpus_table, pe))
+ data->failures++;
+ }

- list_for_each_entry_safe(metric, tmp, &compound_list, list) {
- expr__add_ref(ctx, &metric->metric_ref);
- free(metric);
- }
+ list_for_each_entry_safe(metric, tmp, &compound_list, list) {
+ expr__add_ref(data->ctx, &metric->metric_ref);
+ free(metric);
+ }

- if (expr__parse(&result, ctx, pe->metric_expr)) {
- /*
- * Parsing failed, make numbers go from large to
- * small which can resolve divide by zero
- * issues.
- */
- k = 1024;
- hashmap__for_each_entry(ctx->ids, cur, bkt)
- expr__add_id_val(ctx, strdup(cur->key), k--);
- if (expr__parse(&result, ctx, pe->metric_expr)) {
- expr_failure("Parse failed", pe);
- ret++;
- }
- }
+ if (expr__parse(&result, data->ctx, pe->metric_expr)) {
+ /*
+ * Parsing failed, make numbers go from large to small which can
+ * resolve divide by zero issues.
+ */
+ k = 1024;
+ hashmap__for_each_entry(data->ctx->ids, cur, bkt)
+ expr__add_id_val(data->ctx, strdup(cur->key), k--);
+ if (expr__parse(&result, data->ctx, pe->metric_expr)) {
+ expr_failure("Parse failed", pe);
+ data->failures++;
}
}
- expr__ctx_free(ctx);
- /* TODO: fail when not ok */
-exit:
- return ret == 0 ? TEST_OK : TEST_SKIP;
+ return 0;
+}
+
+static int test__parsing(struct test_suite *test __maybe_unused,
+ int subtest __maybe_unused)
+{
+ struct test__parsing_data data = {
+ .cpus_table = pmu_events_map__find(),
+ .ctx = expr__ctx_new(),
+ .failures = 0,
+ };
+
+ if (!data.ctx) {
+ pr_debug("expr__ctx_new failed");
+ return TEST_FAIL;
+ }
+ pmu_for_each_core_event(test__parsing_callback, &data);
+ pmu_for_each_sys_event(test__parsing_callback, &data);
+
+ expr__ctx_free(data.ctx);
+ return data.failures == 0 ? TEST_OK : TEST_FAIL;
}

struct test_metric {
@@ -1073,6 +1063,16 @@ static int metric_parse_fake(const char *str)
return ret;
}

+static int test__parsing_fake_callback(const struct pmu_event *pe,
+ const struct pmu_event *table __maybe_unused,
+ void *data __maybe_unused)
+{
+ if (!pe->metric_expr)
+ return 0;
+
+ return metric_parse_fake(pe->metric_expr);
+}
+
/*
* Parse all the metrics for current architecture,
* or all defined cpus via the 'fake_pmu'
@@ -1081,37 +1081,19 @@ static int metric_parse_fake(const char *str)
static int test__parsing_fake(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
- unsigned int i, j;
int err = 0;

- for (i = 0; i < ARRAY_SIZE(metrics); i++) {
+ for (size_t i = 0; i < ARRAY_SIZE(metrics); i++) {
err = metric_parse_fake(metrics[i].str);
if (err)
return err;
}

- i = 0;
- for (;;) {
- const struct pmu_events_map *map = &pmu_events_map[i++];
+ err = pmu_for_each_core_event(test__parsing_fake_callback, NULL);
+ if (err)
+ return err;

- if (!map->table)
- break;
- j = 0;
- for (;;) {
- const struct pmu_event *pe = &map->table[j++];
-
- if (!pe->name && !pe->metric_group && !pe->metric_name)
- break;
- if (!pe->metric_expr)
- continue;
- pr_debug("Found metric '%s' for '%s'\n", pe->metric_name, map->cpuid);
- err = metric_parse_fake(pe->metric_expr);
- if (err)
- return err;
- }
- }
-
- return 0;
+ return pmu_for_each_sys_event(test__parsing_fake_callback, NULL);
}

static struct test_case pmu_events_tests[] = {
diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index 4d32b4fbf67d..680f7c63838d 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -502,14 +502,14 @@ struct metricgroup_print_sys_idata {
bool details;
};

-typedef int (*metricgroup_sys_event_iter_fn)(const struct pmu_event *pe, void *);
-
struct metricgroup_iter_data {
- metricgroup_sys_event_iter_fn fn;
+ pmu_event_iter_fn fn;
void *data;
};

-static int metricgroup__sys_event_iter(const struct pmu_event *pe, void *data)
+static int metricgroup__sys_event_iter(const struct pmu_event *pe,
+ const struct pmu_event *table __maybe_unused,
+ void *data)
{
struct metricgroup_iter_data *d = data;
struct perf_pmu *pmu = NULL;
@@ -522,13 +522,15 @@ static int metricgroup__sys_event_iter(const struct pmu_event *pe, void *data)
if (!pmu->id || strcmp(pmu->id, pe->compat))
continue;

- return d->fn(pe, d->data);
+ return d->fn(pe, table, d->data);
}

return 0;
}

-static int metricgroup__print_sys_event_iter(const struct pmu_event *pe, void *data)
+static int metricgroup__print_sys_event_iter(const struct pmu_event *pe,
+ const struct pmu_event *table __maybe_unused,
+ void *data)
{
struct metricgroup_print_sys_idata *d = data;

@@ -1101,6 +1103,7 @@ static int add_metric(struct list_head *metric_list,
}

static int metricgroup__add_metric_sys_event_iter(const struct pmu_event *pe,
+ const struct pmu_event *table __maybe_unused,
void *data)
{
struct metricgroup_add_iter_data *d = data;
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index f3e3c4a147e9..5bb03d4e52a3 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -690,7 +690,7 @@ static int is_arm_pmu_core(const char *name)
return file_available(path);
}

-static char *perf_pmu__getcpuid(struct perf_pmu *pmu)
+char *perf_pmu__getcpuid(struct perf_pmu *pmu)
{
char *cpuid;
static bool printed;
@@ -710,34 +710,6 @@ static char *perf_pmu__getcpuid(struct perf_pmu *pmu)
return cpuid;
}

-const struct pmu_event *perf_pmu__find_table(struct perf_pmu *pmu)
-{
- const struct pmu_event *table = NULL;
- char *cpuid = perf_pmu__getcpuid(pmu);
- int i;
-
- /* on some platforms which uses cpus map, cpuid can be NULL for
- * PMUs other than CORE PMUs.
- */
- if (!cpuid)
- return NULL;
-
- i = 0;
- for (;;) {
- const struct pmu_events_map *map = &pmu_events_map[i++];
-
- if (!map->table)
- break;
-
- if (!strcmp_cpuid_str(map->cpuid, cpuid)) {
- table = map->table;
- break;
- }
- }
- free(cpuid);
- return table;
-}
-
__weak const struct pmu_event *pmu_events_map__find(void)
{
return perf_pmu__find_table(NULL);
@@ -874,7 +846,9 @@ struct pmu_sys_event_iter_data {
struct perf_pmu *pmu;
};

-static int pmu_add_sys_aliases_iter_fn(const struct pmu_event *pe, void *data)
+static int pmu_add_sys_aliases_iter_fn(const struct pmu_event *pe,
+ const struct pmu_event *table __maybe_unused,
+ void *data)
{
struct pmu_sys_event_iter_data *idata = data;
struct perf_pmu *pmu = idata->pmu;
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 015242c83698..06df99ba2029 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -128,7 +128,7 @@ struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu);
void pmu_add_cpu_aliases_map(struct list_head *head, struct perf_pmu *pmu,
const struct pmu_event *map);

-const struct pmu_event *perf_pmu__find_table(struct perf_pmu *pmu);
+char *perf_pmu__getcpuid(struct perf_pmu *pmu);
const struct pmu_event *pmu_events_map__find(void);
bool pmu_uncore_alias_match(const char *pmu_name, const char *name);
void perf_pmu_free_alias(struct perf_pmu_alias *alias);
--
2.37.1.455.g008518b4e5-goog

2022-07-29 07:50:28

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 16/17] perf jevents: Compress the pmu_events_table

The pmu_events array requires 15 pointers per entry which in position
independent code need relocating. Change the array to be an array of
offsets within a big C string. Only the offset of the first variable is
required, subsequent variables are stored in order after the \0
terminator (requiring a byte per variable rather than 4 bytes per
offset).

The file size savings are:
no jevents - the same 19,788,464bytes
x86 jevents - ~16.7% file size saving 23,744,288bytes vs 28,502,632bytes
all jevents - ~19.5% file size saving 24,469,056bytes vs 30,379,920bytes
default build options plus NO_LIBBFD=1.

For example, the x86 build savings come from .rela.dyn and
.data.rel.ro becoming smaller by 3,157,032bytes and 3,042,016bytes
respectively. .rodata increases by 1,432,448bytes, giving an overall
4,766,600bytes saving.

To make metric strings more shareable, the topic is changed from say
'skx metrics' to just 'metrics'.

To try to help with the memory layout the pmu_events are ordered as used
by perf qsort comparator functions.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/pmu-events/jevents.py | 207 ++++++++++++++++++++++++-------
1 file changed, 162 insertions(+), 45 deletions(-)

diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
index aa8df649025a..a5e162558994 100755
--- a/tools/perf/pmu-events/jevents.py
+++ b/tools/perf/pmu-events/jevents.py
@@ -6,7 +6,8 @@ import csv
import json
import os
import sys
-from typing import (Callable, Optional, Sequence)
+from typing import (Callable, Dict, Optional, Sequence, Set, Tuple)
+import collections

# Global command line arguments.
_args = None
@@ -20,6 +21,19 @@ _arch_std_events = {}
_close_table = False
# Events to write out when the table is closed
_pending_events = []
+# Global BigCString shared by all structures.
+_bcs = None
+# Order specific JsonEvent attributes will be visited.
+_json_event_attributes = [
+ # cmp_sevent related attributes.
+ 'name', 'pmu', 'topic', 'desc', 'metric_name', 'metric_group',
+ # Seems useful, put it early.
+ 'event',
+ # Short things in alphabetical order.
+ 'aggr_mode', 'compat', 'deprecated', 'perpkg', 'unit',
+ # Longer things (the last won't be iterated over during decompress).
+ 'metric_constraint', 'metric_expr', 'long_desc'
+]


def removesuffix(s: str, suffix: str) -> str:
@@ -39,6 +53,66 @@ def file_name_to_table_name(parents: Sequence[str], dirname: str) -> str:
tblname += '_' + dirname
return tblname.replace('-', '_')

+def c_len(s: str) -> int:
+ """Return the length of s a C string
+
+ This doesn't handle all escape characters properly. It first assumes
+ all \ are for escaping, it then adjusts as it will have over counted
+ \\. The code uses \000 rather than \0 as a terminator as an adjacent
+ number would be folded into a string of \0 (ie. "\0" + "5" doesn't
+ equal a terminator followed by the number 5 but the escape of
+ \05). The code adjusts for \000 but not properly for all octal, hex
+ or unicode values.
+ """
+ try:
+ utf = s.encode(encoding='utf-8',errors='strict')
+ except:
+ print(f'broken string {s}')
+ raise
+ return len(utf) - utf.count(b'\\') + utf.count(b'\\\\') - (utf.count(b'\\000') * 2)
+
+class BigCString:
+ """A class to hold many strings concatenated together.
+
+ Generating a large number of stand-alone C strings creates a large
+ number of relocations in position independent code. The BigCString
+ is a helper for this case. It builds a single string which within it
+ are all the other C strings (to avoid memory issues the string
+ itself is held as a list of strings). The offsets within the big
+ string are recorded and when stored to disk these don't need
+ relocation.
+ """
+ strings: Set[str]
+ big_string: Sequence[str]
+ offsets: Dict[str, int]
+
+ def __init__(self):
+ self.strings = set()
+
+ def add(self, s: str) -> None:
+ """Called to add to the big string."""
+ self.strings.add(s)
+
+ def compute(self) -> None:
+ """Called once all strings are added to compute the string and offsets."""
+
+ # big_string_offset is the current location within the C string
+ # being appended to - comments, etc. don't count. big_string is
+ # the string contents represented as a list. Strings are immutable
+ # in Python and so appending to one causes memory issues, while
+ # lists are mutable.
+ big_string_offset = 0
+ self.big_string = []
+ self.offsets = {}
+ # Emit all strings in a sorted manner.
+ for s in sorted(self.strings):
+ self.offsets[s] = big_string_offset
+ self.big_string.append(f'/* offset={big_string_offset} */ "')
+ self.big_string.append(s)
+ self.big_string.append('"\n')
+ big_string_offset += c_len(s)
+
+_bcs = BigCString()

class JsonEvent:
"""Representation of an event loaded from a json file dictionary."""
@@ -202,26 +276,18 @@ class JsonEvent:
s += f'\t{attr} = {value},\n'
return s + '}'

+ def build_c_string(self) -> str:
+ s = ''
+ for attr in _json_event_attributes:
+ x = getattr(self, attr)
+ s += f'{x}\\000' if x else '\\000'
+ return s
+
def to_c_string(self) -> str:
"""Representation of the event as a C struct initializer."""

- def attr_string(attr: str, value: str) -> str:
- return f'\t.{attr} = \"{value}\",\n'
-
- def str_if_present(self, attr: str) -> str:
- if not getattr(self, attr):
- return ''
- return attr_string(attr, getattr(self, attr))
-
- s = '{\n'
- for attr in [
- 'aggr_mode', 'compat', 'deprecated', 'desc', 'event', 'long_desc',
- 'metric_constraint', 'metric_expr', 'metric_group', 'metric_name',
- 'name', 'perpkg', 'pmu', 'topic', 'unit'
- ]:
- s += str_if_present(self, attr)
- s += '},\n'
- return s
+ s = self.build_c_string()
+ return f'{{ { _bcs.offsets[s] } }}, /* {s} */\n'


def read_json_events(path: str, topic: str) -> Sequence[JsonEvent]:
@@ -236,7 +302,6 @@ def read_json_events(path: str, topic: str) -> Sequence[JsonEvent]:
event.topic = topic
return result

-
def preprocess_arch_std_files(archpath: str) -> None:
"""Read in all architecture standard events."""
global _arch_std_events
@@ -252,7 +317,7 @@ def print_events_table_prefix(tblname: str) -> None:
global _close_table
if _close_table:
raise IOError('Printing table prefix but last table has no suffix')
- _args.output_file.write(f'static const struct pmu_event {tblname}[] = {{\n')
+ _args.output_file.write(f'static const struct compact_pmu_event {tblname}[] = {{\n')
_close_table = True


@@ -267,13 +332,13 @@ def add_events_table_entries(item: os.DirEntry, topic: str) -> None:
def print_events_table_suffix() -> None:
"""Optionally close events table."""

- def event_cmp_key(j: JsonEvent):
- def fix_none(s: str):
+ def event_cmp_key(j: JsonEvent) -> Tuple[bool, str, str, str, str]:
+ def fix_none(s: Optional[str]) -> str:
if s is None:
return ''
return s

- return (not j.desc is None, fix_none(j.topic), fix_none(j.name), fix_none(j.pmu),
+ return (j.desc is not None, fix_none(j.topic), fix_none(j.name), fix_none(j.pmu),
fix_none(j.metric_name))

global _close_table
@@ -285,23 +350,37 @@ def print_events_table_suffix() -> None:
_args.output_file.write(event.to_c_string())
_pending_events = []

- _args.output_file.write("""{
-\t.name = 0,
-\t.event = 0,
-\t.desc = 0,
-},
-};
-""")
+ _args.output_file.write('};\n\n')
_close_table = False

+def get_topic(topic: str) -> str:
+ if topic.endswith('metrics.json'):
+ return 'metrics'
+ return removesuffix(topic, '.json').replace('-', ' ')
+
+def preprocess_one_file(parents: Sequence[str], item: os.DirEntry) -> None:
+
+ if item.is_dir():
+ return
+
+ # base dir or too deep
+ level = len(parents)
+ if level == 0 or level > 4:
+ return
+
+ # Ignore other directories. If the file name does not have a .json
+ # extension, ignore it. It could be a readme.txt for instance.
+ if not item.is_file() or not item.name.endswith('.json'):
+ return
+
+ topic = get_topic(item.name)
+ for event in read_json_events(item.path, topic):
+ _bcs.add(event.build_c_string())

def process_one_file(parents: Sequence[str], item: os.DirEntry) -> None:
"""Process a JSON file during the main walk."""
global _sys_event_tables

- def get_topic(topic: str) -> str:
- return removesuffix(topic, '.json').replace('-', ' ')
-
def is_leaf_dir(path: str) -> bool:
for item in os.scandir(path):
if item.is_dir():
@@ -336,7 +415,8 @@ def print_mapping_table(archs: Sequence[str]) -> None:
_args.output_file.write("""
/* Struct used to make the PMU event table implementation opaque to callers. */
struct pmu_events_table {
- const struct pmu_event *entries;
+ const struct compact_pmu_event *entries;
+ size_t length;
};

/*
@@ -364,7 +444,10 @@ const struct pmu_events_map pmu_events_map[] = {
_args.output_file.write("""{
\t.arch = "testarch",
\t.cpuid = "testcpu",
-\t.table = { pme_test_soc_cpu },
+\t.table = {
+\t.entries = pme_test_soc_cpu,
+\t.length = ARRAY_SIZE(pme_test_soc_cpu),
+\t}
},
""")
else:
@@ -379,7 +462,10 @@ const struct pmu_events_map pmu_events_map[] = {
_args.output_file.write(f"""{{
\t.arch = "{arch}",
\t.cpuid = "{cpuid}",
-\t.table = {{ {tblname} }}
+\t.table = {{
+\t\t.entries = {tblname},
+\t\t.length = ARRAY_SIZE({tblname})
+\t}}
}},
""")
first = False
@@ -387,7 +473,7 @@ const struct pmu_events_map pmu_events_map[] = {
_args.output_file.write("""{
\t.arch = 0,
\t.cpuid = 0,
-\t.table = { 0 },
+\t.table = { 0, 0 },
}
};
""")
@@ -405,23 +491,41 @@ static const struct pmu_sys_events pmu_sys_event_tables[] = {
""")
for tblname in _sys_event_tables:
_args.output_file.write(f"""\t{{
-\t\t.table = {{ {tblname} }},
+\t\t.table = {{
+\t\t\t.entries = {tblname},
+\t\t\t.length = ARRAY_SIZE({tblname})
+\t\t}},
\t\t.name = \"{tblname}\",
\t}},
""")
_args.output_file.write("""\t{
-\t\t.table = { 0 }
+\t\t.table = { 0, 0 }
\t},
};

-int pmu_events_table_for_each_event(const struct pmu_events_table *table, pmu_event_iter_fn fn,
+static void decompress(int offset, struct pmu_event *pe)
+{
+\tconst char *p = &big_c_string[offset];
+""")
+ for attr in _json_event_attributes:
+ _args.output_file.write(f"""
+\tpe->{attr} = (*p == '\\0' ? NULL : p);
+""")
+ if attr == _json_event_attributes[-1]:
+ continue
+ _args.output_file.write('\twhile (*p++);')
+ _args.output_file.write("""}
+
+int pmu_events_table_for_each_event(const struct pmu_events_table *table,
+ pmu_event_iter_fn fn,
void *data)
{
- for (const struct pmu_event *pe = &table->entries[0];
- pe->name || pe->metric_group || pe->metric_name;
- pe++) {
- int ret = fn(pe, table, data);
+ for (size_t i = 0; i < table->length; i++) {
+ struct pmu_event pe;
+ int ret;

+ decompress(table->entries[i].offset, &pe);
+ ret = fn(&pe, table, data);
if (ret)
return ret;
}
@@ -530,7 +634,7 @@ def main() -> None:
help='Root of tree containing architecture directories containing json files'
)
ap.add_argument(
- 'output_file', type=argparse.FileType('w'), nargs='?', default=sys.stdout)
+ 'output_file', type=argparse.FileType('w', encoding='utf-8'), nargs='?', default=sys.stdout)
_args = ap.parse_args()

_args.output_file.write("""
@@ -540,6 +644,10 @@ def main() -> None:
#include <string.h>
#include <stddef.h>

+struct compact_pmu_event {
+ int offset;
+};
+
""")
archs = []
for item in os.scandir(_args.starting_dir):
@@ -555,6 +663,15 @@ def main() -> None:
for arch in archs:
arch_path = f'{_args.starting_dir}/{arch}'
preprocess_arch_std_files(arch_path)
+ ftw(arch_path, [], preprocess_one_file)
+
+ _bcs.compute()
+ _args.output_file.write('static const char *const big_c_string =\n')
+ for s in _bcs.big_string:
+ _args.output_file.write(s)
+ _args.output_file.write(';\n\n')
+ for arch in archs:
+ arch_path = f'{_args.starting_dir}/{arch}'
ftw(arch_path, [], process_one_file)
print_events_table_suffix()

--
2.37.1.455.g008518b4e5-goog

2022-07-29 08:02:36

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 03/17] perf jevents: Add JEVENTS_ARCH make option

Allow the architecture built into pmu-events.c to be set on the make
command line with JEVENTS_ARCH.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/pmu-events/Build | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/tools/perf/pmu-events/Build b/tools/perf/pmu-events/Build
index 28a9d01b08af..04ef95174660 100644
--- a/tools/perf/pmu-events/Build
+++ b/tools/perf/pmu-events/Build
@@ -7,6 +7,10 @@ JSON_TEST = $(shell [ -d $(JDIR_TEST) ] && \
find $(JDIR_TEST) -name '*.json')
JEVENTS_PY = pmu-events/jevents.py

+ifeq ($(JEVENTS_ARCH),)
+JEVENTS_ARCH=$(SRCARCH)
+endif
+
#
# Locate/process JSON files in pmu-events/arch/
# directory and create tables in pmu-events.c.
@@ -19,5 +23,5 @@ $(OUTPUT)pmu-events/pmu-events.c: pmu-events/empty-pmu-events.c
else
$(OUTPUT)pmu-events/pmu-events.c: $(JSON) $(JSON_TEST) $(JEVENTS_PY)
$(call rule_mkdir)
- $(Q)$(call echo-cmd,gen)$(PYTHON) $(JEVENTS_PY) $(SRCARCH) pmu-events/arch $@
+ $(Q)$(call echo-cmd,gen)$(PYTHON) $(JEVENTS_PY) $(JEVENTS_ARCH) pmu-events/arch $@
endif
--
2.37.1.455.g008518b4e5-goog

2022-07-29 08:02:44

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 15/17] perf metrics: Copy entire pmu_event in find metric

The pmu_event passed to the pmu_events_table_for_each_event is invalid
after the loop. Copy the entire struct in metricgroup__find_metric.
Reduce the scope of this function to static.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/util/metricgroup.c | 33 ++++++++++++++++++---------------
tools/perf/util/metricgroup.h | 2 --
2 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index 8ef44f4b5e9a..95f4b46dba09 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -879,6 +879,10 @@ struct metricgroup_add_iter_data {
const struct pmu_events_table *table;
};

+static bool metricgroup__find_metric(const char *metric,
+ const struct pmu_events_table *table,
+ struct pmu_event *pe);
+
static int add_metric(struct list_head *metric_list,
const struct pmu_event *pe,
const char *modifier,
@@ -914,7 +918,7 @@ static int resolve_metric(struct list_head *metric_list,
size_t bkt;
struct to_resolve {
/* The metric to resolve. */
- const struct pmu_event *pe;
+ struct pmu_event pe;
/*
* The key in the IDs map, this may differ from in case,
* etc. from pe->metric_name.
@@ -928,16 +932,15 @@ static int resolve_metric(struct list_head *metric_list,
* the pending array.
*/
hashmap__for_each_entry(root_metric->pctx->ids, cur, bkt) {
- const struct pmu_event *pe;
+ struct pmu_event pe;

- pe = metricgroup__find_metric(cur->key, table);
- if (pe) {
+ if (metricgroup__find_metric(cur->key, table, &pe)) {
pending = realloc(pending,
(pending_cnt + 1) * sizeof(struct to_resolve));
if (!pending)
return -ENOMEM;

- pending[pending_cnt].pe = pe;
+ memcpy(&pending[pending_cnt].pe, &pe, sizeof(pe));
pending[pending_cnt].key = cur->key;
pending_cnt++;
}
@@ -952,7 +955,7 @@ static int resolve_metric(struct list_head *metric_list,
* context.
*/
for (i = 0; i < pending_cnt; i++) {
- ret = add_metric(metric_list, pending[i].pe, modifier, metric_no_group,
+ ret = add_metric(metric_list, &pending[i].pe, modifier, metric_no_group,
root_metric, visited, table);
if (ret)
break;
@@ -1073,7 +1076,7 @@ static int __add_metric(struct list_head *metric_list,

struct metricgroup__find_metric_data {
const char *metric;
- const struct pmu_event *pe;
+ struct pmu_event *pe;
};

static int metricgroup__find_metric_callback(const struct pmu_event *pe,
@@ -1085,21 +1088,21 @@ static int metricgroup__find_metric_callback(const struct pmu_event *pe,
if (!match_metric(pe->metric_name, data->metric))
return 0;

- data->pe = pe;
- return -1;
+ memcpy(data->pe, pe, sizeof(*pe));
+ return 1;
}

-const struct pmu_event *metricgroup__find_metric(const char *metric,
- const struct pmu_events_table *table)
+static bool metricgroup__find_metric(const char *metric,
+ const struct pmu_events_table *table,
+ struct pmu_event *pe)
{
struct metricgroup__find_metric_data data = {
.metric = metric,
- .pe = NULL,
+ .pe = pe,
};

- pmu_events_table_for_each_event(table, metricgroup__find_metric_callback, &data);
-
- return data.pe;
+ return pmu_events_table_for_each_event(table, metricgroup__find_metric_callback, &data)
+ ? true : false;
}

static int add_metric(struct list_head *metric_list,
diff --git a/tools/perf/util/metricgroup.h b/tools/perf/util/metricgroup.h
index f54d170043e9..016b3b1a289a 100644
--- a/tools/perf/util/metricgroup.h
+++ b/tools/perf/util/metricgroup.h
@@ -69,8 +69,6 @@ int metricgroup__parse_groups(const struct option *opt,
bool metric_no_group,
bool metric_no_merge,
struct rblist *metric_events);
-const struct pmu_event *metricgroup__find_metric(const char *metric,
- const struct pmu_events_table *table);
int metricgroup__parse_groups_test(struct evlist *evlist,
const struct pmu_events_table *table,
const char *str,
--
2.37.1.455.g008518b4e5-goog

2022-07-29 08:04:54

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 01/17] perf jevents: Clean up pytype warnings

Improve type hints to clean up pytype warnings.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/pmu-events/jevents.py | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
index 83e0dcbeac9a..5b72048d50da 100755
--- a/tools/perf/pmu-events/jevents.py
+++ b/tools/perf/pmu-events/jevents.py
@@ -6,8 +6,7 @@ import csv
import json
import os
import sys
-from typing import Callable
-from typing import Sequence
+from typing import (Callable, Optional, Sequence)

# Global command line arguments.
_args = None
@@ -57,7 +56,7 @@ class JsonEvent:
'. '), '.').replace('\n', '\\n').replace(
'\"', '\\"').replace('\r', '\\r')

- def convert_aggr_mode(aggr_mode: str) -> str:
+ def convert_aggr_mode(aggr_mode: str) -> Optional[str]:
"""Returns the aggr_mode_class enum value associated with the JSON string."""
if not aggr_mode:
return None
@@ -67,7 +66,7 @@ class JsonEvent:
}
return aggr_mode_to_enum[aggr_mode]

- def lookup_msr(num: str) -> str:
+ def lookup_msr(num: str) -> Optional[str]:
"""Converts the msr number, or first in a list to the appropriate event field."""
if not num:
return None
@@ -79,7 +78,7 @@ class JsonEvent:
}
return msrmap[int(num.split(',', 1)[0], 0)]

- def real_event(name: str, event: str) -> str:
+ def real_event(name: str, event: str) -> Optional[str]:
"""Convert well known event names to an event string otherwise use the event argument."""
fixed = {
'inst_retired.any': 'event=0xc0,period=2000003',
@@ -95,7 +94,7 @@ class JsonEvent:
return fixed[name.lower()]
return event

- def unit_to_pmu(unit: str) -> str:
+ def unit_to_pmu(unit: str) -> Optional[str]:
"""Convert a JSON Unit to Linux PMU name."""
if not unit:
return None
@@ -154,7 +153,7 @@ class JsonEvent:
if self.metric_expr:
self.metric_expr = self.metric_expr.replace('\\', '\\\\')
arch_std = jd.get('ArchStdEvent')
- if precise and self.desc and not '(Precise Event)' in self.desc:
+ if precise and self.desc and '(Precise Event)' not in self.desc:
extra_desc += ' (Must be precise)' if precise == '2' else (' (Precise '
'event)')
event = f'config={llx(configcode)}' if configcode is not None else f'event={llx(eventcode)}'
--
2.37.1.455.g008518b4e5-goog

2022-07-29 08:06:37

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 05/17] perf jevents: Remove the type/version variables

pmu_events_map has a type variable that is always initialized to "core"
and a version variable that is never read. Remove these from the API as
it is straightforward to add them back when necessary.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/pmu-events/empty-pmu-events.c | 6 ++----
tools/perf/pmu-events/jevents.py | 6 ------
tools/perf/pmu-events/pmu-events.h | 2 --
tools/perf/tests/expand-cgroup.c | 2 --
tools/perf/tests/parse-metric.c | 2 --
5 files changed, 2 insertions(+), 16 deletions(-)

diff --git a/tools/perf/pmu-events/empty-pmu-events.c b/tools/perf/pmu-events/empty-pmu-events.c
index 77e655c6f116..4182a986f505 100644
--- a/tools/perf/pmu-events/empty-pmu-events.c
+++ b/tools/perf/pmu-events/empty-pmu-events.c
@@ -110,15 +110,13 @@ static const struct pmu_event pme_test_soc_cpu[] = {

const struct pmu_events_map pmu_events_map[] = {
{
+ .arch = "testarch",
.cpuid = "testcpu",
- .version = "v1",
- .type = "core",
.table = pme_test_soc_cpu,
},
{
+ .arch = 0,
.cpuid = 0,
- .version = 0,
- .type = 0,
.table = 0,
},
};
diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
index e6e6c42c3f8a..98d18d5c3830 100755
--- a/tools/perf/pmu-events/jevents.py
+++ b/tools/perf/pmu-events/jevents.py
@@ -312,8 +312,6 @@ def print_mapping_table(archs: Sequence[str]) -> None:
_args.output_file.write("""{
\t.arch = "testarch",
\t.cpuid = "testcpu",
-\t.version = "v1",
-\t.type = "core",
\t.table = pme_test_soc_cpu,
},
""")
@@ -329,8 +327,6 @@ def print_mapping_table(archs: Sequence[str]) -> None:
_args.output_file.write(f"""{{
\t.arch = "{arch}",
\t.cpuid = "{cpuid}",
-\t.version = "{row[1]}",
-\t.type = "{row[3]}",
\t.table = {tblname}
}},
""")
@@ -339,8 +335,6 @@ def print_mapping_table(archs: Sequence[str]) -> None:
_args.output_file.write("""{
\t.arch = 0,
\t.cpuid = 0,
-\t.version = 0,
-\t.type = 0,
\t.table = 0,
}
};
diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h
index 7a360792635f..a491b117c8ac 100644
--- a/tools/perf/pmu-events/pmu-events.h
+++ b/tools/perf/pmu-events/pmu-events.h
@@ -40,8 +40,6 @@ struct pmu_event {
struct pmu_events_map {
const char *arch;
const char *cpuid;
- const char *version;
- const char *type; /* core, uncore etc */
const struct pmu_event *table;
};

diff --git a/tools/perf/tests/expand-cgroup.c b/tools/perf/tests/expand-cgroup.c
index dfefe5b60eb2..dc4038f997d7 100644
--- a/tools/perf/tests/expand-cgroup.c
+++ b/tools/perf/tests/expand-cgroup.c
@@ -197,8 +197,6 @@ static int expand_metric_events(void)
};
const struct pmu_events_map ev_map = {
.cpuid = "test",
- .version = "1",
- .type = "core",
.table = pme_test,
};

diff --git a/tools/perf/tests/parse-metric.c b/tools/perf/tests/parse-metric.c
index 07b6f4ec024f..1b811a26f4ee 100644
--- a/tools/perf/tests/parse-metric.c
+++ b/tools/perf/tests/parse-metric.c
@@ -81,8 +81,6 @@ static struct pmu_event pme_test[] = {

static const struct pmu_events_map map = {
.cpuid = "test",
- .version = "1",
- .type = "core",
.table = pme_test,
};

--
2.37.1.455.g008518b4e5-goog

2022-07-29 08:08:20

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 04/17] perf jevent: Add an 'all' architecture argument

When 'all' is passed as the architecture generate a mapping table for
all architectures. This simplifies testing. To identify the table for an
architecture add an arch variable to the pmu_events_map.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/pmu-events/jevents.py | 70 ++++++++++++++++++------------
tools/perf/pmu-events/pmu-events.h | 1 +
tools/perf/tests/pmu-events.c | 3 +-
3 files changed, 45 insertions(+), 29 deletions(-)

diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
index cdfa4e0e7557..e6e6c42c3f8a 100755
--- a/tools/perf/pmu-events/jevents.py
+++ b/tools/perf/pmu-events/jevents.py
@@ -304,38 +304,45 @@ def process_one_file(parents: Sequence[str], item: os.DirEntry) -> None:
print_events_table_entries(item, get_topic(item.name))


-def print_mapping_table() -> None:
+def print_mapping_table(archs: Sequence[str]) -> None:
"""Read the mapfile and generate the struct from cpuid string to event table."""
- with open(f'{_args.starting_dir}/{_args.arch}/mapfile.csv') as csvfile:
- table = csv.reader(csvfile)
- _args.output_file.write(
- 'const struct pmu_events_map pmu_events_map[] = {\n')
- first = True
- for row in table:
- # Skip the first row or any row beginning with #.
- if not first and len(row) > 0 and not row[0].startswith('#'):
- tblname = file_name_to_table_name([], row[2].replace('/', '_'))
- _args.output_file.write("""{
-\t.cpuid = \"%s\",
-\t.version = \"%s\",
-\t.type = \"%s\",
-\t.table = %s
-},
-""" % (row[0].replace('\\', '\\\\'), row[1], row[3], tblname))
- first = False
-
- _args.output_file.write("""{
+ _args.output_file.write('const struct pmu_events_map pmu_events_map[] = {\n')
+ for arch in archs:
+ if arch == 'test':
+ _args.output_file.write("""{
+\t.arch = "testarch",
\t.cpuid = "testcpu",
\t.version = "v1",
\t.type = "core",
\t.table = pme_test_soc_cpu,
},
-{
+""")
+ else:
+ with open(f'{_args.starting_dir}/{arch}/mapfile.csv') as csvfile:
+ table = csv.reader(csvfile)
+ first = True
+ for row in table:
+ # Skip the first row or any row beginning with #.
+ if not first and len(row) > 0 and not row[0].startswith('#'):
+ tblname = file_name_to_table_name([], row[2].replace('/', '_'))
+ cpuid = row[0].replace('\\', '\\\\')
+ _args.output_file.write(f"""{{
+\t.arch = "{arch}",
+\t.cpuid = "{cpuid}",
+\t.version = "{row[1]}",
+\t.type = "{row[3]}",
+\t.table = {tblname}
+}},
+""")
+ first = False
+
+ _args.output_file.write("""{
+\t.arch = 0,
\t.cpuid = 0,
\t.version = 0,
\t.type = 0,
\t.table = 0,
-},
+}
};
""")

@@ -386,15 +393,24 @@ def main() -> None:
_args = ap.parse_args()

_args.output_file.write("#include \"pmu-events/pmu-events.h\"\n")
- for path in [_args.arch, 'test']:
- arch_path = f'{_args.starting_dir}/{path}'
- if not os.path.isdir(arch_path):
- raise IOError(f'Missing architecture directory in \'{arch_path}\'')
+ archs = []
+ for item in os.scandir(_args.starting_dir):
+ if not item.is_dir():
+ continue
+ if item.name == _args.arch or _args.arch == 'all' or item.name == 'test':
+ archs.append(item.name)
+
+ if len(archs) < 2:
+ raise IOError(f'Missing architecture directory \'{_args.arch}\'')
+
+ archs.sort()
+ for arch in archs:
+ arch_path = f'{_args.starting_dir}/{arch}'
preprocess_arch_std_files(arch_path)
ftw(arch_path, [], process_one_file)
print_events_table_suffix()

- print_mapping_table()
+ print_mapping_table(archs)
print_system_mapping_table()


diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h
index 6efe73976440..7a360792635f 100644
--- a/tools/perf/pmu-events/pmu-events.h
+++ b/tools/perf/pmu-events/pmu-events.h
@@ -38,6 +38,7 @@ struct pmu_event {
* The cpuid can contain any character other than the comma.
*/
struct pmu_events_map {
+ const char *arch;
const char *cpuid;
const char *version;
const char *type; /* core, uncore etc */
diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c
index 263cbb67c861..82192f1a7bf7 100644
--- a/tools/perf/tests/pmu-events.c
+++ b/tools/perf/tests/pmu-events.c
@@ -864,8 +864,7 @@ static void expr_failure(const char *msg,
const struct pmu_events_map *map,
const struct pmu_event *pe)
{
- pr_debug("%s for map %s %s %s\n",
- msg, map->cpuid, map->version, map->type);
+ pr_debug("%s for map %s %s\n", msg, map->arch, map->cpuid);
pr_debug("On metric %s\n", pe->metric_name);
pr_debug("On expression %s\n", pe->metric_expr);
}
--
2.37.1.455.g008518b4e5-goog

2022-07-29 08:08:25

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 13/17] perf pmu-events: Don't assume pmu_event is an array

Current code assumes that a struct pmu_event can be iterated over
forward until a NULL pmu_event is encountered. This makes it difficult
to refactor pmu_event. Add a loop function taking a callback function
that's passed the struct pmu_event. This way the pmu_event is only
needed for one element and not an entire array.

Switch existing code iterating over the pmu_event arrays to use the new
loop function pmu_events_table_for_each_event.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/pmu-events/empty-pmu-events.c | 34 +++--
tools/perf/pmu-events/jevents.py | 34 +++--
tools/perf/pmu-events/pmu-events.h | 3 +
tools/perf/tests/pmu-events.c | 136 +++++++++--------
tools/perf/util/metricgroup.c | 181 ++++++++++++++++-------
tools/perf/util/pmu.c | 65 ++++----
tools/perf/util/s390-sample-raw.c | 42 ++++--
7 files changed, 313 insertions(+), 182 deletions(-)

diff --git a/tools/perf/pmu-events/empty-pmu-events.c b/tools/perf/pmu-events/empty-pmu-events.c
index 028f44efe48d..bee1967baa2b 100644
--- a/tools/perf/pmu-events/empty-pmu-events.c
+++ b/tools/perf/pmu-events/empty-pmu-events.c
@@ -247,6 +247,20 @@ static const struct pmu_sys_events pmu_sys_event_tables[] = {
},
};

+int pmu_events_table_for_each_event(const struct pmu_event *table, pmu_event_iter_fn fn,
+ void *data)
+{
+ for (const struct pmu_event *pe = &table[0];
+ pe->name || pe->metric_group || pe->metric_name;
+ pe++) {
+ int ret = fn(pe, table, data);
+
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
const struct pmu_event *perf_pmu__find_table(struct perf_pmu *pmu)
{
const struct pmu_event *table = NULL;
@@ -291,14 +305,10 @@ int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data)
for (const struct pmu_events_map *tables = &pmu_events_map[0];
tables->table;
tables++) {
- for (const struct pmu_event *pe = &tables->table[0];
- pe->name || pe->metric_group || pe->metric_name;
- pe++) {
- int ret = fn(pe, &tables->table[0], data);
+ int ret = pmu_events_table_for_each_event(tables->table, fn, data);

- if (ret)
- return ret;
- }
+ if (ret)
+ return ret;
}
return 0;
}
@@ -319,14 +329,10 @@ int pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data)
for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0];
tables->name;
tables++) {
- for (const struct pmu_event *pe = &tables->table[0];
- pe->name || pe->metric_group || pe->metric_name;
- pe++) {
- int ret = fn(pe, &tables->table[0], data);
+ int ret = pmu_events_table_for_each_event(tables->table, fn, data);

- if (ret)
- return ret;
- }
+ if (ret)
+ return ret;
}
return 0;
}
diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
index e976c5e8e80b..365c960202b0 100755
--- a/tools/perf/pmu-events/jevents.py
+++ b/tools/perf/pmu-events/jevents.py
@@ -409,6 +409,20 @@ static const struct pmu_sys_events pmu_sys_event_tables[] = {
\t},
};

+int pmu_events_table_for_each_event(const struct pmu_event *table, pmu_event_iter_fn fn,
+ void *data)
+{
+ for (const struct pmu_event *pe = &table[0];
+ pe->name || pe->metric_group || pe->metric_name;
+ pe++) {
+ int ret = fn(pe, table, data);
+
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
const struct pmu_event *perf_pmu__find_table(struct perf_pmu *pmu)
{
const struct pmu_event *table = NULL;
@@ -452,14 +466,10 @@ int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data)
for (const struct pmu_events_map *tables = &pmu_events_map[0];
tables->table;
tables++) {
- for (const struct pmu_event *pe = &tables->table[0];
- pe->name || pe->metric_group || pe->metric_name;
- pe++) {
- int ret = fn(pe, &tables->table[0], data);
+ int ret = pmu_events_table_for_each_event(tables->table, fn, data);

- if (ret)
- return ret;
- }
+ if (ret)
+ return ret;
}
return 0;
}
@@ -480,14 +490,10 @@ int pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data)
for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0];
tables->name;
tables++) {
- for (const struct pmu_event *pe = &tables->table[0];
- pe->name || pe->metric_group || pe->metric_name;
- pe++) {
- int ret = fn(pe, &tables->table[0], data);
+ int ret = pmu_events_table_for_each_event(tables->table, fn, data);

- if (ret)
- return ret;
- }
+ if (ret)
+ return ret;
}
return 0;
}
diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h
index 485e730f9922..70672842f77f 100644
--- a/tools/perf/pmu-events/pmu-events.h
+++ b/tools/perf/pmu-events/pmu-events.h
@@ -34,6 +34,9 @@ typedef int (*pmu_event_iter_fn)(const struct pmu_event *pe,
const struct pmu_event *table,
void *data);

+int pmu_events_table_for_each_event(const struct pmu_event *table, pmu_event_iter_fn fn,
+ void *data);
+
const struct pmu_event *perf_pmu__find_table(struct perf_pmu *pmu);
const struct pmu_event *find_core_events_table(const char *arch, const char *cpuid);
int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data);
diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c
index e8df6bc26ebd..1c3479e5890e 100644
--- a/tools/perf/tests/pmu-events.c
+++ b/tools/perf/tests/pmu-events.c
@@ -423,84 +423,104 @@ static int compare_alias_to_test_event(struct perf_pmu_alias *alias,
return 0;
}

-/* Verify generated events from pmu-events.c are as expected */
-static int test__pmu_event_table(struct test_suite *test __maybe_unused,
- int subtest __maybe_unused)
+static int test__pmu_event_table_core_callback(const struct pmu_event *pe,
+ const struct pmu_event *table __maybe_unused,
+ void *data)
{
- const struct pmu_event *sys_event_tables = find_sys_events_table("pme_test_soc_sys");
- const struct pmu_event *table = find_core_events_table("testarch", "testcpu");
- int map_events = 0, expected_events;
+ int *map_events = data;
+ struct perf_pmu_test_event const **test_event_table;
+ bool found = false;

- /* ignore 3x sentinels */
- expected_events = ARRAY_SIZE(core_events) +
- ARRAY_SIZE(uncore_events) +
- ARRAY_SIZE(sys_events) - 3;
+ if (!pe->name)
+ return 0;

- if (!table || !sys_event_tables)
- return -1;
+ if (pe->pmu)
+ test_event_table = &uncore_events[0];
+ else
+ test_event_table = &core_events[0];

- for (; table->name; table++) {
- struct perf_pmu_test_event const **test_event_table;
- bool found = false;
+ 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;

- if (table->pmu)
- test_event_table = &uncore_events[0];
- else
- test_event_table = &core_events[0];
+ if (strcmp(pe->name, event->name))
+ continue;
+ found = true;
+ (*map_events)++;

- 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;
+ if (compare_pmu_events(pe, event))
+ return -1;

- if (strcmp(table->name, event->name))
- continue;
- found = true;
- map_events++;
+ pr_debug("testing event table %s: pass\n", pe->name);
+ }
+ if (!found) {
+ pr_err("testing event table: could not find event %s\n", pe->name);
+ return -1;
+ }
+ return 0;
+}

- if (compare_pmu_events(table, event))
- return -1;
+static int test__pmu_event_table_sys_callback(const struct pmu_event *pe,
+ const struct pmu_event *table __maybe_unused,
+ void *data)
+{
+ int *map_events = data;
+ struct perf_pmu_test_event const **test_event_table;
+ bool found = false;

- pr_debug("testing event table %s: pass\n", table->name);
- }
+ test_event_table = &sys_events[0];

- if (!found) {
- pr_err("testing event table: could not find event %s\n",
- table->name);
- return -1;
- }
- }
+ 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;

- for (table = sys_event_tables; table->name; table++) {
- struct perf_pmu_test_event const **test_event_table;
- bool found = false;
+ if (strcmp(pe->name, event->name))
+ continue;
+ found = true;
+ (*map_events)++;

- test_event_table = &sys_events[0];
+ if (compare_pmu_events(pe, event))
+ return TEST_FAIL;

- 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;
+ pr_debug("testing sys event table %s: pass\n", pe->name);
+ }
+ if (!found) {
+ pr_debug("testing sys event table: could not find event %s\n", pe->name);
+ return TEST_FAIL;
+ }
+ return TEST_OK;
+}

- if (strcmp(table->name, event->name))
- continue;
- found = true;
- map_events++;
+/* Verify generated events from pmu-events.c are as expected */
+static int test__pmu_event_table(struct test_suite *test __maybe_unused,
+ int subtest __maybe_unused)
+{
+ const struct pmu_event *sys_event_table = find_sys_events_table("pme_test_soc_sys");
+ const struct pmu_event *table = find_core_events_table("testarch", "testcpu");
+ int map_events = 0, expected_events, err;

- if (compare_pmu_events(table, event))
- return -1;
+ /* ignore 3x sentinels */
+ expected_events = ARRAY_SIZE(core_events) +
+ ARRAY_SIZE(uncore_events) +
+ ARRAY_SIZE(sys_events) - 3;

- pr_debug("testing sys event table %s: pass\n", table->name);
- }
- if (!found) {
- pr_debug("testing event table: could not find event %s\n",
- table->name);
- return -1;
- }
- }
+ if (!table || !sys_event_table)
+ return -1;
+
+ err = pmu_events_table_for_each_event(table, test__pmu_event_table_core_callback,
+ &map_events);
+ if (err)
+ return err;
+
+ err = pmu_events_table_for_each_event(sys_event_table, test__pmu_event_table_sys_callback,
+ &map_events);
+ if (err)
+ return err;

if (map_events != expected_events) {
pr_err("testing event table: found %d, but expected %d\n",
map_events, expected_events);
- return -1;
+ return TEST_FAIL;
}

return 0;
diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index 680f7c63838d..f702b73a7609 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -538,12 +538,40 @@ static int metricgroup__print_sys_event_iter(const struct pmu_event *pe,
d->details, d->groups, d->metriclist);
}

+struct metricgroup_print_data {
+ const char *pmu_name;
+ struct strlist *metriclist;
+ char *filter;
+ struct rblist *groups;
+ bool metricgroups;
+ bool raw;
+ bool details;
+};
+
+static int metricgroup__print_callback(const struct pmu_event *pe,
+ const struct pmu_event *table __maybe_unused,
+ void *vdata)
+{
+ struct metricgroup_print_data *data = vdata;
+
+ if (!pe->metric_expr)
+ return 0;
+
+ if (data->pmu_name && perf_pmu__is_hybrid(pe->pmu) && strcmp(data->pmu_name, pe->pmu))
+ return 0;
+
+ return metricgroup__print_pmu_event(pe, data->metricgroups, data->filter,
+ data->raw, data->details, data->groups,
+ data->metriclist);
+}
+
void metricgroup__print(bool metrics, bool metricgroups, char *filter,
bool raw, bool details, const char *pmu_name)
{
struct rblist groups;
struct rb_node *node, *next;
struct strlist *metriclist = NULL;
+ const struct pmu_event *table;

if (!metricgroups) {
metriclist = strlist__new(NULL, NULL);
@@ -555,22 +583,22 @@ void metricgroup__print(bool metrics, bool metricgroups, char *filter,
groups.node_new = mep_new;
groups.node_cmp = mep_cmp;
groups.node_delete = mep_delete;
- for (const struct pmu_event *pe = pmu_events_map__find(); pe; pe++) {
+ table = pmu_events_map__find();
+ if (table) {
+ struct metricgroup_print_data data = {
+ .pmu_name = pmu_name,
+ .metriclist = metriclist,
+ .metricgroups = metricgroups,
+ .filter = filter,
+ .raw = raw,
+ .details = details,
+ .groups = &groups,
+ };

- if (!pe->name && !pe->metric_group && !pe->metric_name)
- break;
- if (!pe->metric_expr)
- continue;
- if (pmu_name && perf_pmu__is_hybrid(pe->pmu) &&
- strcmp(pmu_name, pe->pmu)) {
- continue;
- }
- if (metricgroup__print_pmu_event(pe, metricgroups, filter,
- raw, details, &groups,
- metriclist) < 0)
- return;
+ pmu_events_table_for_each_event(table,
+ metricgroup__print_callback,
+ &data);
}
-
{
struct metricgroup_iter_data data = {
.fn = metricgroup__print_sys_event_iter,
@@ -1043,30 +1071,35 @@ static int __add_metric(struct list_head *metric_list,
return ret;
}

-#define table_for_each_event(__pe, __idx, __table) \
- if (__table) \
- for (__idx = 0, __pe = &__table[__idx]; \
- __pe->name || __pe->metric_group || __pe->metric_name; \
- __pe = &__table[++__idx])
+struct metricgroup__find_metric_data {
+ const char *metric;
+ const struct pmu_event *pe;
+};
+
+static int metricgroup__find_metric_callback(const struct pmu_event *pe,
+ const struct pmu_event *table __maybe_unused,
+ void *vdata)
+{
+ struct metricgroup__find_metric_data *data = vdata;
+
+ if (!match_metric(pe->metric_name, data->metric))
+ return 0;

-#define table_for_each_metric(__pe, __idx, __table, __metric) \
- table_for_each_event(__pe, __idx, __table) \
- if (__pe->metric_expr && \
- (match_metric(__pe->metric_group, __metric) || \
- match_metric(__pe->metric_name, __metric)))
+ data->pe = pe;
+ return -1;
+}

const struct pmu_event *metricgroup__find_metric(const char *metric,
const struct pmu_event *table)
{
- const struct pmu_event *pe;
- int i;
+ struct metricgroup__find_metric_data data = {
+ .metric = metric,
+ .pe = NULL,
+ };

- table_for_each_event(pe, i, table) {
- if (match_metric(pe->metric_name, metric))
- return pe;
- }
+ pmu_events_table_for_each_event(table, metricgroup__find_metric_callback, &data);

- return NULL;
+ return data.pe;
}

static int add_metric(struct list_head *metric_list,
@@ -1151,6 +1184,33 @@ static int metric_list_cmp(void *priv __maybe_unused, const struct list_head *l,
return right_count - left_count;
}

+struct metricgroup__add_metric_data {
+ struct list_head *list;
+ const char *metric_name;
+ const char *modifier;
+ bool metric_no_group;
+ bool has_match;
+};
+
+static int metricgroup__add_metric_callback(const struct pmu_event *pe,
+ const struct pmu_event *table,
+ void *vdata)
+{
+ struct metricgroup__add_metric_data *data = vdata;
+ int ret = 0;
+
+ if (pe->metric_expr &&
+ (match_metric(pe->metric_group, data->metric_name) ||
+ match_metric(pe->metric_name, data->metric_name))) {
+
+ data->has_match = true;
+ ret = add_metric(data->list, pe, data->modifier, data->metric_no_group,
+ /*root_metric=*/NULL,
+ /*visited_metrics=*/NULL, table);
+ }
+ return ret;
+}
+
/**
* metricgroup__add_metric - Find and add a metric, or a metric group.
* @metric_name: The name of the metric or metric group. For example, "IPC"
@@ -1169,24 +1229,29 @@ static int metricgroup__add_metric(const char *metric_name, const char *modifier
struct list_head *metric_list,
const struct pmu_event *table)
{
- const struct pmu_event *pe;
LIST_HEAD(list);
- int i, ret;
+ int ret;
bool has_match = false;

- /*
- * Iterate over all metrics seeing if metric matches either the name or
- * group. When it does add the metric to the list.
- */
- table_for_each_metric(pe, i, table, metric_name) {
- has_match = true;
- ret = add_metric(&list, pe, modifier, metric_no_group,
- /*root_metric=*/NULL,
- /*visited_metrics=*/NULL, table);
+ {
+ struct metricgroup__add_metric_data data = {
+ .list = &list,
+ .metric_name = metric_name,
+ .modifier = modifier,
+ .metric_no_group = metric_no_group,
+ .has_match = false,
+ };
+ /*
+ * Iterate over all metrics seeing if metric matches either the
+ * name or group. When it does add the metric to the list.
+ */
+ ret = pmu_events_table_for_each_event(table, metricgroup__add_metric_callback,
+ &data);
if (ret)
goto out;
- }

+ has_match = data.has_match;
+ }
{
struct metricgroup_iter_data data = {
.fn = metricgroup__add_metric_sys_event_iter,
@@ -1602,26 +1667,30 @@ int metricgroup__parse_groups_test(struct evlist *evlist,
metric_no_merge, &perf_pmu__fake, metric_events, table);
}

+static int metricgroup__has_metric_callback(const struct pmu_event *pe,
+ const struct pmu_event *table __maybe_unused,
+ void *vdata)
+{
+ const char *metric = vdata;
+
+ if (!pe->metric_expr)
+ return 0;
+
+ if (match_metric(pe->metric_name, metric))
+ return 1;
+
+ return 0;
+}
+
bool metricgroup__has_metric(const char *metric)
{
const struct pmu_event *table = pmu_events_map__find();
- const struct pmu_event *pe;
- int i;

if (!table)
return false;

- for (i = 0; ; i++) {
- pe = &table[i];
-
- if (!pe->name && !pe->metric_group && !pe->metric_name)
- break;
- if (!pe->metric_expr)
- continue;
- if (match_metric(pe->metric_name, metric))
- return true;
- }
- return false;
+ return pmu_events_table_for_each_event(table, metricgroup__has_metric_callback,
+ (void *)metric) ? true : false;
}

int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp,
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 5bb03d4e52a3..ab94d672d7bf 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -791,6 +791,36 @@ bool pmu_uncore_alias_match(const char *pmu_name, const char *name)
return res;
}

+struct pmu_add_cpu_aliases_map_data {
+ struct list_head *head;
+ const char *name;
+ const char *cpu_name;
+ struct perf_pmu *pmu;
+};
+
+static int pmu_add_cpu_aliases_map_callback(const struct pmu_event *pe,
+ const struct pmu_event *table __maybe_unused,
+ void *vdata)
+{
+ struct pmu_add_cpu_aliases_map_data *data = vdata;
+ const char *pname = pe->pmu ? pe->pmu : data->cpu_name;
+
+ if (!pe->name)
+ return 0;
+
+ if (data->pmu->is_uncore && pmu_uncore_alias_match(pname, data->name))
+ goto new_alias;
+
+ if (strcmp(pname, data->name))
+ return 0;
+
+new_alias:
+ /* need type casts to override 'const' */
+ __perf_pmu__new_alias(data->head, NULL, (char *)pe->name, (char *)pe->desc,
+ (char *)pe->event, pe);
+ return 0;
+}
+
/*
* From the pmu_events_map, find the table of PMU events that corresponds
* to the current running CPU. Then, add all PMU events from that table
@@ -799,35 +829,14 @@ bool pmu_uncore_alias_match(const char *pmu_name, const char *name)
void pmu_add_cpu_aliases_map(struct list_head *head, struct perf_pmu *pmu,
const struct pmu_event *table)
{
- int i;
- const char *name = pmu->name;
- /*
- * Found a matching PMU events table. Create aliases
- */
- i = 0;
- while (1) {
- const char *cpu_name = is_arm_pmu_core(name) ? name : "cpu";
- const struct pmu_event *pe = &table[i++];
- const char *pname = pe->pmu ? pe->pmu : cpu_name;
-
- if (!pe->name) {
- if (pe->metric_group || pe->metric_name)
- continue;
- break;
- }
-
- if (pmu->is_uncore && pmu_uncore_alias_match(pname, name))
- goto new_alias;
-
- if (strcmp(pname, name))
- continue;
+ struct pmu_add_cpu_aliases_map_data data = {
+ .head = head,
+ .name = pmu->name,
+ .cpu_name = is_arm_pmu_core(pmu->name) ? pmu->name : "cpu",
+ .pmu = pmu,
+ };

-new_alias:
- /* need type casts to override 'const' */
- __perf_pmu__new_alias(head, NULL, (char *)pe->name,
- (char *)pe->desc, (char *)pe->event,
- pe);
- }
+ pmu_events_table_for_each_event(table, pmu_add_cpu_aliases_map_callback, &data);
}

static void pmu_add_cpu_aliases(struct list_head *head, struct perf_pmu *pmu)
diff --git a/tools/perf/util/s390-sample-raw.c b/tools/perf/util/s390-sample-raw.c
index 1ecb718fc0eb..b1c052c176bb 100644
--- a/tools/perf/util/s390-sample-raw.c
+++ b/tools/perf/util/s390-sample-raw.c
@@ -129,6 +129,28 @@ static int get_counterset_start(int setnr)
}
}

+struct get_counter_name_data {
+ int wanted;
+ const char *result;
+};
+
+static int get_counter_name_callback(const struct pmu_event *evp,
+ const struct pmu_event *table __maybe_unused,
+ void *vdata)
+{
+ struct get_counter_name_data *data = vdata;
+ int rc, event_nr;
+
+ if (evp->name == NULL || evp->event == NULL)
+ return 0;
+ rc = sscanf(evp->event, "event=%x", &event_nr);
+ if (rc == 1 && event_nr == data->wanted) {
+ data->result = evp->name;
+ return 1; /* Terminate the search. */
+ }
+ return 0;
+}
+
/* Scan the PMU table and extract the logical name of a counter from the
* PMU events table. Input is the counter set and counter number with in the
* set. Construct the event number and use this as key. If they match return
@@ -137,20 +159,16 @@ static int get_counterset_start(int setnr)
*/
static const char *get_counter_name(int set, int nr, const struct pmu_event *table)
{
- int rc, event_nr, wanted = get_counterset_start(set) + nr;
+ struct get_counter_name_data data = {
+ .wanted = get_counterset_start(set) + nr,
+ .result = NULL,
+ };

- if (table) {
- const struct pmu_event *evp = table;
+ if (!table)
+ return NULL;

- for (; evp->name || evp->event || evp->desc; ++evp) {
- if (evp->name == NULL || evp->event == NULL)
- continue;
- rc = sscanf(evp->event, "event=%x", &event_nr);
- if (rc == 1 && event_nr == wanted)
- return evp->name;
- }
- }
- return NULL;
+ pmu_events_table_for_each_event(table, get_counter_name_callback, &data);
+ return data.result;
}

static void s390_cpumcfdg_dump(struct perf_sample *sample)
--
2.37.1.455.g008518b4e5-goog

2022-07-29 08:27:13

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 14/17] perf pmu-events: Hide the pmu_events

Hide that the pmu_event structs are an array with a new wrapper struct.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/arch/arm64/util/pmu.c | 2 +-
tools/perf/pmu-events/empty-pmu-events.c | 44 ++++++++++++----------
tools/perf/pmu-events/jevents.py | 47 +++++++++++++-----------
tools/perf/pmu-events/pmu-events.h | 12 +++---
tools/perf/tests/expand-cgroup.c | 2 +-
tools/perf/tests/parse-metric.c | 2 +-
tools/perf/tests/pmu-events.c | 16 ++++----
tools/perf/util/metricgroup.c | 42 ++++++++++-----------
tools/perf/util/metricgroup.h | 5 +--
tools/perf/util/pmu.c | 10 ++---
tools/perf/util/pmu.h | 4 +-
tools/perf/util/s390-sample-raw.c | 6 +--
12 files changed, 101 insertions(+), 91 deletions(-)

diff --git a/tools/perf/arch/arm64/util/pmu.c b/tools/perf/arch/arm64/util/pmu.c
index 646af8603227..149fb334b40b 100644
--- a/tools/perf/arch/arm64/util/pmu.c
+++ b/tools/perf/arch/arm64/util/pmu.c
@@ -3,7 +3,7 @@
#include "../../../util/cpumap.h"
#include "../../../util/pmu.h"

-const struct pmu_event *pmu_events_map__find(void)
+const struct pmu_events_table *pmu_events_map__find(void)
{
struct perf_pmu *pmu = NULL;

diff --git a/tools/perf/pmu-events/empty-pmu-events.c b/tools/perf/pmu-events/empty-pmu-events.c
index bee1967baa2b..5ed8c0aa4817 100644
--- a/tools/perf/pmu-events/empty-pmu-events.c
+++ b/tools/perf/pmu-events/empty-pmu-events.c
@@ -176,6 +176,10 @@ static const struct pmu_event pme_test_soc_cpu[] = {
},
};

+/* Struct used to make the PMU event table implementation opaque to callers. */
+struct pmu_events_table {
+ const struct pmu_event *entries;
+};

/*
* Map a CPU to its table of PMU events. The CPU is identified by the
@@ -188,7 +192,7 @@ static const struct pmu_event pme_test_soc_cpu[] = {
struct pmu_events_map {
const char *arch;
const char *cpuid;
- const struct pmu_event *table;
+ const struct pmu_events_table table;
};

/*
@@ -199,12 +203,12 @@ static const struct pmu_events_map pmu_events_map[] = {
{
.arch = "testarch",
.cpuid = "testcpu",
- .table = pme_test_soc_cpu,
+ .table = { pme_test_soc_cpu },
},
{
.arch = 0,
.cpuid = 0,
- .table = 0,
+ .table = { 0 },
},
};

@@ -234,23 +238,23 @@ static const struct pmu_event pme_test_soc_sys[] = {

struct pmu_sys_events {
const char *name;
- const struct pmu_event *table;
+ const struct pmu_events_table table;
};

static const struct pmu_sys_events pmu_sys_event_tables[] = {
{
- .table = pme_test_soc_sys,
+ .table = { pme_test_soc_sys },
.name = "pme_test_soc_sys",
},
{
- .table = 0
+ .table = { 0 }
},
};

-int pmu_events_table_for_each_event(const struct pmu_event *table, pmu_event_iter_fn fn,
+int pmu_events_table_for_each_event(const struct pmu_events_table *table, pmu_event_iter_fn fn,
void *data)
{
- for (const struct pmu_event *pe = &table[0];
+ for (const struct pmu_event *pe = &table->entries[0];
pe->name || pe->metric_group || pe->metric_name;
pe++) {
int ret = fn(pe, table, data);
@@ -261,9 +265,9 @@ int pmu_events_table_for_each_event(const struct pmu_event *table, pmu_event_ite
return 0;
}

-const struct pmu_event *perf_pmu__find_table(struct perf_pmu *pmu)
+const struct pmu_events_table *perf_pmu__find_table(struct perf_pmu *pmu)
{
- const struct pmu_event *table = NULL;
+ const struct pmu_events_table *table = NULL;
char *cpuid = perf_pmu__getcpuid(pmu);
int i;

@@ -277,11 +281,11 @@ const struct pmu_event *perf_pmu__find_table(struct perf_pmu *pmu)
for (;;) {
const struct pmu_events_map *map = &pmu_events_map[i++];

- if (!map->table)
+ if (!map->cpuid)
break;

if (!strcmp_cpuid_str(map->cpuid, cpuid)) {
- table = map->table;
+ table = &map->table;
break;
}
}
@@ -289,13 +293,13 @@ const struct pmu_event *perf_pmu__find_table(struct perf_pmu *pmu)
return table;
}

-const struct pmu_event *find_core_events_table(const char *arch, const char *cpuid)
+const struct pmu_events_table *find_core_events_table(const char *arch, const char *cpuid)
{
for (const struct pmu_events_map *tables = &pmu_events_map[0];
- tables->table;
+ tables->arch;
tables++) {
if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid))
- return tables->table;
+ return &tables->table;
}
return NULL;
}
@@ -303,9 +307,9 @@ const struct pmu_event *find_core_events_table(const char *arch, const char *cpu
int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data)
{
for (const struct pmu_events_map *tables = &pmu_events_map[0];
- tables->table;
+ tables->arch;
tables++) {
- int ret = pmu_events_table_for_each_event(tables->table, fn, data);
+ int ret = pmu_events_table_for_each_event(&tables->table, fn, data);

if (ret)
return ret;
@@ -313,13 +317,13 @@ int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data)
return 0;
}

-const struct pmu_event *find_sys_events_table(const char *name)
+const struct pmu_events_table *find_sys_events_table(const char *name)
{
for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0];
tables->name;
tables++) {
if (!strcmp(tables->name, name))
- return tables->table;
+ return &tables->table;
}
return NULL;
}
@@ -329,7 +333,7 @@ int pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data)
for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0];
tables->name;
tables++) {
- int ret = pmu_events_table_for_each_event(tables->table, fn, data);
+ int ret = pmu_events_table_for_each_event(&tables->table, fn, data);

if (ret)
return ret;
diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
index 365c960202b0..aa8df649025a 100755
--- a/tools/perf/pmu-events/jevents.py
+++ b/tools/perf/pmu-events/jevents.py
@@ -334,6 +334,11 @@ def process_one_file(parents: Sequence[str], item: os.DirEntry) -> None:
def print_mapping_table(archs: Sequence[str]) -> None:
"""Read the mapfile and generate the struct from cpuid string to event table."""
_args.output_file.write("""
+/* Struct used to make the PMU event table implementation opaque to callers. */
+struct pmu_events_table {
+ const struct pmu_event *entries;
+};
+
/*
* Map a CPU to its table of PMU events. The CPU is identified by the
* cpuid field, which is an arch-specific identifier for the CPU.
@@ -345,7 +350,7 @@ def print_mapping_table(archs: Sequence[str]) -> None:
struct pmu_events_map {
const char *arch;
const char *cpuid;
- const struct pmu_event *table;
+ struct pmu_events_table table;
};

/*
@@ -359,7 +364,7 @@ const struct pmu_events_map pmu_events_map[] = {
_args.output_file.write("""{
\t.arch = "testarch",
\t.cpuid = "testcpu",
-\t.table = pme_test_soc_cpu,
+\t.table = { pme_test_soc_cpu },
},
""")
else:
@@ -374,7 +379,7 @@ const struct pmu_events_map pmu_events_map[] = {
_args.output_file.write(f"""{{
\t.arch = "{arch}",
\t.cpuid = "{cpuid}",
-\t.table = {tblname}
+\t.table = {{ {tblname} }}
}},
""")
first = False
@@ -382,7 +387,7 @@ const struct pmu_events_map pmu_events_map[] = {
_args.output_file.write("""{
\t.arch = 0,
\t.cpuid = 0,
-\t.table = 0,
+\t.table = { 0 },
}
};
""")
@@ -393,26 +398,26 @@ def print_system_mapping_table() -> None:
_args.output_file.write("""
struct pmu_sys_events {
\tconst char *name;
-\tconst struct pmu_event *table;
+\tstruct pmu_events_table table;
};

static const struct pmu_sys_events pmu_sys_event_tables[] = {
""")
for tblname in _sys_event_tables:
_args.output_file.write(f"""\t{{
-\t\t.table = {tblname},
+\t\t.table = {{ {tblname} }},
\t\t.name = \"{tblname}\",
\t}},
""")
_args.output_file.write("""\t{
-\t\t.table = 0
+\t\t.table = { 0 }
\t},
};

-int pmu_events_table_for_each_event(const struct pmu_event *table, pmu_event_iter_fn fn,
+int pmu_events_table_for_each_event(const struct pmu_events_table *table, pmu_event_iter_fn fn,
void *data)
{
- for (const struct pmu_event *pe = &table[0];
+ for (const struct pmu_event *pe = &table->entries[0];
pe->name || pe->metric_group || pe->metric_name;
pe++) {
int ret = fn(pe, table, data);
@@ -423,9 +428,9 @@ int pmu_events_table_for_each_event(const struct pmu_event *table, pmu_event_ite
return 0;
}

-const struct pmu_event *perf_pmu__find_table(struct perf_pmu *pmu)
+const struct pmu_events_table *perf_pmu__find_table(struct perf_pmu *pmu)
{
- const struct pmu_event *table = NULL;
+ const struct pmu_events_table *table = NULL;
char *cpuid = perf_pmu__getcpuid(pmu);
int i;

@@ -438,11 +443,11 @@ const struct pmu_event *perf_pmu__find_table(struct perf_pmu *pmu)
i = 0;
for (;;) {
const struct pmu_events_map *map = &pmu_events_map[i++];
- if (!map->table)
+ if (!map->arch)
break;

if (!strcmp_cpuid_str(map->cpuid, cpuid)) {
- table = map->table;
+ table = &map->table;
break;
}
}
@@ -450,13 +455,13 @@ const struct pmu_event *perf_pmu__find_table(struct perf_pmu *pmu)
return table;
}

-const struct pmu_event *find_core_events_table(const char *arch, const char *cpuid)
+const struct pmu_events_table *find_core_events_table(const char *arch, const char *cpuid)
{
for (const struct pmu_events_map *tables = &pmu_events_map[0];
- tables->table;
+ tables->arch;
tables++) {
if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid))
- return tables->table;
+ return &tables->table;
}
return NULL;
}
@@ -464,9 +469,9 @@ const struct pmu_event *find_core_events_table(const char *arch, const char *cpu
int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data)
{
for (const struct pmu_events_map *tables = &pmu_events_map[0];
- tables->table;
+ tables->arch;
tables++) {
- int ret = pmu_events_table_for_each_event(tables->table, fn, data);
+ int ret = pmu_events_table_for_each_event(&tables->table, fn, data);

if (ret)
return ret;
@@ -474,13 +479,13 @@ int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data)
return 0;
}

-const struct pmu_event *find_sys_events_table(const char *name)
+const struct pmu_events_table *find_sys_events_table(const char *name)
{
for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0];
tables->name;
tables++) {
if (!strcmp(tables->name, name))
- return tables->table;
+ return &tables->table;
}
return NULL;
}
@@ -490,7 +495,7 @@ int pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data)
for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0];
tables->name;
tables++) {
- int ret = pmu_events_table_for_each_event(tables->table, fn, data);
+ int ret = pmu_events_table_for_each_event(&tables->table, fn, data);

if (ret)
return ret;
diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h
index 70672842f77f..fe343c4d8016 100644
--- a/tools/perf/pmu-events/pmu-events.h
+++ b/tools/perf/pmu-events/pmu-events.h
@@ -30,18 +30,20 @@ struct pmu_event {
const char *metric_constraint;
};

+struct pmu_events_table;
+
typedef int (*pmu_event_iter_fn)(const struct pmu_event *pe,
- const struct pmu_event *table,
+ const struct pmu_events_table *table,
void *data);

-int pmu_events_table_for_each_event(const struct pmu_event *table, pmu_event_iter_fn fn,
+int pmu_events_table_for_each_event(const struct pmu_events_table *table, pmu_event_iter_fn fn,
void *data);

-const struct pmu_event *perf_pmu__find_table(struct perf_pmu *pmu);
-const struct pmu_event *find_core_events_table(const char *arch, const char *cpuid);
+const struct pmu_events_table *perf_pmu__find_table(struct perf_pmu *pmu);
+const struct pmu_events_table *find_core_events_table(const char *arch, const char *cpuid);
int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data);

-const struct pmu_event *find_sys_events_table(const char *name);
+const struct pmu_events_table *find_sys_events_table(const char *name);
int pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data);

#endif
diff --git a/tools/perf/tests/expand-cgroup.c b/tools/perf/tests/expand-cgroup.c
index e79ee8621a90..51fb5f34c1dd 100644
--- a/tools/perf/tests/expand-cgroup.c
+++ b/tools/perf/tests/expand-cgroup.c
@@ -180,7 +180,7 @@ static int expand_metric_events(void)
struct evlist *evlist;
struct rblist metric_events;
const char metric_str[] = "CPI";
- const struct pmu_event *pme_test;
+ const struct pmu_events_table *pme_test;

evlist = evlist__new();
TEST_ASSERT_VAL("failed to get evlist", evlist);
diff --git a/tools/perf/tests/parse-metric.c b/tools/perf/tests/parse-metric.c
index 30c7091857b8..68f5a2a03242 100644
--- a/tools/perf/tests/parse-metric.c
+++ b/tools/perf/tests/parse-metric.c
@@ -72,7 +72,7 @@ static int __compute_metric(const char *name, struct value *vals,
struct rblist metric_events = {
.nr_entries = 0,
};
- const struct pmu_event *pme_test;
+ const struct pmu_events_table *pme_test;
struct perf_cpu_map *cpus;
struct runtime_stat st;
struct evlist *evlist;
diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c
index 1c3479e5890e..9111725041d0 100644
--- a/tools/perf/tests/pmu-events.c
+++ b/tools/perf/tests/pmu-events.c
@@ -424,7 +424,7 @@ static int compare_alias_to_test_event(struct perf_pmu_alias *alias,
}

static int test__pmu_event_table_core_callback(const struct pmu_event *pe,
- const struct pmu_event *table __maybe_unused,
+ const struct pmu_events_table *table __maybe_unused,
void *data)
{
int *map_events = data;
@@ -461,7 +461,7 @@ static int test__pmu_event_table_core_callback(const struct pmu_event *pe,
}

static int test__pmu_event_table_sys_callback(const struct pmu_event *pe,
- const struct pmu_event *table __maybe_unused,
+ const struct pmu_events_table *table __maybe_unused,
void *data)
{
int *map_events = data;
@@ -495,8 +495,8 @@ static int test__pmu_event_table_sys_callback(const struct pmu_event *pe,
static int test__pmu_event_table(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
- const struct pmu_event *sys_event_table = find_sys_events_table("pme_test_soc_sys");
- const struct pmu_event *table = find_core_events_table("testarch", "testcpu");
+ const struct pmu_events_table *sys_event_table = find_sys_events_table("pme_test_soc_sys");
+ const struct pmu_events_table *table = find_core_events_table("testarch", "testcpu");
int map_events = 0, expected_events, err;

/* ignore 3x sentinels */
@@ -544,7 +544,7 @@ static int __test_core_pmu_event_aliases(char *pmu_name, int *count)
struct perf_pmu *pmu;
LIST_HEAD(aliases);
int res = 0;
- const struct pmu_event *table = find_core_events_table("testarch", "testcpu");
+ const struct pmu_events_table *table = find_core_events_table("testarch", "testcpu");
struct perf_pmu_alias *a, *tmp;

if (!table)
@@ -597,7 +597,7 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu)
struct perf_pmu *pmu = &test_pmu->pmu;
const char *pmu_name = pmu->name;
struct perf_pmu_alias *a, *tmp, *alias;
- const struct pmu_event *events_table;
+ const struct pmu_events_table *events_table;
LIST_HEAD(aliases);
int res = 0;

@@ -839,7 +839,7 @@ struct metric {
struct metric_ref metric_ref;
};

-static int test__parsing_callback(const struct pmu_event *pe, const struct pmu_event *table,
+static int test__parsing_callback(const struct pmu_event *pe, const struct pmu_events_table *table,
void *data)
{
int *failures = data;
@@ -1016,7 +1016,7 @@ static int metric_parse_fake(const char *str)
}

static int test__parsing_fake_callback(const struct pmu_event *pe,
- const struct pmu_event *table __maybe_unused,
+ const struct pmu_events_table *table __maybe_unused,
void *data __maybe_unused)
{
if (!pe->metric_expr)
diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index f702b73a7609..8ef44f4b5e9a 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -508,7 +508,7 @@ struct metricgroup_iter_data {
};

static int metricgroup__sys_event_iter(const struct pmu_event *pe,
- const struct pmu_event *table __maybe_unused,
+ const struct pmu_events_table *table,
void *data)
{
struct metricgroup_iter_data *d = data;
@@ -529,7 +529,7 @@ static int metricgroup__sys_event_iter(const struct pmu_event *pe,
}

static int metricgroup__print_sys_event_iter(const struct pmu_event *pe,
- const struct pmu_event *table __maybe_unused,
+ const struct pmu_events_table *table __maybe_unused,
void *data)
{
struct metricgroup_print_sys_idata *d = data;
@@ -549,7 +549,7 @@ struct metricgroup_print_data {
};

static int metricgroup__print_callback(const struct pmu_event *pe,
- const struct pmu_event *table __maybe_unused,
+ const struct pmu_events_table *table __maybe_unused,
void *vdata)
{
struct metricgroup_print_data *data = vdata;
@@ -571,7 +571,7 @@ void metricgroup__print(bool metrics, bool metricgroups, char *filter,
struct rblist groups;
struct rb_node *node, *next;
struct strlist *metriclist = NULL;
- const struct pmu_event *table;
+ const struct pmu_events_table *table;

if (!metricgroups) {
metriclist = strlist__new(NULL, NULL);
@@ -876,7 +876,7 @@ struct metricgroup_add_iter_data {
bool metric_no_group;
struct metric *root_metric;
const struct visited_metric *visited;
- const struct pmu_event *table;
+ const struct pmu_events_table *table;
};

static int add_metric(struct list_head *metric_list,
@@ -885,7 +885,7 @@ static int add_metric(struct list_head *metric_list,
bool metric_no_group,
struct metric *root_metric,
const struct visited_metric *visited,
- const struct pmu_event *table);
+ const struct pmu_events_table *table);

/**
* resolve_metric - Locate metrics within the root metric and recursively add
@@ -908,7 +908,7 @@ static int resolve_metric(struct list_head *metric_list,
bool metric_no_group,
struct metric *root_metric,
const struct visited_metric *visited,
- const struct pmu_event *table)
+ const struct pmu_events_table *table)
{
struct hashmap_entry *cur;
size_t bkt;
@@ -986,7 +986,7 @@ static int __add_metric(struct list_head *metric_list,
int runtime,
struct metric *root_metric,
const struct visited_metric *visited,
- const struct pmu_event *table)
+ const struct pmu_events_table *table)
{
const struct visited_metric *vm;
int ret;
@@ -1077,7 +1077,7 @@ struct metricgroup__find_metric_data {
};

static int metricgroup__find_metric_callback(const struct pmu_event *pe,
- const struct pmu_event *table __maybe_unused,
+ const struct pmu_events_table *table __maybe_unused,
void *vdata)
{
struct metricgroup__find_metric_data *data = vdata;
@@ -1090,7 +1090,7 @@ static int metricgroup__find_metric_callback(const struct pmu_event *pe,
}

const struct pmu_event *metricgroup__find_metric(const char *metric,
- const struct pmu_event *table)
+ const struct pmu_events_table *table)
{
struct metricgroup__find_metric_data data = {
.metric = metric,
@@ -1108,7 +1108,7 @@ static int add_metric(struct list_head *metric_list,
bool metric_no_group,
struct metric *root_metric,
const struct visited_metric *visited,
- const struct pmu_event *table)
+ const struct pmu_events_table *table)
{
int ret = 0;

@@ -1136,8 +1136,8 @@ static int add_metric(struct list_head *metric_list,
}

static int metricgroup__add_metric_sys_event_iter(const struct pmu_event *pe,
- const struct pmu_event *table __maybe_unused,
- void *data)
+ const struct pmu_events_table *table __maybe_unused,
+ void *data)
{
struct metricgroup_add_iter_data *d = data;
int ret;
@@ -1193,7 +1193,7 @@ struct metricgroup__add_metric_data {
};

static int metricgroup__add_metric_callback(const struct pmu_event *pe,
- const struct pmu_event *table,
+ const struct pmu_events_table *table,
void *vdata)
{
struct metricgroup__add_metric_data *data = vdata;
@@ -1227,7 +1227,7 @@ static int metricgroup__add_metric_callback(const struct pmu_event *pe,
static int metricgroup__add_metric(const char *metric_name, const char *modifier,
bool metric_no_group,
struct list_head *metric_list,
- const struct pmu_event *table)
+ const struct pmu_events_table *table)
{
LIST_HEAD(list);
int ret;
@@ -1296,7 +1296,7 @@ static int metricgroup__add_metric(const char *metric_name, const char *modifier
*/
static int metricgroup__add_metric_list(const char *list, bool metric_no_group,
struct list_head *metric_list,
- const struct pmu_event *table)
+ const struct pmu_events_table *table)
{
char *list_itr, *list_copy, *metric_name, *modifier;
int ret, count = 0;
@@ -1504,7 +1504,7 @@ static int parse_groups(struct evlist *perf_evlist, const char *str,
bool metric_no_merge,
struct perf_pmu *fake_pmu,
struct rblist *metric_events_list,
- const struct pmu_event *table)
+ const struct pmu_events_table *table)
{
struct evlist *combined_evlist = NULL;
LIST_HEAD(metric_list);
@@ -1650,14 +1650,14 @@ int metricgroup__parse_groups(const struct option *opt,
struct rblist *metric_events)
{
struct evlist *perf_evlist = *(struct evlist **)opt->value;
- const struct pmu_event *table = pmu_events_map__find();
+ const struct pmu_events_table *table = pmu_events_map__find();

return parse_groups(perf_evlist, str, metric_no_group,
metric_no_merge, NULL, metric_events, table);
}

int metricgroup__parse_groups_test(struct evlist *evlist,
- const struct pmu_event *table,
+ const struct pmu_events_table *table,
const char *str,
bool metric_no_group,
bool metric_no_merge,
@@ -1668,7 +1668,7 @@ int metricgroup__parse_groups_test(struct evlist *evlist,
}

static int metricgroup__has_metric_callback(const struct pmu_event *pe,
- const struct pmu_event *table __maybe_unused,
+ const struct pmu_events_table *table __maybe_unused,
void *vdata)
{
const char *metric = vdata;
@@ -1684,7 +1684,7 @@ static int metricgroup__has_metric_callback(const struct pmu_event *pe,

bool metricgroup__has_metric(const char *metric)
{
- const struct pmu_event *table = pmu_events_map__find();
+ const struct pmu_events_table *table = pmu_events_map__find();

if (!table)
return false;
diff --git a/tools/perf/util/metricgroup.h b/tools/perf/util/metricgroup.h
index 5a1390e73d25..f54d170043e9 100644
--- a/tools/perf/util/metricgroup.h
+++ b/tools/perf/util/metricgroup.h
@@ -11,7 +11,6 @@ struct evlist;
struct evsel;
struct option;
struct rblist;
-struct pmu_events_map;
struct cgroup;

/**
@@ -71,9 +70,9 @@ int metricgroup__parse_groups(const struct option *opt,
bool metric_no_merge,
struct rblist *metric_events);
const struct pmu_event *metricgroup__find_metric(const char *metric,
- const struct pmu_event *table);
+ const struct pmu_events_table *table);
int metricgroup__parse_groups_test(struct evlist *evlist,
- const struct pmu_event *table,
+ const struct pmu_events_table *table,
const char *str,
bool metric_no_group,
bool metric_no_merge,
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index ab94d672d7bf..a84fa532817f 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -710,7 +710,7 @@ char *perf_pmu__getcpuid(struct perf_pmu *pmu)
return cpuid;
}

-__weak const struct pmu_event *pmu_events_map__find(void)
+__weak const struct pmu_events_table *pmu_events_map__find(void)
{
return perf_pmu__find_table(NULL);
}
@@ -799,7 +799,7 @@ struct pmu_add_cpu_aliases_map_data {
};

static int pmu_add_cpu_aliases_map_callback(const struct pmu_event *pe,
- const struct pmu_event *table __maybe_unused,
+ const struct pmu_events_table *table __maybe_unused,
void *vdata)
{
struct pmu_add_cpu_aliases_map_data *data = vdata;
@@ -827,7 +827,7 @@ static int pmu_add_cpu_aliases_map_callback(const struct pmu_event *pe,
* as aliases.
*/
void pmu_add_cpu_aliases_map(struct list_head *head, struct perf_pmu *pmu,
- const struct pmu_event *table)
+ const struct pmu_events_table *table)
{
struct pmu_add_cpu_aliases_map_data data = {
.head = head,
@@ -841,7 +841,7 @@ void pmu_add_cpu_aliases_map(struct list_head *head, struct perf_pmu *pmu,

static void pmu_add_cpu_aliases(struct list_head *head, struct perf_pmu *pmu)
{
- const struct pmu_event *table;
+ const struct pmu_events_table *table;

table = perf_pmu__find_table(pmu);
if (!table)
@@ -856,7 +856,7 @@ struct pmu_sys_event_iter_data {
};

static int pmu_add_sys_aliases_iter_fn(const struct pmu_event *pe,
- const struct pmu_event *table __maybe_unused,
+ const struct pmu_events_table *table __maybe_unused,
void *data)
{
struct pmu_sys_event_iter_data *idata = data;
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 06df99ba2029..78f87b46ddf3 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -126,10 +126,10 @@ int perf_pmu__test(void);

struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu);
void pmu_add_cpu_aliases_map(struct list_head *head, struct perf_pmu *pmu,
- const struct pmu_event *map);
+ const struct pmu_events_table *map);

char *perf_pmu__getcpuid(struct perf_pmu *pmu);
-const struct pmu_event *pmu_events_map__find(void);
+const struct pmu_events_table *pmu_events_map__find(void);
bool pmu_uncore_alias_match(const char *pmu_name, const char *name);
void perf_pmu_free_alias(struct perf_pmu_alias *alias);

diff --git a/tools/perf/util/s390-sample-raw.c b/tools/perf/util/s390-sample-raw.c
index b1c052c176bb..fad1e96c74ba 100644
--- a/tools/perf/util/s390-sample-raw.c
+++ b/tools/perf/util/s390-sample-raw.c
@@ -135,7 +135,7 @@ struct get_counter_name_data {
};

static int get_counter_name_callback(const struct pmu_event *evp,
- const struct pmu_event *table __maybe_unused,
+ const struct pmu_events_table *table __maybe_unused,
void *vdata)
{
struct get_counter_name_data *data = vdata;
@@ -157,7 +157,7 @@ static int get_counter_name_callback(const struct pmu_event *evp,
* the name of this counter.
* If no match is found a NULL pointer is returned.
*/
-static const char *get_counter_name(int set, int nr, const struct pmu_event *table)
+static const char *get_counter_name(int set, int nr, const struct pmu_events_table *table)
{
struct get_counter_name_data data = {
.wanted = get_counterset_start(set) + nr,
@@ -177,7 +177,7 @@ static void s390_cpumcfdg_dump(struct perf_sample *sample)
unsigned char *buf = sample->raw_data;
const char *color = PERF_COLOR_BLUE;
struct cf_ctrset_entry *cep, ce;
- const struct pmu_event *table;
+ const struct pmu_events_table *table;
u64 *p;

table = pmu_events_map__find();
--
2.37.1.455.g008518b4e5-goog

2022-07-29 08:27:21

by Ian Rogers

[permalink] [raw]
Subject: [PATCH v3 17/17] perf jevents: Fold strings optimization

If a shorter string ends a longer string then the shorter string may
reuse the longer string at an offset. For example, on x86 the event
arith.cycles_div_busy and cycles_div_busy can be folded, even though
they have difference names the strings are identical after 6
characters. cycles_div_busy can reuse the arith.cycles_div_busy string
at an offset of 6.

In pmu-events.c this looks like the following where the 'also:' lists
folded strings:
/* offset=177541 */ "arith.cycles_div_busy\000\000pipeline\000Cycles the divider is busy\000\000\000event=0x14,period=2000000,umask=0x1\000\000\000\000\000\000\000\000\000" /* also: cycles_div_busy\000\000pipeline\000Cycles the divider is busy\000\000\000event=0x14,period=2000000,umask=0x1\000\000\000\000\000\000\000\000\000 */

As jevents.py combines multiple strings for an event into a larger
string, the amount of folding is minimal as all parts of the event must
align. Other organizations can benefit more from folding, but lose space
by say recording more offsets.

Signed-off-by: Ian Rogers <[email protected]>
---
tools/perf/pmu-events/jevents.py | 55 ++++++++++++++++++++++++++++----
1 file changed, 48 insertions(+), 7 deletions(-)

diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
index a5e162558994..e5545758c92d 100755
--- a/tools/perf/pmu-events/jevents.py
+++ b/tools/perf/pmu-events/jevents.py
@@ -80,7 +80,9 @@ class BigCString:
are all the other C strings (to avoid memory issues the string
itself is held as a list of strings). The offsets within the big
string are recorded and when stored to disk these don't need
- relocation.
+ relocation. To reduce the size of the string further, identical
+ strings are merged. If a longer string ends-with the same value as a
+ shorter string, these entries are also merged.
"""
strings: Set[str]
big_string: Sequence[str]
@@ -96,6 +98,33 @@ class BigCString:
def compute(self) -> None:
"""Called once all strings are added to compute the string and offsets."""

+ folded_strings = {}
+ # Determine if two strings can be folded, ie. let 1 string use the
+ # end of another. First reverse all strings and sort them.
+ sorted_reversed_strings = sorted([x[::-1] for x in self.strings])
+
+ # Strings 'xyz' and 'yz' will now be [ 'zy', 'zyx' ]. Scan forward
+ # for each string to see if there is a better candidate to fold it
+ # into, in the example rather than using 'yz' we can use'xyz' at
+ # an offset of 1. We record which string can be folded into which
+ # in folded_strings, we don't need to record the offset as it is
+ # trivially computed from the string lengths.
+ for pos,s in enumerate(sorted_reversed_strings):
+ best_pos = pos
+ for check_pos in range(pos + 1, len(sorted_reversed_strings)):
+ if sorted_reversed_strings[check_pos].startswith(s):
+ best_pos = check_pos
+ else:
+ break
+ if pos != best_pos:
+ folded_strings[s[::-1]] = sorted_reversed_strings[best_pos][::-1]
+
+ # Compute reverse mappings for debugging.
+ fold_into_strings = collections.defaultdict(set)
+ for key, val in folded_strings.items():
+ if key != val:
+ fold_into_strings[val].add(key)
+
# big_string_offset is the current location within the C string
# being appended to - comments, etc. don't count. big_string is
# the string contents represented as a list. Strings are immutable
@@ -104,13 +133,25 @@ class BigCString:
big_string_offset = 0
self.big_string = []
self.offsets = {}
- # Emit all strings in a sorted manner.
+
+ # Emit all strings that aren't folded in a sorted manner.
for s in sorted(self.strings):
- self.offsets[s] = big_string_offset
- self.big_string.append(f'/* offset={big_string_offset} */ "')
- self.big_string.append(s)
- self.big_string.append('"\n')
- big_string_offset += c_len(s)
+ if s not in folded_strings:
+ self.offsets[s] = big_string_offset
+ self.big_string.append(f'/* offset={big_string_offset} */ "')
+ self.big_string.append(s)
+ self.big_string.append('"')
+ if s in fold_into_strings:
+ self.big_string.append(' /* also: ' + ', '.join(fold_into_strings[s]) + ' */')
+ self.big_string.append('\n')
+ big_string_offset += c_len(s)
+ continue
+
+ # Compute the offsets of the folded strings.
+ for s in folded_strings.keys():
+ assert s not in self.offsets
+ folded_s = folded_strings[s]
+ self.offsets[s] = self.offsets[folded_s] + c_len(folded_s) - c_len(s)

_bcs = BigCString()

--
2.37.1.455.g008518b4e5-goog

2022-07-29 08:40:29

by John Garry

[permalink] [raw]
Subject: Re: [PATCH v3 05/17] perf jevents: Remove the type/version variables

On 29/07/2022 08:43, Ian Rogers wrote:
> pmu_events_map has a type variable that is always initialized to "core"
> and a version variable that is never read. Remove these from the API as
> it is straightforward to add them back when necessary.
>
> Signed-off-by: Ian Rogers <[email protected]>

Reviewed-by: John Garry <[email protected]>

Please note the questions, below.

> ---
> tools/perf/pmu-events/empty-pmu-events.c | 6 ++----
> tools/perf/pmu-events/jevents.py | 6 ------
> tools/perf/pmu-events/pmu-events.h | 2 --
> tools/perf/tests/expand-cgroup.c | 2 --
> tools/perf/tests/parse-metric.c | 2 --
> 5 files changed, 2 insertions(+), 16 deletions(-)
>
> diff --git a/tools/perf/pmu-events/empty-pmu-events.c b/tools/perf/pmu-events/empty-pmu-events.c
> index 77e655c6f116..4182a986f505 100644
> --- a/tools/perf/pmu-events/empty-pmu-events.c
> +++ b/tools/perf/pmu-events/empty-pmu-events.c
> @@ -110,15 +110,13 @@ static const struct pmu_event pme_test_soc_cpu[] = {
>
> const struct pmu_events_map pmu_events_map[] = {
> {
> + .arch = "testarch",

Is this really supposed to be part of this patch?

> .cpuid = "testcpu",
> - .version = "v1",
> - .type = "core",
> .table = pme_test_soc_cpu,
> },
> {
> + .arch = 0,

Same as above

> .cpuid = 0,
> - .version = 0,
> - .type = 0,
> .table = 0,
> },
> };
> diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
> index e6e6c42c3f8a..98d18d5c3830 100755
> --- a/tools/perf/pmu-events/jevents.py
> +++ b/tools/perf/pmu-events/jevents.py
> @@ -312,8 +312,6 @@ def print_mapping_table(archs: Sequence[str]) -> None:
> _args.output_file.write("""{
> \t.arch = "testarch",
> \t.cpuid = "testcpu",
> -\t.version = "v1",
> -\t.type = "core",
> \t.table = pme_test_soc_cpu,
> },
> """)
> @@ -329,8 +327,6 @@ def print_mapping_table(archs: Sequence[str]) -> None:
> _args.output_file.write(f"""{{
> \t.arch = "{arch}",
> \t.cpuid = "{cpuid}",
> -\t.version = "{row[1]}",
> -\t.type = "{row[3]}",
> \t.table = {tblname}
> }},
> """)
> @@ -339,8 +335,6 @@ def print_mapping_table(archs: Sequence[str]) -> None:
> _args.output_file.write("""{
> \t.arch = 0,
> \t.cpuid = 0,
> -\t.version = 0,
> -\t.type = 0,
> \t.table = 0,
> }
> };
> diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h
> index 7a360792635f..a491b117c8ac 100644
> --- a/tools/perf/pmu-events/pmu-events.h
> +++ b/tools/perf/pmu-events/pmu-events.h
> @@ -40,8 +40,6 @@ struct pmu_event {
> struct pmu_events_map {
> const char *arch;
> const char *cpuid;
> - const char *version;
> - const char *type; /* core, uncore etc */
> const struct pmu_event *table;
> };
>
> diff --git a/tools/perf/tests/expand-cgroup.c b/tools/perf/tests/expand-cgroup.c
> index dfefe5b60eb2..dc4038f997d7 100644
> --- a/tools/perf/tests/expand-cgroup.c
> +++ b/tools/perf/tests/expand-cgroup.c
> @@ -197,8 +197,6 @@ static int expand_metric_events(void)
> };
> const struct pmu_events_map ev_map = {
> .cpuid = "test",
> - .version = "1",
> - .type = "core",
> .table = pme_test,
> };
>
> diff --git a/tools/perf/tests/parse-metric.c b/tools/perf/tests/parse-metric.c
> index 07b6f4ec024f..1b811a26f4ee 100644
> --- a/tools/perf/tests/parse-metric.c
> +++ b/tools/perf/tests/parse-metric.c
> @@ -81,8 +81,6 @@ static struct pmu_event pme_test[] = {
>
> static const struct pmu_events_map map = {
> .cpuid = "test",
> - .version = "1",
> - .type = "core",
> .table = pme_test,
> };
>

2022-07-29 14:41:01

by Ian Rogers

[permalink] [raw]
Subject: Re: [PATCH v3 05/17] perf jevents: Remove the type/version variables

On Fri, Jul 29, 2022 at 1:30 AM John Garry <[email protected]> wrote:
>
> On 29/07/2022 08:43, Ian Rogers wrote:
> > pmu_events_map has a type variable that is always initialized to "core"
> > and a version variable that is never read. Remove these from the API as
> > it is straightforward to add them back when necessary.
> >
> > Signed-off-by: Ian Rogers <[email protected]>
>
> Reviewed-by: John Garry <[email protected]>
>
> Please note the questions, below.
>
> > ---
> > tools/perf/pmu-events/empty-pmu-events.c | 6 ++----
> > tools/perf/pmu-events/jevents.py | 6 ------
> > tools/perf/pmu-events/pmu-events.h | 2 --
> > tools/perf/tests/expand-cgroup.c | 2 --
> > tools/perf/tests/parse-metric.c | 2 --
> > 5 files changed, 2 insertions(+), 16 deletions(-)
> >
> > diff --git a/tools/perf/pmu-events/empty-pmu-events.c b/tools/perf/pmu-events/empty-pmu-events.c
> > index 77e655c6f116..4182a986f505 100644
> > --- a/tools/perf/pmu-events/empty-pmu-events.c
> > +++ b/tools/perf/pmu-events/empty-pmu-events.c
> > @@ -110,15 +110,13 @@ static const struct pmu_event pme_test_soc_cpu[] = {
> >
> > const struct pmu_events_map pmu_events_map[] = {
> > {
> > + .arch = "testarch",
>
> Is this really supposed to be part of this patch?

Agreed. Looks like it should have been in the previous.

Thanks,
Ian

> > .cpuid = "testcpu",
> > - .version = "v1",
> > - .type = "core",
> > .table = pme_test_soc_cpu,
> > },
> > {
> > + .arch = 0,
>
> Same as above
>
> > .cpuid = 0,
> > - .version = 0,
> > - .type = 0,
> > .table = 0,
> > },
> > };
> > diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
> > index e6e6c42c3f8a..98d18d5c3830 100755
> > --- a/tools/perf/pmu-events/jevents.py
> > +++ b/tools/perf/pmu-events/jevents.py
> > @@ -312,8 +312,6 @@ def print_mapping_table(archs: Sequence[str]) -> None:
> > _args.output_file.write("""{
> > \t.arch = "testarch",
> > \t.cpuid = "testcpu",
> > -\t.version = "v1",
> > -\t.type = "core",
> > \t.table = pme_test_soc_cpu,
> > },
> > """)
> > @@ -329,8 +327,6 @@ def print_mapping_table(archs: Sequence[str]) -> None:
> > _args.output_file.write(f"""{{
> > \t.arch = "{arch}",
> > \t.cpuid = "{cpuid}",
> > -\t.version = "{row[1]}",
> > -\t.type = "{row[3]}",
> > \t.table = {tblname}
> > }},
> > """)
> > @@ -339,8 +335,6 @@ def print_mapping_table(archs: Sequence[str]) -> None:
> > _args.output_file.write("""{
> > \t.arch = 0,
> > \t.cpuid = 0,
> > -\t.version = 0,
> > -\t.type = 0,
> > \t.table = 0,
> > }
> > };
> > diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h
> > index 7a360792635f..a491b117c8ac 100644
> > --- a/tools/perf/pmu-events/pmu-events.h
> > +++ b/tools/perf/pmu-events/pmu-events.h
> > @@ -40,8 +40,6 @@ struct pmu_event {
> > struct pmu_events_map {
> > const char *arch;
> > const char *cpuid;
> > - const char *version;
> > - const char *type; /* core, uncore etc */
> > const struct pmu_event *table;
> > };
> >
> > diff --git a/tools/perf/tests/expand-cgroup.c b/tools/perf/tests/expand-cgroup.c
> > index dfefe5b60eb2..dc4038f997d7 100644
> > --- a/tools/perf/tests/expand-cgroup.c
> > +++ b/tools/perf/tests/expand-cgroup.c
> > @@ -197,8 +197,6 @@ static int expand_metric_events(void)
> > };
> > const struct pmu_events_map ev_map = {
> > .cpuid = "test",
> > - .version = "1",
> > - .type = "core",
> > .table = pme_test,
> > };
> >
> > diff --git a/tools/perf/tests/parse-metric.c b/tools/perf/tests/parse-metric.c
> > index 07b6f4ec024f..1b811a26f4ee 100644
> > --- a/tools/perf/tests/parse-metric.c
> > +++ b/tools/perf/tests/parse-metric.c
> > @@ -81,8 +81,6 @@ static struct pmu_event pme_test[] = {
> >
> > static const struct pmu_events_map map = {
> > .cpuid = "test",
> > - .version = "1",
> > - .type = "core",
> > .table = pme_test,
> > };
> >
>

2022-07-29 15:06:15

by John Garry

[permalink] [raw]
Subject: Re: [PATCH v3 00/17] Compress the pmu_event tables

On 29/07/2022 08:43, Ian Rogers wrote:
> jevents.py creates a number of large arrays from the json events. The
> arrays contain pointers to strings that need relocating. The
> relocations have file size, run time and memory costs. These changes
> refactor the pmu_events API so that the storage of the pmu_event
> struct isn't exposed. The format is then changed to an offset within a
> combined big string, with adjacent pmu_event struct variables being
> next to each other in the string separated by \0 - meaning only the
> first variable of the struct needs its offset recording.
>
> Some related fixes are contained with the patches. The architecture
> jevents.py creates tables for can now be set by the JEVENTS_ARCH make
> variable, with a new 'all' that generates the events and metrics for
> all architectures.

Hi Ian,

I am going through this series currently.

But I just wanted to mention my idea again on how to compress the
tables. Maybe you thought that there was no value in my idea or didn't
get it, but I'll mention it again just in case...

Background:
There is much duplication in events between cores. And currently we have
something like this:

pmu-events/pmu-events.c:
struct pmu_event core0[] {
{
.name = event0,
.event = "event=0x0",
},
{
.name = event1,
.event = "event=0x1",
},
{
.name = event2,
.event = "event=0x2",
.desc = "event2 common desc",
},
...
};

struct pmu_event core1[] {
{
.name = event0,
.event = "event=0x0",
},
{
.name = event1,
.event = "event=0x1",
},
{
.name = event2,
.event = "event=0x2",
.desc = "event2 desc for core1",
},
...
};


struct pmu_evenets_map map[] = {
{
.cpuid = "0000",
.table = core0,
},
{
.cpuid = "0001",
.table = core1,
},
...
};

If you check broadwell and broadwellde frontent.json you will notice
that they are identical, which is an extreme example of duplication.

Proposal for change:
Make each event in the per-core pmu event table point to common event.
Each common event is unique, and each event per-core will point to a
common event. So if 2x cores have same event but small difference, then
there would be still 2x common events.

pmu-events/pmu-events.c:
struct pmu_event common_events[] {
{
.name = event0,
.event = "event=0x0",
},
{
.name = event1,
.event = "event=0x1",
},
{
.name = event2,
.event = "event=0x2",
.desc = "event2 common desc",
},
{
.name = event2,
.event = "event=0x2",
.desc = "event2 desc for core1",
},
...
};

struct pmu_event_ptr {
struct pmu_event *pmu_event;
}

struct pmu_event_ptr core0[] {
{
.pmu_event = &common_events[0],
},
{
.pmu_event = &common_events[1],
},
{
.pmu_event = &common_events[2],
},
...
};

struct pmu_event_ptr core0[] {
{
.pmu_event = &common_events[0],
},
{
.pmu_event = &common_events[1],
},
{
.pmu_event = &common_events[3],
},
...
};

struct pmu_evenets_map map[] = {
{
.cpuid = "0000",
.table = core0,
},
{
.cpuid = "0001",
.table = core1,
},
...
};

For x86, first step in JSON parsing would be to go through the JSON
files and compile a list of unique events. Then second step is to
process each per-core JSON to create the pmu events table, using the
common events. Using a per common event hash would make the lookup quicker.

I'm not sure what you think. From figures below you seem to be saving
~20% at best - I would guess (with a capital G) that my method could
save a lot more.

This implementation would require core pmu.c to be changed, but there is
ways that this could be done without needing to change core pmu.c

Thanks,
John

>
> An example of the improvement to the file size on x86 is:
> no jevents - the same 19,788,464bytes
> x86 jevents - ~16.7% file size saving 23,744,288bytes vs 28,502,632bytes
> all jevents - ~19.5% file size saving 24,469,056bytes vs 30,379,920bytes
> default build options plus NO_LIBBFD=1.
>
> I originally suggested fixing this problem in:
> https://lore.kernel.org/linux-perf-users/CAP-5=fVB8G4bdb9T=FncRTh9oBVKCS=+=eowAO+YSgAhab+Dtg@mail.gmail.com/
>
> v3. Fix an ARM build issue with a missed weak symbol. Perform some
> pytype clean up.
> v2. Split the substring folding optimization to its own patch and
> comment tweaks as suggested by Namhyung Kim
> <[email protected]>. Recompute the file size savings with the
> latest json events and metrics.
>
> Ian Rogers (17):
> perf jevents: Clean up pytype warnings
> perf jevents: Simplify generation of C-string
> perf jevents: Add JEVENTS_ARCH make option
> perf jevent: Add an 'all' architecture argument
> perf jevents: Remove the type/version variables
> perf jevents: Provide path to json file on error
> perf jevents: Sort json files entries
> perf pmu-events: Hide pmu_sys_event_tables
> perf pmu-events: Avoid passing pmu_events_map
> perf pmu-events: Hide pmu_events_map
> perf test: Use full metric resolution
> perf pmu-events: Move test events/metrics to json
> perf pmu-events: Don't assume pmu_event is an array
> perf pmu-events: Hide the pmu_events
> perf metrics: Copy entire pmu_event in find metric
> perf jevents: Compress the pmu_events_table
> perf jevents: Fold strings optimization
>
> tools/perf/arch/arm64/util/pmu.c | 4 +-
> tools/perf/pmu-events/Build | 6 +-
> .../arch/test/test_soc/cpu/metrics.json | 64 +++
> tools/perf/pmu-events/empty-pmu-events.c | 204 +++++++-
> tools/perf/pmu-events/jevents.py | 495 ++++++++++++++----
> tools/perf/pmu-events/pmu-events.h | 40 +-
> tools/perf/tests/expand-cgroup.c | 25 +-
> tools/perf/tests/parse-metric.c | 77 +--
> tools/perf/tests/pmu-events.c | 466 +++++++----------
> tools/perf/util/metricgroup.c | 275 ++++++----
> tools/perf/util/metricgroup.h | 5 +-
> tools/perf/util/pmu.c | 139 ++---
> tools/perf/util/pmu.h | 8 +-
> tools/perf/util/s390-sample-raw.c | 50 +-
> 14 files changed, 1140 insertions(+), 718 deletions(-)
> create mode 100644 tools/perf/pmu-events/arch/test/test_soc/cpu/metrics.json
>

2022-07-29 17:39:16

by Ian Rogers

[permalink] [raw]
Subject: Re: [PATCH v3 00/17] Compress the pmu_event tables

On Fri, Jul 29, 2022 at 8:03 AM John Garry <[email protected]> wrote:
>
> On 29/07/2022 08:43, Ian Rogers wrote:
> > jevents.py creates a number of large arrays from the json events. The
> > arrays contain pointers to strings that need relocating. The
> > relocations have file size, run time and memory costs. These changes
> > refactor the pmu_events API so that the storage of the pmu_event
> > struct isn't exposed. The format is then changed to an offset within a
> > combined big string, with adjacent pmu_event struct variables being
> > next to each other in the string separated by \0 - meaning only the
> > first variable of the struct needs its offset recording.
> >
> > Some related fixes are contained with the patches. The architecture
> > jevents.py creates tables for can now be set by the JEVENTS_ARCH make
> > variable, with a new 'all' that generates the events and metrics for
> > all architectures.
>
> Hi Ian,
>
> I am going through this series currently.
>
> But I just wanted to mention my idea again on how to compress the
> tables. Maybe you thought that there was no value in my idea or didn't
> get it, but I'll mention it again just in case...
>
> Background:
> There is much duplication in events between cores. And currently we have
> something like this:
>
> pmu-events/pmu-events.c:
> struct pmu_event core0[] {
> {
> .name = event0,
> .event = "event=0x0",
> },
> {
> .name = event1,
> .event = "event=0x1",
> },
> {
> .name = event2,
> .event = "event=0x2",
> .desc = "event2 common desc",
> },
> ...
> };
>
> struct pmu_event core1[] {
> {
> .name = event0,
> .event = "event=0x0",
> },
> {
> .name = event1,
> .event = "event=0x1",
> },
> {
> .name = event2,
> .event = "event=0x2",
> .desc = "event2 desc for core1",
> },
> ...
> };
>
>
> struct pmu_evenets_map map[] = {
> {
> .cpuid = "0000",
> .table = core0,
> },
> {
> .cpuid = "0001",
> .table = core1,
> },
> ...
> };
>
> If you check broadwell and broadwellde frontent.json you will notice
> that they are identical, which is an extreme example of duplication.
>
> Proposal for change:
> Make each event in the per-core pmu event table point to common event.
> Each common event is unique, and each event per-core will point to a
> common event. So if 2x cores have same event but small difference, then
> there would be still 2x common events.
>
> pmu-events/pmu-events.c:
> struct pmu_event common_events[] {
> {
> .name = event0,
> .event = "event=0x0",
> },
> {
> .name = event1,
> .event = "event=0x1",
> },
> {
> .name = event2,
> .event = "event=0x2",
> .desc = "event2 common desc",
> },
> {
> .name = event2,
> .event = "event=0x2",
> .desc = "event2 desc for core1",
> },
> ...
> };
>
> struct pmu_event_ptr {
> struct pmu_event *pmu_event;
> }
>
> struct pmu_event_ptr core0[] {
> {
> .pmu_event = &common_events[0],
> },
> {
> .pmu_event = &common_events[1],
> },
> {
> .pmu_event = &common_events[2],
> },
> ...
> };
>
> struct pmu_event_ptr core0[] {
> {
> .pmu_event = &common_events[0],
> },
> {
> .pmu_event = &common_events[1],
> },
> {
> .pmu_event = &common_events[3],
> },
> ...
> };
>
> struct pmu_evenets_map map[] = {
> {
> .cpuid = "0000",
> .table = core0,
> },
> {
> .cpuid = "0001",
> .table = core1,
> },
> ...
> };
>
> For x86, first step in JSON parsing would be to go through the JSON
> files and compile a list of unique events. Then second step is to
> process each per-core JSON to create the pmu events table, using the
> common events. Using a per common event hash would make the lookup quicker.
>
> I'm not sure what you think. From figures below you seem to be saving
> ~20% at best - I would guess (with a capital G) that my method could
> save a lot more.
>
> This implementation would require core pmu.c to be changed, but there is
> ways that this could be done without needing to change core pmu.c
>
> Thanks,
> John

Thanks John!

You are right about broadwell, it is an extreme case of sharing. IIRC
BDX is the server core/uncore events, BDW is the consumer core/uncore
and BDW-DE is consumer core with server uncore - so the sharing is
inherent in this. Metrics become interesting as they may mix core and
uncore, but I'll ignore that here.

In the old code every event needs 15 char*s, with 64-bit that is 15*8
bytes per entry with 741 core and 23 uncore entries for BDW, and 372
core and 1284 uncore for BDX. I expect the strings themselves will be
shared by the C compiler, and so I just focus on the pointer sizes.
With the new code every event is just 1 32-bit int. So for BDW we go
from 15*8*(741+23)=91,680 to 4*(741+23)=3056, BDX is
15*8*(372+1284)=198720 to 4*(372+1284)=6624. This means we've gone
from 290,400bytes to 9,680bytes for BDW and BDX. BDW-DE goes from
243,000bytes to 8,100bytes - we can ignore the costs of the strings as
they should be fully shared, especially for BDW-DE.

If we added some kind of table sharing, so BDW-DE was core from BDW
and uncore from BDX and the tables shared, then in the old code you
could save nearly 0.25MB but with the new code the saving is only
around 8KB. I think we can go after that 8KB but it is less urgent
after this change which gets 96% of the benefit. We have 72
architectures for jevents at the moment and so I'd ball park (assuming
they all saved as much as BDW-DE) the max saving as about 0.5MB, which
is 12% of what is saved here.

Longer term I'd like to make the pmu-events.c logic look closer to the
sysfs API. Perhaps we can unify the uncore events for BDX and BDW-DE
with some notion of a common PMU, or PMUs with common events and
tables, and automate deduction of this. It also isn't clear to me the
advantage of storing the metrics inside the events, separate tables
feel cleaner. Anyway, there's lots of follow up.

Thanks,
Ian


> >
> > An example of the improvement to the file size on x86 is:
> > no jevents - the same 19,788,464bytes
> > x86 jevents - ~16.7% file size saving 23,744,288bytes vs 28,502,632bytes
> > all jevents - ~19.5% file size saving 24,469,056bytes vs 30,379,920bytes
> > default build options plus NO_LIBBFD=1.
> >
> > I originally suggested fixing this problem in:
> > https://lore.kernel.org/linux-perf-users/CAP-5=fVB8G4bdb9T=FncRTh9oBVKCS=+=eowAO+YSgAhab+Dtg@mail.gmail.com/
> >
> > v3. Fix an ARM build issue with a missed weak symbol. Perform some
> > pytype clean up.
> > v2. Split the substring folding optimization to its own patch and
> > comment tweaks as suggested by Namhyung Kim
> > <[email protected]>. Recompute the file size savings with the
> > latest json events and metrics.
> >
> > Ian Rogers (17):
> > perf jevents: Clean up pytype warnings
> > perf jevents: Simplify generation of C-string
> > perf jevents: Add JEVENTS_ARCH make option
> > perf jevent: Add an 'all' architecture argument
> > perf jevents: Remove the type/version variables
> > perf jevents: Provide path to json file on error
> > perf jevents: Sort json files entries
> > perf pmu-events: Hide pmu_sys_event_tables
> > perf pmu-events: Avoid passing pmu_events_map
> > perf pmu-events: Hide pmu_events_map
> > perf test: Use full metric resolution
> > perf pmu-events: Move test events/metrics to json
> > perf pmu-events: Don't assume pmu_event is an array
> > perf pmu-events: Hide the pmu_events
> > perf metrics: Copy entire pmu_event in find metric
> > perf jevents: Compress the pmu_events_table
> > perf jevents: Fold strings optimization
> >
> > tools/perf/arch/arm64/util/pmu.c | 4 +-
> > tools/perf/pmu-events/Build | 6 +-
> > .../arch/test/test_soc/cpu/metrics.json | 64 +++
> > tools/perf/pmu-events/empty-pmu-events.c | 204 +++++++-
> > tools/perf/pmu-events/jevents.py | 495 ++++++++++++++----
> > tools/perf/pmu-events/pmu-events.h | 40 +-
> > tools/perf/tests/expand-cgroup.c | 25 +-
> > tools/perf/tests/parse-metric.c | 77 +--
> > tools/perf/tests/pmu-events.c | 466 +++++++----------
> > tools/perf/util/metricgroup.c | 275 ++++++----
> > tools/perf/util/metricgroup.h | 5 +-
> > tools/perf/util/pmu.c | 139 ++---
> > tools/perf/util/pmu.h | 8 +-
> > tools/perf/util/s390-sample-raw.c | 50 +-
> > 14 files changed, 1140 insertions(+), 718 deletions(-)
> > create mode 100644 tools/perf/pmu-events/arch/test/test_soc/cpu/metrics.json
> >
>

2022-08-02 09:14:47

by John Garry

[permalink] [raw]
Subject: Re: [PATCH v3 00/17] Compress the pmu_event tables

On 29/07/2022 18:27, Ian Rogers wrote:
>> This implementation would require core pmu.c to be changed, but there is
>> ways that this could be done without needing to change core pmu.c
>>
>> Thanks,
>> John
> Thanks John!
>
> You are right about broadwell, it is an extreme case of sharing. IIRC
> BDX is the server core/uncore events, BDW is the consumer core/uncore
> and BDW-DE is consumer core with server uncore - so the sharing is
> inherent in this. Metrics become interesting as they may mix core and
> uncore, but I'll ignore that here.
>
> In the old code every event needs 15 char*s, with 64-bit that is 15*8
> bytes per entry with 741 core and 23 uncore entries for BDW, and 372
> core and 1284 uncore for BDX. I expect the strings themselves will be
> shared by the C compiler, and so I just focus on the pointer sizes.
> With the new code every event is just 1 32-bit int. So for BDW we go
> from 15*8*(741+23)=91,680 to 4*(741+23)=3056, BDX is
> 15*8*(372+1284)=198720 to 4*(372+1284)=6624. This means we've gone
> from 290,400bytes to 9,680bytes for BDW and BDX. BDW-DE goes from
> 243,000bytes to 8,100bytes -


> we can ignore the costs of the strings as
> they should be fully shared, especially for BDW-DE.

Are you sure about this? I did not think that the compiler would have
the intelligence to try to share strings. That is the basis of the size
optimisation which I was proposing (that the compiler would not share
strings).

>
> If we added some kind of table sharing, so BDW-DE was core from BDW
> and uncore from BDX and the tables shared, then in the old code you
> could save nearly 0.25MB but with the new code the saving is only
> around 8KB. I think we can go after that 8KB but it is less urgent
> after this change which gets 96% of the benefit. We have 72
> architectures for jevents at the moment and so I'd ball park (assuming
> they all saved as much as BDW-DE) the max saving as about 0.5MB, which
> is 12% of what is saved here.
>
> Longer term I'd like to make the pmu-events.c logic look closer to the
> sysfs API. Perhaps we can unify the uncore events for BDX and BDW-DE
> with some notion of a common PMU, or PMUs with common events and
> tables, and automate deduction of this. It also isn't clear to me the
> advantage of storing the metrics inside the events, separate tables
> feel cleaner. Anyway, there's lots of follow up.

Thanks,
John


2022-08-05 08:50:33

by John Garry

[permalink] [raw]
Subject: Re: [PATCH v3 00/17] Compress the pmu_event tables

On 02/08/2022 10:08, John Garry wrote:
>
>
>> we can ignore the costs of the strings as
>> they should be fully shared, especially for BDW-DE.
>
> Are you sure about this? I did not think that the compiler would have
> the intelligence to try to share strings. That is the basis of the size
> optimisation which I was proposing (that the compiler would not share
> strings).

JFYI, I am finding that all identical strings are shared, which is good.

Thanks,
John