Commit 2efbb73d authored by Ian Rogers's avatar Ian Rogers Committed by Arnaldo Carvalho de Melo

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: default avatarKajol Jain <kjain@linux.ibm.com>
Signed-off-by: default avatarIan 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.comSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 3241cd11
...@@ -4,7 +4,7 @@ import ast ...@@ -4,7 +4,7 @@ import ast
import decimal import decimal
import json import json
import re import re
from typing import Dict, List, Optional, Set, Union from typing import Dict, List, Optional, Set, Tuple, Union
class Expression: class Expression:
...@@ -26,6 +26,9 @@ class Expression: ...@@ -26,6 +26,9 @@ class Expression:
"""Returns true when two expressions are the same.""" """Returns true when two expressions are the same."""
raise NotImplementedError() raise NotImplementedError()
def Substitute(self, name: str, expression: 'Expression') -> 'Expression':
raise NotImplementedError()
def __str__(self) -> str: def __str__(self) -> str:
return self.ToPerfJson() return self.ToPerfJson()
...@@ -186,6 +189,15 @@ class Operator(Expression): ...@@ -186,6 +189,15 @@ class Operator(Expression):
other.lhs) and self.rhs.Equals(other.rhs) other.lhs) and self.rhs.Equals(other.rhs)
return False 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): class Select(Expression):
"""Represents a select ternary in the parse tree.""" """Represents a select ternary in the parse tree."""
...@@ -225,6 +237,14 @@ class Select(Expression): ...@@ -225,6 +237,14 @@ class Select(Expression):
other.false_val) and self.true_val.Equals(other.true_val) other.false_val) and self.true_val.Equals(other.true_val)
return False 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): class Function(Expression):
"""A function in an expression like min, max, d_ratio.""" """A function in an expression like min, max, d_ratio."""
...@@ -267,6 +287,15 @@ class Function(Expression): ...@@ -267,6 +287,15 @@ class Function(Expression):
return result return result
return False 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: def _FixEscapes(s: str) -> str:
s = re.sub(r'([^\\]),', r'\1\\,', s) s = re.sub(r'([^\\]),', r'\1\\,', s)
...@@ -293,6 +322,9 @@ class Event(Expression): ...@@ -293,6 +322,9 @@ class Event(Expression):
def Equals(self, other: Expression) -> bool: def Equals(self, other: Expression) -> bool:
return isinstance(other, Event) and self.name == other.name return isinstance(other, Event) and self.name == other.name
def Substitute(self, name: str, expression: Expression) -> Expression:
return self
class Constant(Expression): class Constant(Expression):
"""A constant within the expression tree.""" """A constant within the expression tree."""
...@@ -317,6 +349,9 @@ class Constant(Expression): ...@@ -317,6 +349,9 @@ class Constant(Expression):
def Equals(self, other: Expression) -> bool: def Equals(self, other: Expression) -> bool:
return isinstance(other, Constant) and self.value == other.value return isinstance(other, Constant) and self.value == other.value
def Substitute(self, name: str, expression: Expression) -> Expression:
return self
class Literal(Expression): class Literal(Expression):
"""A runtime literal within the expression tree.""" """A runtime literal within the expression tree."""
...@@ -336,6 +371,9 @@ class Literal(Expression): ...@@ -336,6 +371,9 @@ class Literal(Expression):
def Equals(self, other: Expression) -> bool: def Equals(self, other: Expression) -> bool:
return isinstance(other, Literal) and self.value == other.value 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, def min(lhs: Union[int, float, Expression], rhs: Union[int, float,
Expression]) -> Function: Expression]) -> Function:
...@@ -461,6 +499,7 @@ class MetricGroup: ...@@ -461,6 +499,7 @@ class MetricGroup:
class _RewriteIfExpToSelect(ast.NodeTransformer): class _RewriteIfExpToSelect(ast.NodeTransformer):
"""Transformer to convert if-else nodes to Select expressions."""
def visit_IfExp(self, node): def visit_IfExp(self, node):
# pylint: disable=invalid-name # pylint: disable=invalid-name
...@@ -498,7 +537,37 @@ def ParsePerfJson(orig: str) -> Expression: ...@@ -498,7 +537,37 @@ def ParsePerfJson(orig: str) -> Expression:
for kw in keywords: for kw in keywords:
py = re.sub(rf'Event\(r"{kw}"\)', kw, py) py = re.sub(rf'Event\(r"{kw}"\)', kw, py)
try:
parsed = ast.parse(py, mode='eval') parsed = ast.parse(py, mode='eval')
except SyntaxError as e:
raise SyntaxError(f'Parsing expression:\n{orig}') from e
_RewriteIfExpToSelect().visit(parsed) _RewriteIfExpToSelect().visit(parsed)
parsed = ast.fix_missing_locations(parsed) parsed = ast.fix_missing_locations(parsed)
return _Constify(eval(compile(parsed, orig, 'eval'))) 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
...@@ -2,7 +2,9 @@ ...@@ -2,7 +2,9 @@
import unittest import unittest
from metric import Constant from metric import Constant
from metric import Event from metric import Event
from metric import Expression
from metric import ParsePerfJson from metric import ParsePerfJson
from metric import RewriteMetricsInTermsOfOthers
class TestMetricExpressions(unittest.TestCase): class TestMetricExpressions(unittest.TestCase):
...@@ -153,5 +155,13 @@ class TestMetricExpressions(unittest.TestCase): ...@@ -153,5 +155,13 @@ class TestMetricExpressions(unittest.TestCase):
after = '0 * SLOTS' after = '0 * SLOTS'
self.assertEqual(ParsePerfJson(before).Simplify().ToPerfJson(), after) 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__': if __name__ == '__main__':
unittest.main() unittest.main()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment