perf jevents metric: Add ability to rewrite metrics in terms of others

Add RewriteMetricsInTermsOfOthers that iterates over pairs of names and
expressions trying to replace an expression, within the current
expression, with its name.

Reviewed-by: Kajol Jain <kjain@linux.ibm.com>
Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Caleb Biggers <caleb.biggers@intel.com>
Cc: Florian Fischer <florian.fischer@muhq.space>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jing Zhang <renyu.zj@linux.alibaba.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: John Garry <john.g.garry@oracle.com>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Kang Minchul <tegongkang@gmail.com>
Cc: Kim Phillips <kim.phillips@amd.com>
Cc: Leo Yan <leo.yan@linaro.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Mike Leach <mike.leach@linaro.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Perry Taylor <perry.taylor@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ravi Bangoria <ravi.bangoria@amd.com>
Cc: Rob Herring <robh@kernel.org>
Cc: Sandipan Das <sandipan.das@amd.com>
Cc: Stephane Eranian <eranian@google.com>
Cc: Will Deacon <will@kernel.org>
Cc: Xing Zhengjun <zhengjun.xing@linux.intel.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: linuxppc-dev@lists.ozlabs.org
Link: https://lore.kernel.org/r/20230126233645.200509-3-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Ian Rogers 2023-01-26 15:36:32 -08:00 committed by Arnaldo Carvalho de Melo
parent 3241cd11d9
commit 2efbb73d46
2 changed files with 81 additions and 2 deletions

View file

@ -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

View file

@ -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()