[PATCH v2 14/15] perf jevents: Add support for metricgroup descriptions

From: Ian Rogers
Date: Wed May 17 2023 - 11:29:09 EST


Metrics have a field where the groups they belong to are listed like
the following from
tools/perf/pmu-events/arch/x86/skylakex/skx-metrics.json:

"MetricGroup": "PGO;TmaL1;TopdownL1;tma_L1_group",
"MetricName": "tma_frontend_bound",

The metric groups are shown in 'perf list' like the following where
TopdownL1 is a metric group:

TopdownL1:
tma_backend_bound
[This category represents fraction of slots where no uops are being
delivered due to a lack of required resources for accepting new uops
in the Backend]
tma_bad_speculation
[This category represents fraction of slots wasted due to incorrect
speculations]
tma_frontend_bound
[This category represents fraction of slots where the processor's
Frontend undersupplies its Backend]
tma_retiring
[This category represents fraction of slots utilized by useful work
i.e. issued uops that eventually get retired]

This patch adds support for a new json file in each model directory
called metricgroups.json that comprises a dictionary containing
entries that map from a metric group to a description:

{
...
"TopdownL1": "Metrics for top-down breakdown at level 1",
...
}

perf list is then updated to support this changing the above output
to:

TopdownL1: [Metrics for top-down breakdown at level 1]
Signed-off-by: Ian Rogers <irogers@xxxxxxxxxx>
---
tools/perf/builtin-list.c | 11 ++++--
tools/perf/pmu-events/empty-pmu-events.c | 5 +++
tools/perf/pmu-events/jevents.py | 49 +++++++++++++++++++++++-
tools/perf/pmu-events/pmu-events.h | 2 +
4 files changed, 62 insertions(+), 5 deletions(-)

diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
index c6bd0aa4a56e..e8520a027b45 100644
--- a/tools/perf/builtin-list.c
+++ b/tools/perf/builtin-list.c
@@ -192,9 +192,14 @@ static void default_print_metric(void *ps,
if (group && print_state->metricgroups) {
if (print_state->name_only)
printf("%s ", group);
- else if (print_state->metrics)
- printf("\n%s:\n", group);
- else
+ else if (print_state->metrics) {
+ const char *gdesc = describe_metricgroup(group);
+
+ if (gdesc)
+ printf("\n%s: [%s]\n", group, gdesc);
+ else
+ printf("\n%s:\n", group);
+ } else
printf("%s\n", group);
}
zfree(&print_state->last_metricgroups);
diff --git a/tools/perf/pmu-events/empty-pmu-events.c b/tools/perf/pmu-events/empty-pmu-events.c
index e74defb5284f..a630c617e879 100644
--- a/tools/perf/pmu-events/empty-pmu-events.c
+++ b/tools/perf/pmu-events/empty-pmu-events.c
@@ -420,3 +420,8 @@ int pmu_for_each_sys_metric(pmu_metric_iter_fn fn __maybe_unused, void *data __m
{
return 0;
}
+
+const char *describe_metricgroup(const char *group __maybe_unused)
+{
+ return NULL;
+}
diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
index 487ff01baf1b..8fca7c9adee0 100755
--- a/tools/perf/pmu-events/jevents.py
+++ b/tools/perf/pmu-events/jevents.py
@@ -37,6 +37,8 @@ _pending_metrics = []
_pending_metrics_tblname = None
# Global BigCString shared by all structures.
_bcs = None
+# Map from the name of a metric group to a description of the group.
+_metricgroups = {}
# Order specific JsonEvent attributes will be visited.
_json_event_attributes = [
# cmp_sevent related attributes.
@@ -512,6 +514,17 @@ def preprocess_one_file(parents: Sequence[str], item: os.DirEntry) -> None:
if not item.is_file() or not item.name.endswith('.json'):
return

+ if item.name == 'metricgroups.json':
+ metricgroup_descriptions = json.load(open(item.path))
+ for mgroup in metricgroup_descriptions:
+ assert len(mgroup) > 1, parents
+ description = f"{metricgroup_descriptions[mgroup]}\\000"
+ mgroup = f"{mgroup}\\000"
+ _bcs.add(mgroup)
+ _bcs.add(description)
+ _metricgroups[mgroup] = description
+ return
+
topic = get_topic(item.name)
for event in read_json_events(item.path, topic):
if event.name:
@@ -548,7 +561,7 @@ def process_one_file(parents: Sequence[str], item: os.DirEntry) -> None:

# 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'):
+ if not item.is_file() or not item.name.endswith('.json') or item.name == 'metricgroups.json':
return

add_events_table_entries(item, get_topic(item.name))
@@ -911,6 +924,38 @@ int pmu_for_each_sys_metric(pmu_metric_iter_fn fn, void *data)
}
""")

+def print_metricgroups() -> None:
+ _args.output_file.write("""
+static const int metricgroups[][2] = {
+""")
+ for mgroup in sorted(_metricgroups):
+ description = _metricgroups[mgroup]
+ _args.output_file.write(
+ f'\t{{ {_bcs.offsets[mgroup]}, {_bcs.offsets[description]} }}, /* {mgroup} => {description} */\n'
+ )
+ _args.output_file.write("""
+};
+
+const char *describe_metricgroup(const char *group)
+{
+ int low = 0, high = ARRAY_SIZE(metricgroups) - 1;
+
+ while (low <= high) {
+ int mid = (low + high) / 2;
+ const char *mgroup = &big_c_string[metricgroups[mid][0]];
+ int cmp = strcmp(mgroup, group);
+
+ if (cmp == 0) {
+ return &big_c_string[metricgroups[mid][1]];
+ } else if (cmp < 0) {
+ low = mid + 1;
+ } else {
+ high = mid - 1;
+ }
+ }
+ return NULL;
+}
+""")

def main() -> None:
global _args
@@ -993,7 +1038,7 @@ struct compact_pmu_event {

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

if __name__ == '__main__':
main()
diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h
index 3549e6971a4d..8cd23d656a5d 100644
--- a/tools/perf/pmu-events/pmu-events.h
+++ b/tools/perf/pmu-events/pmu-events.h
@@ -93,4 +93,6 @@ const struct pmu_metrics_table *find_sys_metrics_table(const char *name);
int pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data);
int pmu_for_each_sys_metric(pmu_metric_iter_fn fn, void *data);

+const char *describe_metricgroup(const char *group);
+
#endif
--
2.40.1.606.ga4b1b128d6-goog