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

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.com


Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 3241cd11
Loading
Loading
Loading
Loading
+71 −2
Original line number Diff line number Diff line
@@ -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)

  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
+10 −0
Original line number Diff line number Diff line
@@ -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()