From patchwork Wed Dec 21 22:34:13 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ian Rogers X-Patchwork-Id: 13079205 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id A7570C4167B for ; Wed, 21 Dec 2022 22:36:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:Cc:To:From:Subject:References: Mime-Version:Message-Id:In-Reply-To:Date:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=yEzSbjZ2ejwoJr2fNnAMOQw3Y79b0l4zfJBWWO1QkCg=; b=Kz/z7GmUcUUNkq8VVgw/BSlcMM D778YgnSzg5QyQSp4HRTOHVXo/2I6Imwo5J59/708sYOdUfxhTxKwsSK7iL9kKi/cSYOSg8b4iFXK vqO6QLh9xQn8aDUrQrd9Shyv0WL7Vmydg6moRYOQ64CHdkL+7UX5tJNNhMw9QQJfWDsPNnVgWsuLD YAJtWouQAYKL5akzf/NZBvbIUTuuujoOJNcXl9OFR3HT1Q+zrrbaaQhAWCZtKpaai7cVjnRDG5tM6 8ra1hMY6eRoTPbWBhmkQrRcAWg60chvKsKUgfO4aLp0H4AJdxoXsad0M+lRA6N7ukQJcvF4v1ffDM lU/FpPrg==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1p87g4-0039Fd-5p; Wed, 21 Dec 2022 22:35:08 +0000 Received: from mail-yw1-x1149.google.com ([2607:f8b0:4864:20::1149]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1p87fj-00393N-6J for linux-arm-kernel@lists.infradead.org; Wed, 21 Dec 2022 22:34:49 +0000 Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-460ab8a327eso3216517b3.23 for ; Wed, 21 Dec 2022 14:34:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:references:mime-version:message-id:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=XSRdXmmYpH3FUi2ntSEaBZfMzdFPCToyU2P+KywsVtE=; b=NvffAvuaPXn6ccEpX8X07e5RRoMC12LkowBZEDZmy/VL6P7LQ5DbzNaf40i08etEDX ZrCCHFTZaM6dTI5lWq5CZwfAtt5n11ZgnoYtIjS0eps9AxnGF4SzE+3NhHhYCf97teX9 +h+YsC4zqbDV5Tiu6c+sFYAd0Ev+oGyiUjqGEOAvjj1NEaOigELNK7b/GcGevDv5w33X xkHa3VCMZNC3Z1g0KJ4z8rZNEoJhAawuX8F/MtsRICQjsUggG/bkv4zNHCj9TOQOZgIZ lx+HZ4IkQJ71oIH0yk+VPa7/ExO0mpUQFZd9/JgUWcBeKEHpAPsGnquGoF2Znl8+kPL3 kGyw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:references:mime-version:message-id:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=XSRdXmmYpH3FUi2ntSEaBZfMzdFPCToyU2P+KywsVtE=; b=wq1h61osgmkUauOlSqt3q9sdkmLv0Qp4sWXwG2ViE1Vny14dY1ciLbzBna8PRYHv6A zNwhUfwIvy/d3oTj6aPnFu0WF2mLRnrlxhL+sMj4X0MF+i6EQ2SfL8fOj1Sj+ckS4TEW OXmDNgb0Nh8cSjNuklhq8IwvfwWHz1lVYMMaq4dcsatIg+7Qiws0jgq+62TmUCfMkFr1 qalkKqtNnirJ6uu3MkyVnZDV8HOqHmZv9u3Q07CQLxa0nCPpw3h2rSW6ZTbXzcaC2y3J 35M3ZENYVKUVqw2ccGIBgwrf+CiuKXASIuwRBmZzOT+SIL+4aXBFO43JqI4l0oufxalu zJdg== X-Gm-Message-State: AFqh2kpJnD0c7L5pKz0+g5cKjPBpp4R01Dl/tL3EJGOR9YeNSSjJD6ku 9cYWvI5/XeQu98OLlH7lQgWSjzhvK3SH X-Google-Smtp-Source: AMrXdXseIoVcrpixKyOR38LsCfRTlml7LxBXKDfiBv7hPRHdbJC1+xCKUhQtTkCoFSX5S/w0c23+hrejYK1Q X-Received: from irogers.svl.corp.google.com ([2620:15c:2d4:203:62bd:f120:1fd8:1d21]) (user=irogers job=sendgmr) by 2002:a0d:ca54:0:b0:370:4a99:df7d with SMTP id m81-20020a0dca54000000b003704a99df7dmr254226ywd.308.1671662084298; Wed, 21 Dec 2022 14:34:44 -0800 (PST) Date: Wed, 21 Dec 2022 14:34:13 -0800 In-Reply-To: <20221221223420.2157113-1-irogers@google.com> Message-Id: <20221221223420.2157113-3-irogers@google.com> Mime-Version: 1.0 References: <20221221223420.2157113-1-irogers@google.com> X-Mailer: git-send-email 2.39.0.314.g84b9a713c41-goog Subject: [PATCH v2 2/9] perf jevents metric: Add ability to rewrite metrics in terms of others From: Ian Rogers To: John Garry , Will Deacon , James Clark , Mike Leach , Leo Yan , Peter Zijlstra , Ingo Molnar , Arnaldo Carvalho de Melo , Mark Rutland , Alexander Shishkin , Jiri Olsa , Namhyung Kim , Adrian Hunter , Kan Liang , Kim Phillips , Florian Fischer , Ravi Bangoria , Xing Zhengjun , Rob Herring , Kang Minchul , linux-arm-kernel@lists.infradead.org, linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org, Sandipan Das , Jing Zhang , linuxppc-dev@lists.ozlabs.org, Kajol Jain Cc: Stephane Eranian , Perry Taylor , Caleb Biggers , Ian Rogers X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20221221_143447_290571_3617207A X-CRM114-Status: GOOD ( 15.24 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org Add RewriteMetricsInTermsOfOthers that iterates over pairs of names and expressions trying to replace an expression, within the current expression, with its name. Signed-off-by: Ian Rogers --- tools/perf/pmu-events/metric.py | 73 +++++++++++++++++++++++++++- tools/perf/pmu-events/metric_test.py | 10 ++++ 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/tools/perf/pmu-events/metric.py b/tools/perf/pmu-events/metric.py index 2f2fd220e843..ed13efac7389 100644 --- a/tools/perf/pmu-events/metric.py +++ b/tools/perf/pmu-events/metric.py @@ -4,7 +4,7 @@ import ast import decimal import json import re -from typing import Dict, List, Optional, Set, Union +from typing import Dict, List, Optional, Set, Tuple, Union class Expression: @@ -26,6 +26,9 @@ class Expression: """Returns true when two expressions are the same.""" raise NotImplementedError() + def Substitute(self, name: str, expression: 'Expression') -> 'Expression': + raise NotImplementedError() + def __str__(self) -> str: return self.ToPerfJson() @@ -186,6 +189,15 @@ class Operator(Expression): other.lhs) and self.rhs.Equals(other.rhs) return False + def Substitute(self, name: str, expression: Expression) -> Expression: + if self.Equals(expression): + return Event(name) + lhs = self.lhs.Substitute(name, expression) + rhs = None + if self.rhs: + rhs = self.rhs.Substitute(name, expression) + return Operator(self.operator, lhs, rhs) + class Select(Expression): """Represents a select ternary in the parse tree.""" @@ -225,6 +237,14 @@ class Select(Expression): other.false_val) and self.true_val.Equals(other.true_val) return False + def Substitute(self, name: str, expression: Expression) -> Expression: + if self.Equals(expression): + return Event(name) + true_val = self.true_val.Substitute(name, expression) + cond = self.cond.Substitute(name, expression) + false_val = self.false_val.Substitute(name, expression) + return Select(true_val, cond, false_val) + class Function(Expression): """A function in an expression like min, max, d_ratio.""" @@ -267,6 +287,15 @@ class Function(Expression): return result return False + def Substitute(self, name: str, expression: Expression) -> Expression: + if self.Equals(expression): + return Event(name) + lhs = self.lhs.Substitute(name, expression) + rhs = None + if self.rhs: + rhs = self.rhs.Substitute(name, expression) + return Function(self.fn, lhs, rhs) + def _FixEscapes(s: str) -> str: s = re.sub(r'([^\\]),', r'\1\\,', s) @@ -293,6 +322,9 @@ class Event(Expression): def Equals(self, other: Expression) -> bool: return isinstance(other, Event) and self.name == other.name + def Substitute(self, name: str, expression: Expression) -> Expression: + return self + class Constant(Expression): """A constant within the expression tree.""" @@ -317,6 +349,9 @@ class Constant(Expression): def Equals(self, other: Expression) -> bool: return isinstance(other, Constant) and self.value == other.value + def Substitute(self, name: str, expression: Expression) -> Expression: + return self + class Literal(Expression): """A runtime literal within the expression tree.""" @@ -336,6 +371,9 @@ class Literal(Expression): def Equals(self, other: Expression) -> bool: return isinstance(other, Literal) and self.value == other.value + def Substitute(self, name: str, expression: Expression) -> Expression: + return self + def min(lhs: Union[int, float, Expression], rhs: Union[int, float, Expression]) -> Function: @@ -461,6 +499,7 @@ class MetricGroup: class _RewriteIfExpToSelect(ast.NodeTransformer): + """Transformer to convert if-else nodes to Select expressions.""" def visit_IfExp(self, node): # pylint: disable=invalid-name @@ -498,7 +537,37 @@ def ParsePerfJson(orig: str) -> Expression: for kw in keywords: py = re.sub(rf'Event\(r"{kw}"\)', kw, py) - parsed = ast.parse(py, mode='eval') + try: + parsed = ast.parse(py, mode='eval') + except SyntaxError as e: + raise SyntaxError(f'Parsing expression:\n{orig}') from e _RewriteIfExpToSelect().visit(parsed) parsed = ast.fix_missing_locations(parsed) return _Constify(eval(compile(parsed, orig, 'eval'))) + + +def RewriteMetricsInTermsOfOthers(metrics: list[Tuple[str, Expression]] + )-> Dict[str, Expression]: + """Shorten metrics by rewriting in terms of others. + + Args: + metrics (list): pairs of metric names and their expressions. + Returns: + Dict: mapping from a metric name to a shortened expression. + """ + updates: Dict[str, Expression] = dict() + for outer_name, outer_expression in metrics: + updated = outer_expression + while True: + for inner_name, inner_expression in metrics: + if inner_name.lower() == outer_name.lower(): + continue + if inner_name in updates: + inner_expression = updates[inner_name] + updated = updated.Substitute(inner_name, inner_expression) + if updated.Equals(outer_expression): + break + if outer_name in updates and updated.Equals(updates[outer_name]): + break + updates[outer_name] = updated + return updates diff --git a/tools/perf/pmu-events/metric_test.py b/tools/perf/pmu-events/metric_test.py index 15315d0f716c..ced5998bd827 100644 --- a/tools/perf/pmu-events/metric_test.py +++ b/tools/perf/pmu-events/metric_test.py @@ -2,7 +2,9 @@ import unittest from metric import Constant from metric import Event +from metric import Expression from metric import ParsePerfJson +from metric import RewriteMetricsInTermsOfOthers class TestMetricExpressions(unittest.TestCase): @@ -153,5 +155,13 @@ class TestMetricExpressions(unittest.TestCase): after = '0 * SLOTS' self.assertEqual(ParsePerfJson(before).Simplify().ToPerfJson(), after) + def test_RewriteMetricsInTermsOfOthers(self): + Expression.__eq__ = lambda e1, e2: e1.Equals(e2) + before = [('m1', ParsePerfJson('a + b + c + d')), + ('m2', ParsePerfJson('a + b + c'))] + after = {'m1': ParsePerfJson('m2 + d')} + self.assertEqual(RewriteMetricsInTermsOfOthers(before), after) + Expression.__eq__ = None + if __name__ == '__main__': unittest.main()