From mboxrd@z Thu Jan 1 00:00:00 1970 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DE7D5C27C76 for ; Thu, 26 Jan 2023 01:19:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236154AbjAZBT3 (ORCPT ); Wed, 25 Jan 2023 20:19:29 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37162 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236206AbjAZBT1 (ORCPT ); Wed, 25 Jan 2023 20:19:27 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AFB5961D65 for ; Wed, 25 Jan 2023 17:19:23 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id a14-20020a5b0ace000000b007bf99065fcbso377077ybr.2 for ; Wed, 25 Jan 2023 17:19:23 -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=huD0JuZGEuI+QB16/3OseXYdE1VwZ132kWp/N/R3RrQ=; b=rTbDbMoZwX1IxiSAhBMvgsAMkwqnPSsCsT3OJVNe8UCTB/FWTprz5k4uKZp1tDZ7lF duLEgY0/eRTz7UK6fvuf2BUpehJvLT5EfvcQ2ERgEj3mMXfpcN32qGV95ZLPxlFKFat6 gxGUbfLV2lIuWvBk42OWCvATH8vWKdZfRGTf9iZ3hReoi9b54Ji6SVJsUsROsBPZ5zfa RUXgztW1PdZ5x/JW7fHO2R8nM3cplbx4cM4jIL2AUR4SpjIBb2l72iodLt9cLTf/ylZy /AZP1zGM0b00OZ6/Dl5o2ximJbHk2eytTwW9w7RdiPgvK1bL0//e3wmE+VpFGOoHfhUZ QlcA== 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=huD0JuZGEuI+QB16/3OseXYdE1VwZ132kWp/N/R3RrQ=; b=PetJoWs31qd8NyZB/CAUMuYaL/rLLwNqcoFUr0EqczQYkpMEo/Qhj66A8Z9ReEATbC 6AMT3Cicb+XTsqbSqacjy2ZsAhVnHy3DUGcyAY3J+erE+28Xly6fFTZogT0JlksarrUP DGV0SR9PelpOIiNpFojPd+Q2cgcSrkWwp9+WFPvmXXirRQd1Dm/EDAA4+K3haTn3tzcq G2/22PnYrbcGgUL/qmYdg0q21s1t9nHKCVJn5yNLxySacxh6fB1NvPnEzBSMS11QIfzp asLjJHvlCne2szHEAo4CWRfMHFvwEmbW0xLPJgxGfSaVLHnhQYIk10KSVH3S1uhH4R4K +3BA== X-Gm-Message-State: AO0yUKXghD3+VFGrDZw4HCRyxJr99xVfWg5yLghZgIh3QHtIGKvr6q7z PC6UHJxxrdAKwFs1QDUS7dmN60Ojfryb X-Google-Smtp-Source: AK7set9vO1vrQhi595V0ugIEqxSyCu9JqVLPO/3EUj900ZH0P/aY8yQ4pOCJKudRnOJIIVkFi/3xg+JuKtlm X-Received: from irogers.svl.corp.google.com ([2620:15c:2d4:203:568d:4d98:b468:b025]) (user=irogers job=sendgmr) by 2002:a25:abee:0:b0:80b:82a1:c2f7 with SMTP id v101-20020a25abee000000b0080b82a1c2f7mr841341ybi.391.1674695962350; Wed, 25 Jan 2023 17:19:22 -0800 (PST) Date: Wed, 25 Jan 2023 17:18:44 -0800 In-Reply-To: <20230126011854.198243-1-irogers@google.com> Message-Id: <20230126011854.198243-3-irogers@google.com> Mime-Version: 1.0 References: <20230126011854.198243-1-irogers@google.com> X-Mailer: git-send-email 2.39.1.456.gfc5497dd1b-goog Subject: [PATCH v4 02/12] 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 Content-Type: text/plain; charset="UTF-8" Precedence: bulk List-ID: X-Mailing-List: linux-perf-users@vger.kernel.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() -- 2.39.1.456.gfc5497dd1b-goog From mboxrd@z Thu Jan 1 00:00:00 1970 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 lists.ozlabs.org (lists.ozlabs.org [112.213.38.117]) (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 34CE6C54E94 for ; Thu, 26 Jan 2023 01:21:51 +0000 (UTC) Received: from boromir.ozlabs.org (localhost [IPv6:::1]) by lists.ozlabs.org (Postfix) with ESMTP id 4P2NGP5x2Hz3fDb for ; Thu, 26 Jan 2023 12:21:49 +1100 (AEDT) Authentication-Results: lists.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20210112 header.b=rTbDbMoZ; dkim-atps=neutral Authentication-Results: lists.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=flex--irogers.bounces.google.com (client-ip=2607:f8b0:4864:20::b4a; helo=mail-yb1-xb4a.google.com; envelope-from=3gtxrywckddevebtreftbbtyr.pbzyvahkccp-qriyvfgf.bmynof.bet@flex--irogers.bounces.google.com; receiver=) Authentication-Results: lists.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20210112 header.b=rTbDbMoZ; dkim-atps=neutral Received: from mail-yb1-xb4a.google.com (mail-yb1-xb4a.google.com [IPv6:2607:f8b0:4864:20::b4a]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 4P2NCc5tsGz3chL for ; Thu, 26 Jan 2023 12:19:24 +1100 (AEDT) Received: by mail-yb1-xb4a.google.com with SMTP id x188-20020a2531c5000000b00716de19d76bso318058ybx.19 for ; Wed, 25 Jan 2023 17:19:24 -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=huD0JuZGEuI+QB16/3OseXYdE1VwZ132kWp/N/R3RrQ=; b=rTbDbMoZwX1IxiSAhBMvgsAMkwqnPSsCsT3OJVNe8UCTB/FWTprz5k4uKZp1tDZ7lF duLEgY0/eRTz7UK6fvuf2BUpehJvLT5EfvcQ2ERgEj3mMXfpcN32qGV95ZLPxlFKFat6 gxGUbfLV2lIuWvBk42OWCvATH8vWKdZfRGTf9iZ3hReoi9b54Ji6SVJsUsROsBPZ5zfa RUXgztW1PdZ5x/JW7fHO2R8nM3cplbx4cM4jIL2AUR4SpjIBb2l72iodLt9cLTf/ylZy /AZP1zGM0b00OZ6/Dl5o2ximJbHk2eytTwW9w7RdiPgvK1bL0//e3wmE+VpFGOoHfhUZ QlcA== 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=huD0JuZGEuI+QB16/3OseXYdE1VwZ132kWp/N/R3RrQ=; b=lZ0eJabth0htbk5EI5MH0PZdcLj6SbN6vMBzj9vBA1HfNQm8U+VylH03WjKfW4zHmj Ey38u0qihycQ6Qng7amabxxN5OlTjgLJebHZcPlMdD1dBG8gbFxtVjGK18vGcMDcpyn+ 5y3wF6KXLgsyRdOlIxKpP8hhEVFKVMpm4J6Jiu7tjdtZSRzI6vjh0I+/LfYP5ztIY4AV 9vQ+JbZYWjKF4GeurnBJeqor01TveWJuzu2nbIt2/sM0FtZ0+Zkttmi39YwwdLWzUS3K 3JNPoNeo64ehkYF/pmxUroQ3sat73Qa+gdxzZKxIX8C0BCeF1+twSaAlZU/NMd2qF9ie lT6Q== X-Gm-Message-State: AO0yUKVPwZ5lAoSwF/wh8O2Wn0O2rm2jgacaGU0WPtzug6L9rXDaeLxE dLIcIPPuSOCUtGTIMAPk2321k+WkJYhV X-Google-Smtp-Source: AK7set9vO1vrQhi595V0ugIEqxSyCu9JqVLPO/3EUj900ZH0P/aY8yQ4pOCJKudRnOJIIVkFi/3xg+JuKtlm X-Received: from irogers.svl.corp.google.com ([2620:15c:2d4:203:568d:4d98:b468:b025]) (user=irogers job=sendgmr) by 2002:a25:abee:0:b0:80b:82a1:c2f7 with SMTP id v101-20020a25abee000000b0080b82a1c2f7mr841341ybi.391.1674695962350; Wed, 25 Jan 2023 17:19:22 -0800 (PST) Date: Wed, 25 Jan 2023 17:18:44 -0800 In-Reply-To: <20230126011854.198243-1-irogers@google.com> Message-Id: <20230126011854.198243-3-irogers@google.com> Mime-Version: 1.0 References: <20230126011854.198243-1-irogers@google.com> X-Mailer: git-send-email 2.39.1.456.gfc5497dd1b-goog Subject: [PATCH v4 02/12] 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 Content-Type: text/plain; charset="UTF-8" X-BeenThere: linuxppc-dev@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Linux on PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Ian Rogers , Perry Taylor , Caleb Biggers , Stephane Eranian Errors-To: linuxppc-dev-bounces+linuxppc-dev=archiver.kernel.org@lists.ozlabs.org Sender: "Linuxppc-dev" 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() -- 2.39.1.456.gfc5497dd1b-goog From mboxrd@z Thu Jan 1 00:00:00 1970 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 CF42CC54E94 for ; Thu, 26 Jan 2023 01:20:39 +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=af6xGsMUbd1ceu8ufdZkF1HM8UBEV21N69w6zKcZsCE=; b=tdDkaz3gik1yDu6STk+8w0F5xL J2eBdMF7uD5b505natcOl4BBSjiwxZHLCSKVj4agyM3cvVzlZPxeE9scw1YG/jqyQlN/0rW9mvtyO e1pkIQld2SM29zz1/kXcLVvxH0jjyU9e4xDqvd7RbrjX2pnNbMEjQ155MxaaMebxS3+5/kiru6eP+ si1bF4foK6vSk0wlYJ8gMBQjyMOXT1IdpvfqqdHZU0x/dMccvH4F/K985MZDGEz/vPtiRZpKKh+oG mpEH7fmphUh+I60QvmjB6LzYXhm/nCqYnkyfrBsEFFHYh8sQPODktBhvZSoHVkakx8pmDIkUEoKwI ahiitpXw==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1pKqvM-009JJS-QH; Thu, 26 Jan 2023 01:19:33 +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 1pKqvG-009JGx-DW for linux-arm-kernel@lists.infradead.org; Thu, 26 Jan 2023 01:19:28 +0000 Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-5028c723d28so3723257b3.15 for ; Wed, 25 Jan 2023 17:19:23 -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=huD0JuZGEuI+QB16/3OseXYdE1VwZ132kWp/N/R3RrQ=; b=rTbDbMoZwX1IxiSAhBMvgsAMkwqnPSsCsT3OJVNe8UCTB/FWTprz5k4uKZp1tDZ7lF duLEgY0/eRTz7UK6fvuf2BUpehJvLT5EfvcQ2ERgEj3mMXfpcN32qGV95ZLPxlFKFat6 gxGUbfLV2lIuWvBk42OWCvATH8vWKdZfRGTf9iZ3hReoi9b54Ji6SVJsUsROsBPZ5zfa RUXgztW1PdZ5x/JW7fHO2R8nM3cplbx4cM4jIL2AUR4SpjIBb2l72iodLt9cLTf/ylZy /AZP1zGM0b00OZ6/Dl5o2ximJbHk2eytTwW9w7RdiPgvK1bL0//e3wmE+VpFGOoHfhUZ QlcA== 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=huD0JuZGEuI+QB16/3OseXYdE1VwZ132kWp/N/R3RrQ=; b=g3uyc2WS12oHHKIfRlXzK+j1ttLFjRmc2Bilh7fXOGQXRUK5Ir/RtBRFSJ61RmOQno t97W1RDv5wl+yJqzcrfSqd8rMFO+589fsf9zzLI4fV9qiNDpFXpAaoZZdm19mSjgh0X0 XbvOFgjL86CipF7DWJTvpId0i5tRpr09+fEn3VFXeScuwA/4jSNGK+rNTJ4KXcmSEcOi 8O3Jq6/OlmTo6b/j2BfehsJ7iGt9BfGJCOOy8fzoJjlMDdt+gd76nyGqUEX6oaoj0lPg Joh85QGMhut8MTD/DNNx204fVyn6AV7xSeZ4eC7yJs+N3+R82M6ge6IaC/QX3Oke34D5 1SiQ== X-Gm-Message-State: AO0yUKVSMPaip85loF2IH5oMbqSA4hnZGIT1wKo8Os5I0RsKqNPkHT8k TWWEJc7JXsvpYB0QjR6VSLWhnWmfnHUM X-Google-Smtp-Source: AK7set9vO1vrQhi595V0ugIEqxSyCu9JqVLPO/3EUj900ZH0P/aY8yQ4pOCJKudRnOJIIVkFi/3xg+JuKtlm X-Received: from irogers.svl.corp.google.com ([2620:15c:2d4:203:568d:4d98:b468:b025]) (user=irogers job=sendgmr) by 2002:a25:abee:0:b0:80b:82a1:c2f7 with SMTP id v101-20020a25abee000000b0080b82a1c2f7mr841341ybi.391.1674695962350; Wed, 25 Jan 2023 17:19:22 -0800 (PST) Date: Wed, 25 Jan 2023 17:18:44 -0800 In-Reply-To: <20230126011854.198243-1-irogers@google.com> Message-Id: <20230126011854.198243-3-irogers@google.com> Mime-Version: 1.0 References: <20230126011854.198243-1-irogers@google.com> X-Mailer: git-send-email 2.39.1.456.gfc5497dd1b-goog Subject: [PATCH v4 02/12] 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-20230125_171926_489751_F616AA71 X-CRM114-Status: GOOD ( 15.89 ) 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: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit 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() -- 2.39.1.456.gfc5497dd1b-goog _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel