From 08dbbb11c1ce409a8638f7d8519ee45319c19cba Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 25 Sep 2024 10:30:13 -0700 Subject: [PATCH 01/55] perf test: Ignore security failures in all PMU test Refactor code to have some more error diagnosis on traps, etc. and to do less work on each line. Add an ignore situation for security failures. Link: https://lore.kernel.org/r/20240925173013.12789-1-irogers@google.com Cc: Mark Rutland Cc: Colin Ian King Cc: Ian Rogers Cc: Peter Zijlstra Cc: Adrian Hunter Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Namhyung Kim Cc: Alexander Shishkin Cc: Kan Liang Cc: Athira Jajeev Cc: Ingo Molnar Cc: linux-kernel@vger.kernel.org Cc: linux-perf-users@vger.kernel.org Signed-off-by: Ian Rogers --- tools/perf/tests/shell/stat_all_pmu.sh | 52 ++++++++++++++++++++------ 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/tools/perf/tests/shell/stat_all_pmu.sh b/tools/perf/tests/shell/stat_all_pmu.sh index d2a3506e0d19..42456d89c5da 100755 --- a/tools/perf/tests/shell/stat_all_pmu.sh +++ b/tools/perf/tests/shell/stat_all_pmu.sh @@ -1,23 +1,51 @@ -#!/bin/sh +#!/bin/bash # perf all PMU test # SPDX-License-Identifier: GPL-2.0 set -e +err=0 +result="" + +trap_cleanup() { + echo "Unexpected signal in ${FUNCNAME[1]}" + echo "$result" + exit 1 +} +trap trap_cleanup EXIT TERM INT # Test all PMU events; however exclude parameterized ones (name contains '?') -for p in $(perf list --raw-dump pmu | sed 's/[[:graph:]]\+?[[:graph:]]\+[[:space:]]//g'); do +for p in $(perf list --raw-dump pmu | sed 's/[[:graph:]]\+?[[:graph:]]\+[[:space:]]//g') +do echo "Testing $p" result=$(perf stat -e "$p" true 2>&1) - if ! echo "$result" | grep -q "$p" && ! echo "$result" | grep -q "" ; then - # We failed to see the event and it is supported. Possibly the workload was - # too small so retry with something longer. - result=$(perf stat -e "$p" perf bench internals synthesize 2>&1) - if ! echo "$result" | grep -q "$p" ; then - echo "Event '$p' not printed in:" - echo "$result" - exit 1 - fi + if echo "$result" | grep -q "$p" + then + # Event seen in output. + continue + fi + if echo "$result" | grep -q "" + then + # Event not supported, so ignore. + continue + fi + if echo "$result" | grep -q "Access to performance monitoring and observability operations is limited." + then + # Access is limited, so ignore. + continue + fi + + # We failed to see the event and it is supported. Possibly the workload was + # too small so retry with something longer. + result=$(perf stat -e "$p" perf bench internals synthesize 2>&1) + if echo "$result" | grep -q "$p" + then + # Event seen in output. + continue fi + echo "Error: event '$p' not printed in:" + echo "$result" + err=1 done -exit 0 +trap - EXIT TERM INT +exit $err From 2ac2aa4f73ec07871280062e642ccb549b02a539 Mon Sep 17 00:00:00 2001 From: Peng Wei Date: Wed, 26 Apr 2023 08:53:57 -0700 Subject: [PATCH 02/55] bpf: Make bpf_helper_defs.h c++ friendly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compiling C++ BPF programs with existing bpf_helper_defs.h is not possible due to stricter C++ type conversions. C++ complains about (void *) type conversions: $ clang++ --include linux/types.h ./tools/lib/bpf/bpf_helper_defs.h bpf_helper_defs.h:57:67: error: invalid conversion from ‘void*’ to ‘void* (*)(void*, const void*)’ [-fpermissive] 57 | static void *(*bpf_map_lookup_elem)(void *map, const void *key) = (void *) 1; | ^~~~~~~~~~ | | | void* Extend bpf_doc.py to use proper function type instead of void. Before: static void *(*bpf_map_lookup_elem)(void *map, const void *key) = (void *) 1; After: static void *(*bpf_map_lookup_elem)(void *map, const void *key) = (void *(*)(void *map, const void *key)) 1; v2: - add clang++ invocation example (Yonghong) Cc: Yonghong Song Signed-off-by: Peng Wei Signed-off-by: Stanislav Fomichev Acked-by: Yonghong Song Link: https://lore.kernel.org/r/20230426155357.4158846-1-sdf@google.com Cc: john.fastabend@gmail.com Cc: haoluo@google.com Cc: daniel@iogearbox.net Cc: andrii@kernel.org Cc: ast@kernel.org Cc: jolsa@kernel.org Cc: kpsingh@kernel.org Cc: song@kernel.org Cc: martin.lau@linux.dev Cc: bpf@vger.kernel.org --- scripts/bpf_doc.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/bpf_doc.py b/scripts/bpf_doc.py index c55878bddfdd..f15bafb9fe97 100755 --- a/scripts/bpf_doc.py +++ b/scripts/bpf_doc.py @@ -827,6 +827,9 @@ def print_one(self, helper): print(' *{}{}'.format(' \t' if line else '', line)) print(' */') + fptr_type = '%s%s(*)(' % ( + self.map_type(proto['ret_type']), + ((' ' + proto['ret_star']) if proto['ret_star'] else '')) print('static %s %s(* const %s)(' % (self.map_type(proto['ret_type']), proto['ret_star'], proto['name']), end='') comma = '' @@ -845,8 +848,10 @@ def print_one(self, helper): one_arg += '{}'.format(n) comma = ', ' print(one_arg, end='') + fptr_type += one_arg - print(') = (void *) %d;' % helper.enum_val) + fptr_type += ')' + print(') = (%s) %d;' % (fptr_type, helper.enum_val)) print('') ############################################################################### From 05737cf3a921c198596e9e067667937544547e26 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 7 Feb 2024 16:12:39 -0800 Subject: [PATCH 03/55] perf jevents: Allow multiple metricgroups.json files Allow multiple metricgroups.json files by handling any file ending with metricgroups.json as a metricgroups file. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/jevents.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py index bb0a5d92df4a..8d2cecb87e88 100755 --- a/tools/perf/pmu-events/jevents.py +++ b/tools/perf/pmu-events/jevents.py @@ -612,7 +612,7 @@ 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': + if item.name.endswith('metricgroups.json'): metricgroup_descriptions = json.load(open(item.path)) for mgroup in metricgroup_descriptions: assert len(mgroup) > 1, parents @@ -665,7 +665,7 @@ def is_leaf_dir_ignoring_sys(path: str) -> bool: # 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') or item.name == 'metricgroups.json': + if not item.is_file() or not item.name.endswith('.json') or item.name.endswith('metricgroups.json'): return add_events_table_entries(item, get_topic(item.name)) From 8995b36eee47dc31dd9581c7a90a3936ce3ebded Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 7 Feb 2024 16:22:59 -0800 Subject: [PATCH 04/55] perf jevents: Update metric constraint support Previous metric constraints were binary, either none or don't group when the NMI watchdog is present. Update to match the definitions in 'enum metric_event_groups' in pmu-events.h. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/metric.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tools/perf/pmu-events/metric.py b/tools/perf/pmu-events/metric.py index 92acd89ed97a..8a718dd4b1fe 100644 --- a/tools/perf/pmu-events/metric.py +++ b/tools/perf/pmu-events/metric.py @@ -4,8 +4,14 @@ import decimal import json import re +from enum import Enum from typing import Dict, List, Optional, Set, Tuple, Union +class MetricConstraint(Enum): + GROUPED_EVENTS = 0 + NO_GROUP_EVENTS = 1 + NO_GROUP_EVENTS_NMI = 2 + NO_GROUP_EVENTS_SMT = 3 class Expression: """Abstract base class of elements in a metric expression.""" @@ -423,14 +429,14 @@ class Metric: groups: Set[str] expr: Expression scale_unit: str - constraint: bool + constraint: MetricConstraint def __init__(self, name: str, description: str, expr: Expression, scale_unit: str, - constraint: bool = False): + constraint: MetricConstraint = MetricConstraint.GROUPED_EVENTS): self.name = name self.description = description self.expr = expr.Simplify() @@ -464,8 +470,8 @@ def ToPerfJson(self) -> Dict[str, str]: 'MetricExpr': self.expr.ToPerfJson(), 'ScaleUnit': self.scale_unit } - if self.constraint: - result['MetricConstraint'] = 'NO_NMI_WATCHDOG' + if self.constraint != MetricConstraint.GROUPED_EVENTS: + result['MetricConstraint'] = self.constraint.name return result From 2e1c0fb84fbd5708613ed179736ba208ef60a0b0 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 7 Feb 2024 16:26:39 -0800 Subject: [PATCH 05/55] perf jevents: Add descriptions to metricgroup abstraction Add a function to recursively generate metric group descriptions. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/metric.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tools/perf/pmu-events/metric.py b/tools/perf/pmu-events/metric.py index 8a718dd4b1fe..1de4fb72c75e 100644 --- a/tools/perf/pmu-events/metric.py +++ b/tools/perf/pmu-events/metric.py @@ -475,6 +475,8 @@ def ToPerfJson(self) -> Dict[str, str]: return result + def ToMetricGroupDescriptions(self, root: bool = True) -> Dict[str, str]: + return {} class _MetricJsonEncoder(json.JSONEncoder): """Special handling for Metric objects.""" @@ -493,10 +495,12 @@ class MetricGroup: which can facilitate arrangements similar to trees. """ - def __init__(self, name: str, metric_list: List[Union[Metric, - 'MetricGroup']]): + def __init__(self, name: str, + metric_list: List[Union[Metric, 'MetricGroup']], + description: Optional[str] = None): self.name = name self.metric_list = metric_list + self.description = description for metric in metric_list: metric.AddToMetricGroup(self) @@ -516,6 +520,12 @@ def Flatten(self) -> Set[Metric]: def ToPerfJson(self) -> str: return json.dumps(sorted(self.Flatten()), indent=2, cls=_MetricJsonEncoder) + def ToMetricGroupDescriptions(self, root: bool = True) -> Dict[str, str]: + result = {self.name: self.description} if self.description else {} + for x in self.metric_list: + result.update(x.ToMetricGroupDescriptions(False)) + return result + def __str__(self) -> str: return self.ToPerfJson() From e2d374e13dd1097c284886e09284da98f7283bc0 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Sun, 18 Feb 2024 20:36:07 -0800 Subject: [PATCH 06/55] perf jevents: Allow metric groups not to be named It can be convenient to have unnamed metric groups for the sake of organizing other metrics and metric groups. An unspecified name shouldn't contribute to the MetricGroup json value, so don't record it. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/metric.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/pmu-events/metric.py b/tools/perf/pmu-events/metric.py index 1de4fb72c75e..847b614d40d5 100644 --- a/tools/perf/pmu-events/metric.py +++ b/tools/perf/pmu-events/metric.py @@ -455,7 +455,8 @@ def __lt__(self, other): def AddToMetricGroup(self, group): """Callback used when being added to a MetricGroup.""" - self.groups.add(group.name) + if group.name: + self.groups.add(group.name) def Flatten(self) -> Set['Metric']: """Return a leaf metric.""" From 59b4e2d1ef711705296c1fc3813c7dfca78a7336 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 7 Feb 2024 21:42:55 -0800 Subject: [PATCH 07/55] perf jevents: Support parsing negative exponents Support negative exponents when parsing from a json metric string by making the numbers after the 'e' optional in the 'Event' insertion fix up. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/metric.py | 2 +- tools/perf/pmu-events/metric_test.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/perf/pmu-events/metric.py b/tools/perf/pmu-events/metric.py index 847b614d40d5..31eea2f45152 100644 --- a/tools/perf/pmu-events/metric.py +++ b/tools/perf/pmu-events/metric.py @@ -573,7 +573,7 @@ def ParsePerfJson(orig: str) -> Expression: # a double by the Bison parser py = re.sub(r'0Event\(r"[xX]([0-9a-fA-F]*)"\)', r'Event("0x\1")', py) # Convert accidentally converted scientific notation constants back - py = re.sub(r'([0-9]+)Event\(r"(e[0-9]+)"\)', r'\1\2', py) + py = re.sub(r'([0-9]+)Event\(r"(e[0-9]*)"\)', r'\1\2', py) # Convert all the known keywords back from events to just the keyword keywords = ['if', 'else', 'min', 'max', 'd_ratio', 'source_count', 'has_event', 'strcmp_cpuid_str'] for kw in keywords: diff --git a/tools/perf/pmu-events/metric_test.py b/tools/perf/pmu-events/metric_test.py index ee22ff43ddd7..8acfe4652b55 100755 --- a/tools/perf/pmu-events/metric_test.py +++ b/tools/perf/pmu-events/metric_test.py @@ -61,6 +61,10 @@ def test_ParsePerfJson(self): after = before self.assertEqual(ParsePerfJson(before).ToPerfJson(), after) + before = r'a + 3e-12 + b' + after = before + self.assertEqual(ParsePerfJson(before).ToPerfJson(), after) + def test_IfElseTests(self): # if-else needs rewriting to Select and back. before = r'Event1 if #smt_on else Event2' From 833dbe3e086d583459c81bdbcd12fbd1827b6a34 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 23 Feb 2024 19:06:38 -0800 Subject: [PATCH 08/55] perf jevents: Term list fix in event parsing Fix events seemingly broken apart at a comma. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/metric.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/perf/pmu-events/metric.py b/tools/perf/pmu-events/metric.py index 31eea2f45152..0f4e67e5cfea 100644 --- a/tools/perf/pmu-events/metric.py +++ b/tools/perf/pmu-events/metric.py @@ -568,6 +568,12 @@ def ParsePerfJson(orig: str) -> Expression: r'Event(r"\1")', py) # If it started with a # it should have been a literal, rather than an event name py = re.sub(r'#Event\(r"([^"]*)"\)', r'Literal("#\1")', py) + # Fix events wrongly broken at a ',' + while True: + prev_py = py + py = re.sub(r'Event\(r"([^"]*)"\),Event\(r"([^"]*)"\)', r'Event(r"\1,\2")', py) + if py == prev_py: + break # Convert accidentally converted hex constants ("0Event(r"xDEADBEEF)"") back to a constant, # but keep it wrapped in Event(), otherwise Python drops the 0x prefix and it gets interpreted as # a double by the Bison parser @@ -586,7 +592,6 @@ def ParsePerfJson(orig: str) -> Expression: parsed = ast.fix_missing_locations(parsed) return _Constify(eval(compile(parsed, orig, 'eval'))) - def RewriteMetricsInTermsOfOthers(metrics: List[Tuple[str, str, Expression]] )-> Dict[Tuple[str, str], Expression]: """Shorten metrics by rewriting in terms of others. From d1ca86c5d538f5f287b162a3d51a638d095218ab Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 8 Feb 2024 13:35:56 -0800 Subject: [PATCH 09/55] perf jevents: Add threshold expressions to Metric Allow threshold expressions for metrics to be generated. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/metric.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/perf/pmu-events/metric.py b/tools/perf/pmu-events/metric.py index 0f4e67e5cfea..e81fed2e29b5 100644 --- a/tools/perf/pmu-events/metric.py +++ b/tools/perf/pmu-events/metric.py @@ -430,13 +430,15 @@ class Metric: expr: Expression scale_unit: str constraint: MetricConstraint + threshold: Optional[Expression] def __init__(self, name: str, description: str, expr: Expression, scale_unit: str, - constraint: MetricConstraint = MetricConstraint.GROUPED_EVENTS): + constraint: MetricConstraint = MetricConstraint.GROUPED_EVENTS, + threshold: Optional[Expression] = None): self.name = name self.description = description self.expr = expr.Simplify() @@ -447,6 +449,7 @@ def __init__(self, else: self.scale_unit = f'1{scale_unit}' self.constraint = constraint + self.threshold = threshold self.groups = set() def __lt__(self, other): @@ -473,6 +476,8 @@ def ToPerfJson(self) -> Dict[str, str]: } if self.constraint != MetricConstraint.GROUPED_EVENTS: result['MetricConstraint'] = self.constraint.name + if self.threshold: + result['MetricThreshold'] = self.threshold.ToPerfJson() return result From bafc98822174f99da2d59785cbd8e7fff5a94b53 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 16 Feb 2024 21:28:02 -0800 Subject: [PATCH 10/55] perf jevents: Move json encoding to its own functions Have dedicate encode functions rather than having them embedded in MetricGroup. This is to provide some uniformity in the Metric ToXXX routines. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/metric.py | 34 +++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/tools/perf/pmu-events/metric.py b/tools/perf/pmu-events/metric.py index e81fed2e29b5..b39189182608 100644 --- a/tools/perf/pmu-events/metric.py +++ b/tools/perf/pmu-events/metric.py @@ -484,15 +484,6 @@ def ToPerfJson(self) -> Dict[str, str]: def ToMetricGroupDescriptions(self, root: bool = True) -> Dict[str, str]: return {} -class _MetricJsonEncoder(json.JSONEncoder): - """Special handling for Metric objects.""" - - def default(self, o): - if isinstance(o, Metric): - return o.ToPerfJson() - return json.JSONEncoder.default(self, o) - - class MetricGroup: """A group of metrics. @@ -523,8 +514,11 @@ def Flatten(self) -> Set[Metric]: return result - def ToPerfJson(self) -> str: - return json.dumps(sorted(self.Flatten()), indent=2, cls=_MetricJsonEncoder) + def ToPerfJson(self) -> List[Dict[str, str]]: + result = [] + for x in sorted(self.Flatten()): + result.append(x.ToPerfJson()) + return result def ToMetricGroupDescriptions(self, root: bool = True) -> Dict[str, str]: result = {self.name: self.description} if self.description else {} @@ -533,7 +527,23 @@ def ToMetricGroupDescriptions(self, root: bool = True) -> Dict[str, str]: return result def __str__(self) -> str: - return self.ToPerfJson() + return str(self.ToPerfJson()) + + +def JsonEncodeMetric(x: MetricGroup): + class MetricJsonEncoder(json.JSONEncoder): + """Special handling for Metric objects.""" + + def default(self, o): + if isinstance(o, Metric) or isinstance(o, MetricGroup): + return o.ToPerfJson() + return json.JSONEncoder.default(self, o) + + return json.dumps(x, indent=2, cls=MetricJsonEncoder) + + +def JsonEncodeMetricGroupDescriptions(x: MetricGroup): + return json.dumps(x.ToMetricGroupDescriptions(), indent=2) class _RewriteIfExpToSelect(ast.NodeTransformer): From 6717e29ae2201d30d8486a675e65e820274f93cd Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 8 Feb 2024 13:51:39 -0800 Subject: [PATCH 11/55] perf jevents: Drop duplicate pending metrics Drop adding a pending metric if there is an existing one. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/jevents.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py index 8d2cecb87e88..7b4239e8b08b 100755 --- a/tools/perf/pmu-events/jevents.py +++ b/tools/perf/pmu-events/jevents.py @@ -473,7 +473,7 @@ def add_events_table_entries(item: os.DirEntry, topic: str) -> None: for e in read_json_events(item.path, topic): if e.name: _pending_events.append(e) - if e.metric_name: + if e.metric_name and not any(e.metric_name == x.metric_name for x in _pending_metrics): _pending_metrics.append(e) From 1ae0794ee68aea554d09ed7aed6ec9acec811043 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 8 Feb 2024 14:19:43 -0800 Subject: [PATCH 12/55] perf jevents: Skip optional metrics in metric group list For metric groups, skip metrics in the list that are None. This allows functions to better optionally return metrics. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/metric.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tools/perf/pmu-events/metric.py b/tools/perf/pmu-events/metric.py index b39189182608..dd8fd06940e6 100644 --- a/tools/perf/pmu-events/metric.py +++ b/tools/perf/pmu-events/metric.py @@ -493,13 +493,15 @@ class MetricGroup: """ def __init__(self, name: str, - metric_list: List[Union[Metric, 'MetricGroup']], + metric_list: List[Union[Optional[Metric], Optional['MetricGroup']]], description: Optional[str] = None): self.name = name - self.metric_list = metric_list + self.metric_list = [] self.description = description for metric in metric_list: - metric.AddToMetricGroup(self) + if metric: + self.metric_list.append(metric) + metric.AddToMetricGroup(self) def AddToMetricGroup(self, group): """Callback used when a MetricGroup is added into another.""" From 1b2e82373da71b5ef32617202e39d7fb9b70adaa Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 12 May 2023 20:20:56 -0700 Subject: [PATCH 13/55] perf jevents: Build support for generating metrics from python Generate extra-metrics.json and extra-metricgroups.json from python architecture specific scripts. The metrics themselves will be added in later patches. If a build takes place in tools/perf/ then extra-metrics.json and extra-metricgroups.json are generated in that directory and so added to .gitignore. If there is an OUTPUT directory then the tools/perf/pmu-events/arch files are copied to it so the generated extra-metrics.json and extra-metricgroups.json can be added/generated there. Signed-off-by: Ian Rogers --- tools/perf/.gitignore | 2 + tools/perf/Makefile.perf | 23 +++++++--- tools/perf/pmu-events/Build | 62 ++++++++++++++++++++++++-- tools/perf/pmu-events/amd_metrics.py | 38 ++++++++++++++++ tools/perf/pmu-events/arm64_metrics.py | 39 ++++++++++++++++ tools/perf/pmu-events/intel_metrics.py | 38 ++++++++++++++++ 6 files changed, 193 insertions(+), 9 deletions(-) create mode 100755 tools/perf/pmu-events/amd_metrics.py create mode 100755 tools/perf/pmu-events/arm64_metrics.py create mode 100755 tools/perf/pmu-events/intel_metrics.py diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore index f5b81d439387..c9a8da5bfc56 100644 --- a/tools/perf/.gitignore +++ b/tools/perf/.gitignore @@ -39,6 +39,8 @@ trace/beauty/generated/ pmu-events/pmu-events.c pmu-events/jevents pmu-events/metric_test.log +pmu-events/arch/**/extra-metrics.json +pmu-events/arch/**/extra-metricgroups.json tests/shell/*.shellcheck_log tests/shell/coresight/*.shellcheck_log tests/shell/lib/*.shellcheck_log diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 9dd2e8d3f3c9..75b278497526 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -1265,9 +1265,26 @@ endif # CONFIG_PERF_BPF_SKEL bpf-skel-clean: $(call QUIET_CLEAN, bpf-skel) $(RM) -r $(SKEL_TMP_OUT) $(SKELETONS) $(SKEL_OUT)/vmlinux.h +pmu-events-clean: +ifeq ($(OUTPUT),) + $(call QUIET_CLEAN, pmu-events) $(RM) \ + pmu-events/pmu-events.c \ + pmu-events/metric_test.log \ + pmu-events/test-empty-pmu-events.c \ + pmu-events/empty-pmu-events.log + $(Q)find pmu-events/arch -name 'extra-metrics.json' -delete -o \ + -name 'extra-metricgroups.json' -delete +else + $(call QUIET_CLEAN, pmu-events) $(RM) -r $(OUTPUT)pmu-events/arch \ + $(OUTPUT)pmu-events/pmu-events.c \ + $(OUTPUT)pmu-events/metric_test.log \ + $(OUTPUT)pmu-events/test-empty-pmu-events.c \ + $(OUTPUT)pmu-events/empty-pmu-events.log +endif + clean:: $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clean $(LIBSYMBOL)-clean $(LIBPERF)-clean \ arm64-sysreg-defs-clean fixdep-clean python-clean bpf-skel-clean \ - tests-coresight-targets-clean + tests-coresight-targets-clean pmu-events-clean $(call QUIET_CLEAN, core-objs) $(RM) $(LIBPERF_A) $(OUTPUT)perf-archive \ $(OUTPUT)perf-iostat $(LANG_BINDINGS) $(Q)find $(or $(OUTPUT),.) -name '*.o' -delete -o -name '*.a' -delete -o \ @@ -1280,10 +1297,6 @@ clean:: $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clean $(LIBSYMBOL)-clean $( $(OUTPUT)FEATURE-DUMP $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* \ $(OUTPUT)util/intel-pt-decoder/inat-tables.c \ $(OUTPUT)tests/llvm-src-{base,kbuild,prologue,relocation}.c \ - $(OUTPUT)pmu-events/pmu-events.c \ - $(OUTPUT)pmu-events/test-empty-pmu-events.c \ - $(OUTPUT)pmu-events/empty-pmu-events.log \ - $(OUTPUT)pmu-events/metric_test.log \ $(OUTPUT)$(fadvise_advice_array) \ $(OUTPUT)$(fsconfig_arrays) \ $(OUTPUT)$(fsmount_arrays) \ diff --git a/tools/perf/pmu-events/Build b/tools/perf/pmu-events/Build index d941bc9d16e9..d89275c4f485 100644 --- a/tools/perf/pmu-events/Build +++ b/tools/perf/pmu-events/Build @@ -1,7 +1,6 @@ pmu-events-y += pmu-events.o JDIR = pmu-events/arch/$(SRCARCH) -JSON = $(shell [ -d $(JDIR) ] && \ - find $(JDIR) -name '*.json' -o -name 'mapfile.csv') +JSON = $(shell find pmu-events/arch -name *.json -o -name *.csv) JDIR_TEST = pmu-events/arch/test JSON_TEST = $(shell [ -d $(JDIR_TEST) ] && \ find $(JDIR_TEST) -name '*.json') @@ -29,6 +28,61 @@ $(PMU_EVENTS_C): $(EMPTY_PMU_EVENTS_C) $(call rule_mkdir) $(Q)$(call echo-cmd,gen)cp $< $@ else +# Extract the model from a extra-metrics.json or extra-metricgroups.json path +model_name = $(shell echo $(1)|sed -e 's@.\+/\(.*\)/extra-metric.*\.json@\1@') +vendor_name = $(shell echo $(1)|sed -e 's@.\+/\(.*\)/[^/]*/extra-metric.*\.json@\1@') + +# Copy checked-in json for generation. +$(OUTPUT)pmu-events/arch/%: pmu-events/arch/% + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)cp $< $@ + +GEN_METRIC_DEPS := pmu-events/metric.py + +# Generate AMD Json +ZENS = $(shell ls -d pmu-events/arch/x86/amdzen*) +ZEN_METRICS = $(foreach x,$(ZENS),$(OUTPUT)$(x)/extra-metrics.json) +ZEN_METRICGROUPS = $(foreach x,$(ZENS),$(OUTPUT)$(x)/extra-metricgroups.json) + +$(ZEN_METRICS): pmu-events/amd_metrics.py $(GEN_METRIC_DEPS) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(PYTHON) $< $(call model_name,$@) arch > $@ + +$(ZEN_METRICGROUPS): pmu-events/amd_metrics.py $(GEN_METRIC_DEPS) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(PYTHON) $< -metricgroups $(call model_name,$@) arch > $@ + +# Generate ARM Json +ARMS = $(shell ls -d pmu-events/arch/arm64/arm/*) +ARM_METRICS = $(foreach x,$(ARMS),$(OUTPUT)$(x)/extra-metrics.json) +ARM_METRICGROUPS = $(foreach x,$(ARMS),$(OUTPUT)$(x)/extra-metricgroups.json) + +$(ARM_METRICS): pmu-events/arm64_metrics.py $(GEN_METRIC_DEPS) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(PYTHON) $< $(call vendor_name,$@) $(call model_name,$@) arch > $@ + +$(ARM_METRICGROUPS): pmu-events/arm64_metrics.py $(GEN_METRIC_DEPS) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(PYTHON) $< -metricgroups $(call vendor_name,$@) $(call model_name,$@) arch > $@ + +# Generate Intel Json +INTELS = $(shell ls -d pmu-events/arch/x86/*|grep -v amdzen|grep -v mapfile.csv) +INTEL_METRICS = $(foreach x,$(INTELS),$(OUTPUT)$(x)/extra-metrics.json) +INTEL_METRICGROUPS = $(foreach x,$(INTELS),$(OUTPUT)$(x)/extra-metricgroups.json) + +$(INTEL_METRICS): pmu-events/intel_metrics.py $(GEN_METRIC_DEPS) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(PYTHON) $< $(call model_name,$@) arch > $@ + +$(INTEL_METRICGROUPS): pmu-events/intel_metrics.py $(GEN_METRIC_DEPS) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(PYTHON) $< -metricgroups $(call model_name,$@) arch > $@ + +GEN_JSON = $(patsubst %,$(OUTPUT)%,$(JSON)) \ + $(ZEN_METRICS) $(ZEN_METRICGROUPS) \ + $(ARM_METRICS) $(ARM_METRICGROUPS) \ + $(INTEL_METRICS) $(INTEL_METRICGROUPS) + $(METRIC_TEST_LOG): $(METRIC_TEST_PY) $(METRIC_PY) $(call rule_mkdir) $(Q)$(call echo-cmd,test)$(PYTHON) $< 2> $@ || (cat $@ && false) @@ -41,9 +95,9 @@ $(EMPTY_PMU_EVENTS_TEST_LOG): $(EMPTY_PMU_EVENTS_C) $(TEST_EMPTY_PMU_EVENTS_C) $(call rule_mkdir) $(Q)$(call echo-cmd,test)diff -u $^ 2> $@ || (cat $@ && false) -$(PMU_EVENTS_C): $(JSON) $(JSON_TEST) $(JEVENTS_PY) $(METRIC_PY) $(METRIC_TEST_LOG) $(EMPTY_PMU_EVENTS_TEST_LOG) +$(PMU_EVENTS_C): $(GEN_JSON) $(JSON_TEST) $(JEVENTS_PY) $(METRIC_PY) $(METRIC_TEST_LOG) $(EMPTY_PMU_EVENTS_TEST_LOG) $(call rule_mkdir) - $(Q)$(call echo-cmd,gen)$(PYTHON) $(JEVENTS_PY) $(JEVENTS_ARCH) $(JEVENTS_MODEL) pmu-events/arch $@ + $(Q)$(call echo-cmd,gen)$(PYTHON) $(JEVENTS_PY) $(JEVENTS_ARCH) $(JEVENTS_MODEL) $(OUTPUT)pmu-events/arch $@ endif # pmu-events.c file is generated in the OUTPUT directory so it needs a diff --git a/tools/perf/pmu-events/amd_metrics.py b/tools/perf/pmu-events/amd_metrics.py new file mode 100755 index 000000000000..7ab2ee4fdb17 --- /dev/null +++ b/tools/perf/pmu-events/amd_metrics.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +from metric import (JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, MetricGroup) +import argparse +import json +import os + +# Global command line arguments. +_args = None + +def main() -> None: + global _args + + def dir_path(path: str) -> str: + """Validate path is a directory for argparse.""" + if os.path.isdir(path): + return path + raise argparse.ArgumentTypeError(f'\'{path}\' is not a valid directory') + + parser = argparse.ArgumentParser(description="AMD perf json generator") + parser.add_argument("-metricgroups", help="Generate metricgroups data", action='store_true') + parser.add_argument("model", help="e.g. amdzen[123]") + parser.add_argument( + 'events_path', + type=dir_path, + help='Root of tree containing architecture directories containing json files' + ) + _args = parser.parse_args() + + all_metrics = MetricGroup("",[]) + + if _args.metricgroups: + print(JsonEncodeMetricGroupDescriptions(all_metrics)) + else: + print(JsonEncodeMetric(all_metrics)) + +if __name__ == '__main__': + main() diff --git a/tools/perf/pmu-events/arm64_metrics.py b/tools/perf/pmu-events/arm64_metrics.py new file mode 100755 index 000000000000..a9f0e6bc751b --- /dev/null +++ b/tools/perf/pmu-events/arm64_metrics.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +from metric import (JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, MetricGroup) +import argparse +import json +import os + +# Global command line arguments. +_args = None + +def main() -> None: + global _args + + def dir_path(path: str) -> str: + """Validate path is a directory for argparse.""" + if os.path.isdir(path): + return path + raise argparse.ArgumentTypeError(f'\'{path}\' is not a valid directory') + + parser = argparse.ArgumentParser(description="ARM perf json generator") + parser.add_argument("-metricgroups", help="Generate metricgroups data", action='store_true') + parser.add_argument("vendor", help="e.g. arm") + parser.add_argument("model", help="e.g. neoverse-n1") + parser.add_argument( + 'events_path', + type=dir_path, + help='Root of tree containing architecture directories containing json files' + ) + _args = parser.parse_args() + + all_metrics = MetricGroup("",[]) + + if _args.metricgroups: + print(JsonEncodeMetricGroupDescriptions(all_metrics)) + else: + print(JsonEncodeMetric(all_metrics)) + +if __name__ == '__main__': + main() diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py new file mode 100755 index 000000000000..f004c27640d2 --- /dev/null +++ b/tools/perf/pmu-events/intel_metrics.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +from metric import (JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, MetricGroup) +import argparse +import json +import os + +# Global command line arguments. +_args = None + +def main() -> None: + global _args + + def dir_path(path: str) -> str: + """Validate path is a directory for argparse.""" + if os.path.isdir(path): + return path + raise argparse.ArgumentTypeError(f'\'{path}\' is not a valid directory') + + parser = argparse.ArgumentParser(description="Intel perf json generator") + parser.add_argument("-metricgroups", help="Generate metricgroups data", action='store_true') + parser.add_argument("model", help="e.g. skylakex") + parser.add_argument( + 'events_path', + type=dir_path, + help='Root of tree containing architecture directories containing json files' + ) + _args = parser.parse_args() + + all_metrics = MetricGroup("",[]) + + if _args.metricgroups: + print(JsonEncodeMetricGroupDescriptions(all_metrics)) + else: + print(JsonEncodeMetric(all_metrics)) + +if __name__ == '__main__': + main() From 302b9b0b957aa611759efcd71ca338079c4470e9 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Feb 2024 22:18:36 -0800 Subject: [PATCH 14/55] perf jevents: Add load event json to verify and allow fallbacks Add a LoadEvents function that loads all event json files in a directory. In the Event constructor ensure all events are defined in the event json except for legacy events like "cycles". If the initial event isn't found then legacy_event1 is used, and if that isn't found legacy_event2 is used. This allows a single Event to have multiple event names as models will often rename the same event over time. If the event doesn't exist an exception is raised. So that references to metrics can be added, add the MetricRef class. This doesn't validate as an event name and so provides an escape hatch for metrics to refer to each other. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/Build | 12 ++-- tools/perf/pmu-events/amd_metrics.py | 6 +- tools/perf/pmu-events/arm64_metrics.py | 6 +- tools/perf/pmu-events/intel_metrics.py | 6 +- tools/perf/pmu-events/metric.py | 77 +++++++++++++++++++++++++- 5 files changed, 95 insertions(+), 12 deletions(-) diff --git a/tools/perf/pmu-events/Build b/tools/perf/pmu-events/Build index d89275c4f485..f3bc6c093360 100644 --- a/tools/perf/pmu-events/Build +++ b/tools/perf/pmu-events/Build @@ -46,11 +46,11 @@ ZEN_METRICGROUPS = $(foreach x,$(ZENS),$(OUTPUT)$(x)/extra-metricgroups.json) $(ZEN_METRICS): pmu-events/amd_metrics.py $(GEN_METRIC_DEPS) $(call rule_mkdir) - $(Q)$(call echo-cmd,gen)$(PYTHON) $< $(call model_name,$@) arch > $@ + $(Q)$(call echo-cmd,gen)$(PYTHON) $< $(call model_name,$@) pmu-events/arch > $@ $(ZEN_METRICGROUPS): pmu-events/amd_metrics.py $(GEN_METRIC_DEPS) $(call rule_mkdir) - $(Q)$(call echo-cmd,gen)$(PYTHON) $< -metricgroups $(call model_name,$@) arch > $@ + $(Q)$(call echo-cmd,gen)$(PYTHON) $< -metricgroups $(call model_name,$@) pmu-events/arch > $@ # Generate ARM Json ARMS = $(shell ls -d pmu-events/arch/arm64/arm/*) @@ -59,11 +59,11 @@ ARM_METRICGROUPS = $(foreach x,$(ARMS),$(OUTPUT)$(x)/extra-metricgroups.json) $(ARM_METRICS): pmu-events/arm64_metrics.py $(GEN_METRIC_DEPS) $(call rule_mkdir) - $(Q)$(call echo-cmd,gen)$(PYTHON) $< $(call vendor_name,$@) $(call model_name,$@) arch > $@ + $(Q)$(call echo-cmd,gen)$(PYTHON) $< $(call vendor_name,$@) $(call model_name,$@) pmu-events/arch > $@ $(ARM_METRICGROUPS): pmu-events/arm64_metrics.py $(GEN_METRIC_DEPS) $(call rule_mkdir) - $(Q)$(call echo-cmd,gen)$(PYTHON) $< -metricgroups $(call vendor_name,$@) $(call model_name,$@) arch > $@ + $(Q)$(call echo-cmd,gen)$(PYTHON) $< -metricgroups $(call vendor_name,$@) $(call model_name,$@) pmu-events/arch > $@ # Generate Intel Json INTELS = $(shell ls -d pmu-events/arch/x86/*|grep -v amdzen|grep -v mapfile.csv) @@ -72,11 +72,11 @@ INTEL_METRICGROUPS = $(foreach x,$(INTELS),$(OUTPUT)$(x)/extra-metricgroups.json $(INTEL_METRICS): pmu-events/intel_metrics.py $(GEN_METRIC_DEPS) $(call rule_mkdir) - $(Q)$(call echo-cmd,gen)$(PYTHON) $< $(call model_name,$@) arch > $@ + $(Q)$(call echo-cmd,gen)$(PYTHON) $< $(call model_name,$@) pmu-events/arch > $@ $(INTEL_METRICGROUPS): pmu-events/intel_metrics.py $(GEN_METRIC_DEPS) $(call rule_mkdir) - $(Q)$(call echo-cmd,gen)$(PYTHON) $< -metricgroups $(call model_name,$@) arch > $@ + $(Q)$(call echo-cmd,gen)$(PYTHON) $< -metricgroups $(call model_name,$@) pmu-events/arch > $@ GEN_JSON = $(patsubst %,$(OUTPUT)%,$(JSON)) \ $(ZEN_METRICS) $(ZEN_METRICGROUPS) \ diff --git a/tools/perf/pmu-events/amd_metrics.py b/tools/perf/pmu-events/amd_metrics.py index 7ab2ee4fdb17..4f728e7aae4a 100755 --- a/tools/perf/pmu-events/amd_metrics.py +++ b/tools/perf/pmu-events/amd_metrics.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) -from metric import (JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, MetricGroup) +from metric import (JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, LoadEvents, + MetricGroup) import argparse import json import os @@ -27,6 +28,9 @@ def dir_path(path: str) -> str: ) _args = parser.parse_args() + directory = f"{_args.events_path}/x86/{_args.model}/" + LoadEvents(directory) + all_metrics = MetricGroup("",[]) if _args.metricgroups: diff --git a/tools/perf/pmu-events/arm64_metrics.py b/tools/perf/pmu-events/arm64_metrics.py index a9f0e6bc751b..c9aa2d827a82 100755 --- a/tools/perf/pmu-events/arm64_metrics.py +++ b/tools/perf/pmu-events/arm64_metrics.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) -from metric import (JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, MetricGroup) +from metric import (JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, LoadEvents, + MetricGroup) import argparse import json import os @@ -30,6 +31,9 @@ def dir_path(path: str) -> str: all_metrics = MetricGroup("",[]) + directory = f"{_args.events_path}/arm64/{_args.vendor}/{_args.model}/" + LoadEvents(directory) + if _args.metricgroups: print(JsonEncodeMetricGroupDescriptions(all_metrics)) else: diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py index f004c27640d2..04a19d05c6c1 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) -from metric import (JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, MetricGroup) +from metric import (JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, LoadEvents, + MetricGroup) import argparse import json import os @@ -27,6 +28,9 @@ def dir_path(path: str) -> str: ) _args = parser.parse_args() + directory = f"{_args.events_path}/x86/{_args.model}/" + LoadEvents(directory) + all_metrics = MetricGroup("",[]) if _args.metricgroups: diff --git a/tools/perf/pmu-events/metric.py b/tools/perf/pmu-events/metric.py index dd8fd06940e6..03312cd6d491 100644 --- a/tools/perf/pmu-events/metric.py +++ b/tools/perf/pmu-events/metric.py @@ -3,10 +3,50 @@ import ast import decimal import json +import os import re from enum import Enum from typing import Dict, List, Optional, Set, Tuple, Union +all_events = set() + +def LoadEvents(directory: str) -> None: + """Populate a global set of all known events for the purpose of validating Event names""" + global all_events + all_events = { + "context\-switches", + "cycles", + "duration_time", + "instructions", + "l2_itlb_misses", + } + for file in os.listdir(os.fsencode(directory)): + filename = os.fsdecode(file) + if filename.endswith(".json"): + for x in json.load(open(f"{directory}/{filename}")): + if "EventName" in x: + all_events.add(x["EventName"]) + elif "ArchStdEvent" in x: + all_events.add(x["ArchStdEvent"]) + + +def CheckEvent(name: str) -> bool: + """Check the event name exists in the set of all loaded events""" + global all_events + if len(all_events) == 0: + # No events loaded so assume any event is good. + return True + + if ':' in name: + # Remove trailing modifier. + name = name[:name.find(':')] + elif '/' in name: + # Name could begin with a PMU or an event, for now assume it is good. + return True + + return name in all_events + + class MetricConstraint(Enum): GROUPED_EVENTS = 0 NO_GROUP_EVENTS = 1 @@ -317,9 +357,18 @@ def _FixEscapes(s: str) -> str: class Event(Expression): """An event in an expression.""" - def __init__(self, name: str, legacy_name: str = ''): - self.name = _FixEscapes(name) - self.legacy_name = _FixEscapes(legacy_name) + def __init__(self, *args: str): + error = "" + for name in args: + if CheckEvent(name): + self.name = _FixEscapes(name) + return + if error: + error += " or " + name + else: + error = name + global all_events + raise Exception(f"No event {error} in:\n{all_events}") def ToPerfJson(self): result = re.sub('/', '@', self.name) @@ -338,6 +387,28 @@ def Substitute(self, name: str, expression: Expression) -> Expression: return self +class MetricRef(Expression): + """A metric reference in an expression.""" + + def __init__(self, name: str): + self.name = _FixEscapes(name) + + def ToPerfJson(self): + return self.name + + def ToPython(self): + return f'MetricRef(r"{self.name}")' + + def Simplify(self) -> Expression: + return self + + def Equals(self, other: Expression) -> bool: + return isinstance(other, MetricRef) and self.name == other.name + + def Substitute(self, name: str, expression: Expression) -> Expression: + return self + + class Constant(Expression): """A constant within the expression tree.""" From 91f4d7e805cd9f0f56022eaa1bad9bc01ebc2d2a Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 7 Feb 2024 16:59:18 -0800 Subject: [PATCH 15/55] perf jevents: Add RAPL event metric for AMD zen models Add power per second metrics based on RAPL. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/amd_metrics.py | 31 +++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/tools/perf/pmu-events/amd_metrics.py b/tools/perf/pmu-events/amd_metrics.py index 4f728e7aae4a..fd420466b999 100755 --- a/tools/perf/pmu-events/amd_metrics.py +++ b/tools/perf/pmu-events/amd_metrics.py @@ -1,14 +1,37 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) -from metric import (JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, LoadEvents, - MetricGroup) +from metric import (d_ratio, has_event, Event, JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, + LoadEvents, Metric, MetricGroup, Select) import argparse import json +import math import os # Global command line arguments. _args = None +interval_sec = Event("duration_time") + +def Rapl() -> MetricGroup: + """Processor socket power consumption estimate. + + Use events from the running average power limit (RAPL) driver. + """ + # Watts = joules/second + # Currently only energy-pkg is supported by AMD: + # https://lore.kernel.org/lkml/20220105185659.643355-1-eranian@google.com/ + pkg = Event("power/energy\-pkg/") + cond_pkg = Select(pkg, has_event(pkg), math.nan) + scale = 2.3283064365386962890625e-10 + metrics = [ + Metric("cpu_power_pkg", "", + d_ratio(cond_pkg * scale, interval_sec), "Watts"), + ] + + return MetricGroup("cpu_power", metrics, + description="Processor socket power consumption estimates") + + def main() -> None: global _args @@ -31,7 +54,9 @@ def dir_path(path: str) -> str: directory = f"{_args.events_path}/x86/{_args.model}/" LoadEvents(directory) - all_metrics = MetricGroup("",[]) + all_metrics = MetricGroup("", [ + Rapl(), + ]) if _args.metricgroups: print(JsonEncodeMetricGroupDescriptions(all_metrics)) From 08aa9ba8207fac4600ef6d45cbbb7c195914f0ba Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 7 Feb 2024 22:24:41 -0800 Subject: [PATCH 16/55] perf jevents: Add idle metric for AMD zen models Compute using the msr PMU the percentage of wallclock cycles where the CPUs are in a low power state. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/amd_metrics.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tools/perf/pmu-events/amd_metrics.py b/tools/perf/pmu-events/amd_metrics.py index fd420466b999..e069b95d6470 100755 --- a/tools/perf/pmu-events/amd_metrics.py +++ b/tools/perf/pmu-events/amd_metrics.py @@ -1,7 +1,8 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) -from metric import (d_ratio, has_event, Event, JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, - LoadEvents, Metric, MetricGroup, Select) +from metric import (d_ratio, has_event, max, Event, JsonEncodeMetric, + JsonEncodeMetricGroupDescriptions, LoadEvents, Metric, + MetricGroup, Select) import argparse import json import math @@ -12,6 +13,16 @@ interval_sec = Event("duration_time") +def Idle() -> Metric: + cyc = Event("msr/mperf/") + tsc = Event("msr/tsc/") + low = max(tsc - cyc, 0) + return Metric( + "idle", + "Percentage of total wallclock cycles where CPUs are in low power state (C1 or deeper sleep state)", + d_ratio(low, tsc), "100%") + + def Rapl() -> MetricGroup: """Processor socket power consumption estimate. @@ -55,6 +66,7 @@ def dir_path(path: str) -> str: LoadEvents(directory) all_metrics = MetricGroup("", [ + Idle(), Rapl(), ]) From 2901c454c4a2cfecc30b65c8e690c2b3430f3773 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 8 Feb 2024 17:19:10 -0800 Subject: [PATCH 17/55] perf jevents: Add upc metric for uops per cycle for AMD The metric adjusts for whether or not SMT is on. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/amd_metrics.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/tools/perf/pmu-events/amd_metrics.py b/tools/perf/pmu-events/amd_metrics.py index e069b95d6470..c64470d4822e 100755 --- a/tools/perf/pmu-events/amd_metrics.py +++ b/tools/perf/pmu-events/amd_metrics.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) from metric import (d_ratio, has_event, max, Event, JsonEncodeMetric, - JsonEncodeMetricGroupDescriptions, LoadEvents, Metric, - MetricGroup, Select) + JsonEncodeMetricGroupDescriptions, Literal, LoadEvents, + Metric, MetricGroup, Select) import argparse import json import math @@ -10,8 +10,17 @@ # Global command line arguments. _args = None - +_zen_model: int = 1 interval_sec = Event("duration_time") +cycles = Event("cycles") +# Number of CPU cycles scaled for SMT. +smt_cycles = Select(cycles / 2, Literal("#smt_on"), cycles) + +def AmdUpc() -> Metric: + ops = Event("ex_ret_ops", "ex_ret_cops") + upc = d_ratio(ops, smt_cycles) + return Metric("upc", "Micro-ops retired per core cycle (higher is better)", + upc, "uops/cycle") def Idle() -> Metric: cyc = Event("msr/mperf/") @@ -45,6 +54,7 @@ def Rapl() -> MetricGroup: def main() -> None: global _args + global _zen_model def dir_path(path: str) -> str: """Validate path is a directory for argparse.""" @@ -65,7 +75,10 @@ def dir_path(path: str) -> str: directory = f"{_args.events_path}/x86/{_args.model}/" LoadEvents(directory) + _zen_model = int(_args.model[6:]) + all_metrics = MetricGroup("", [ + AmdUpc(), Idle(), Rapl(), ]) From 1b178e2d6e75b2cfacfb519a570e54d7d7002fbd Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 8 Feb 2024 18:12:10 -0800 Subject: [PATCH 18/55] perf jevents: Add br metric group for branch statistics on AMD The br metric group for branches itself comprises metric groups for total, taken, conditional, fused and far metric groups using json events. The lack of conditional events on anything but zen2 means this category is lacking on zen1, zen3 and zen4. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/amd_metrics.py | 105 +++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/tools/perf/pmu-events/amd_metrics.py b/tools/perf/pmu-events/amd_metrics.py index c64470d4822e..90545d4862a6 100755 --- a/tools/perf/pmu-events/amd_metrics.py +++ b/tools/perf/pmu-events/amd_metrics.py @@ -7,15 +7,119 @@ import json import math import os +from typing import Optional # Global command line arguments. _args = None _zen_model: int = 1 interval_sec = Event("duration_time") +ins = Event("instructions") cycles = Event("cycles") # Number of CPU cycles scaled for SMT. smt_cycles = Select(cycles / 2, Literal("#smt_on"), cycles) +def AmdBr(): + def Total() -> MetricGroup: + br = Event("ex_ret_brn") + br_m_all = Event("ex_ret_brn_misp") + br_clr = Event("ex_ret_msprd_brnch_instr_dir_msmtch", "ex_ret_brn_resync") + + br_r = d_ratio(br, interval_sec) + ins_r = d_ratio(ins, br) + misp_r = d_ratio(br_m_all, br) + clr_r = d_ratio(br_clr, interval_sec) + + return MetricGroup("br_total", [ + Metric("br_total_retired", + "The number of branch instructions retired per second.", br_r, + "insn/s"), + Metric( + "br_total_mispred", + "The number of branch instructions retired, of any type, that were " + "not correctly predicted as a percentage of all branch instrucions.", + misp_r, "100%"), + Metric("br_total_insn_between_branches", + "The number of instructions divided by the number of branches.", + ins_r, "insn"), + Metric("br_total_insn_fe_resteers", + "The number of resync branches per second.", clr_r, "req/s") + ]) + + def Taken() -> MetricGroup: + br = Event("ex_ret_brn_tkn") + br_m_tk = Event("ex_ret_brn_tkn_misp") + br_r = d_ratio(br, interval_sec) + ins_r = d_ratio(ins, br) + misp_r = d_ratio(br_m_tk, br) + return MetricGroup("br_taken", [ + Metric("br_taken_retired", + "The number of taken branches that were retired per second.", + br_r, "insn/s"), + Metric( + "br_taken_mispred", + "The number of retired taken branch instructions that were " + "mispredicted as a percentage of all taken branches.", misp_r, + "100%"), + Metric( + "br_taken_insn_between_branches", + "The number of instructions divided by the number of taken branches.", + ins_r, "insn"), + ]) + + def Conditional() -> Optional[MetricGroup]: + global _zen_model + br = Event("ex_ret_cond") + br_r = d_ratio(br, interval_sec) + ins_r = d_ratio(ins, br) + + metrics = [ + Metric("br_cond_retired", "Retired conditional branch instructions.", + br_r, "insn/s"), + Metric("br_cond_insn_between_branches", + "The number of instructions divided by the number of conditional " + "branches.", ins_r, "insn"), + ] + if _zen_model == 2: + br_m_cond = Event("ex_ret_cond_misp") + misp_r = d_ratio(br_m_cond, br) + metrics += [ + Metric("br_cond_mispred", + "Retired conditional branch instructions mispredicted as a " + "percentage of all conditional branches.", misp_r, "100%"), + ] + + return MetricGroup("br_cond", metrics) + + def Fused() -> MetricGroup: + br = Event("ex_ret_fused_instr", "ex_ret_fus_brnch_inst") + br_r = d_ratio(br, interval_sec) + ins_r = d_ratio(ins, br) + return MetricGroup("br_cond", [ + Metric("br_fused_retired", + "Retired fused branch instructions per second.", br_r, "insn/s"), + Metric( + "br_fused_insn_between_branches", + "The number of instructions divided by the number of fused " + "branches.", ins_r, "insn"), + ]) + + def Far() -> MetricGroup: + br = Event("ex_ret_brn_far") + br_r = d_ratio(br, interval_sec) + ins_r = d_ratio(ins, br) + return MetricGroup("br_far", [ + Metric("br_far_retired", "Retired far control transfers per second.", + br_r, "insn/s"), + Metric( + "br_far_insn_between_branches", + "The number of instructions divided by the number of far branches.", + ins_r, "insn"), + ]) + + return MetricGroup("br", [Total(), Taken(), Conditional(), Fused(), Far()], + description="breakdown of retired branch instructions") + + def AmdUpc() -> Metric: ops = Event("ex_ret_ops", "ex_ret_cops") upc = d_ratio(ops, smt_cycles) @@ -78,6 +182,7 @@ def dir_path(path: str) -> str: _zen_model = int(_args.model[6:]) all_metrics = MetricGroup("", [ + AmdBr(), AmdUpc(), Idle(), Rapl(), From 0b56105fcdfe289dd89efaccc5bc17a5194c2956 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 8 Feb 2024 19:15:04 -0800 Subject: [PATCH 19/55] perf jevents: Add software prefetch (swpf) metric group for AMD Add metrics that give the utility of software prefetches on zen2, zen3 and zen4. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/amd_metrics.py | 96 ++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/tools/perf/pmu-events/amd_metrics.py b/tools/perf/pmu-events/amd_metrics.py index 90545d4862a6..05a16e748d2e 100755 --- a/tools/perf/pmu-events/amd_metrics.py +++ b/tools/perf/pmu-events/amd_metrics.py @@ -120,6 +120,101 @@ def Far() -> MetricGroup: description="breakdown of retired branch instructions") +def AmdSwpf() -> Optional[MetricGroup]: + """Returns a MetricGroup representing AMD software prefetch metrics.""" + global _zen_model + if _zen_model <= 1: + return None + + swp_ld = Event("ls_dispatch.ld_dispatch") + swp_t0 = Event("ls_pref_instr_disp.prefetch") + swp_w = Event("ls_pref_instr_disp.prefetch_w") # Missing on Zen1 + swp_nt = Event("ls_pref_instr_disp.prefetch_nta") + swp_mab = Event("ls_inef_sw_pref.mab_mch_cnt") + swp_l2 = Event("ls_sw_pf_dc_fills.local_l2", + "ls_sw_pf_dc_fills.lcl_l2", + "ls_sw_pf_dc_fill.ls_mabresp_lcl_l2") + swp_lc = Event("ls_sw_pf_dc_fills.local_ccx", + "ls_sw_pf_dc_fills.int_cache", + "ls_sw_pf_dc_fill.ls_mabresp_lcl_cache") + swp_lm = Event("ls_sw_pf_dc_fills.dram_io_near", + "ls_sw_pf_dc_fills.mem_io_local", + "ls_sw_pf_dc_fill.ls_mabresp_lcl_dram") + swp_rc = Event("ls_sw_pf_dc_fills.far_cache", + "ls_sw_pf_dc_fills.ext_cache_remote", + "ls_sw_pf_dc_fill.ls_mabresp_rmt_cache") + swp_rm = Event("ls_sw_pf_dc_fills.dram_io_far", + "ls_sw_pf_dc_fills.mem_io_remote", + "ls_sw_pf_dc_fill.ls_mabresp_rmt_dram") + + # All the swpf that were satisfied beyond L1D are good. + all_pf = swp_t0 + swp_w + swp_nt + good_pf = swp_l2 + swp_lc + swp_lm + swp_rc + swp_rm + bad_pf = max(all_pf - good_pf, 0) + + loc_pf = swp_l2 + swp_lc + swp_lm + rem_pf = swp_rc + swp_rm + + req_pend = max(0, bad_pf - swp_mab) + + r1 = d_ratio(ins, all_pf) + r2 = d_ratio(swp_ld, all_pf) + r3 = d_ratio(swp_t0, interval_sec) + r4 = d_ratio(swp_w, interval_sec) + r5 = d_ratio(swp_nt, interval_sec) + overview = MetricGroup("swpf_overview", [ + Metric("swpf_ov_insn_bt_swpf", "Insn between SWPF", r1, "insns"), + Metric("swpf_ov_loads_bt_swpf", "Loads between SWPF", r2, "loads"), + Metric("swpf_ov_rate_prefetch_t0_t1_t2", "Rate prefetch TO_T1_T2", r3, + "insns/sec"), + Metric("swpf_ov_rate_prefetch_w", "Rate prefetch W", r4, "insns/sec"), + Metric("swpf_ov_rate_preftech_nta", "Rate prefetch NTA", r5, "insns/sec"), + ]) + + r1 = d_ratio(swp_mab, all_pf) + r2 = d_ratio(req_pend, all_pf) + usefulness_bad = MetricGroup("swpf_usefulness_bad", [ + Metric("swpf_use_bad_hit_l1", "Usefulness bad hit L1", r1, "100%"), + Metric("swpf_use_bad_req_pend", "Usefulness bad req pending", r2, "100%"), + ]) + + r1 = d_ratio(good_pf, all_pf) + usefulness_good = MetricGroup("swpf_usefulness_good", [ + Metric("swpf_use_good_other_src", "Usefulness good other src", r1, + "100%"), + ]) + + usefulness = MetricGroup("swpf_usefulness", [ + usefulness_bad, + usefulness_good, + ]) + + r1 = d_ratio(swp_l2, good_pf) + r2 = d_ratio(swp_lc, good_pf) + r3 = d_ratio(swp_lm, good_pf) + data_src_local = MetricGroup("swpf_data_src_local", [ + Metric("swpf_data_src_local_l2", "Data source local l2", r1, "100%"), + Metric("swpf_data_src_local_ccx_l3_loc_ccx", + "Data source local ccx l3 loc ccx", r2, "100%"), + Metric("swpf_data_src_local_memory_or_io", + "Data source local memory or IO", r3, "100%"), + ]) + + r1 = d_ratio(swp_rc, good_pf) + r2 = d_ratio(swp_rm, good_pf) + data_src_remote = MetricGroup("swpf_data_src_remote", [ + Metric("swpf_data_src_remote_cache", "Data source remote cache", r1, + "100%"), + Metric("swpf_data_src_remote_memory_or_io", + "Data source remote memory or IO", r2, "100%"), + ]) + + data_src = MetricGroup("swpf_data_src", [data_src_local, data_src_remote]) + + return MetricGroup("swpf", [overview, usefulness, data_src], + description="Software prefetch breakdown (CCX L3 = L3 of current thread, Loc CCX = CCX cache on some socket)") + + def AmdUpc() -> Metric: ops = Event("ex_ret_ops", "ex_ret_cops") upc = d_ratio(ops, smt_cycles) @@ -183,6 +278,7 @@ def dir_path(path: str) -> str: all_metrics = MetricGroup("", [ AmdBr(), + AmdSwpf(), AmdUpc(), Idle(), Rapl(), From f6fb5b7b852532905707387746e52ba93f83c283 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 8 Feb 2024 19:41:00 -0800 Subject: [PATCH 20/55] perf jevents: Add hardware prefetch (hwpf) metric group for AMD Add metrics that give the utility of hardware prefetches on zen2, zen3 and zen4. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/amd_metrics.py | 62 ++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/tools/perf/pmu-events/amd_metrics.py b/tools/perf/pmu-events/amd_metrics.py index 05a16e748d2e..2d1fc8411d61 100755 --- a/tools/perf/pmu-events/amd_metrics.py +++ b/tools/perf/pmu-events/amd_metrics.py @@ -120,6 +120,67 @@ def Far() -> MetricGroup: description="breakdown of retired branch instructions") +def AmdHwpf(): + """Returns a MetricGroup representing AMD hardware prefetch metrics.""" + global _zen_model + if _zen_model <= 1: + return None + + hwp_ld = Event("ls_dispatch.ld_dispatch") + hwp_l2 = Event("ls_hw_pf_dc_fills.local_l2", + "ls_hw_pf_dc_fills.lcl_l2", + "ls_hw_pf_dc_fill.ls_mabresp_lcl_l2") + hwp_lc = Event("ls_hw_pf_dc_fills.local_ccx", + "ls_hw_pf_dc_fills.int_cache", + "ls_hw_pf_dc_fill.ls_mabresp_lcl_cache") + hwp_lm = Event("ls_hw_pf_dc_fills.dram_io_near", + "ls_hw_pf_dc_fills.mem_io_local", + "ls_hw_pf_dc_fill.ls_mabresp_lcl_dram") + hwp_rc = Event("ls_hw_pf_dc_fills.far_cache", + "ls_hw_pf_dc_fills.ext_cache_remote", + "ls_hw_pf_dc_fill.ls_mabresp_rmt_cache") + hwp_rm = Event("ls_hw_pf_dc_fills.dram_io_far", + "ls_hw_pf_dc_fills.mem_io_remote", + "ls_hw_pf_dc_fill.ls_mabresp_rmt_dram") + + loc_pf = hwp_l2 + hwp_lc + hwp_lm + rem_pf = hwp_rc + hwp_rm + all_pf = loc_pf + rem_pf + + r1 = d_ratio(ins, all_pf) + r2 = d_ratio(hwp_ld, all_pf) + r3 = d_ratio(all_pf, interval_sec) + + overview = MetricGroup("hwpf_overview", [ + Metric("hwpf_ov_insn_bt_hwpf", "Insn between HWPF", r1, "insns"), + Metric("hwpf_ov_loads_bt_hwpf", "Loads between HWPF", r2, "loads"), + Metric("hwpf_ov_rate", "HWPF per second", r3, "hwpf/s"), + ]) + r1 = d_ratio(hwp_l2, all_pf) + r2 = d_ratio(hwp_lc, all_pf) + r3 = d_ratio(hwp_lm, all_pf) + data_src_local = MetricGroup("hwpf_data_src_local", [ + Metric("hwpf_data_src_local_l2", "Data source local l2", r1, "100%"), + Metric("hwpf_data_src_local_ccx_l3_loc_ccx", + "Data source local ccx l3 loc ccx", r2, "100%"), + Metric("hwpf_data_src_local_memory_or_io", + "Data source local memory or IO", r3, "100%"), + ]) + + r1 = d_ratio(hwp_rc, all_pf) + r2 = d_ratio(hwp_rm, all_pf) + data_src_remote = MetricGroup("hwpf_data_src_remote", [ + Metric("hwpf_data_src_remote_cache", "Data source remote cache", r1, + "100%"), + Metric("hwpf_data_src_remote_memory_or_io", + "Data source remote memory or IO", r2, "100%"), + ]) + + data_src = MetricGroup("hwpf_data_src", [data_src_local, data_src_remote]) + return MetricGroup("hwpf", [overview, data_src], + description="Hardware prefetch breakdown (CCX L3 = L3 of current thread, Loc CCX = CCX cache on some socket)") + + def AmdSwpf() -> Optional[MetricGroup]: """Returns a MetricGroup representing AMD software prefetch metrics.""" global _zen_model @@ -278,6 +339,7 @@ def dir_path(path: str) -> str: all_metrics = MetricGroup("", [ AmdBr(), + AmdHwpf(), AmdSwpf(), AmdUpc(), Idle(), From c51d72eb0d839b2e0a8a37373547927d008546a9 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 8 Feb 2024 22:00:30 -0800 Subject: [PATCH 21/55] perf jevents: Add itlb metric group for AMD Add metrics that give an overview and details of the l1 itlb (zen1, zen2, zen3) and l2 itlb (all zens). Signed-off-by: Ian Rogers --- tools/perf/pmu-events/amd_metrics.py | 48 ++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tools/perf/pmu-events/amd_metrics.py b/tools/perf/pmu-events/amd_metrics.py index 2d1fc8411d61..bfed478f508b 100755 --- a/tools/perf/pmu-events/amd_metrics.py +++ b/tools/perf/pmu-events/amd_metrics.py @@ -120,6 +120,53 @@ def Far() -> MetricGroup: description="breakdown of retired branch instructions") +def AmdItlb(): + global _zen_model + l2h = Event("bp_l1_tlb_miss_l2_tlb_hit", "bp_l1_tlb_miss_l2_hit") + l2m = Event("l2_itlb_misses") + l2r = l2h + l2m + + itlb_l1_mg = None + l1m = l2r + if _zen_model <= 3: + l1r = Event("ic_fw32") + l1h = max(l1r - l1m, 0) + itlb_l1_mg = MetricGroup("itlb_l1", [ + Metric("itlb_l1_hits", + "L1 ITLB hits as a perecentage of L1 ITLB accesses.", + d_ratio(l1h, l1h + l1m), "100%"), + Metric("itlb_l1_miss", + "L1 ITLB misses as a perecentage of L1 ITLB accesses.", + d_ratio(l1m, l1h + l1m), "100%"), + Metric("itlb_l1_reqs", + "The number of 32B fetch windows transferred from IC pipe to DE " + "instruction decoder per second.", d_ratio(l1r, interval_sec), + "windows/sec"), + ]) + + return MetricGroup("itlb", [ + MetricGroup("itlb_ov", [ + Metric("itlb_ov_insn_bt_l1_miss", + "Number of instructions between l1 misses", d_ratio( + ins, l1m), "insns"), + Metric("itlb_ov_insn_bt_l2_miss", + "Number of instructions between l2 misses", d_ratio( + ins, l2m), "insns"), + ]), + itlb_l1_mg, + MetricGroup("itlb_l2", [ + Metric("itlb_l2_hits", + "L2 ITLB hits as a percentage of all L2 ITLB accesses.", + d_ratio(l2h, l2r), "100%"), + Metric("itlb_l2_miss", + "L2 ITLB misses as a percentage of all L2 ITLB accesses.", + d_ratio(l2m, l2r), "100%"), + Metric("itlb_l2_reqs", "ITLB accesses per second.", + d_ratio(l2r, interval_sec), "accesses/sec"), + ]), + ], description="Instruction TLB breakdown") + + def AmdHwpf(): """Returns a MetricGroup representing AMD hardware prefetch metrics.""" global _zen_model @@ -339,6 +386,7 @@ def dir_path(path: str) -> str: all_metrics = MetricGroup("", [ AmdBr(), + AmdItlb(), AmdHwpf(), AmdSwpf(), AmdUpc(), From 08b4f742a51fff4db3b775f4cd040f52aab3c84e Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 8 Feb 2024 22:54:46 -0800 Subject: [PATCH 22/55] perf jevents: Add dtlb metric group for AMD Add metrics that give an overview and details of the dtlb (zen1, zen2, zen3). Signed-off-by: Ian Rogers --- tools/perf/pmu-events/amd_metrics.py | 109 +++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/tools/perf/pmu-events/amd_metrics.py b/tools/perf/pmu-events/amd_metrics.py index bfed478f508b..7fd2139766f9 100755 --- a/tools/perf/pmu-events/amd_metrics.py +++ b/tools/perf/pmu-events/amd_metrics.py @@ -120,6 +120,114 @@ def Far() -> MetricGroup: description="breakdown of retired branch instructions") +def AmdDtlb() -> Optional[MetricGroup]: + global _zen_model + if _zen_model >= 4: + return None + + d_dat = Event("ls_dc_accesses") if _zen_model <= 3 else None + d_h4k = Event("ls_l1_d_tlb_miss.tlb_reload_4k_l2_hit") + d_hcoal = Event("ls_l1_d_tlb_miss.tlb_reload_coalesced_page_hit") if _zen_model >= 2 else 0 + d_h2m = Event("ls_l1_d_tlb_miss.tlb_reload_2m_l2_hit") + d_h1g = Event("ls_l1_d_tlb_miss.tlb_reload_1g_l2_hit") + + d_m4k = Event("ls_l1_d_tlb_miss.tlb_reload_4k_l2_miss") + d_mcoal = Event("ls_l1_d_tlb_miss.tlb_reload_coalesced_page_miss") if _zen_model >= 2 else 0 + d_m2m = Event("ls_l1_d_tlb_miss.tlb_reload_2m_l2_miss") + d_m1g = Event("ls_l1_d_tlb_miss.tlb_reload_1g_l2_miss") + + d_w0 = Event("ls_tablewalker.dc_type0") if _zen_model <= 3 else None + d_w1 = Event("ls_tablewalker.dc_type1") if _zen_model <= 3 else None + walks = d_w0 + d_w1 + walks_r = d_ratio(walks, interval_sec) + ins_w = d_ratio(ins, walks) + l1 = d_dat + l1_r = d_ratio(l1, interval_sec) + l2_hits = d_h4k + d_hcoal + d_h2m + d_h1g + l2_miss = d_m4k + d_mcoal + d_m2m + d_m1g + l2_r = d_ratio(l2_hits + l2_miss, interval_sec) + l1_miss = l2_hits + l2_miss + walks + l1_hits = max(l1 - l1_miss, 0) + ins_l = d_ratio(ins, l1_miss) + + return MetricGroup("dtlb", [ + MetricGroup("dtlb_ov", [ + Metric("dtlb_ov_insn_bt_l1_miss", + "DTLB overview: instructions between l1 misses.", ins_l, + "insns"), + Metric("dtlb_ov_insn_bt_walks", + "DTLB overview: instructions between dtlb page table walks.", + ins_w, "insns"), + ]), + MetricGroup("dtlb_l1", [ + Metric("dtlb_l1_hits", + "DTLB L1 hits as percentage of all DTLB L1 accesses.", + d_ratio(l1_hits, l1), "100%"), + Metric("dtlb_l1_miss", + "DTLB L1 misses as percentage of all DTLB L1 accesses.", + d_ratio(l1_miss, l1), "100%"), + Metric("dtlb_l1_reqs", "DTLB L1 accesses per second.", l1_r, + "insns/s"), + ]), + MetricGroup("dtlb_l2", [ + Metric("dtlb_l2_hits", + "DTLB L2 hits as percentage of all DTLB L2 accesses.", + d_ratio(l2_hits, l2_hits + l2_miss), "100%"), + Metric("dtlb_l2_miss", + "DTLB L2 misses as percentage of all DTLB L2 accesses.", + d_ratio(l2_miss, l2_hits + l2_miss), "100%"), + Metric("dtlb_l2_reqs", "DTLB L2 accesses per second.", l2_r, + "insns/s"), + MetricGroup("dtlb_l2_4kb", [ + Metric( + "dtlb_l2_4kb_hits", + "DTLB L2 4kb page size hits as percentage of all DTLB L2 4kb " + "accesses.", d_ratio(d_h4k, d_h4k + d_m4k), "100%"), + Metric( + "dtlb_l2_4kb_miss", + "DTLB L2 4kb page size misses as percentage of all DTLB L2 4kb" + "accesses.", d_ratio(d_m4k, d_h4k + d_m4k), "100%") + ]), + MetricGroup("dtlb_l2_coalesced", [ + Metric( + "dtlb_l2_coal_hits", + "DTLB L2 coalesced page (16kb) hits as percentage of all DTLB " + "L2 coalesced accesses.", d_ratio(d_hcoal, + d_hcoal + d_mcoal), "100%"), + Metric( + "dtlb_l2_coal_miss", + "DTLB L2 coalesced page (16kb) misses as percentage of all " + "DTLB L2 coalesced accesses.", + d_ratio(d_mcoal, d_hcoal + d_mcoal), "100%") + ]), + MetricGroup("dtlb_l2_2mb", [ + Metric( + "dtlb_l2_2mb_hits", + "DTLB L2 2mb page size hits as percentage of all DTLB L2 2mb " + "accesses.", d_ratio(d_h2m, d_h2m + d_m2m), "100%"), + Metric( + "dtlb_l2_2mb_miss", + "DTLB L2 2mb page size misses as percentage of all DTLB L2 " + "accesses.", d_ratio(d_m2m, d_h2m + d_m2m), "100%") + ]), + MetricGroup("dtlb_l2_1g", [ + Metric( + "dtlb_l2_1g_hits", + "DTLB L2 1gb page size hits as percentage of all DTLB L2 1gb " + "accesses.", d_ratio(d_h1g, d_h1g + d_m1g), "100%"), + Metric( + "dtlb_l2_1g_miss", + "DTLB L2 1gb page size misses as percentage of all DTLB L2 " + "1gb accesses.", d_ratio(d_m1g, d_h1g + d_m1g), "100%") + ]), + ]), + MetricGroup("dtlb_walks", [ + Metric("dtlb_walks_reqs", "DTLB page table walks per second.", + walks_r, "walks/s"), + ]), + ], description="Data TLB metrics") + + def AmdItlb(): global _zen_model l2h = Event("bp_l1_tlb_miss_l2_tlb_hit", "bp_l1_tlb_miss_l2_hit") @@ -386,6 +494,7 @@ def dir_path(path: str) -> str: all_metrics = MetricGroup("", [ AmdBr(), + AmdDtlb(), AmdItlb(), AmdHwpf(), AmdSwpf(), From faa02675d9de60503a80c15be0eaba5f3e52f617 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 9 Feb 2024 10:52:36 -0800 Subject: [PATCH 23/55] perf jevents: Add uncore l3 metric group for AMD Metrics use the amd_l3 PMU for access/miss/hit information. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/amd_metrics.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tools/perf/pmu-events/amd_metrics.py b/tools/perf/pmu-events/amd_metrics.py index 7fd2139766f9..af99da14d946 100755 --- a/tools/perf/pmu-events/amd_metrics.py +++ b/tools/perf/pmu-events/amd_metrics.py @@ -466,6 +466,22 @@ def Rapl() -> MetricGroup: return MetricGroup("cpu_power", metrics, description="Processor socket power consumption estimates") +def UncoreL3(): + acc = Event("l3_lookup_state.all_coherent_accesses_to_l3", + "l3_lookup_state.all_l3_req_typs") + miss = Event("l3_lookup_state.l3_miss", + "l3_comb_clstr_state.request_miss") + acc = max(acc, miss) + hits = acc - miss + + return MetricGroup("l3", [ + Metric("l3_accesses", "L3 victim cache accesses", + d_ratio(acc, interval_sec), "accesses/sec"), + Metric("l3_hits", "L3 victim cache hit rate", d_ratio(hits, acc), "100%"), + Metric("l3_miss", "L3 victim cache miss rate", d_ratio(miss, acc), + "100%"), + ], description="L3 cache breakdown per CCX") + def main() -> None: global _args @@ -501,6 +517,7 @@ def dir_path(path: str) -> str: AmdUpc(), Idle(), Rapl(), + UncoreL3(), ]) if _args.metricgroups: From 3009290da4991f72d0a2c6b4a6ba7f7c89b509d2 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 22 Feb 2024 10:22:44 -0800 Subject: [PATCH 24/55] perf jevents: Add load store breakdown metrics ldst for AMD Give breakdown of number of instructions. Use the counter mask (cmask) to show the number of cycles taken to retire the instructions. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/amd_metrics.py | 75 ++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/tools/perf/pmu-events/amd_metrics.py b/tools/perf/pmu-events/amd_metrics.py index af99da14d946..3f77d1bbf1c9 100755 --- a/tools/perf/pmu-events/amd_metrics.py +++ b/tools/perf/pmu-events/amd_metrics.py @@ -275,6 +275,80 @@ def AmdItlb(): ], description="Instruction TLB breakdown") +def AmdLdSt() -> MetricGroup: + ldst_ld = Event("ls_dispatch.ld_dispatch") + ldst_st = Event("ls_dispatch.store_dispatch") + ldst_ldc1 = Event(f"{ldst_ld}/cmask=1/") + ldst_stc1 = Event(f"{ldst_st}/cmask=1/") + ldst_ldc2 = Event(f"{ldst_ld}/cmask=2/") + ldst_stc2 = Event(f"{ldst_st}/cmask=2/") + ldst_ldc3 = Event(f"{ldst_ld}/cmask=3/") + ldst_stc3 = Event(f"{ldst_st}/cmask=3/") + ldst_cyc = Event("ls_not_halted_cyc") + + ld_rate = d_ratio(ldst_ld, interval_sec) + st_rate = d_ratio(ldst_st, interval_sec) + + ld_v1 = max(ldst_ldc1 - ldst_ldc2, 0) + ld_v2 = max(ldst_ldc2 - ldst_ldc3, 0) + ld_v3 = ldst_ldc3 + + st_v1 = max(ldst_stc1 - ldst_stc2, 0) + st_v2 = max(ldst_stc2 - ldst_stc3, 0) + st_v3 = ldst_stc3 + + return MetricGroup("ldst", [ + MetricGroup("ldst_total", [ + Metric("ldst_total_ld", "Number of loads dispatched per second.", + ld_rate, "insns/sec"), + Metric("ldst_total_st", "Number of stores dispatched per second.", + st_rate, "insns/sec"), + ]), + MetricGroup("ldst_percent_insn", [ + Metric("ldst_percent_insn_ld", + "Load instructions as a percentage of all instructions.", + d_ratio(ldst_ld, ins), "100%"), + Metric("ldst_percent_insn_st", + "Store instructions as a percentage of all instructions.", + d_ratio(ldst_st, ins), "100%"), + ]), + MetricGroup("ldst_ret_loads_per_cycle", [ + Metric( + "ldst_ret_loads_per_cycle_1", + "Load instructions retiring in 1 cycle as a percentage of all " + "unhalted cycles.", d_ratio(ld_v1, ldst_cyc), "100%"), + Metric( + "ldst_ret_loads_per_cycle_2", + "Load instructions retiring in 2 cycles as a percentage of all " + "unhalted cycles.", d_ratio(ld_v2, ldst_cyc), "100%"), + Metric( + "ldst_ret_loads_per_cycle_3", + "Load instructions retiring in 3 or more cycles as a percentage" + "of all unhalted cycles.", d_ratio(ld_v3, ldst_cyc), "100%"), + ]), + MetricGroup("ldst_ret_stores_per_cycle", [ + Metric( + "ldst_ret_stores_per_cycle_1", + "Store instructions retiring in 1 cycle as a percentage of all " + "unhalted cycles.", d_ratio(st_v1, ldst_cyc), "100%"), + Metric( + "ldst_ret_stores_per_cycle_2", + "Store instructions retiring in 2 cycles as a percentage of all " + "unhalted cycles.", d_ratio(st_v2, ldst_cyc), "100%"), + Metric( + "ldst_ret_stores_per_cycle_3", + "Store instructions retiring in 3 or more cycles as a percentage" + "of all unhalted cycles.", d_ratio(st_v3, ldst_cyc), "100%"), + ]), + MetricGroup("ldst_insn_bt", [ + Metric("ldst_insn_bt_ld", "Number of instructions between loads.", + d_ratio(ins, ldst_ld), "insns"), + Metric("ldst_insn_bt_st", "Number of instructions between stores.", + d_ratio(ins, ldst_st), "insns"), + ]) + ], description="Breakdown of load/store instructions") + + def AmdHwpf(): """Returns a MetricGroup representing AMD hardware prefetch metrics.""" global _zen_model @@ -512,6 +586,7 @@ def dir_path(path: str) -> str: AmdBr(), AmdDtlb(), AmdItlb(), + AmdLdSt(), AmdHwpf(), AmdSwpf(), AmdUpc(), From 8d4d2d375171257a7494ac2f04a5255040198de5 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 22 Feb 2024 09:49:56 -0800 Subject: [PATCH 25/55] perf jevents: Add ILP metrics for AMD Use the counter mask (cmask) to see how many cycles an instruction takes to retire. Present as a set of ILP metrics. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/amd_metrics.py | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tools/perf/pmu-events/amd_metrics.py b/tools/perf/pmu-events/amd_metrics.py index 3f77d1bbf1c9..157fa4fa3d62 100755 --- a/tools/perf/pmu-events/amd_metrics.py +++ b/tools/perf/pmu-events/amd_metrics.py @@ -120,6 +120,35 @@ def Far() -> MetricGroup: description="breakdown of retired branch instructions") +def AmdIlp() -> MetricGroup: + tsc = Event("msr/tsc/") + c0 = Event("msr/mperf/") + low = tsc - c0 + inst_ret = Event("ex_ret_instr") + inst_ret_c = [Event(f"{inst_ret.name}/cmask={x}/") for x in range(1, 6)] + ilp = [d_ratio(max(inst_ret_c[x] - inst_ret_c[x + 1], 0), cycles) for x in range(0, 4)] + ilp.append(d_ratio(inst_ret_c[4], cycles)) + ilp0 = 1 + for x in ilp: + ilp0 -= x + return MetricGroup("ilp", [ + Metric("ilp_idle", "Lower power cycles as a percentage of all cycles", + d_ratio(low, tsc), "100%"), + Metric("ilp_inst_ret_0", "Instructions retired in 0 cycles as a percentage of all cycles", + ilp0, "100%"), + Metric("ilp_inst_ret_1", "Instructions retired in 1 cycles as a percentage of all cycles", + ilp[0], "100%"), + Metric("ilp_inst_ret_2", "Instructions retired in 2 cycles as a percentage of all cycles", + ilp[1], "100%"), + Metric("ilp_inst_ret_3", "Instructions retired in 3 cycles as a percentage of all cycles", + ilp[2], "100%"), + Metric("ilp_inst_ret_4", "Instructions retired in 4 cycles as a percentage of all cycles", + ilp[3], "100%"), + Metric("ilp_inst_ret_5", "Instructions retired in 5 or more cycles as a percentage of all cycles", + ilp[4], "100%"), + ]) + + def AmdDtlb() -> Optional[MetricGroup]: global _zen_model if _zen_model >= 4: @@ -584,6 +613,7 @@ def dir_path(path: str) -> str: all_metrics = MetricGroup("", [ AmdBr(), + AmdIlp(), AmdDtlb(), AmdItlb(), AmdLdSt(), From a2c9024698567a232de55f2ac82193d1a1280274 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 22 Feb 2024 14:23:26 -0800 Subject: [PATCH 26/55] perf jevents: Add context switch metrics for AMD Metrics break down context switches for different kinds of instruction. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/amd_metrics.py | 32 ++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tools/perf/pmu-events/amd_metrics.py b/tools/perf/pmu-events/amd_metrics.py index 157fa4fa3d62..422b119553ff 100755 --- a/tools/perf/pmu-events/amd_metrics.py +++ b/tools/perf/pmu-events/amd_metrics.py @@ -120,6 +120,37 @@ def Far() -> MetricGroup: description="breakdown of retired branch instructions") +def AmdCtxSw() -> MetricGroup: + cs = Event("context\-switches") + metrics = [ + Metric("cs_rate", "Context switches per second", d_ratio(cs, interval_sec), "ctxsw/s") + ] + + ev = Event("instructions") + metrics.append(Metric("cs_instr", "Instructions per context switch", + d_ratio(ev, cs), "instr/cs")) + + ev = Event("cycles") + metrics.append(Metric("cs_cycles", "Cycles per context switch", + d_ratio(ev, cs), "cycles/cs")) + + ev = Event("ls_dispatch.ld_dispatch") + metrics.append(Metric("cs_loads", "Loads per context switch", + d_ratio(ev, cs), "loads/cs")) + + ev = Event("ls_dispatch.store_dispatch") + metrics.append(Metric("cs_stores", "Stores per context switch", + d_ratio(ev, cs), "stores/cs")) + + ev = Event("ex_ret_brn_tkn") + metrics.append(Metric("cs_br_taken", "Branches taken per context switch", + d_ratio(ev, cs), "br_taken/cs")) + + return MetricGroup("cs", metrics, + description = ("Number of context switches per second, instructions " + "retired & core cycles between context switches")) + + def AmdIlp() -> MetricGroup: tsc = Event("msr/tsc/") c0 = Event("msr/mperf/") @@ -613,6 +644,7 @@ def dir_path(path: str) -> str: all_metrics = MetricGroup("", [ AmdBr(), + AmdCtxSw(), AmdIlp(), AmdDtlb(), AmdItlb(), From 4a8aebf9ca176a3016bdcdda254590a58e1904cc Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 14 May 2024 13:55:49 -0700 Subject: [PATCH 27/55] perf jevents: Add uop cache hit/miss rates for AMD Add metrics giving ratio of uop cache hits to misses. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/amd_metrics.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tools/perf/pmu-events/amd_metrics.py b/tools/perf/pmu-events/amd_metrics.py index 422b119553ff..44fe82b15394 100755 --- a/tools/perf/pmu-events/amd_metrics.py +++ b/tools/perf/pmu-events/amd_metrics.py @@ -565,6 +565,23 @@ def AmdSwpf() -> Optional[MetricGroup]: description="Software prefetch breakdown (CCX L3 = L3 of current thread, Loc CCX = CCX cache on some socket)") +def AmdUopCache() -> Optional[MetricGroup]: + try: + op_cache_hit = Event("op_cache_hit_miss.op_cache_hit") + op_cache_miss = Event("op_cache_hit_miss.op_cache_miss") + except: + return None + op_cache_total = op_cache_hit + op_cache_miss + return MetricGroup("uop_cache", [ + Metric("uop_cache_hit_ratio", "Uop cache full or partial hits rate", + d_ratio(op_cache_hit, op_cache_total), + "100%"), + Metric("uop_cache_miss_ratio", "Uop cache misses rate", + d_ratio(op_cache_miss, op_cache_total), + "100%"), + ], description="Micro-op (uop) hit and miss rates.") + + def AmdUpc() -> Metric: ops = Event("ex_ret_ops", "ex_ret_cops") upc = d_ratio(ops, smt_cycles) @@ -651,6 +668,7 @@ def dir_path(path: str) -> str: AmdLdSt(), AmdHwpf(), AmdSwpf(), + AmdUopCache(), AmdUpc(), Idle(), Rapl(), From cffe38b24a55983672f6ed55207cc158c971b664 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 7 Feb 2024 22:02:37 -0800 Subject: [PATCH 28/55] perf jevents: Add RAPL metrics for all Intel models Add a 'cpu_power' metric group that computes the power consumption from RAPL events if they are present. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/intel_metrics.py | 45 ++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py index 04a19d05c6c1..58e23eb48312 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -1,13 +1,49 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) -from metric import (JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, LoadEvents, - MetricGroup) +from metric import (d_ratio, has_event, Event, JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, + LoadEvents, Metric, MetricGroup, Select) import argparse import json +import math import os # Global command line arguments. _args = None +interval_sec = Event("duration_time") + +def Rapl() -> MetricGroup: + """Processor power consumption estimate. + + Use events from the running average power limit (RAPL) driver. + """ + # Watts = joules/second + pkg = Event("power/energy\-pkg/") + cond_pkg = Select(pkg, has_event(pkg), math.nan) + cores = Event("power/energy\-cores/") + cond_cores = Select(cores, has_event(cores), math.nan) + ram = Event("power/energy\-ram/") + cond_ram = Select(ram, has_event(ram), math.nan) + gpu = Event("power/energy\-gpu/") + cond_gpu = Select(gpu, has_event(gpu), math.nan) + psys = Event("power/energy\-psys/") + cond_psys = Select(psys, has_event(psys), math.nan) + scale = 2.3283064365386962890625e-10 + metrics = [ + Metric("cpu_power_pkg", "", + d_ratio(cond_pkg * scale, interval_sec), "Watts"), + Metric("cpu_power_cores", "", + d_ratio(cond_cores * scale, interval_sec), "Watts"), + Metric("cpu_power_ram", "", + d_ratio(cond_ram * scale, interval_sec), "Watts"), + Metric("cpu_power_gpu", "", + d_ratio(cond_gpu * scale, interval_sec), "Watts"), + Metric("cpu_power_psys", "", + d_ratio(cond_psys * scale, interval_sec), "Watts"), + ] + + return MetricGroup("cpu_power", metrics, + description="Running Average Power Limit (RAPL) power consumption estimates") + def main() -> None: global _args @@ -31,7 +67,10 @@ def dir_path(path: str) -> str: directory = f"{_args.events_path}/x86/{_args.model}/" LoadEvents(directory) - all_metrics = MetricGroup("",[]) + all_metrics = MetricGroup("", [ + Rapl(), + ]) + if _args.metricgroups: print(JsonEncodeMetricGroupDescriptions(all_metrics)) From 75ffafe6cd9842dd7bb15ae7fb964b25ad5cae7d Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 7 Feb 2024 22:21:02 -0800 Subject: [PATCH 29/55] perf jevents: Add idle metric for Intel models Compute using the msr PMU the percentage of wallclock cycles where the CPUs are in a low power state. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/intel_metrics.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py index 58e23eb48312..f875eb844c78 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -1,7 +1,8 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) -from metric import (d_ratio, has_event, Event, JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, - LoadEvents, Metric, MetricGroup, Select) +from metric import (d_ratio, has_event, max, Event, JsonEncodeMetric, + JsonEncodeMetricGroupDescriptions, LoadEvents, Metric, + MetricGroup, Select) import argparse import json import math @@ -11,6 +12,16 @@ _args = None interval_sec = Event("duration_time") +def Idle() -> Metric: + cyc = Event("msr/mperf/") + tsc = Event("msr/tsc/") + low = max(tsc - cyc, 0) + return Metric( + "idle", + "Percentage of total wallclock cycles where CPUs are in low power state (C1 or deeper sleep state)", + d_ratio(low, tsc), "100%") + + def Rapl() -> MetricGroup: """Processor power consumption estimate. @@ -68,6 +79,7 @@ def dir_path(path: str) -> str: LoadEvents(directory) all_metrics = MetricGroup("", [ + Idle(), Rapl(), ]) From d2cb809128472aa1bd802c7c6b3c38827b8bd206 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 8 Feb 2024 13:56:23 -0800 Subject: [PATCH 30/55] perf jevents: Add smi metric group for Intel models Allow duplicated metric to be dropped from json files. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/intel_metrics.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py index f875eb844c78..f34b4230a4ee 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -2,7 +2,7 @@ # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) from metric import (d_ratio, has_event, max, Event, JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, LoadEvents, Metric, - MetricGroup, Select) + MetricGroup, MetricRef, Select) import argparse import json import math @@ -56,6 +56,24 @@ def Rapl() -> MetricGroup: description="Running Average Power Limit (RAPL) power consumption estimates") +def Smi() -> MetricGroup: + aperf = Event('msr/aperf/') + cycles = Event('cycles') + smi_num = Event('msr/smi/') + smi_cycles = Select(Select((aperf - cycles) / aperf, smi_num > 0, 0), + has_event(aperf), + 0) + return MetricGroup('smi', [ + Metric('smi_num', 'Number of SMI interrupts.', + Select(smi_num, has_event(smi_num), 0), 'SMI#'), + # Note, the smi_cycles "Event" is really a reference to the metric. + Metric('smi_cycles', + 'Percentage of cycles spent in System Management Interrupts. ' + 'Requires /sys/devices/cpu/freeze_on_smi to be 1.', + smi_cycles, '100%', threshold=(MetricRef('smi_cycles') > 0.10)) + ], description = 'System Management Interrupt metrics') + + def main() -> None: global _args @@ -81,6 +99,7 @@ def dir_path(path: str) -> str: all_metrics = MetricGroup("", [ Idle(), Rapl(), + Smi(), ]) From f77d15a718c3f3dfa37c822426db2059554048ea Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 29 Feb 2024 17:48:41 -0800 Subject: [PATCH 31/55] perf jevents: Add CheckPmu to see if a PMU is in loaded json events CheckPmu can be used to determine if hybrid events are present, allowing for hybrid conditional metrics/events/pmus to be premised on the json files rather than hard coded tables. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/metric.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/perf/pmu-events/metric.py b/tools/perf/pmu-events/metric.py index 03312cd6d491..e1847cccfdb0 100644 --- a/tools/perf/pmu-events/metric.py +++ b/tools/perf/pmu-events/metric.py @@ -8,10 +8,12 @@ from enum import Enum from typing import Dict, List, Optional, Set, Tuple, Union +all_pmus = set() all_events = set() def LoadEvents(directory: str) -> None: """Populate a global set of all known events for the purpose of validating Event names""" + global all_pmus global all_events all_events = { "context\-switches", @@ -24,12 +26,18 @@ def LoadEvents(directory: str) -> None: filename = os.fsdecode(file) if filename.endswith(".json"): for x in json.load(open(f"{directory}/{filename}")): + if "Unit" in x: + all_pmus.add(x["Unit"]) if "EventName" in x: all_events.add(x["EventName"]) elif "ArchStdEvent" in x: all_events.add(x["ArchStdEvent"]) +def CheckPmu(name: str) -> bool: + return name in all_pmus + + def CheckEvent(name: str) -> bool: """Check the event name exists in the set of all loaded events""" global all_events From ca71931818573e319ca16db561376c8efa4746b1 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 26 Sep 2024 09:41:34 -0700 Subject: [PATCH 32/55] perf jevents: Mark metrics with experimental events as experimental When metrics are made with experimental events it is desirable the metric description also carries this information in case of metric inaccuracies. Suggested-by: Perry Taylor --- tools/perf/pmu-events/metric.py | 44 +++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tools/perf/pmu-events/metric.py b/tools/perf/pmu-events/metric.py index e1847cccfdb0..5a5b149dd286 100644 --- a/tools/perf/pmu-events/metric.py +++ b/tools/perf/pmu-events/metric.py @@ -10,11 +10,13 @@ all_pmus = set() all_events = set() +experimental_events = set() def LoadEvents(directory: str) -> None: """Populate a global set of all known events for the purpose of validating Event names""" global all_pmus global all_events + global experimental_events all_events = { "context\-switches", "cycles", @@ -30,6 +32,8 @@ def LoadEvents(directory: str) -> None: all_pmus.add(x["Unit"]) if "EventName" in x: all_events.add(x["EventName"]) + if "Experimental" in x and x["Experimental"] == "1": + experimental_events.add(x["EventName"]) elif "ArchStdEvent" in x: all_events.add(x["ArchStdEvent"]) @@ -55,6 +59,18 @@ def CheckEvent(name: str) -> bool: return name in all_events +def IsExperimentalEvent(name: str) -> bool: + global experimental_events + if ':' in name: + # Remove trailing modifier. + name = name[:name.find(':')] + elif '/' in name: + # Name could begin with a PMU or an event, for now assume it is not experimental. + return False + + return name in experimental_events + + class MetricConstraint(Enum): GROUPED_EVENTS = 0 NO_GROUP_EVENTS = 1 @@ -76,6 +92,10 @@ def Simplify(self): """Returns a simplified version of self.""" raise NotImplementedError() + def HasExperimentalEvents(self) -> bool: + """Are experimental events used in the expression?""" + raise NotImplementedError() + def Equals(self, other) -> bool: """Returns true when two expressions are the same.""" raise NotImplementedError() @@ -243,6 +263,9 @@ def Simplify(self) -> Expression: return Operator(self.operator, lhs, rhs) + def HasExperimentalEvents(self) -> bool: + return self.lhs.HasExperimentalEvents() or self.rhs.HasExperimentalEvents() + def Equals(self, other: Expression) -> bool: if isinstance(other, Operator): return self.operator == other.operator and self.lhs.Equals( @@ -291,6 +314,10 @@ def Simplify(self) -> Expression: return Select(true_val, cond, false_val) + def HasExperimentalEvents(self) -> bool: + return (self.cond.HasExperimentalEvents() or self.true_val.HasExperimentalEvents() or + self.false_val.HasExperimentalEvents()) + def Equals(self, other: Expression) -> bool: if isinstance(other, Select): return self.cond.Equals(other.cond) and self.false_val.Equals( @@ -339,6 +366,9 @@ def Simplify(self) -> Expression: return Function(self.fn, lhs, rhs) + def HasExperimentalEvents(self) -> bool: + return self.lhs.HasExperimentalEvents() or (self.rhs and self.rhs.HasExperimentalEvents()) + def Equals(self, other: Expression) -> bool: if isinstance(other, Function): result = self.fn == other.fn and self.lhs.Equals(other.lhs) @@ -378,6 +408,9 @@ def __init__(self, *args: str): global all_events raise Exception(f"No event {error} in:\n{all_events}") + def HasExperimentalEvents(self) -> bool: + return IsExperimentalEvent(self.name) + def ToPerfJson(self): result = re.sub('/', '@', self.name) return result @@ -410,6 +443,9 @@ def ToPython(self): def Simplify(self) -> Expression: return self + def HasExperimentalEvents(self) -> bool: + return False + def Equals(self, other: Expression) -> bool: return isinstance(other, MetricRef) and self.name == other.name @@ -437,6 +473,9 @@ def ToPython(self): def Simplify(self) -> Expression: return self + def HasExperimentalEvents(self) -> bool: + return False + def Equals(self, other: Expression) -> bool: return isinstance(other, Constant) and self.value == other.value @@ -459,6 +498,9 @@ def ToPython(self): def Simplify(self) -> Expression: return self + def HasExperimentalEvents(self) -> bool: + return False + def Equals(self, other: Expression) -> bool: return isinstance(other, Literal) and self.value == other.value @@ -521,6 +563,8 @@ def __init__(self, self.name = name self.description = description self.expr = expr.Simplify() + if self.expr.HasExperimentalEvents(): + self.description += " (metric should be considered experimental as it contains experimental events)." # Workraound valid_only_metric hiding certain metrics based on unit. scale_unit = scale_unit.replace('/sec', ' per sec') if scale_unit[0].isdigit(): From 5c1f4ae0a535f858ed3802968da2f164aaa30b0a Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 8 Feb 2024 14:26:32 -0800 Subject: [PATCH 33/55] perf jevents: Add tsx metric group for Intel models Allow duplicated metric to be dropped from json files. Detect when TSX is supported by a model by using the json events, use sysfs events at runtime as hypervisors, etc. may disable TSX. Add CheckPmu to metric to determine if which PMUs have been associated with the loaded events. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/intel_metrics.py | 52 +++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py index f34b4230a4ee..58e243695f0a 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -1,12 +1,13 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) -from metric import (d_ratio, has_event, max, Event, JsonEncodeMetric, +from metric import (d_ratio, has_event, max, CheckPmu, Event, JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, LoadEvents, Metric, MetricGroup, MetricRef, Select) import argparse import json import math import os +from typing import Optional # Global command line arguments. _args = None @@ -74,6 +75,54 @@ def Smi() -> MetricGroup: ], description = 'System Management Interrupt metrics') +def Tsx() -> Optional[MetricGroup]: + pmu = "cpu_core" if CheckPmu("cpu_core") else "cpu" + cycles = Event('cycles') + cycles_in_tx = Event(f'{pmu}/cycles\-t/') + cycles_in_tx_cp = Event(f'{pmu}/cycles\-ct/') + try: + # Test if the tsx event is present in the json, prefer the + # sysfs version so that we can detect its presence at runtime. + transaction_start = Event("RTM_RETIRED.START") + transaction_start = Event(f'{pmu}/tx\-start/') + except: + return None + + elision_start = None + try: + # Elision start isn't supported by all models, but we'll not + # generate the tsx_cycles_per_elision metric in that + # case. Again, prefer the sysfs encoding of the event. + elision_start = Event("HLE_RETIRED.START") + elision_start = Event(f'{pmu}/el\-start/') + except: + pass + + return MetricGroup('transaction', [ + Metric('tsx_transactional_cycles', + 'Percentage of cycles within a transaction region.', + Select(cycles_in_tx / cycles, has_event(cycles_in_tx), 0), + '100%'), + Metric('tsx_aborted_cycles', 'Percentage of cycles in aborted transactions.', + Select(max(cycles_in_tx - cycles_in_tx_cp, 0) / cycles, + has_event(cycles_in_tx), + 0), + '100%'), + Metric('tsx_cycles_per_transaction', + 'Number of cycles within a transaction divided by the number of transactions.', + Select(cycles_in_tx / transaction_start, + has_event(cycles_in_tx), + 0), + "cycles / transaction"), + Metric('tsx_cycles_per_elision', + 'Number of cycles within a transaction divided by the number of elisions.', + Select(cycles_in_tx / elision_start, + has_event(elision_start), + 0), + "cycles / elision") if elision_start else None, + ], description="Breakdown of transactional memory statistics") + + def main() -> None: global _args @@ -100,6 +149,7 @@ def dir_path(path: str) -> str: Idle(), Rapl(), Smi(), + Tsx(), ]) From 3c00be570ae20d73f654105419c847259cd01ab9 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Feb 2024 18:49:20 -0800 Subject: [PATCH 34/55] perf jevents: Add br metric group for branch statistics on Intel The br metric group for branches itself comprises metric groups for total, taken, conditional, fused and far metric groups using json events. Conditional taken and not taken metrics are specific to Icelake and later generations, so the presence of the event is used to determine whether the metric should exist. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/intel_metrics.py | 138 +++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py index 58e243695f0a..09f7b7159e7c 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -123,6 +123,143 @@ def Tsx() -> Optional[MetricGroup]: ], description="Breakdown of transactional memory statistics") +def IntelBr(): + ins = Event("instructions") + + def Total() -> MetricGroup: + br_all = Event ("BR_INST_RETIRED.ALL_BRANCHES", "BR_INST_RETIRED.ANY") + br_m_all = Event("BR_MISP_RETIRED.ALL_BRANCHES", + "BR_INST_RETIRED.MISPRED", + "BR_MISP_EXEC.ANY") + br_clr = None + try: + br_clr = Event("BACLEARS.ANY", "BACLEARS.ALL") + except: + pass + + br_r = d_ratio(br_all, interval_sec) + ins_r = d_ratio(ins, br_all) + misp_r = d_ratio(br_m_all, br_all) + clr_r = d_ratio(br_clr, interval_sec) if br_clr else None + + return MetricGroup("br_total", [ + Metric("br_total_retired", + "The number of branch instructions retired per second.", br_r, + "insn/s"), + Metric( + "br_total_mispred", + "The number of branch instructions retired, of any type, that were " + "not correctly predicted as a percentage of all branch instrucions.", + misp_r, "100%"), + Metric("br_total_insn_between_branches", + "The number of instructions divided by the number of branches.", + ins_r, "insn"), + Metric("br_total_insn_fe_resteers", + "The number of resync branches per second.", clr_r, "req/s" + ) if clr_r else None + ]) + + def Taken() -> MetricGroup: + br_all = Event("BR_INST_RETIRED.ALL_BRANCHES", "BR_INST_RETIRED.ANY") + br_m_tk = None + try: + br_m_tk = Event("BR_MISP_RETIRED.NEAR_TAKEN", + "BR_MISP_RETIRED.TAKEN_JCC", + "BR_INST_RETIRED.MISPRED_TAKEN") + except: + pass + br_r = d_ratio(br_all, interval_sec) + ins_r = d_ratio(ins, br_all) + misp_r = d_ratio(br_m_tk, br_all) if br_m_tk else None + return MetricGroup("br_taken", [ + Metric("br_taken_retired", + "The number of taken branches that were retired per second.", + br_r, "insn/s"), + Metric( + "br_taken_mispred", + "The number of retired taken branch instructions that were " + "mispredicted as a percentage of all taken branches.", misp_r, + "100%") if misp_r else None, + Metric( + "br_taken_insn_between_branches", + "The number of instructions divided by the number of taken branches.", + ins_r, "insn"), + ]) + + def Conditional() -> Optional[MetricGroup]: + try: + br_cond = Event("BR_INST_RETIRED.COND", + "BR_INST_RETIRED.CONDITIONAL", + "BR_INST_RETIRED.TAKEN_JCC") + br_m_cond = Event("BR_MISP_RETIRED.COND", + "BR_MISP_RETIRED.CONDITIONAL", + "BR_MISP_RETIRED.TAKEN_JCC") + except: + return None + + br_cond_nt = None + br_m_cond_nt = None + try: + br_cond_nt = Event("BR_INST_RETIRED.COND_NTAKEN") + br_m_cond_nt = Event("BR_MISP_RETIRED.COND_NTAKEN") + except: + pass + br_r = d_ratio(br_cond, interval_sec) + ins_r = d_ratio(ins, br_cond) + misp_r = d_ratio(br_m_cond, br_cond) + taken_metrics = [ + Metric("br_cond_retired", "Retired conditional branch instructions.", + br_r, "insn/s"), + Metric("br_cond_insn_between_branches", + "The number of instructions divided by the number of conditional " + "branches.", ins_r, "insn"), + Metric("br_cond_mispred", + "Retired conditional branch instructions mispredicted as a " + "percentage of all conditional branches.", misp_r, "100%"), + ] + if not br_m_cond_nt: + return MetricGroup("br_cond", taken_metrics) + + br_r = d_ratio(br_cond_nt, interval_sec) + ins_r = d_ratio(ins, br_cond_nt) + misp_r = d_ratio(br_m_cond_nt, br_cond_nt) + + not_taken_metrics = [ + Metric("br_cond_retired", "Retired conditional not taken branch instructions.", + br_r, "insn/s"), + Metric("br_cond_insn_between_branches", + "The number of instructions divided by the number of not taken conditional " + "branches.", ins_r, "insn"), + Metric("br_cond_mispred", + "Retired not taken conditional branch instructions mispredicted as a " + "percentage of all not taken conditional branches.", misp_r, "100%"), + ] + return MetricGroup("br_cond", [ + MetricGroup("br_cond_nt", not_taken_metrics), + MetricGroup("br_cond_tkn", taken_metrics), + ]) + + def Far() -> Optional[MetricGroup]: + try: + br_far = Event("BR_INST_RETIRED.FAR_BRANCH") + except: + return None + + br_r = d_ratio(br_far, interval_sec) + ins_r = d_ratio(ins, br_far) + return MetricGroup("br_far", [ + Metric("br_far_retired", "Retired far control transfers per second.", + br_r, "insn/s"), + Metric( + "br_far_insn_between_branches", + "The number of instructions divided by the number of far branches.", + ins_r, "insn"), + ]) + + return MetricGroup("br", [Total(), Taken(), Conditional(), Far()], + description="breakdown of retired branch instructions") + + def main() -> None: global _args @@ -150,6 +287,7 @@ def dir_path(path: str) -> str: Rapl(), Smi(), Tsx(), + IntelBr(), ]) From 00cd1894c95a3569cac5e38ab3c4379b3186b1b7 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 23 Feb 2024 07:42:03 -0800 Subject: [PATCH 35/55] perf jevents: Add software prefetch (swpf) metric group for Intel Add metrics that breakdown software prefetch instruction use. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/intel_metrics.py | 65 ++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py index 09f7b7159e7c..f4707e964f75 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -260,6 +260,70 @@ def Far() -> Optional[MetricGroup]: description="breakdown of retired branch instructions") +def IntelSwpf() -> Optional[MetricGroup]: + ins = Event("instructions") + try: + s_ld = Event("MEM_INST_RETIRED.ALL_LOADS", "MEM_UOPS_RETIRED.ALL_LOADS") + s_nta = Event("SW_PREFETCH_ACCESS.NTA") + s_t0 = Event("SW_PREFETCH_ACCESS.T0") + s_t1 = Event("SW_PREFETCH_ACCESS.T1_T2") + s_w = Event("SW_PREFETCH_ACCESS.PREFETCHW") + except: + return None + + all_sw = s_nta + s_t0 + s_t1 + s_w + swp_r = d_ratio(all_sw, interval_sec) + ins_r = d_ratio(ins, all_sw) + ld_r = d_ratio(s_ld, all_sw) + + return MetricGroup("swpf", [ + MetricGroup("swpf_totals", [ + Metric("swpf_totals_exec", "Software prefetch instructions per second", + swp_r, "swpf/s"), + Metric("swpf_totals_insn_per_pf", + "Average number of instructions between software prefetches", + ins_r, "insn/swpf"), + Metric("swpf_totals_loads_per_pf", + "Average number of loads between software prefetches", + ld_r, "loads/swpf"), + ]), + MetricGroup("swpf_bkdwn", [ + MetricGroup("swpf_bkdwn_nta", [ + Metric("swpf_bkdwn_nta_per_swpf", + "Software prefetch NTA instructions as a percent of all prefetch instructions", + d_ratio(s_nta, all_sw), "100%"), + Metric("swpf_bkdwn_nta_rate", + "Software prefetch NTA instructions per second", + d_ratio(s_nta, interval_sec), "insn/s"), + ]), + MetricGroup("swpf_bkdwn_t0", [ + Metric("swpf_bkdwn_t0_per_swpf", + "Software prefetch T0 instructions as a percent of all prefetch instructions", + d_ratio(s_t0, all_sw), "100%"), + Metric("swpf_bkdwn_t0_rate", + "Software prefetch T0 instructions per second", + d_ratio(s_t0, interval_sec), "insn/s"), + ]), + MetricGroup("swpf_bkdwn_t1_t2", [ + Metric("swpf_bkdwn_t1_t2_per_swpf", + "Software prefetch T1 or T2 instructions as a percent of all prefetch instructions", + d_ratio(s_t1, all_sw), "100%"), + Metric("swpf_bkdwn_t1_t2_rate", + "Software prefetch T1 or T2 instructions per second", + d_ratio(s_t1, interval_sec), "insn/s"), + ]), + MetricGroup("swpf_bkdwn_w", [ + Metric("swpf_bkdwn_w_per_swpf", + "Software prefetch W instructions as a percent of all prefetch instructions", + d_ratio(s_w, all_sw), "100%"), + Metric("swpf_bkdwn_w_rate", + "Software prefetch W instructions per second", + d_ratio(s_w, interval_sec), "insn/s"), + ]), + ]), + ], description="Software prefetch instruction breakdown") + + def main() -> None: global _args @@ -288,6 +352,7 @@ def dir_path(path: str) -> str: Smi(), Tsx(), IntelBr(), + IntelSwpf(), ]) From 977822759c968a5d53d50b07d27c2f4751790ca5 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Feb 2024 20:49:01 -0800 Subject: [PATCH 36/55] perf jevents: Add ports metric group giving utilization on Intel The ports metric group contains a metric for each port giving its utilization as a ratio of cycles. The metrics are created by looking for UOPS_DISPATCHED.PORT events. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/intel_metrics.py | 33 ++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py index f4707e964f75..3ef4eb868580 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -1,12 +1,13 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) from metric import (d_ratio, has_event, max, CheckPmu, Event, JsonEncodeMetric, - JsonEncodeMetricGroupDescriptions, LoadEvents, Metric, - MetricGroup, MetricRef, Select) + JsonEncodeMetricGroupDescriptions, Literal, LoadEvents, + Metric, MetricGroup, MetricRef, Select) import argparse import json import math import os +import re from typing import Optional # Global command line arguments. @@ -260,6 +261,33 @@ def Far() -> Optional[MetricGroup]: description="breakdown of retired branch instructions") +def IntelPorts() -> Optional[MetricGroup]: + pipeline_events = json.load(open(f"{_args.events_path}/x86/{_args.model}/pipeline.json")) + + core_cycles = Event("CPU_CLK_UNHALTED.THREAD_P_ANY", + "CPU_CLK_UNHALTED.DISTRIBUTED", + "cycles") + # Number of CPU cycles scaled for SMT. + smt_cycles = Select(core_cycles / 2, Literal("#smt_on"), core_cycles) + + metrics = [] + for x in pipeline_events: + if "EventName" in x and re.search("^UOPS_DISPATCHED.PORT", x["EventName"]): + name = x["EventName"] + port = re.search(r"(PORT_[0-9].*)", name).group(0).lower() + if name.endswith("_CORE"): + cyc = core_cycles + else: + cyc = smt_cycles + metrics.append(Metric(port, f"{port} utilization (higher is better)", + d_ratio(Event(name), cyc), "100%")) + if len(metrics) == 0: + return None + + return MetricGroup("ports", metrics, "functional unit (port) utilization -- " + "fraction of cycles each port is utilized (higher is better)") + + def IntelSwpf() -> Optional[MetricGroup]: ins = Event("instructions") try: @@ -352,6 +380,7 @@ def dir_path(path: str) -> str: Smi(), Tsx(), IntelBr(), + IntelPorts(), IntelSwpf(), ]) From 51fec5c29496cd97649966a3802f7f5503fe2dec Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 20 Feb 2024 17:58:42 -0800 Subject: [PATCH 37/55] perf jevents: Add L2 metrics for Intel Give a breakdown of various L2 counters as metrics, including totals, reads, hardware prefetcher, RFO, code and evictions. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/intel_metrics.py | 158 +++++++++++++++++++++++++ 1 file changed, 158 insertions(+) diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py index 3ef4eb868580..4ddc68006b10 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -261,6 +261,163 @@ def Far() -> Optional[MetricGroup]: description="breakdown of retired branch instructions") +def IntelL2() -> Optional[MetricGroup]: + try: + DC_HIT = Event("L2_RQSTS.DEMAND_DATA_RD_HIT") + except: + return None + try: + DC_MISS = Event("L2_RQSTS.DEMAND_DATA_RD_MISS") + l2_dmnd_miss = DC_MISS + l2_dmnd_rd_all = DC_MISS + DC_HIT + except: + DC_ALL = Event("L2_RQSTS.ALL_DEMAND_DATA_RD") + l2_dmnd_miss = DC_ALL - DC_HIT + l2_dmnd_rd_all = DC_ALL + l2_dmnd_mrate = d_ratio(l2_dmnd_miss, interval_sec) + l2_dmnd_rrate = d_ratio(l2_dmnd_rd_all, interval_sec) + + DC_PFH = None + DC_PFM = None + l2_pf_all = None + l2_pf_mrate = None + l2_pf_rrate = None + try: + DC_PFH = Event("L2_RQSTS.PF_HIT") + DC_PFM = Event("L2_RQSTS.PF_MISS") + l2_pf_all = DC_PFH + DC_PFM + l2_pf_mrate = d_ratio(DC_PFM, interval_sec) + l2_pf_rrate = d_ratio(l2_pf_all, interval_sec) + except: + pass + + DC_RFOH = Event("L2_RQSTS.RFO_HIT") + DC_RFOM = Event("L2_RQSTS.RFO_MISS") + l2_rfo_all = DC_RFOH + DC_RFOM + l2_rfo_mrate = d_ratio(DC_RFOM, interval_sec) + l2_rfo_rrate = d_ratio(l2_rfo_all, interval_sec) + + DC_CH = Event("L2_RQSTS.CODE_RD_HIT") + DC_CM = Event("L2_RQSTS.CODE_RD_MISS") + DC_IN = Event("L2_LINES_IN.ALL") + DC_OUT_NS = None + DC_OUT_S = None + l2_lines_out = None + l2_out_rate = None + wbn = None + isd = None + try: + DC_OUT_NS = Event("L2_LINES_OUT.NON_SILENT", + "L2_LINES_OUT.DEMAND_DIRTY", + "L2_LINES_IN.S") + DC_OUT_S = Event("L2_LINES_OUT.SILENT", + "L2_LINES_OUT.DEMAND_CLEAN", + "L2_LINES_IN.I") + if DC_OUT_S.name == "L2_LINES_OUT.SILENT" and ( + args.model.startswith("skylake") or + args.model == "cascadelakex"): + DC_OUT_S.name = "L2_LINES_OUT.SILENT/any/" + # bring is back to per-CPU + l2_s = Select(DC_OUT_S / 2, Literal("#smt_on"), DC_OUT_S) + l2_ns = DC_OUT_NS + l2_lines_out = l2_s + l2_ns; + l2_out_rate = d_ratio(l2_lines_out, interval_sec); + nlr = max(l2_ns - DC_WB_U - DC_WB_D, 0) + wbn = d_ratio(nlr, interval_sec) + isd = d_ratio(l2_s, interval_sec) + except: + pass + DC_OUT_U = None + l2_pf_useless = None + l2_useless_rate = None + try: + DC_OUT_U = Event("L2_LINES_OUT.USELESS_HWPF") + l2_pf_useless = DC_OUT_U + l2_useless_rate = d_ratio(l2_pf_useless, interval_sec) + except: + pass + DC_WB_U = None + DC_WB_D = None + wbu = None + wbd = None + try: + DC_WB_U = Event("IDI_MISC.WB_UPGRADE") + DC_WB_D = Event("IDI_MISC.WB_DOWNGRADE") + wbu = d_ratio(DC_WB_U, interval_sec) + wbd = d_ratio(DC_WB_D, interval_sec) + except: + pass + + l2_lines_in = DC_IN + l2_code_all = DC_CH + DC_CM + l2_code_rate = d_ratio(l2_code_all, interval_sec) + l2_code_miss_rate = d_ratio(DC_CM, interval_sec) + l2_in_rate = d_ratio(l2_lines_in, interval_sec) + + return MetricGroup("l2", [ + MetricGroup("l2_totals", [ + Metric("l2_totals_in", "L2 cache total in per second", + l2_in_rate, "In/s"), + Metric("l2_totals_out", "L2 cache total out per second", + l2_out_rate, "Out/s") if l2_out_rate else None, + ]), + MetricGroup("l2_rd", [ + Metric("l2_rd_hits", "L2 cache data read hits", + d_ratio(DC_HIT, l2_dmnd_rd_all), "100%"), + Metric("l2_rd_hits", "L2 cache data read hits", + d_ratio(l2_dmnd_miss, l2_dmnd_rd_all), "100%"), + Metric("l2_rd_requests", "L2 cache data read requests per second", + l2_dmnd_rrate, "requests/s"), + Metric("l2_rd_misses", "L2 cache data read misses per second", + l2_dmnd_mrate, "misses/s"), + ]), + MetricGroup("l2_hwpf", [ + Metric("l2_hwpf_hits", "L2 cache hardware prefetcher hits", + d_ratio(DC_PFH, l2_pf_all), "100%"), + Metric("l2_hwpf_misses", "L2 cache hardware prefetcher misses", + d_ratio(DC_PFM, l2_pf_all), "100%"), + Metric("l2_hwpf_useless", "L2 cache hardware prefetcher useless prefetches per second", + l2_useless_rate, "100%") if l2_useless_rate else None, + Metric("l2_hwpf_requests", "L2 cache hardware prefetcher requests per second", + l2_pf_rrate, "100%"), + Metric("l2_hwpf_misses", "L2 cache hardware prefetcher misses per second", + l2_pf_mrate, "100%"), + ]) if DC_PFH else None, + MetricGroup("l2_rfo", [ + Metric("l2_rfo_hits", "L2 cache request for ownership (RFO) hits", + d_ratio(DC_RFOH, l2_rfo_all), "100%"), + Metric("l2_rfo_misses", "L2 cache request for ownership (RFO) misses", + d_ratio(DC_RFOM, l2_rfo_all), "100%"), + Metric("l2_rfo_requests", "L2 cache request for ownership (RFO) requests per second", + l2_rfo_rrate, "requests/s"), + Metric("l2_rfo_misses", "L2 cache request for ownership (RFO) misses per second", + l2_rfo_mrate, "misses/s"), + ]), + MetricGroup("l2_code", [ + Metric("l2_code_hits", "L2 cache code hits", + d_ratio(DC_CH, l2_code_all), "100%"), + Metric("l2_code_misses", "L2 cache code misses", + d_ratio(DC_CM, l2_code_all), "100%"), + Metric("l2_code_requests", "L2 cache code requests per second", + l2_code_rate, "requests/s"), + Metric("l2_code_misses", "L2 cache code misses per second", + l2_code_miss_rate, "misses/s"), + ]), + MetricGroup("l2_evict", [ + MetricGroup("l2_evict_mef_lines", [ + Metric("l2_evict_mef_lines_l3_hot_lru", "L2 evictions M/E/F lines L3 hot LRU per second", + wbu, "HotLRU/s") if wbu else None, + Metric("l2_evict_mef_lines_l3_norm_lru", "L2 evictions M/E/F lines L3 normal LRU per second", + wbn, "NormLRU/s") if wbn else None, + Metric("l2_evict_mef_lines_dropped", "L2 evictions M/E/F lines dropped per second", + wbd, "dropped/s") if wbd else None, + Metric("l2_evict_is_lines_dropped", "L2 evictions I/S lines dropped per second", + isd, "dropped/s") if isd else None, + ]), + ]), + ], description = "L2 data cache analysis") + + def IntelPorts() -> Optional[MetricGroup]: pipeline_events = json.load(open(f"{_args.events_path}/x86/{_args.model}/pipeline.json")) @@ -380,6 +537,7 @@ def dir_path(path: str) -> str: Smi(), Tsx(), IntelBr(), + IntelL2(), IntelPorts(), IntelSwpf(), ]) From bcc5808bb0f94cdae25109a854fe22a0122e879b Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 21 Feb 2024 23:12:06 -0800 Subject: [PATCH 38/55] perf jevents: Add load store breakdown metrics ldst for Intel Give breakdown of number of instructions. Use the counter mask (cmask) to show the number of cycles taken to retire the instructions. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/intel_metrics.py | 86 +++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py index 4ddc68006b10..d528b97e8822 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -2,7 +2,7 @@ # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) from metric import (d_ratio, has_event, max, CheckPmu, Event, JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, Literal, LoadEvents, - Metric, MetricGroup, MetricRef, Select) + Metric, MetricConstraint, MetricGroup, MetricRef, Select) import argparse import json import math @@ -509,6 +509,89 @@ def IntelSwpf() -> Optional[MetricGroup]: ], description="Software prefetch instruction breakdown") +def IntelLdSt() -> Optional[MetricGroup]: + if _args.model in [ + "bonnell", + "nehalemep", + "nehalemex", + "westmereep-dp", + "westmereep-sp", + "westmereex", + ]: + return None + LDST_LD = Event("MEM_INST_RETIRED.ALL_LOADS", "MEM_UOPS_RETIRED.ALL_LOADS") + LDST_ST = Event("MEM_INST_RETIRED.ALL_STORES", "MEM_UOPS_RETIRED.ALL_STORES") + LDST_LDC1 = Event(f"{LDST_LD.name}/cmask=1/") + LDST_STC1 = Event(f"{LDST_ST.name}/cmask=1/") + LDST_LDC2 = Event(f"{LDST_LD.name}/cmask=2/") + LDST_STC2 = Event(f"{LDST_ST.name}/cmask=2/") + LDST_LDC3 = Event(f"{LDST_LD.name}/cmask=3/") + LDST_STC3 = Event(f"{LDST_ST.name}/cmask=3/") + ins = Event("instructions") + LDST_CYC = Event("CPU_CLK_UNHALTED.THREAD", + "CPU_CLK_UNHALTED.CORE_P", + "CPU_CLK_UNHALTED.THREAD_P") + LDST_PRE = None + try: + LDST_PRE = Event("LOAD_HIT_PREFETCH.SWPF", "LOAD_HIT_PRE.SW_PF") + except: + pass + LDST_AT = None + try: + LDST_AT = Event("MEM_INST_RETIRED.LOCK_LOADS") + except: + pass + cyc = LDST_CYC + + ld_rate = d_ratio(LDST_LD, interval_sec) + st_rate = d_ratio(LDST_ST, interval_sec) + pf_rate = d_ratio(LDST_PRE, interval_sec) if LDST_PRE else None + at_rate = d_ratio(LDST_AT, interval_sec) if LDST_AT else None + + ldst_ret_constraint = MetricConstraint.GROUPED_EVENTS + if LDST_LD.name == "MEM_UOPS_RETIRED.ALL_LOADS": + ldst_ret_constraint = MetricConstraint.NO_GROUP_EVENTS_NMI + + return MetricGroup("ldst", [ + MetricGroup("ldst_total", [ + Metric("ldst_total_loads", "Load/store instructions total loads", + ld_rate, "loads"), + Metric("ldst_total_stores", "Load/store instructions total stores", + st_rate, "stores"), + ]), + MetricGroup("ldst_prcnt", [ + Metric("ldst_prcnt_loads", "Percent of all instructions that are loads", + d_ratio(LDST_LD, ins), "100%"), + Metric("ldst_prcnt_stores", "Percent of all instructions that are stores", + d_ratio(LDST_ST, ins), "100%"), + ]), + MetricGroup("ldst_ret_lds", [ + Metric("ldst_ret_lds_1", "Retired loads in 1 cycle", + d_ratio(max(LDST_LDC1 - LDST_LDC2, 0), cyc), "100%", + constraint = ldst_ret_constraint), + Metric("ldst_ret_lds_2", "Retired loads in 2 cycles", + d_ratio(max(LDST_LDC2 - LDST_LDC3, 0), cyc), "100%", + constraint = ldst_ret_constraint), + Metric("ldst_ret_lds_3", "Retired loads in 3 or more cycles", + d_ratio(LDST_LDC3, cyc), "100%"), + ]), + MetricGroup("ldst_ret_sts", [ + Metric("ldst_ret_sts_1", "Retired stores in 1 cycle", + d_ratio(max(LDST_STC1 - LDST_STC2, 0), cyc), "100%", + constraint = ldst_ret_constraint), + Metric("ldst_ret_sts_2", "Retired stores in 2 cycles", + d_ratio(max(LDST_STC2 - LDST_STC3, 0), cyc), "100%", + constraint = ldst_ret_constraint), + Metric("ldst_ret_sts_3", "Retired stores in 3 more cycles", + d_ratio(LDST_STC3, cyc), "100%"), + ]), + Metric("ldst_ld_hit_swpf", "Load hit software prefetches per second", + pf_rate, "swpf/s") if pf_rate else None, + Metric("ldst_atomic_lds", "Atomic loads per second", + at_rate, "loads/s") if at_rate else None, + ], description = "Breakdown of load/store instructions") + + def main() -> None: global _args @@ -538,6 +621,7 @@ def dir_path(path: str) -> str: Tsx(), IntelBr(), IntelL2(), + IntelLdSt(), IntelPorts(), IntelSwpf(), ]) From 9855e958824bb5270b28276102adb9cf03e9a687 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 22 Feb 2024 09:47:00 -0800 Subject: [PATCH 39/55] perf jevents: Add ILP metrics for Intel Use the counter mask (cmask) to see how many cycles an instruction takes to retire. Present as a set of ILP metrics. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/intel_metrics.py | 33 ++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py index d528b97e8822..1d886e416e7f 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -261,6 +261,38 @@ def Far() -> Optional[MetricGroup]: description="breakdown of retired branch instructions") +def IntelIlp() -> MetricGroup: + tsc = Event("msr/tsc/") + c0 = Event("msr/mperf/") + low = tsc - c0 + inst_ret = Event("INST_RETIRED.ANY_P") + inst_ret_c = [Event(f"{inst_ret.name}/cmask={x}/") for x in range(1, 6)] + core_cycles = Event("CPU_CLK_UNHALTED.THREAD_P_ANY", + "CPU_CLK_UNHALTED.DISTRIBUTED", + "cycles") + ilp = [d_ratio(max(inst_ret_c[x] - inst_ret_c[x + 1], 0), core_cycles) for x in range(0, 4)] + ilp.append(d_ratio(inst_ret_c[4], core_cycles)) + ilp0 = 1 + for x in ilp: + ilp0 -= x + return MetricGroup("ilp", [ + Metric("ilp_idle", "Lower power cycles as a percentage of all cycles", + d_ratio(low, tsc), "100%"), + Metric("ilp_inst_ret_0", "Instructions retired in 0 cycles as a percentage of all cycles", + ilp0, "100%"), + Metric("ilp_inst_ret_1", "Instructions retired in 1 cycles as a percentage of all cycles", + ilp[0], "100%"), + Metric("ilp_inst_ret_2", "Instructions retired in 2 cycles as a percentage of all cycles", + ilp[1], "100%"), + Metric("ilp_inst_ret_3", "Instructions retired in 3 cycles as a percentage of all cycles", + ilp[2], "100%"), + Metric("ilp_inst_ret_4", "Instructions retired in 4 cycles as a percentage of all cycles", + ilp[3], "100%"), + Metric("ilp_inst_ret_5", "Instructions retired in 5 or more cycles as a percentage of all cycles", + ilp[4], "100%"), + ]) + + def IntelL2() -> Optional[MetricGroup]: try: DC_HIT = Event("L2_RQSTS.DEMAND_DATA_RD_HIT") @@ -620,6 +652,7 @@ def dir_path(path: str) -> str: Smi(), Tsx(), IntelBr(), + IntelIlp(), IntelL2(), IntelLdSt(), IntelPorts(), From 8fc9db8b21d0d1f23d08053a59b34738ab55ed11 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 22 Feb 2024 14:10:49 -0800 Subject: [PATCH 40/55] perf jevents: Add context switch metrics for Intel Metrics break down context switches for different kinds of instruction. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/intel_metrics.py | 55 ++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py index 1d886e416e7f..7cd933a28cfd 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -261,6 +261,60 @@ def Far() -> Optional[MetricGroup]: description="breakdown of retired branch instructions") +def IntelCtxSw() -> MetricGroup: + cs = Event("context\-switches") + metrics = [ + Metric("cs_rate", "Context switches per second", d_ratio(cs, interval_sec), "ctxsw/s") + ] + + ev = Event("instructions") + metrics.append(Metric("cs_instr", "Instructions per context switch", + d_ratio(ev, cs), "instr/cs")) + + ev = Event("cycles") + metrics.append(Metric("cs_cycles", "Cycles per context switch", + d_ratio(ev, cs), "cycles/cs")) + + try: + ev = Event("MEM_INST_RETIRED.ALL_LOADS", "MEM_UOPS_RETIRED.ALL_LOADS") + metrics.append(Metric("cs_loads", "Loads per context switch", + d_ratio(ev, cs), "loads/cs")) + except: + pass + + try: + ev = Event("MEM_INST_RETIRED.ALL_STORES", "MEM_UOPS_RETIRED.ALL_STORES") + metrics.append(Metric("cs_stores", "Stores per context switch", + d_ratio(ev, cs), "stores/cs")) + except: + pass + + try: + ev = Event("BR_INST_RETIRED.NEAR_TAKEN", "BR_INST_RETIRED.TAKEN_JCC") + metrics.append(Metric("cs_br_taken", "Branches taken per context switch", + d_ratio(ev, cs), "br_taken/cs")) + except: + pass + + try: + l2_misses = (Event("L2_RQSTS.DEMAND_DATA_RD_MISS") + + Event("L2_RQSTS.RFO_MISS") + + Event("L2_RQSTS.CODE_RD_MISS")) + try: + l2_misses += Event("L2_RQSTS.HWPF_MISS", "L2_RQSTS.L2_PF_MISS", "L2_RQSTS.PF_MISS") + except: + pass + + metrics.append(Metric("cs_l2_misses", "L2 misses per context switch", + d_ratio(l2_misses, cs), "l2_misses/cs")) + except: + pass + + return MetricGroup("cs", metrics, + description = ("Number of context switches per second, instructions " + "retired & core cycles between context switches")) + + def IntelIlp() -> MetricGroup: tsc = Event("msr/tsc/") c0 = Event("msr/mperf/") @@ -652,6 +706,7 @@ def dir_path(path: str) -> str: Smi(), Tsx(), IntelBr(), + IntelCtxSw(), IntelIlp(), IntelL2(), IntelLdSt(), From bfb78461b41f818d089319db36f7f937b1b973fe Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 22 Feb 2024 22:20:54 -0800 Subject: [PATCH 41/55] perf jevents: Add FPU metrics for Intel Metrics break down of floating point operations. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/intel_metrics.py | 90 ++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py index 7cd933a28cfd..dc14fff7abc3 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -315,6 +315,95 @@ def IntelCtxSw() -> MetricGroup: "retired & core cycles between context switches")) +def IntelFpu() -> Optional[MetricGroup]: + cyc = Event("cycles") + try: + s_64 = Event("FP_ARITH_INST_RETIRED.SCALAR_SINGLE", + "SIMD_INST_RETIRED.SCALAR_SINGLE") + except: + return None + d_64 = Event("FP_ARITH_INST_RETIRED.SCALAR_DOUBLE", + "SIMD_INST_RETIRED.SCALAR_DOUBLE") + s_128 = Event("FP_ARITH_INST_RETIRED.128B_PACKED_SINGLE", + "SIMD_INST_RETIRED.PACKED_SINGLE") + + flop = s_64 + d_64 + 4 * s_128 + + d_128 = None + s_256 = None + d_256 = None + s_512 = None + d_512 = None + try: + d_128 = Event("FP_ARITH_INST_RETIRED.128B_PACKED_DOUBLE") + flop += 2 * d_128 + s_256 = Event("FP_ARITH_INST_RETIRED.256B_PACKED_SINGLE") + flop += 8 * s_256 + d_256 = Event("FP_ARITH_INST_RETIRED.256B_PACKED_DOUBLE") + flop += 4 * d_256 + s_512 = Event("FP_ARITH_INST_RETIRED.512B_PACKED_SINGLE") + flop += 16 * s_512 + d_512 = Event("FP_ARITH_INST_RETIRED.512B_PACKED_DOUBLE") + flop += 8 * d_512 + except: + pass + + f_assist = Event("ASSISTS.FP", "FP_ASSIST.ANY", "FP_ASSIST.S") + if f_assist in [ + "ASSISTS.FP", + "FP_ASSIST.S", + ]: + f_assist += "/cmask=1/" + + flop_r = d_ratio(flop, interval_sec) + flop_c = d_ratio(flop, cyc) + nmi_constraint = MetricConstraint.GROUPED_EVENTS + if f_assist.name == "ASSISTS.FP": # Icelake+ + nmi_constraint = MetricConstraint.NO_GROUP_EVENTS_NMI + def FpuMetrics(group: str, fl: Optional[Event], mult: int, desc: str) -> Optional[MetricGroup]: + if not fl: + return None + + f = fl * mult + fl_r = d_ratio(f, interval_sec) + r_s = d_ratio(fl, interval_sec) + return MetricGroup(group, [ + Metric(f"{group}_of_total", desc + " floating point operations per second", + d_ratio(f, flop), "100%"), + Metric(f"{group}_flops", desc + " floating point operations per second", + fl_r, "flops/s"), + Metric(f"{group}_ops", desc + " operations per second", + r_s, "ops/s"), + ]) + + return MetricGroup("fpu", [ + MetricGroup("fpu_total", [ + Metric("fpu_total_flops", "Floating point operations per second", + flop_r, "flops/s"), + Metric("fpu_total_flopc", "Floating point operations per cycle", + flop_c, "flops/cycle", constraint=nmi_constraint), + ]), + MetricGroup("fpu_64", [ + FpuMetrics("fpu_64_single", s_64, 1, "64-bit single"), + FpuMetrics("fpu_64_double", d_64, 1, "64-bit double"), + ]), + MetricGroup("fpu_128", [ + FpuMetrics("fpu_128_single", s_128, 4, "128-bit packed single"), + FpuMetrics("fpu_128_double", d_128, 2, "128-bit packed double"), + ]), + MetricGroup("fpu_256", [ + FpuMetrics("fpu_256_single", s_256, 8, "128-bit packed single"), + FpuMetrics("fpu_256_double", d_256, 4, "128-bit packed double"), + ]), + MetricGroup("fpu_512", [ + FpuMetrics("fpu_512_single", s_512, 16, "128-bit packed single"), + FpuMetrics("fpu_512_double", d_512, 8, "128-bit packed double"), + ]), + Metric("fpu_assists", "FP assists as a percentage of cycles", + d_ratio(f_assist, cyc), "100%"), + ]) + + def IntelIlp() -> MetricGroup: tsc = Event("msr/tsc/") c0 = Event("msr/mperf/") @@ -707,6 +796,7 @@ def dir_path(path: str) -> str: Tsx(), IntelBr(), IntelCtxSw(), + IntelFpu(), IntelIlp(), IntelL2(), IntelLdSt(), From bb97715ad29ff70c558d5415feaec4c3cce0189a Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 22 Feb 2024 23:32:20 -0800 Subject: [PATCH 42/55] perf jevents: Add Miss Level Parallelism (MLP) metric for Intel Number of outstanding load misses per cycle. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/intel_metrics.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py index dc14fff7abc3..8c6be9e1883f 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -593,6 +593,20 @@ def IntelL2() -> Optional[MetricGroup]: ], description = "L2 data cache analysis") +def IntelMlp() -> Optional[Metric]: + try: + l1d = Event("L1D_PEND_MISS.PENDING") + l1dc = Event("L1D_PEND_MISS.PENDING_CYCLES") + except: + return None + + l1dc = Select(l1dc / 2, Literal("#smt_on"), l1dc) + ml = d_ratio(l1d, l1dc) + return Metric("mlp", + "Miss level parallelism - number of outstanding load misses per cycle (higher is better)", + ml, "load_miss_pending/cycle") + + def IntelPorts() -> Optional[MetricGroup]: pipeline_events = json.load(open(f"{_args.events_path}/x86/{_args.model}/pipeline.json")) @@ -800,6 +814,7 @@ def dir_path(path: str) -> str: IntelIlp(), IntelL2(), IntelLdSt(), + IntelMlp(), IntelPorts(), IntelSwpf(), ]) From 404bdef8250f4fa35e826634a4027f1585fb9e34 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 23 Feb 2024 13:24:31 -0800 Subject: [PATCH 43/55] perf jevents: Add mem_bw metric for Intel Break down memory bandwidth using uncore counters. For many models this matches the memory_bandwidth_* metrics, but these metrics aren't made available on all models. Add support for free running counters. Query the event json when determining which what events/counters are available. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/intel_metrics.py | 62 ++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py index 8c6be9e1883f..05e803286f29 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -781,6 +781,67 @@ def IntelLdSt() -> Optional[MetricGroup]: ], description = "Breakdown of load/store instructions") +def UncoreMemBw() -> Optional[MetricGroup]: + mem_events = [] + try: + mem_events = json.load(open(f"{os.path.dirname(os.path.realpath(__file__))}" + f"/arch/x86/{args.model}/uncore-memory.json")) + except: + pass + + ddr_rds = 0 + ddr_wrs = 0 + ddr_total = 0 + for x in mem_events: + if "EventName" in x: + name = x["EventName"] + if re.search("^UNC_MC[0-9]+_RDCAS_COUNT_FREERUN", name): + ddr_rds += Event(name) + elif re.search("^UNC_MC[0-9]+_WRCAS_COUNT_FREERUN", name): + ddr_wrs += Event(name) + #elif re.search("^UNC_MC[0-9]+_TOTAL_REQCOUNT_FREERUN", name): + # ddr_total += Event(name) + + if ddr_rds == 0: + try: + ddr_rds = Event("UNC_M_CAS_COUNT.RD") + ddr_wrs = Event("UNC_M_CAS_COUNT.WR") + except: + return None + + ddr_total = ddr_rds + ddr_wrs + + pmm_rds = 0 + pmm_wrs = 0 + try: + pmm_rds = Event("UNC_M_PMM_RPQ_INSERTS") + pmm_wrs = Event("UNC_M_PMM_WPQ_INSERTS") + except: + pass + + pmm_total = pmm_rds + pmm_wrs + + scale = 64 / 1_000_000 + return MetricGroup("mem_bw", [ + MetricGroup("mem_bw_ddr", [ + Metric("mem_bw_ddr_read", "DDR memory read bandwidth", + d_ratio(ddr_rds, interval_sec), f"{scale}MB/s"), + Metric("mem_bw_ddr_write", "DDR memory write bandwidth", + d_ratio(ddr_wrs, interval_sec), f"{scale}MB/s"), + Metric("mem_bw_ddr_total", "DDR memory write bandwidth", + d_ratio(ddr_total, interval_sec), f"{scale}MB/s"), + ], description = "DDR Memory Bandwidth"), + MetricGroup("mem_bw_pmm", [ + Metric("mem_bw_pmm_read", "PMM memory read bandwidth", + d_ratio(pmm_rds, interval_sec), f"{scale}MB/s"), + Metric("mem_bw_pmm_write", "PMM memory write bandwidth", + d_ratio(pmm_wrs, interval_sec), f"{scale}MB/s"), + Metric("mem_bw_pmm_total", "PMM memory write bandwidth", + d_ratio(pmm_total, interval_sec), f"{scale}MB/s"), + ], description = "PMM Memory Bandwidth") if pmm_rds != 0 else None, + ], description = "Memory Bandwidth") + + def main() -> None: global _args @@ -817,6 +878,7 @@ def dir_path(path: str) -> str: IntelMlp(), IntelPorts(), IntelSwpf(), + UncoreMemBw(), ]) From c0f42cc17fda3d64c51be182af698a62fbf88643 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 23 Feb 2024 15:57:03 -0800 Subject: [PATCH 44/55] perf jevents: Add local/remote "mem" breakdown metrics for Intel Breakdown local and remote memory bandwidth, read and writes. The implementation uses the HA and CHA PMUs present in server models broadwellde, broadwellx cascadelakex, emeraldrapids, haswellx, icelakex, ivytown, sapphirerapids and skylakex. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/intel_metrics.py | 27 ++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py index 05e803286f29..62d504036ba0 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -781,6 +781,32 @@ def IntelLdSt() -> Optional[MetricGroup]: ], description = "Breakdown of load/store instructions") +def UncoreMem() -> Optional[MetricGroup]: + try: + loc_rds = Event("UNC_CHA_REQUESTS.READS_LOCAL", "UNC_H_REQUESTS.READS_LOCAL") + rem_rds = Event("UNC_CHA_REQUESTS.READS_REMOTE", "UNC_H_REQUESTS.READS_REMOTE") + loc_wrs = Event("UNC_CHA_REQUESTS.WRITES_LOCAL", "UNC_H_REQUESTS.WRITES_LOCAL") + rem_wrs = Event("UNC_CHA_REQUESTS.WRITES_REMOTE", "UNC_H_REQUESTS.WRITES_REMOTE") + except: + return None + + scale = 64 / 1_000_000 + return MetricGroup("mem", [ + MetricGroup("mem_local", [ + Metric("mem_local_read", "Local memory read bandwidth not including directory updates", + d_ratio(loc_rds, interval_sec), f"{scale}MB/s"), + Metric("mem_local_write", "Local memory write bandwidth not including directory updates", + d_ratio(loc_wrs, interval_sec), f"{scale}MB/s"), + ]), + MetricGroup("mem_remote", [ + Metric("mem_remote_read", "Remote memory read bandwidth not including directory updates", + d_ratio(rem_rds, interval_sec), f"{scale}MB/s"), + Metric("mem_remote_write", "Remote memory write bandwidth not including directory updates", + d_ratio(rem_wrs, interval_sec), f"{scale}MB/s"), + ]), + ], description = "Memory Bandwidth breakdown local vs. remote (remote requests in). directory updates not included") + + def UncoreMemBw() -> Optional[MetricGroup]: mem_events = [] try: @@ -878,6 +904,7 @@ def dir_path(path: str) -> str: IntelMlp(), IntelPorts(), IntelSwpf(), + UncoreMem(), UncoreMemBw(), ]) From 9f790feb638f5e3c613da0390bb50259a413bc84 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 23 Feb 2024 19:07:42 -0800 Subject: [PATCH 45/55] perf jevents: Add dir breakdown metrics for Intel Breakdown directory hit, misses and requests. The implementation uses the M2M and CHA PMUs present in server models broadwellde, broadwellx cascadelakex, emeraldrapids, icelakex, sapphirerapids and skylakex. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/intel_metrics.py | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py index 62d504036ba0..77ac048c5451 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -781,6 +781,41 @@ def IntelLdSt() -> Optional[MetricGroup]: ], description = "Breakdown of load/store instructions") +def UncoreDir() -> Optional[MetricGroup]: + try: + m2m_upd = Event("UNC_M2M_DIRECTORY_UPDATE.ANY") + m2m_hits = Event("UNC_M2M_DIRECTORY_HIT.DIRTY_I") + # Turn the umask into a ANY rather than DIRTY_I filter. + m2m_hits.name += "/umask=0xFF,name=UNC_M2M_DIRECTORY_HIT.ANY/" + m2m_miss = Event("UNC_M2M_DIRECTORY_MISS.DIRTY_I") + # Turn the umask into a ANY rather than DIRTY_I filter. + m2m_miss.name += "/umask=0xFF,name=UNC_M2M_DIRECTORY_MISS.ANY/" + cha_upd = Event("UNC_CHA_DIR_UPDATE.HA") + # Turn the umask into a ANY rather than HA filter. + cha_upd.name += "/umask=3,name=UNC_CHA_DIR_UPDATE.ANY/" + except: + return None + + m2m_total = m2m_hits + m2m_miss + upd = m2m_upd + cha_upd # in cache lines + upd_r = upd / interval_sec + look_r = m2m_total / interval_sec + + scale = 64 / 1_000_000 # Cache lines to MB + return MetricGroup("dir", [ + Metric("dir_lookup_rate", "", + d_ratio(m2m_total, interval_sec), "requests/s"), + Metric("dir_lookup_hits", "", + d_ratio(m2m_hits, m2m_total), "100%"), + Metric("dir_lookup_misses", "", + d_ratio(m2m_miss, m2m_total), "100%"), + Metric("dir_update_requests", "", + d_ratio(m2m_upd + cha_upd, interval_sec), "requests/s"), + Metric("dir_update_bw", "", + d_ratio(m2m_upd + cha_upd, interval_sec), f"{scale}MB/s"), + ]) + + def UncoreMem() -> Optional[MetricGroup]: try: loc_rds = Event("UNC_CHA_REQUESTS.READS_LOCAL", "UNC_H_REQUESTS.READS_LOCAL") @@ -904,6 +939,7 @@ def dir_path(path: str) -> str: IntelMlp(), IntelPorts(), IntelSwpf(), + UncoreDir(), UncoreMem(), UncoreMemBw(), ]) From e46f6fe6f19c4e4ea88b69935629340b6b25a691 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 26 Feb 2024 13:49:28 -0800 Subject: [PATCH 46/55] perf jevents: Add C-State metrics from the PCU PMU for Intel Use occupancy events fixed in: https://lore.kernel.org/lkml/20240226201517.3540187-1-irogers@google.com/ Metrics are at the socket level referring to cores, not hyperthreads. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/intel_metrics.py | 27 ++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py index 77ac048c5451..5668128273b3 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -781,6 +781,32 @@ def IntelLdSt() -> Optional[MetricGroup]: ], description = "Breakdown of load/store instructions") +def UncoreCState() -> Optional[MetricGroup]: + try: + pcu_ticks = Event("UNC_P_CLOCKTICKS") + c0 = Event("UNC_P_POWER_STATE_OCCUPANCY.CORES_C0") + c3 = Event("UNC_P_POWER_STATE_OCCUPANCY.CORES_C3") + c6 = Event("UNC_P_POWER_STATE_OCCUPANCY.CORES_C6") + except: + return None + + num_cores = Literal("#num_cores") / Literal("#num_packages") + + max_cycles = pcu_ticks * num_cores; + total_cycles = c0 + c3 + c6 + + # remove fused-off cores which show up in C6/C7. + c6 = Select(max(c6 - (total_cycles - max_cycles), 0), + total_cycles > max_cycles, + c6) + + return MetricGroup("cstate", [ + Metric("cstate_c0", "C-State cores in C0/C1", d_ratio(c0, pcu_ticks), "cores"), + Metric("cstate_c3", "C-State cores in C3", d_ratio(c3, pcu_ticks), "cores"), + Metric("cstate_c6", "C-State cores in C6/C7", d_ratio(c6, pcu_ticks), "cores"), + ]) + + def UncoreDir() -> Optional[MetricGroup]: try: m2m_upd = Event("UNC_M2M_DIRECTORY_UPDATE.ANY") @@ -939,6 +965,7 @@ def dir_path(path: str) -> str: IntelMlp(), IntelPorts(), IntelSwpf(), + UncoreCState(), UncoreDir(), UncoreMem(), UncoreMemBw(), From 774813c0eaf0a349d28f834e12b118035a7e5546 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 26 Feb 2024 22:20:28 -0800 Subject: [PATCH 47/55] perf jevents: Add local/remote miss latency metrics for Intel Derive from CBOX/CHA occupancy and inserts the average latency as is provided in Intel's uncore performance monitoring reference. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/intel_metrics.py | 68 ++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py index 5668128273b3..ec15653e2cb6 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) -from metric import (d_ratio, has_event, max, CheckPmu, Event, JsonEncodeMetric, - JsonEncodeMetricGroupDescriptions, Literal, LoadEvents, - Metric, MetricConstraint, MetricGroup, MetricRef, Select) +from metric import (d_ratio, has_event, max, source_count, CheckPmu, Event, + JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, + Literal, LoadEvents, Metric, MetricConstraint, MetricGroup, + MetricRef, Select) import argparse import json import math @@ -593,6 +594,66 @@ def IntelL2() -> Optional[MetricGroup]: ], description = "L2 data cache analysis") +def IntelMissLat() -> Optional[MetricGroup]: + try: + ticks = Event("UNC_CHA_CLOCKTICKS", "UNC_C_CLOCKTICKS") + data_rd_loc_occ = Event("UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_LOCAL", + "UNC_CHA_TOR_OCCUPANCY.IA_MISS", + "UNC_C_TOR_OCCUPANCY.MISS_LOCAL_OPCODE", + "UNC_C_TOR_OCCUPANCY.MISS_OPCODE") + data_rd_loc_ins = Event("UNC_CHA_TOR_INSERTS.IA_MISS_DRD_LOCAL", + "UNC_CHA_TOR_INSERTS.IA_MISS", + "UNC_C_TOR_INSERTS.MISS_LOCAL_OPCODE", + "UNC_C_TOR_INSERTS.MISS_OPCODE") + data_rd_rem_occ = Event("UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_REMOTE", + "UNC_CHA_TOR_OCCUPANCY.IA_MISS", + "UNC_C_TOR_OCCUPANCY.MISS_REMOTE_OPCODE", + "UNC_C_TOR_OCCUPANCY.NID_MISS_OPCODE") + data_rd_rem_ins = Event("UNC_CHA_TOR_INSERTS.IA_MISS_DRD_REMOTE", + "UNC_CHA_TOR_INSERTS.IA_MISS", + "UNC_C_TOR_INSERTS.MISS_REMOTE_OPCODE", + "UNC_C_TOR_INSERTS.NID_MISS_OPCODE") + except: + return None + + if (data_rd_loc_occ.name == "UNC_C_TOR_OCCUPANCY.MISS_LOCAL_OPCODE" or + data_rd_loc_occ.name == "UNC_C_TOR_OCCUPANCY.MISS_OPCODE"): + data_rd = 0x182 + for e in [data_rd_loc_occ, data_rd_loc_ins, data_rd_rem_occ, data_rd_rem_ins]: + e.name += f"/filter_opc={hex(data_rd)}/" + elif data_rd_loc_occ.name == "UNC_CHA_TOR_OCCUPANCY.IA_MISS": + # Demand Data Read - Full cache-line read requests from core for + # lines to be cached in S or E, typically for data + demand_data_rd = 0x202 + # LLC Prefetch Data - Uncore will first look up the line in the + # LLC; for a cache hit, the LRU will be updated, on a miss, the + # DRd will be initiated + llc_prefetch_data = 0x25a + local_filter = (f"/filter_opc0={hex(demand_data_rd)}," + f"filter_opc1={hex(llc_prefetch_data)}," + "filter_loc,filter_nm,filter_not_nm/") + remote_filter = (f"/filter_opc0={hex(demand_data_rd)}," + f"filter_opc1={hex(llc_prefetch_data)}," + "filter_rem,filter_nm,filter_not_nm/") + for e in [data_rd_loc_occ, data_rd_loc_ins]: + e.name += local_filter + for e in [data_rd_rem_occ, data_rd_rem_ins]: + e.name += remote_filter + else: + assert data_rd_loc_occ.name == "UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_LOCAL", data_rd_loc_occ + + ticks_per_cha = ticks / source_count(data_rd_loc_ins) + loc_lat = interval_sec * 1e9 * data_rd_loc_occ / (ticks_per_cha * data_rd_loc_ins) + ticks_per_cha = ticks / source_count(data_rd_rem_ins) + rem_lat = interval_sec * 1e9 * data_rd_rem_occ / (ticks_per_cha * data_rd_rem_ins) + return MetricGroup("miss_lat", [ + Metric("miss_lat_loc", "Local to a socket miss latency in nanoseconds", + loc_lat, "ns"), + Metric("miss_lat_rem", "Remote to a socket miss latency in nanoseconds", + rem_lat, "ns"), + ]) + + def IntelMlp() -> Optional[Metric]: try: l1d = Event("L1D_PEND_MISS.PENDING") @@ -962,6 +1023,7 @@ def dir_path(path: str) -> str: IntelIlp(), IntelL2(), IntelLdSt(), + IntelMissLat(), IntelMlp(), IntelPorts(), IntelSwpf(), From 0a2be6a960dce7befe27271721d97b48c9804867 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 27 Feb 2024 16:32:25 -0800 Subject: [PATCH 48/55] perf jevents: Add upi_bw metric for Intel Break down UPI read and write bandwidth using uncore_upi counters. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/intel_metrics.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py index ec15653e2cb6..8e1c0bc17b8a 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -990,6 +990,27 @@ def UncoreMemBw() -> Optional[MetricGroup]: ], description = "Memory Bandwidth") +def UncoreUpiBw() -> Optional[MetricGroup]: + try: + upi_rds = Event("UNC_UPI_RxL_FLITS.ALL_DATA") + upi_wrs = Event("UNC_UPI_TxL_FLITS.ALL_DATA") + except: + return None + + upi_total = upi_rds + upi_wrs + + # From "Uncore Performance Monitoring": When measuring the amount of + # bandwidth consumed by transmission of the data (i.e. NOT including + # the header), it should be .ALL_DATA / 9 * 64B. + scale = (64 / 9) / 1_000_000 + return MetricGroup("upi_bw", [ + Metric("upi_bw_read", "UPI read bandwidth", + d_ratio(upi_rds, interval_sec), f"{scale}MB/s"), + Metric("upi_bw_write", "DDR memory write bandwidth", + d_ratio(upi_wrs, interval_sec), f"{scale}MB/s"), + ], description = "UPI Bandwidth") + + def main() -> None: global _args @@ -1031,6 +1052,7 @@ def dir_path(path: str) -> str: UncoreDir(), UncoreMem(), UncoreMemBw(), + UncoreUpiBw(), ]) From 76f29a8d9854bf857500d5890ae732bfaa406ff2 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 29 Mar 2024 13:46:55 -0700 Subject: [PATCH 49/55] perf jevents: Add mesh bandwidth saturation metric for Intel Memory bandwidth saturation from CBOX/CHA events present in broadwellde, broadwellx, cascadelakex, haswellx, icelakex, skylakex and snowridgex. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/intel_metrics.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py index 8e1c0bc17b8a..a3a317d13841 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -990,6 +990,22 @@ def UncoreMemBw() -> Optional[MetricGroup]: ], description = "Memory Bandwidth") +def UncoreMemSat() -> Optional[Metric]: + try: + clocks = Event("UNC_CHA_CLOCKTICKS", "UNC_C_CLOCKTICKS") + sat = Event("UNC_CHA_DISTRESS_ASSERTED.VERT", "UNC_CHA_FAST_ASSERTED.VERT", + "UNC_C_FAST_ASSERTED") + except: + return None + + desc = ("Mesh Bandwidth saturation (% CBOX cycles with FAST signal asserted, " + "include QPI bandwidth saturation), lower is better") + if "UNC_CHA_" in sat.name: + desc = ("Mesh Bandwidth saturation (% CHA cycles with FAST signal asserted, " + "include UPI bandwidth saturation), lower is better") + return Metric("mem_sat", desc, d_ratio(sat, clocks), "100%") + + def UncoreUpiBw() -> Optional[MetricGroup]: try: upi_rds = Event("UNC_UPI_RxL_FLITS.ALL_DATA") @@ -1052,6 +1068,7 @@ def dir_path(path: str) -> str: UncoreDir(), UncoreMem(), UncoreMemBw(), + UncoreMemSat(), UncoreUpiBw(), ]) From 54d94f986559063b0e07720dc6f00a7b0dbc7730 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 8 Feb 2024 16:22:16 -0800 Subject: [PATCH 50/55] perf jevents: Add collection of topdown like metrics for arm64 Metrics are created using legacy, common and recommended events. As events may be missing a TryEvent function will give None if an event is missing. To workaround missing JSON events for cortex-a53, sysfs encodings are used. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/arm64_metrics.py | 147 ++++++++++++++++++++++++- 1 file changed, 143 insertions(+), 4 deletions(-) diff --git a/tools/perf/pmu-events/arm64_metrics.py b/tools/perf/pmu-events/arm64_metrics.py index c9aa2d827a82..bfac570600d9 100755 --- a/tools/perf/pmu-events/arm64_metrics.py +++ b/tools/perf/pmu-events/arm64_metrics.py @@ -1,14 +1,151 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) -from metric import (JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, LoadEvents, - MetricGroup) +from metric import (d_ratio, Event, JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, + LoadEvents, Metric, MetricGroup) import argparse import json import os +from typing import Optional # Global command line arguments. _args = None +def Arm64Topdown() -> MetricGroup: + """Returns a MetricGroup representing ARM64 topdown like metrics.""" + def TryEvent(name: str) -> Optional[Event]: + # Skip an event if not in the json files. + try: + return Event(name) + except: + return None + # ARM models like a53 lack JSON for INST_RETIRED but have the + # architetural standard event in sysfs. Use the PMU name to identify + # the sysfs event. + pmu_name = f'armv8_{_args.model.replace("-", "_")}' + ins = Event("instructions") + ins_ret = Event("INST_RETIRED", f"{pmu_name}/inst_retired/") + cycles = Event("cycles") + stall_fe = TryEvent("STALL_FRONTEND") + stall_be = TryEvent("STALL_BACKEND") + br_ret = TryEvent("BR_RETIRED") + br_mp_ret = TryEvent("BR_MIS_PRED_RETIRED") + dtlb_walk = TryEvent("DTLB_WALK") + itlb_walk = TryEvent("ITLB_WALK") + l1d_tlb = TryEvent("L1D_TLB") + l1i_tlb = TryEvent("L1I_TLB") + l1d_refill = Event("L1D_CACHE_REFILL", f"{pmu_name}/l1d_cache_refill/") + l2d_refill = Event("L2D_CACHE_REFILL", f"{pmu_name}/l2d_cache_refill/") + l1i_refill = Event("L1I_CACHE_REFILL", f"{pmu_name}/l1i_cache_refill/") + l1d_access = Event("L1D_CACHE", f"{pmu_name}/l1d_cache/") + l2d_access = Event("L2D_CACHE", f"{pmu_name}/l2d_cache/") + llc_access = TryEvent("LL_CACHE_RD") + l1i_access = Event("L1I_CACHE", f"{pmu_name}/l1i_cache/") + llc_miss_rd = TryEvent("LL_CACHE_MISS_RD") + ase_spec = TryEvent("ASE_SPEC") + ld_spec = TryEvent("LD_SPEC") + st_spec = TryEvent("ST_SPEC") + vfp_spec = TryEvent("VFP_SPEC") + dp_spec = TryEvent("DP_SPEC") + br_immed_spec = TryEvent("BR_IMMED_SPEC") + br_indirect_spec = TryEvent("BR_INDIRECT_SPEC") + br_ret_spec = TryEvent("BR_RETURN_SPEC") + crypto_spec = TryEvent("CRYPTO_SPEC") + inst_spec = TryEvent("INST_SPEC") + + return MetricGroup("topdown", [ + MetricGroup("topdown_tl", [ + Metric("topdown_tl_ipc", "Instructions per cycle", d_ratio( + ins, cycles), "insn/cycle"), + Metric("topdown_tl_stall_fe_rate", "Frontend stalls to all cycles", + d_ratio(stall_fe, cycles), "100%") if stall_fe else None, + Metric("topdown_tl_stall_be_rate", "Backend stalls to all cycles", + d_ratio(stall_be, cycles), "100%") if stall_be else None, + ]), + MetricGroup("topdown_fe_bound", [ + MetricGroup("topdown_fe_br", [ + Metric("topdown_fe_br_mp_per_insn", + "Branch mispredicts per instruction retired", + d_ratio(br_mp_ret, ins_ret), "br/insn") if br_mp_ret else None, + Metric("topdown_fe_br_ins_rate", + "Branches per instruction retired", d_ratio( + br_ret, ins_ret), "100%") if br_ret else None, + Metric("topdown_fe_br_mispredict", + "Branch mispredicts per branch instruction", + d_ratio(br_mp_ret, br_ret), "100%") if br_mp_ret else None, + ]), + MetricGroup("topdown_fe_itlb", [ + Metric("topdown_fe_itlb_walks", "Itlb walks per insn", + d_ratio(itlb_walk, ins_ret), "walk/insn"), + Metric("topdown_fe_itlb_walk_rate", "Itlb walks per l1i access", + d_ratio(itlb_walk, l1i_tlb), "100%"), + ]) if itlb_walk else None, + MetricGroup("topdown_fe_icache", [ + Metric("topdown_fe_icache_l1i_per_insn", + "L1I cache refills per instruction", + d_ratio(l1i_refill, ins_ret), "l1i/insn"), + Metric("topdown_fe_icache_l1i_miss_rate", + "L1I cache refills per L1I cache access", + d_ratio(l1i_refill, l1i_access), "100%"), + ]), + ]), + MetricGroup("topdown_be_bound", [ + MetricGroup("topdown_be_dtlb", [ + Metric("topdown_be_dtlb_walks", "Dtlb walks per instruction", + d_ratio(dtlb_walk, ins_ret), "walk/insn"), + Metric("topdown_be_dtlb_walk_rate", "Dtlb walks per l1d access", + d_ratio(dtlb_walk, l1d_tlb), "100%"), + ]) if dtlb_walk else None, + MetricGroup("topdown_be_mix", [ + Metric("topdown_be_mix_ld", "Percentage of load instructions", + d_ratio(ld_spec, inst_spec), "100%") if ld_spec else None, + Metric("topdown_be_mix_st", "Percentage of store instructions", + d_ratio(st_spec, inst_spec), "100%") if st_spec else None, + Metric("topdown_be_mix_simd", "Percentage of SIMD instructions", + d_ratio(ase_spec, inst_spec), "100%") if ase_spec else None, + Metric("topdown_be_mix_fp", + "Percentage of floating point instructions", + d_ratio(vfp_spec, inst_spec), "100%") if vfp_spec else None, + Metric("topdown_be_mix_dp", + "Percentage of data processing instructions", + d_ratio(dp_spec, inst_spec), "100%") if dp_spec else None, + Metric("topdown_be_mix_crypto", + "Percentage of data processing instructions", + d_ratio(crypto_spec, inst_spec), "100%") if crypto_spec else None, + Metric( + "topdown_be_mix_br", "Percentage of branch instructions", + d_ratio(br_immed_spec + br_indirect_spec + br_ret_spec, + inst_spec), "100%") if br_immed_spec and br_indirect_spec and br_ret_spec else None, + ]) if inst_spec else None, + MetricGroup("topdown_be_dcache", [ + MetricGroup("topdown_be_dcache_l1", [ + Metric("topdown_be_dcache_l1_per_insn", + "L1D cache refills per instruction", + d_ratio(l1d_refill, ins_ret), "refills/insn"), + Metric("topdown_be_dcache_l1_miss_rate", + "L1D cache refills per L1D cache access", + d_ratio(l1d_refill, l1d_access), "100%") + ]), + MetricGroup("topdown_be_dcache_l2", [ + Metric("topdown_be_dcache_l2_per_insn", + "L2D cache refills per instruction", + d_ratio(l2d_refill, ins_ret), "refills/insn"), + Metric("topdown_be_dcache_l2_miss_rate", + "L2D cache refills per L2D cache access", + d_ratio(l2d_refill, l2d_access), "100%") + ]), + MetricGroup("topdown_be_dcache_llc", [ + Metric("topdown_be_dcache_llc_per_insn", + "Last level cache misses per instruction", + d_ratio(llc_miss_rd, ins_ret), "miss/insn"), + Metric("topdown_be_dcache_llc_miss_rate", + "Last level cache misses per L2D cache access", + d_ratio(llc_miss_rd, llc_access), "100%") + ]) if llc_miss_rd and llc_access else None, + ]), + ]), + ]) + + def main() -> None: global _args @@ -29,11 +166,13 @@ def dir_path(path: str) -> str: ) _args = parser.parse_args() - all_metrics = MetricGroup("",[]) - directory = f"{_args.events_path}/arm64/{_args.vendor}/{_args.model}/" LoadEvents(directory) + all_metrics = MetricGroup("",[ + Arm64Topdown(), + ]) + if _args.metricgroups: print(JsonEncodeMetricGroupDescriptions(all_metrics)) else: From 87954a2359e5fec8a1e223d49995747f62f3e337 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 23 Feb 2024 12:54:59 -0800 Subject: [PATCH 51/55] perf jevents: Add cycles breakdown metric for arm64/AMD/Intel Breakdown cycles to user, kernel and guest. Add a common_metrics.py file for such metrics. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/Build | 2 +- tools/perf/pmu-events/amd_metrics.py | 3 +++ tools/perf/pmu-events/arm64_metrics.py | 2 ++ tools/perf/pmu-events/common_metrics.py | 18 ++++++++++++++++++ tools/perf/pmu-events/intel_metrics.py | 2 ++ 5 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 tools/perf/pmu-events/common_metrics.py diff --git a/tools/perf/pmu-events/Build b/tools/perf/pmu-events/Build index f3bc6c093360..91b6837e32c9 100644 --- a/tools/perf/pmu-events/Build +++ b/tools/perf/pmu-events/Build @@ -37,7 +37,7 @@ $(OUTPUT)pmu-events/arch/%: pmu-events/arch/% $(call rule_mkdir) $(Q)$(call echo-cmd,gen)cp $< $@ -GEN_METRIC_DEPS := pmu-events/metric.py +GEN_METRIC_DEPS := pmu-events/metric.py pmu-events/common_metrics.py # Generate AMD Json ZENS = $(shell ls -d pmu-events/arch/x86/amdzen*) diff --git a/tools/perf/pmu-events/amd_metrics.py b/tools/perf/pmu-events/amd_metrics.py index 44fe82b15394..8dbd4041d6f1 100755 --- a/tools/perf/pmu-events/amd_metrics.py +++ b/tools/perf/pmu-events/amd_metrics.py @@ -4,6 +4,7 @@ JsonEncodeMetricGroupDescriptions, Literal, LoadEvents, Metric, MetricGroup, Select) import argparse +from common_metrics import Cycles import json import math import os @@ -588,6 +589,7 @@ def AmdUpc() -> Metric: return Metric("upc", "Micro-ops retired per core cycle (higher is better)", upc, "uops/cycle") + def Idle() -> Metric: cyc = Event("msr/mperf/") tsc = Event("msr/tsc/") @@ -670,6 +672,7 @@ def dir_path(path: str) -> str: AmdSwpf(), AmdUopCache(), AmdUpc(), + Cycles(), Idle(), Rapl(), UncoreL3(), diff --git a/tools/perf/pmu-events/arm64_metrics.py b/tools/perf/pmu-events/arm64_metrics.py index bfac570600d9..5285a22ff0c8 100755 --- a/tools/perf/pmu-events/arm64_metrics.py +++ b/tools/perf/pmu-events/arm64_metrics.py @@ -3,6 +3,7 @@ from metric import (d_ratio, Event, JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, LoadEvents, Metric, MetricGroup) import argparse +from common_metrics import Cycles import json import os from typing import Optional @@ -171,6 +172,7 @@ def dir_path(path: str) -> str: all_metrics = MetricGroup("",[ Arm64Topdown(), + Cycles(), ]) if _args.metricgroups: diff --git a/tools/perf/pmu-events/common_metrics.py b/tools/perf/pmu-events/common_metrics.py new file mode 100644 index 000000000000..74c58f9ab020 --- /dev/null +++ b/tools/perf/pmu-events/common_metrics.py @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +from metric import (d_ratio, Event, Metric, MetricGroup) + +def Cycles() -> MetricGroup: + cyc_k = Event("cycles:kHh") + cyc_g = Event("cycles:G") + cyc_u = Event("cycles:uH") + cyc = cyc_k + cyc_g + cyc_u + + return MetricGroup("cycles", [ + Metric("cycles_total", "Total number of cycles", cyc, "cycles"), + Metric("cycles_user", "User cycles as a percentage of all cycles", + d_ratio(cyc_u, cyc), "100%"), + Metric("cycles_kernel", "Kernel cycles as a percentage of all cycles", + d_ratio(cyc_k, cyc), "100%"), + Metric("cycles_guest", "Hypervisor guest cycles as a percentage of all cycles", + d_ratio(cyc_g, cyc), "100%"), + ], description = "cycles breakdown per privilege level (users, kernel, guest)") diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py index a3a317d13841..4b7668e25e54 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -5,6 +5,7 @@ Literal, LoadEvents, Metric, MetricConstraint, MetricGroup, MetricRef, Select) import argparse +from common_metrics import Cycles import json import math import os @@ -1050,6 +1051,7 @@ def dir_path(path: str) -> str: LoadEvents(directory) all_metrics = MetricGroup("", [ + Cycles(), Idle(), Rapl(), Smi(), From de50f4e3c4571748b7f3b61ad0b198e74a48ea84 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 28 Feb 2024 16:09:43 -0800 Subject: [PATCH 52/55] perf jevents: Validate that all names given an Event Validate they exist in a json file from one directory found from one directory above the model's json directory. This avoids broken fallback encodings being created. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/metric.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tools/perf/pmu-events/metric.py b/tools/perf/pmu-events/metric.py index 5a5b149dd286..f9390677b7b6 100644 --- a/tools/perf/pmu-events/metric.py +++ b/tools/perf/pmu-events/metric.py @@ -11,12 +11,14 @@ all_pmus = set() all_events = set() experimental_events = set() +all_events_all_models = set() def LoadEvents(directory: str) -> None: """Populate a global set of all known events for the purpose of validating Event names""" global all_pmus global all_events global experimental_events + global all_events_all_models all_events = { "context\-switches", "cycles", @@ -36,6 +38,15 @@ def LoadEvents(directory: str) -> None: experimental_events.add(x["EventName"]) elif "ArchStdEvent" in x: all_events.add(x["ArchStdEvent"]) + all_events_all_models = all_events.copy() + for root, dirs, files in os.walk(directory + ".."): + for filename in files: + if filename.endswith(".json"): + for x in json.load(open(f"{root}/{filename}")): + if "EventName" in x: + all_events_all_models.add(x["EventName"]) + elif "ArchStdEvent" in x: + all_events_all_models.add(x["ArchStdEvent"]) def CheckPmu(name: str) -> bool: @@ -58,6 +69,25 @@ def CheckEvent(name: str) -> bool: return name in all_events +def CheckEveryEvent(*names: str) -> None: + """Check all the events exist in at least one json file""" + global all_events_all_models + if len(all_events_all_models) == 0: + assert len(names) == 1, f"Cannot determine valid events in {names}" + # No events loaded so assume any event is good. + return + + for name in names: + # Remove trailing modifier. + if ':' in name: + name = name[:name.find(':')] + elif '/' in name: + name = name[:name.find('/')] + if any([name.startswith(x) for x in ['amd', 'arm', 'cpu', 'msr', 'power']]): + continue + if name not in all_events_all_models: + raise Exception(f"Is {name} a named json event?") + def IsExperimentalEvent(name: str) -> bool: global experimental_events @@ -397,6 +427,7 @@ class Event(Expression): def __init__(self, *args: str): error = "" + CheckEveryEvent(*args) for name in args: if CheckEvent(name): self.name = _FixEscapes(name) From 5e432dd34c1db6b4cce72ae2c5214179cb6bc764 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Apr 2024 18:17:33 -0700 Subject: [PATCH 53/55] perf test: Be more tolerant of metricgroup failures Previously "set -e" meant any non-zero exit code from perf stat would cause a test failure. As a non-zero exit happens when there aren't sufficient permissions, check for this case and make the exit code 2/skip for it. --- .../perf/tests/shell/stat_all_metricgroups.sh | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/tools/perf/tests/shell/stat_all_metricgroups.sh b/tools/perf/tests/shell/stat_all_metricgroups.sh index 55ef9c9ded2d..d6db192b9f18 100755 --- a/tools/perf/tests/shell/stat_all_metricgroups.sh +++ b/tools/perf/tests/shell/stat_all_metricgroups.sh @@ -1,9 +1,7 @@ -#!/bin/sh +#!/bin/bash # perf all metricgroups test # SPDX-License-Identifier: GPL-2.0 -set -e - ParanoidAndNotRoot() { [ "$(id -u)" != 0 ] && [ "$(cat /proc/sys/kernel/perf_event_paranoid)" -gt $1 ] @@ -14,11 +12,29 @@ if ParanoidAndNotRoot 0 then system_wide_flag="" fi - +err=0 for m in $(perf list --raw-dump metricgroups) do echo "Testing $m" - perf stat -M "$m" $system_wide_flag sleep 0.01 + result=$(perf stat -M "$m" $system_wide_flag sleep 0.01 2>&1) + result_err=$? + if [[ $result_err -gt 0 ]] + then + if [[ "$result" =~ \ + "Access to performance monitoring and observability operations is limited" ]] + then + echo "Permission failure" + echo $result + if [[ $err -eq 0 ]] + then + err=2 # Skip + fi + else + echo "Metric group $m failed" + echo $result + err=1 # Fail + fi + fi done -exit 0 +exit $err From ce161b0bf6b46c962da335b06a1bce504e02c34f Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 17 Apr 2024 10:14:23 -0700 Subject: [PATCH 54/55] perf test: Avoid hard coded metrics in stat std output test Hard coded metric names fail on ARM testing. --- tools/perf/tests/shell/stat+std_output.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/tests/shell/stat+std_output.sh b/tools/perf/tests/shell/stat+std_output.sh index cbf2894b2c84..845f83213855 100755 --- a/tools/perf/tests/shell/stat+std_output.sh +++ b/tools/perf/tests/shell/stat+std_output.sh @@ -13,7 +13,7 @@ stat_output=$(mktemp /tmp/__perf_test.stat_output.std.XXXXX) event_name=(cpu-clock task-clock context-switches cpu-migrations page-faults stalled-cycles-frontend stalled-cycles-backend cycles instructions branches branch-misses) event_metric=("CPUs utilized" "CPUs utilized" "/sec" "/sec" "/sec" "frontend cycles idle" "backend cycles idle" "GHz" "insn per cycle" "/sec" "of all branches") -skip_metric=("stalled cycles per insn" "tma_" "retiring" "frontend_bound" "bad_speculation" "backend_bound") +skip_metric=($(perf list --raw Default 2> /dev/null)) cleanup() { rm -f "${stat_output}" From 044afafbdba119ef3b8e9b0efc106f0f539551c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 04:50:35 +0000 Subject: [PATCH 55/55] Bump requests from 2.31.0 to 2.32.2 in /drivers/gpu/drm/ci/xfails Bumps [requests](https://github.com/psf/requests) from 2.31.0 to 2.32.2. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.31.0...v2.32.2) --- updated-dependencies: - dependency-name: requests dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- drivers/gpu/drm/ci/xfails/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/ci/xfails/requirements.txt b/drivers/gpu/drm/ci/xfails/requirements.txt index 5e6d48d98e4e..4e880aa73bbe 100644 --- a/drivers/gpu/drm/ci/xfails/requirements.txt +++ b/drivers/gpu/drm/ci/xfails/requirements.txt @@ -7,7 +7,7 @@ charset-normalizer==3.2.0 idna==3.4 pip==23.3 python-gitlab==3.15.0 -requests==2.31.0 +requests==2.32.2 requests-toolbelt==1.0.0 ruamel.yaml==0.17.32 ruamel.yaml.clib==0.2.7