From nobody Tue Feb 10 04:12:39 2026 Received: from mail-dl1-f73.google.com (mail-dl1-f73.google.com [74.125.82.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BE600328B70 for ; Tue, 27 Jan 2026 18:45:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769539532; cv=none; b=X6fE9puEVoau+it0CjXW0QwNuEuSreVLNfTEcA+Npq86FSMyi8F/xQg6iXk50U6TSk96jKZiB6fIa/BbuFgwC7EZ0JD6QoSwYueiycv1JK/Sutst7HS7AxCKJqv+luUVRm2drbVgoI7UgkPFFfqY9N615EarcG2j8/FBBiMA8FE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769539532; c=relaxed/simple; bh=1vZESDUQIUEjDxJVh7J4k16LRLOQkyT8gAwiI0WIf9s=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Content-Type; b=eyZ/Odk6PyDHkKQ6ml+GEAmA1yIo5JXGTwj1QobqNgorhg2KC5A3DpF3481nwKR8Hb/aE+zVLKGUA5sIhpInSIg4HN9Rf5K3TY3z5hn9sY3KXYxiQ4X6hgerZ7f+mYBfZJJoUuw6sfHujL0XuQBviT/TsIdqyNudNWWk4D99Z+g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=dbLRp1AS; arc=none smtp.client-ip=74.125.82.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="dbLRp1AS" Received: by mail-dl1-f73.google.com with SMTP id a92af1059eb24-124a2d3d1b7so935667c88.0 for ; Tue, 27 Jan 2026 10:45:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1769539530; x=1770144330; darn=vger.kernel.org; h=to:from:subject:message-id:references:mime-version:in-reply-to:date :from:to:cc:subject:date:message-id:reply-to; bh=7nf5yOpaWFgwkL2L53uXTvwWf0qlTtHzBTCtpqyJsvQ=; b=dbLRp1AS44R9LBN8JGvLDVKt6MUBJQnEzsGaVZRsrE6sg3WGShHVniNZbOY1qzDmgu Prq58DQ1icoZdXLu6Ge5V7Mc29M0jApjLsuM0VwxTbbKeeNYYhwn0F3WAeJWJbI1kI3Q o9xZQrztLoaIMW+6W5XT9jyKhLQMKterjK4zip9tNAVQrqoNbYtZKm9hb8iXm1d8lV0u ZIU18W/QKszEtvjyKkVQXU2TdhJiWdkJzzJD86y152uK8KRpHNQCPY7a1zqfB8+zOJIt PWc5lnpxh9Yt29DLcIfceYQZi72QVhrgjriVPE3H8DzISy7vfxYZQnI77AifptWycF0g KuHQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769539530; x=1770144330; h=to:from:subject:message-id:references:mime-version:in-reply-to:date :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=7nf5yOpaWFgwkL2L53uXTvwWf0qlTtHzBTCtpqyJsvQ=; b=kXRoMO18S4Z2yXX/ZlBWS8M2nc+SRVJEvdq9PMLSL9W5bzkM8hHCQ6DgLuy9MLb8G3 oWhdrD8Iw0wQNJmjV1PvzLZcLcgnvBFTdyyJ0xN/v+aU7nSFAxyPpBna92itnE1R3A7H zs7EoHpKj0E+YWR5mArP+PPj+5ym+N4C7YwTugL83Dt5PYRjwlFMQ07f78K10cflOmXm 2HtLKkP3dbNbMIXBHsQzjDd8Q2Mj1f0ovTDydx/HLKes9FgLSPAV9jXuUB05vu4UgH/w om/5cw4rHAXSliWphjMq+V/p+1LJhI6m1ek4AYfHP5PyFSHJThGUkcxZmZSsi+e4OXN6 h98g== X-Forwarded-Encrypted: i=1; AJvYcCVSvsFRCMWR0EaFn5XGjljr6vcp4K3DLW+jkMZ6b2bkLp4VRO6XE6VsfWlb5J9rLPWvMcHKY+0oapqmLaQ=@vger.kernel.org X-Gm-Message-State: AOJu0YxIbBLhZ9GKkC3NDQaLWOgXprRUG85RiPLFcVEXTbZM5yIi4n0m F+XCfrN2DZo7OdWUDgQOCy+PhqWCfU/99RclfPeL8YnIL0G12ZSb3gK4Pvci9PA12ofOEvoR/J0 ankGu0fTs3g== X-Received: from dlii13.prod.google.com ([2002:a05:7022:418d:b0:121:7afb:490]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:6b94:b0:123:3488:8969 with SMTP id a92af1059eb24-124a00e576bmr1587671c88.45.1769539529822; Tue, 27 Jan 2026 10:45:29 -0800 (PST) Date: Tue, 27 Jan 2026 10:44:33 -0800 In-Reply-To: <20260127184506.3059493-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260127184506.3059493-1-irogers@google.com> X-Mailer: git-send-email 2.52.0.457.g6b5491de43-goog Message-ID: <20260127184506.3059493-3-irogers@google.com> Subject: [PATCH v11 02/35] perf jevents: Add load event json to verify and allow fallbacks From: Ian Rogers To: Adrian Hunter , Alexander Shishkin , Arnaldo Carvalho de Melo , Benjamin Gray , Caleb Biggers , Edward Baker , Ian Rogers , Ingo Molnar , James Clark , Jing Zhang , Jiri Olsa , John Garry , Leo Yan , Namhyung Kim , Perry Taylor , Peter Zijlstra , Sandipan Das , Thomas Falcon , Weilin Wang , Xu Yang , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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. Tested-by: Thomas Falcon Signed-off-by: Ian Rogers --- tools/perf/pmu-events/Build | 12 ++-- tools/perf/pmu-events/amd_metrics.py | 7 ++- tools/perf/pmu-events/arm64_metrics.py | 7 ++- tools/perf/pmu-events/intel_metrics.py | 7 ++- tools/perf/pmu-events/metric.py | 83 +++++++++++++++++++++++++- 5 files changed, 101 insertions(+), 15 deletions(-) diff --git a/tools/perf/pmu-events/Build b/tools/perf/pmu-events/Build index 3f2b26e5c6d5..4878d922a732 100644 --- a/tools/perf/pmu-events/Build +++ b/tools/perf/pmu-events/Build @@ -53,11 +53,11 @@ ZEN_METRICGROUPS =3D $(foreach x,$(ZENS),$(OUTPUT)$(x)/= extra-metricgroups.json) =20 $(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/arc= h > $@ =20 $(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 > $@ =20 # Generate ARM Json ARMS =3D $(shell ls -d pmu-events/arch/arm64/arm/*|grep -v cmn) @@ -66,11 +66,11 @@ ARM_METRICGROUPS =3D $(foreach x,$(ARMS),$(OUTPUT)$(x)/= extra-metricgroups.json) =20 $(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 > $@ =20 $(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 > $@ =20 # Generate Intel Json INTELS =3D $(shell ls -d pmu-events/arch/x86/*|grep -v amdzen|grep -v mapf= ile.csv) @@ -79,11 +79,11 @@ INTEL_METRICGROUPS =3D $(foreach x,$(INTELS),$(OUTPUT)$= (x)/extra-metricgroups.json =20 $(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/arc= h > $@ =20 $(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 > $@ =20 GEN_JSON =3D $(patsubst %,$(OUTPUT)%,$(JSON)) \ $(LEGACY_CACHE_JSON) \ diff --git a/tools/perf/pmu-events/amd_metrics.py b/tools/perf/pmu-events/a= md_metrics.py index 5f44687d8d20..bc91d9c120fa 100755 --- a/tools/perf/pmu-events/amd_metrics.py +++ b/tools/perf/pmu-events/amd_metrics.py @@ -2,8 +2,8 @@ # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) import argparse import os -from metric import ( - JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, MetricGroup) +from metric import (JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, L= oadEvents, + MetricGroup) =20 # Global command line arguments. _args =3D None @@ -30,6 +30,9 @@ def main() -> None: ) _args =3D parser.parse_args() =20 + directory =3D f"{_args.events_path}/x86/{_args.model}/" + LoadEvents(directory) + all_metrics =3D MetricGroup("", []) =20 if _args.metricgroups: diff --git a/tools/perf/pmu-events/arm64_metrics.py b/tools/perf/pmu-events= /arm64_metrics.py index 204b3b08c680..ac717ca3513a 100755 --- a/tools/perf/pmu-events/arm64_metrics.py +++ b/tools/perf/pmu-events/arm64_metrics.py @@ -2,8 +2,8 @@ # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) import argparse import os -from metric import ( - JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, MetricGroup) +from metric import (JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, L= oadEvents, + MetricGroup) =20 # Global command line arguments. _args =3D None @@ -31,6 +31,9 @@ def main() -> None: ) _args =3D parser.parse_args() =20 + directory =3D f"{_args.events_path}/arm64/{_args.vendor}/{_args.model}= /" + LoadEvents(directory) + all_metrics =3D MetricGroup("", []) =20 if _args.metricgroups: diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events= /intel_metrics.py index 65ada006d05a..b287ef115193 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -2,8 +2,8 @@ # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) import argparse import os -from metric import ( - JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, MetricGroup) +from metric import (JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, L= oadEvents, + MetricGroup) =20 # Global command line arguments. _args =3D None @@ -30,6 +30,9 @@ def main() -> None: ) _args =3D parser.parse_args() =20 + directory =3D f"{_args.events_path}/x86/{_args.model}/" + LoadEvents(directory) + all_metrics =3D MetricGroup("", []) =20 if _args.metricgroups: diff --git a/tools/perf/pmu-events/metric.py b/tools/perf/pmu-events/metric= .py index dd8fd06940e6..e33e163b2815 100644 --- a/tools/perf/pmu-events/metric.py +++ b/tools/perf/pmu-events/metric.py @@ -3,10 +3,56 @@ import ast import decimal import json +import os import re from enum import Enum from typing import Dict, List, Optional, Set, Tuple, Union =20 +all_events =3D set() + +def LoadEvents(directory: str) -> None: + """Populate a global set of all known events for the purpose of validati= ng Event names""" + global all_events + all_events =3D { + "context\\-switches", + "cpu\\-cycles", + "cycles", + "duration_time", + "instructions", + "l2_itlb_misses", + } + for file in os.listdir(os.fsencode(directory)): + filename =3D os.fsdecode(file) + if filename.endswith(".json"): + try: + 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"]) + except json.decoder.JSONDecodeError: + # The generated directory may be the same as the input, which + # causes partial json files. Ignore errors. + pass + + +def CheckEvent(name: str) -> bool: + """Check the event name exists in the set of all loaded events""" + global all_events + if len(all_events) =3D=3D 0: + # No events loaded so assume any event is good. + return True + + if ':' in name: + # Remove trailing modifier. + name =3D 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 =3D 0 NO_GROUP_EVENTS =3D 1 @@ -317,9 +363,18 @@ def _FixEscapes(s: str) -> str: class Event(Expression): """An event in an expression.""" =20 - def __init__(self, name: str, legacy_name: str =3D ''): - self.name =3D _FixEscapes(name) - self.legacy_name =3D _FixEscapes(legacy_name) + def __init__(self, *args: str): + error =3D "" + for name in args: + if CheckEvent(name): + self.name =3D _FixEscapes(name) + return + if error: + error +=3D " or " + name + else: + error =3D name + global all_events + raise Exception(f"No event {error} in:\n{all_events}") =20 def ToPerfJson(self): result =3D re.sub('/', '@', self.name) @@ -338,6 +393,28 @@ class Event(Expression): return self =20 =20 +class MetricRef(Expression): + """A metric reference in an expression.""" + + def __init__(self, name: str): + self.name =3D _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 =3D=3D other.name + + def Substitute(self, name: str, expression: Expression) -> Expression: + return self + + class Constant(Expression): """A constant within the expression tree.""" =20 --=20 2.52.0.457.g6b5491de43-goog