162306a36Sopenharmony_ci# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 262306a36Sopenharmony_ci"""Parse or generate representations of perf metrics.""" 362306a36Sopenharmony_ciimport ast 462306a36Sopenharmony_ciimport decimal 562306a36Sopenharmony_ciimport json 662306a36Sopenharmony_ciimport re 762306a36Sopenharmony_cifrom typing import Dict, List, Optional, Set, Tuple, Union 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ciclass Expression: 1162306a36Sopenharmony_ci """Abstract base class of elements in a metric expression.""" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci def ToPerfJson(self) -> str: 1462306a36Sopenharmony_ci """Returns a perf json file encoded representation.""" 1562306a36Sopenharmony_ci raise NotImplementedError() 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci def ToPython(self) -> str: 1862306a36Sopenharmony_ci """Returns a python expr parseable representation.""" 1962306a36Sopenharmony_ci raise NotImplementedError() 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci def Simplify(self): 2262306a36Sopenharmony_ci """Returns a simplified version of self.""" 2362306a36Sopenharmony_ci raise NotImplementedError() 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci def Equals(self, other) -> bool: 2662306a36Sopenharmony_ci """Returns true when two expressions are the same.""" 2762306a36Sopenharmony_ci raise NotImplementedError() 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci def Substitute(self, name: str, expression: 'Expression') -> 'Expression': 3062306a36Sopenharmony_ci raise NotImplementedError() 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci def __str__(self) -> str: 3362306a36Sopenharmony_ci return self.ToPerfJson() 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci def __or__(self, other: Union[int, float, 'Expression']) -> 'Operator': 3662306a36Sopenharmony_ci return Operator('|', self, other) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci def __ror__(self, other: Union[int, float, 'Expression']) -> 'Operator': 3962306a36Sopenharmony_ci return Operator('|', other, self) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci def __xor__(self, other: Union[int, float, 'Expression']) -> 'Operator': 4262306a36Sopenharmony_ci return Operator('^', self, other) 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci def __and__(self, other: Union[int, float, 'Expression']) -> 'Operator': 4562306a36Sopenharmony_ci return Operator('&', self, other) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci def __rand__(self, other: Union[int, float, 'Expression']) -> 'Operator': 4862306a36Sopenharmony_ci return Operator('&', other, self) 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci def __lt__(self, other: Union[int, float, 'Expression']) -> 'Operator': 5162306a36Sopenharmony_ci return Operator('<', self, other) 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci def __gt__(self, other: Union[int, float, 'Expression']) -> 'Operator': 5462306a36Sopenharmony_ci return Operator('>', self, other) 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci def __add__(self, other: Union[int, float, 'Expression']) -> 'Operator': 5762306a36Sopenharmony_ci return Operator('+', self, other) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci def __radd__(self, other: Union[int, float, 'Expression']) -> 'Operator': 6062306a36Sopenharmony_ci return Operator('+', other, self) 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci def __sub__(self, other: Union[int, float, 'Expression']) -> 'Operator': 6362306a36Sopenharmony_ci return Operator('-', self, other) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci def __rsub__(self, other: Union[int, float, 'Expression']) -> 'Operator': 6662306a36Sopenharmony_ci return Operator('-', other, self) 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci def __mul__(self, other: Union[int, float, 'Expression']) -> 'Operator': 6962306a36Sopenharmony_ci return Operator('*', self, other) 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci def __rmul__(self, other: Union[int, float, 'Expression']) -> 'Operator': 7262306a36Sopenharmony_ci return Operator('*', other, self) 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci def __truediv__(self, other: Union[int, float, 'Expression']) -> 'Operator': 7562306a36Sopenharmony_ci return Operator('/', self, other) 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci def __rtruediv__(self, other: Union[int, float, 'Expression']) -> 'Operator': 7862306a36Sopenharmony_ci return Operator('/', other, self) 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci def __mod__(self, other: Union[int, float, 'Expression']) -> 'Operator': 8162306a36Sopenharmony_ci return Operator('%', self, other) 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cidef _Constify(val: Union[bool, int, float, Expression]) -> Expression: 8562306a36Sopenharmony_ci """Used to ensure that the nodes in the expression tree are all Expression.""" 8662306a36Sopenharmony_ci if isinstance(val, bool): 8762306a36Sopenharmony_ci return Constant(1 if val else 0) 8862306a36Sopenharmony_ci if isinstance(val, (int, float)): 8962306a36Sopenharmony_ci return Constant(val) 9062306a36Sopenharmony_ci return val 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci# Simple lookup for operator precedence, used to avoid unnecessary 9462306a36Sopenharmony_ci# brackets. Precedence matches that of the simple expression parser 9562306a36Sopenharmony_ci# but differs from python where comparisons are lower precedence than 9662306a36Sopenharmony_ci# the bitwise &, ^, | but not the logical versions that the expression 9762306a36Sopenharmony_ci# parser doesn't have. 9862306a36Sopenharmony_ci_PRECEDENCE = { 9962306a36Sopenharmony_ci '|': 0, 10062306a36Sopenharmony_ci '^': 1, 10162306a36Sopenharmony_ci '&': 2, 10262306a36Sopenharmony_ci '<': 3, 10362306a36Sopenharmony_ci '>': 3, 10462306a36Sopenharmony_ci '+': 4, 10562306a36Sopenharmony_ci '-': 4, 10662306a36Sopenharmony_ci '*': 5, 10762306a36Sopenharmony_ci '/': 5, 10862306a36Sopenharmony_ci '%': 5, 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ciclass Operator(Expression): 11362306a36Sopenharmony_ci """Represents a binary operator in the parse tree.""" 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci def __init__(self, operator: str, lhs: Union[int, float, Expression], 11662306a36Sopenharmony_ci rhs: Union[int, float, Expression]): 11762306a36Sopenharmony_ci self.operator = operator 11862306a36Sopenharmony_ci self.lhs = _Constify(lhs) 11962306a36Sopenharmony_ci self.rhs = _Constify(rhs) 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci def Bracket(self, 12262306a36Sopenharmony_ci other: Expression, 12362306a36Sopenharmony_ci other_str: str, 12462306a36Sopenharmony_ci rhs: bool = False) -> str: 12562306a36Sopenharmony_ci """If necessary brackets the given other value. 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci If ``other`` is an operator then a bracket is necessary when 12862306a36Sopenharmony_ci this/self operator has higher precedence. Consider: '(a + b) * c', 12962306a36Sopenharmony_ci ``other_str`` will be 'a + b'. A bracket is necessary as without 13062306a36Sopenharmony_ci the bracket 'a + b * c' will evaluate 'b * c' first. However, '(a 13162306a36Sopenharmony_ci * b) + c' doesn't need a bracket as 'a * b' will always be 13262306a36Sopenharmony_ci evaluated first. For 'a / (b * c)' (ie the same precedence level 13362306a36Sopenharmony_ci operations) then we add the bracket to best match the original 13462306a36Sopenharmony_ci input, but not for '(a / b) * c' where the bracket is unnecessary. 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci Args: 13762306a36Sopenharmony_ci other (Expression): is a lhs or rhs operator 13862306a36Sopenharmony_ci other_str (str): ``other`` in the appropriate string form 13962306a36Sopenharmony_ci rhs (bool): is ``other`` on the RHS 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci Returns: 14262306a36Sopenharmony_ci str: possibly bracketed other_str 14362306a36Sopenharmony_ci """ 14462306a36Sopenharmony_ci if isinstance(other, Operator): 14562306a36Sopenharmony_ci if _PRECEDENCE.get(self.operator, -1) > _PRECEDENCE.get( 14662306a36Sopenharmony_ci other.operator, -1): 14762306a36Sopenharmony_ci return f'({other_str})' 14862306a36Sopenharmony_ci if rhs and _PRECEDENCE.get(self.operator, -1) == _PRECEDENCE.get( 14962306a36Sopenharmony_ci other.operator, -1): 15062306a36Sopenharmony_ci return f'({other_str})' 15162306a36Sopenharmony_ci return other_str 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci def ToPerfJson(self): 15462306a36Sopenharmony_ci return (f'{self.Bracket(self.lhs, self.lhs.ToPerfJson())} {self.operator} ' 15562306a36Sopenharmony_ci f'{self.Bracket(self.rhs, self.rhs.ToPerfJson(), True)}') 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci def ToPython(self): 15862306a36Sopenharmony_ci return (f'{self.Bracket(self.lhs, self.lhs.ToPython())} {self.operator} ' 15962306a36Sopenharmony_ci f'{self.Bracket(self.rhs, self.rhs.ToPython(), True)}') 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci def Simplify(self) -> Expression: 16262306a36Sopenharmony_ci lhs = self.lhs.Simplify() 16362306a36Sopenharmony_ci rhs = self.rhs.Simplify() 16462306a36Sopenharmony_ci if isinstance(lhs, Constant) and isinstance(rhs, Constant): 16562306a36Sopenharmony_ci return Constant(ast.literal_eval(lhs + self.operator + rhs)) 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if isinstance(self.lhs, Constant): 16862306a36Sopenharmony_ci if self.operator in ('+', '|') and lhs.value == '0': 16962306a36Sopenharmony_ci return rhs 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci # Simplify multiplication by 0 except for the slot event which 17262306a36Sopenharmony_ci # is deliberately introduced using this pattern. 17362306a36Sopenharmony_ci if self.operator == '*' and lhs.value == '0' and ( 17462306a36Sopenharmony_ci not isinstance(rhs, Event) or 'slots' not in rhs.name.lower()): 17562306a36Sopenharmony_ci return Constant(0) 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if self.operator == '*' and lhs.value == '1': 17862306a36Sopenharmony_ci return rhs 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if isinstance(rhs, Constant): 18162306a36Sopenharmony_ci if self.operator in ('+', '|') and rhs.value == '0': 18262306a36Sopenharmony_ci return lhs 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if self.operator == '*' and rhs.value == '0': 18562306a36Sopenharmony_ci return Constant(0) 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if self.operator == '*' and self.rhs.value == '1': 18862306a36Sopenharmony_ci return lhs 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci return Operator(self.operator, lhs, rhs) 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci def Equals(self, other: Expression) -> bool: 19362306a36Sopenharmony_ci if isinstance(other, Operator): 19462306a36Sopenharmony_ci return self.operator == other.operator and self.lhs.Equals( 19562306a36Sopenharmony_ci other.lhs) and self.rhs.Equals(other.rhs) 19662306a36Sopenharmony_ci return False 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci def Substitute(self, name: str, expression: Expression) -> Expression: 19962306a36Sopenharmony_ci if self.Equals(expression): 20062306a36Sopenharmony_ci return Event(name) 20162306a36Sopenharmony_ci lhs = self.lhs.Substitute(name, expression) 20262306a36Sopenharmony_ci rhs = None 20362306a36Sopenharmony_ci if self.rhs: 20462306a36Sopenharmony_ci rhs = self.rhs.Substitute(name, expression) 20562306a36Sopenharmony_ci return Operator(self.operator, lhs, rhs) 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ciclass Select(Expression): 20962306a36Sopenharmony_ci """Represents a select ternary in the parse tree.""" 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci def __init__(self, true_val: Union[int, float, Expression], 21262306a36Sopenharmony_ci cond: Union[int, float, Expression], 21362306a36Sopenharmony_ci false_val: Union[int, float, Expression]): 21462306a36Sopenharmony_ci self.true_val = _Constify(true_val) 21562306a36Sopenharmony_ci self.cond = _Constify(cond) 21662306a36Sopenharmony_ci self.false_val = _Constify(false_val) 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci def ToPerfJson(self): 21962306a36Sopenharmony_ci true_str = self.true_val.ToPerfJson() 22062306a36Sopenharmony_ci cond_str = self.cond.ToPerfJson() 22162306a36Sopenharmony_ci false_str = self.false_val.ToPerfJson() 22262306a36Sopenharmony_ci return f'({true_str} if {cond_str} else {false_str})' 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci def ToPython(self): 22562306a36Sopenharmony_ci return (f'Select({self.true_val.ToPython()}, {self.cond.ToPython()}, ' 22662306a36Sopenharmony_ci f'{self.false_val.ToPython()})') 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci def Simplify(self) -> Expression: 22962306a36Sopenharmony_ci cond = self.cond.Simplify() 23062306a36Sopenharmony_ci true_val = self.true_val.Simplify() 23162306a36Sopenharmony_ci false_val = self.false_val.Simplify() 23262306a36Sopenharmony_ci if isinstance(cond, Constant): 23362306a36Sopenharmony_ci return false_val if cond.value == '0' else true_val 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if true_val.Equals(false_val): 23662306a36Sopenharmony_ci return true_val 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci return Select(true_val, cond, false_val) 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci def Equals(self, other: Expression) -> bool: 24162306a36Sopenharmony_ci if isinstance(other, Select): 24262306a36Sopenharmony_ci return self.cond.Equals(other.cond) and self.false_val.Equals( 24362306a36Sopenharmony_ci other.false_val) and self.true_val.Equals(other.true_val) 24462306a36Sopenharmony_ci return False 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci def Substitute(self, name: str, expression: Expression) -> Expression: 24762306a36Sopenharmony_ci if self.Equals(expression): 24862306a36Sopenharmony_ci return Event(name) 24962306a36Sopenharmony_ci true_val = self.true_val.Substitute(name, expression) 25062306a36Sopenharmony_ci cond = self.cond.Substitute(name, expression) 25162306a36Sopenharmony_ci false_val = self.false_val.Substitute(name, expression) 25262306a36Sopenharmony_ci return Select(true_val, cond, false_val) 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ciclass Function(Expression): 25662306a36Sopenharmony_ci """A function in an expression like min, max, d_ratio.""" 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci def __init__(self, 25962306a36Sopenharmony_ci fn: str, 26062306a36Sopenharmony_ci lhs: Union[int, float, Expression], 26162306a36Sopenharmony_ci rhs: Optional[Union[int, float, Expression]] = None): 26262306a36Sopenharmony_ci self.fn = fn 26362306a36Sopenharmony_ci self.lhs = _Constify(lhs) 26462306a36Sopenharmony_ci self.rhs = _Constify(rhs) 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci def ToPerfJson(self): 26762306a36Sopenharmony_ci if self.rhs: 26862306a36Sopenharmony_ci return f'{self.fn}({self.lhs.ToPerfJson()}, {self.rhs.ToPerfJson()})' 26962306a36Sopenharmony_ci return f'{self.fn}({self.lhs.ToPerfJson()})' 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci def ToPython(self): 27262306a36Sopenharmony_ci if self.rhs: 27362306a36Sopenharmony_ci return f'{self.fn}({self.lhs.ToPython()}, {self.rhs.ToPython()})' 27462306a36Sopenharmony_ci return f'{self.fn}({self.lhs.ToPython()})' 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci def Simplify(self) -> Expression: 27762306a36Sopenharmony_ci lhs = self.lhs.Simplify() 27862306a36Sopenharmony_ci rhs = self.rhs.Simplify() if self.rhs else None 27962306a36Sopenharmony_ci if isinstance(lhs, Constant) and isinstance(rhs, Constant): 28062306a36Sopenharmony_ci if self.fn == 'd_ratio': 28162306a36Sopenharmony_ci if rhs.value == '0': 28262306a36Sopenharmony_ci return Constant(0) 28362306a36Sopenharmony_ci Constant(ast.literal_eval(f'{lhs} / {rhs}')) 28462306a36Sopenharmony_ci return Constant(ast.literal_eval(f'{self.fn}({lhs}, {rhs})')) 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci return Function(self.fn, lhs, rhs) 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci def Equals(self, other: Expression) -> bool: 28962306a36Sopenharmony_ci if isinstance(other, Function): 29062306a36Sopenharmony_ci result = self.fn == other.fn and self.lhs.Equals(other.lhs) 29162306a36Sopenharmony_ci if self.rhs: 29262306a36Sopenharmony_ci result = result and self.rhs.Equals(other.rhs) 29362306a36Sopenharmony_ci return result 29462306a36Sopenharmony_ci return False 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci def Substitute(self, name: str, expression: Expression) -> Expression: 29762306a36Sopenharmony_ci if self.Equals(expression): 29862306a36Sopenharmony_ci return Event(name) 29962306a36Sopenharmony_ci lhs = self.lhs.Substitute(name, expression) 30062306a36Sopenharmony_ci rhs = None 30162306a36Sopenharmony_ci if self.rhs: 30262306a36Sopenharmony_ci rhs = self.rhs.Substitute(name, expression) 30362306a36Sopenharmony_ci return Function(self.fn, lhs, rhs) 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cidef _FixEscapes(s: str) -> str: 30762306a36Sopenharmony_ci s = re.sub(r'([^\\]),', r'\1\\,', s) 30862306a36Sopenharmony_ci return re.sub(r'([^\\])=', r'\1\\=', s) 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ciclass Event(Expression): 31262306a36Sopenharmony_ci """An event in an expression.""" 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci def __init__(self, name: str, legacy_name: str = ''): 31562306a36Sopenharmony_ci self.name = _FixEscapes(name) 31662306a36Sopenharmony_ci self.legacy_name = _FixEscapes(legacy_name) 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci def ToPerfJson(self): 31962306a36Sopenharmony_ci result = re.sub('/', '@', self.name) 32062306a36Sopenharmony_ci return result 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci def ToPython(self): 32362306a36Sopenharmony_ci return f'Event(r"{self.name}")' 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci def Simplify(self) -> Expression: 32662306a36Sopenharmony_ci return self 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci def Equals(self, other: Expression) -> bool: 32962306a36Sopenharmony_ci return isinstance(other, Event) and self.name == other.name 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci def Substitute(self, name: str, expression: Expression) -> Expression: 33262306a36Sopenharmony_ci return self 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ciclass Constant(Expression): 33662306a36Sopenharmony_ci """A constant within the expression tree.""" 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci def __init__(self, value: Union[float, str]): 33962306a36Sopenharmony_ci ctx = decimal.Context() 34062306a36Sopenharmony_ci ctx.prec = 20 34162306a36Sopenharmony_ci dec = ctx.create_decimal(repr(value) if isinstance(value, float) else value) 34262306a36Sopenharmony_ci self.value = dec.normalize().to_eng_string() 34362306a36Sopenharmony_ci self.value = self.value.replace('+', '') 34462306a36Sopenharmony_ci self.value = self.value.replace('E', 'e') 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci def ToPerfJson(self): 34762306a36Sopenharmony_ci return self.value 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci def ToPython(self): 35062306a36Sopenharmony_ci return f'Constant({self.value})' 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci def Simplify(self) -> Expression: 35362306a36Sopenharmony_ci return self 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci def Equals(self, other: Expression) -> bool: 35662306a36Sopenharmony_ci return isinstance(other, Constant) and self.value == other.value 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci def Substitute(self, name: str, expression: Expression) -> Expression: 35962306a36Sopenharmony_ci return self 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ciclass Literal(Expression): 36362306a36Sopenharmony_ci """A runtime literal within the expression tree.""" 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci def __init__(self, value: str): 36662306a36Sopenharmony_ci self.value = value 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci def ToPerfJson(self): 36962306a36Sopenharmony_ci return self.value 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci def ToPython(self): 37262306a36Sopenharmony_ci return f'Literal({self.value})' 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci def Simplify(self) -> Expression: 37562306a36Sopenharmony_ci return self 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci def Equals(self, other: Expression) -> bool: 37862306a36Sopenharmony_ci return isinstance(other, Literal) and self.value == other.value 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci def Substitute(self, name: str, expression: Expression) -> Expression: 38162306a36Sopenharmony_ci return self 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cidef min(lhs: Union[int, float, Expression], rhs: Union[int, float, 38562306a36Sopenharmony_ci Expression]) -> Function: 38662306a36Sopenharmony_ci # pylint: disable=redefined-builtin 38762306a36Sopenharmony_ci # pylint: disable=invalid-name 38862306a36Sopenharmony_ci return Function('min', lhs, rhs) 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cidef max(lhs: Union[int, float, Expression], rhs: Union[int, float, 39262306a36Sopenharmony_ci Expression]) -> Function: 39362306a36Sopenharmony_ci # pylint: disable=redefined-builtin 39462306a36Sopenharmony_ci # pylint: disable=invalid-name 39562306a36Sopenharmony_ci return Function('max', lhs, rhs) 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cidef d_ratio(lhs: Union[int, float, Expression], 39962306a36Sopenharmony_ci rhs: Union[int, float, Expression]) -> Function: 40062306a36Sopenharmony_ci # pylint: disable=redefined-builtin 40162306a36Sopenharmony_ci # pylint: disable=invalid-name 40262306a36Sopenharmony_ci return Function('d_ratio', lhs, rhs) 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cidef source_count(event: Event) -> Function: 40662306a36Sopenharmony_ci # pylint: disable=redefined-builtin 40762306a36Sopenharmony_ci # pylint: disable=invalid-name 40862306a36Sopenharmony_ci return Function('source_count', event) 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cidef has_event(event: Event) -> Function: 41262306a36Sopenharmony_ci # pylint: disable=redefined-builtin 41362306a36Sopenharmony_ci # pylint: disable=invalid-name 41462306a36Sopenharmony_ci return Function('has_event', event) 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cidef strcmp_cpuid_str(cpuid: Event) -> Function: 41762306a36Sopenharmony_ci # pylint: disable=redefined-builtin 41862306a36Sopenharmony_ci # pylint: disable=invalid-name 41962306a36Sopenharmony_ci return Function('strcmp_cpuid_str', cpuid) 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ciclass Metric: 42262306a36Sopenharmony_ci """An individual metric that will specifiable on the perf command line.""" 42362306a36Sopenharmony_ci groups: Set[str] 42462306a36Sopenharmony_ci expr: Expression 42562306a36Sopenharmony_ci scale_unit: str 42662306a36Sopenharmony_ci constraint: bool 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci def __init__(self, 42962306a36Sopenharmony_ci name: str, 43062306a36Sopenharmony_ci description: str, 43162306a36Sopenharmony_ci expr: Expression, 43262306a36Sopenharmony_ci scale_unit: str, 43362306a36Sopenharmony_ci constraint: bool = False): 43462306a36Sopenharmony_ci self.name = name 43562306a36Sopenharmony_ci self.description = description 43662306a36Sopenharmony_ci self.expr = expr.Simplify() 43762306a36Sopenharmony_ci # Workraound valid_only_metric hiding certain metrics based on unit. 43862306a36Sopenharmony_ci scale_unit = scale_unit.replace('/sec', ' per sec') 43962306a36Sopenharmony_ci if scale_unit[0].isdigit(): 44062306a36Sopenharmony_ci self.scale_unit = scale_unit 44162306a36Sopenharmony_ci else: 44262306a36Sopenharmony_ci self.scale_unit = f'1{scale_unit}' 44362306a36Sopenharmony_ci self.constraint = constraint 44462306a36Sopenharmony_ci self.groups = set() 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci def __lt__(self, other): 44762306a36Sopenharmony_ci """Sort order.""" 44862306a36Sopenharmony_ci return self.name < other.name 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci def AddToMetricGroup(self, group): 45162306a36Sopenharmony_ci """Callback used when being added to a MetricGroup.""" 45262306a36Sopenharmony_ci self.groups.add(group.name) 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci def Flatten(self) -> Set['Metric']: 45562306a36Sopenharmony_ci """Return a leaf metric.""" 45662306a36Sopenharmony_ci return set([self]) 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci def ToPerfJson(self) -> Dict[str, str]: 45962306a36Sopenharmony_ci """Return as dictionary for Json generation.""" 46062306a36Sopenharmony_ci result = { 46162306a36Sopenharmony_ci 'MetricName': self.name, 46262306a36Sopenharmony_ci 'MetricGroup': ';'.join(sorted(self.groups)), 46362306a36Sopenharmony_ci 'BriefDescription': self.description, 46462306a36Sopenharmony_ci 'MetricExpr': self.expr.ToPerfJson(), 46562306a36Sopenharmony_ci 'ScaleUnit': self.scale_unit 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci if self.constraint: 46862306a36Sopenharmony_ci result['MetricConstraint'] = 'NO_NMI_WATCHDOG' 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci return result 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ciclass _MetricJsonEncoder(json.JSONEncoder): 47462306a36Sopenharmony_ci """Special handling for Metric objects.""" 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci def default(self, o): 47762306a36Sopenharmony_ci if isinstance(o, Metric): 47862306a36Sopenharmony_ci return o.ToPerfJson() 47962306a36Sopenharmony_ci return json.JSONEncoder.default(self, o) 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ciclass MetricGroup: 48362306a36Sopenharmony_ci """A group of metrics. 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci Metric groups may be specificd on the perf command line, but within 48662306a36Sopenharmony_ci the json they aren't encoded. Metrics may be in multiple groups 48762306a36Sopenharmony_ci which can facilitate arrangements similar to trees. 48862306a36Sopenharmony_ci """ 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci def __init__(self, name: str, metric_list: List[Union[Metric, 49162306a36Sopenharmony_ci 'MetricGroup']]): 49262306a36Sopenharmony_ci self.name = name 49362306a36Sopenharmony_ci self.metric_list = metric_list 49462306a36Sopenharmony_ci for metric in metric_list: 49562306a36Sopenharmony_ci metric.AddToMetricGroup(self) 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci def AddToMetricGroup(self, group): 49862306a36Sopenharmony_ci """Callback used when a MetricGroup is added into another.""" 49962306a36Sopenharmony_ci for metric in self.metric_list: 50062306a36Sopenharmony_ci metric.AddToMetricGroup(group) 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci def Flatten(self) -> Set[Metric]: 50362306a36Sopenharmony_ci """Returns a set of all leaf metrics.""" 50462306a36Sopenharmony_ci result = set() 50562306a36Sopenharmony_ci for x in self.metric_list: 50662306a36Sopenharmony_ci result = result.union(x.Flatten()) 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci return result 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci def ToPerfJson(self) -> str: 51162306a36Sopenharmony_ci return json.dumps(sorted(self.Flatten()), indent=2, cls=_MetricJsonEncoder) 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci def __str__(self) -> str: 51462306a36Sopenharmony_ci return self.ToPerfJson() 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ciclass _RewriteIfExpToSelect(ast.NodeTransformer): 51862306a36Sopenharmony_ci """Transformer to convert if-else nodes to Select expressions.""" 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci def visit_IfExp(self, node): 52162306a36Sopenharmony_ci # pylint: disable=invalid-name 52262306a36Sopenharmony_ci self.generic_visit(node) 52362306a36Sopenharmony_ci call = ast.Call( 52462306a36Sopenharmony_ci func=ast.Name(id='Select', ctx=ast.Load()), 52562306a36Sopenharmony_ci args=[node.body, node.test, node.orelse], 52662306a36Sopenharmony_ci keywords=[]) 52762306a36Sopenharmony_ci ast.copy_location(call, node.test) 52862306a36Sopenharmony_ci return call 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cidef ParsePerfJson(orig: str) -> Expression: 53262306a36Sopenharmony_ci """A simple json metric expression decoder. 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci Converts a json encoded metric expression by way of python's ast and 53562306a36Sopenharmony_ci eval routine. First tokens are mapped to Event calls, then 53662306a36Sopenharmony_ci accidentally converted keywords or literals are mapped to their 53762306a36Sopenharmony_ci appropriate calls. Python's ast is used to match if-else that can't 53862306a36Sopenharmony_ci be handled via operator overloading. Finally the ast is evaluated. 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci Args: 54162306a36Sopenharmony_ci orig (str): String to parse. 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci Returns: 54462306a36Sopenharmony_ci Expression: The parsed string. 54562306a36Sopenharmony_ci """ 54662306a36Sopenharmony_ci # pylint: disable=eval-used 54762306a36Sopenharmony_ci py = orig.strip() 54862306a36Sopenharmony_ci # First try to convert everything that looks like a string (event name) into Event(r"EVENT_NAME"). 54962306a36Sopenharmony_ci # This isn't very selective so is followed up by converting some unwanted conversions back again 55062306a36Sopenharmony_ci py = re.sub(r'([a-zA-Z][^-+/\* \\\(\),]*(?:\\.[^-+/\* \\\(\),]*)*)', 55162306a36Sopenharmony_ci r'Event(r"\1")', py) 55262306a36Sopenharmony_ci # If it started with a # it should have been a literal, rather than an event name 55362306a36Sopenharmony_ci py = re.sub(r'#Event\(r"([^"]*)"\)', r'Literal("#\1")', py) 55462306a36Sopenharmony_ci # Convert accidentally converted hex constants ("0Event(r"xDEADBEEF)"") back to a constant, 55562306a36Sopenharmony_ci # but keep it wrapped in Event(), otherwise Python drops the 0x prefix and it gets interpreted as 55662306a36Sopenharmony_ci # a double by the Bison parser 55762306a36Sopenharmony_ci py = re.sub(r'0Event\(r"[xX]([0-9a-fA-F]*)"\)', r'Event("0x\1")', py) 55862306a36Sopenharmony_ci # Convert accidentally converted scientific notation constants back 55962306a36Sopenharmony_ci py = re.sub(r'([0-9]+)Event\(r"(e[0-9]+)"\)', r'\1\2', py) 56062306a36Sopenharmony_ci # Convert all the known keywords back from events to just the keyword 56162306a36Sopenharmony_ci keywords = ['if', 'else', 'min', 'max', 'd_ratio', 'source_count', 'has_event', 'strcmp_cpuid_str', 56262306a36Sopenharmony_ci 'cpuid_not_more_than'] 56362306a36Sopenharmony_ci for kw in keywords: 56462306a36Sopenharmony_ci py = re.sub(rf'Event\(r"{kw}"\)', kw, py) 56562306a36Sopenharmony_ci try: 56662306a36Sopenharmony_ci parsed = ast.parse(py, mode='eval') 56762306a36Sopenharmony_ci except SyntaxError as e: 56862306a36Sopenharmony_ci raise SyntaxError(f'Parsing expression:\n{orig}') from e 56962306a36Sopenharmony_ci _RewriteIfExpToSelect().visit(parsed) 57062306a36Sopenharmony_ci parsed = ast.fix_missing_locations(parsed) 57162306a36Sopenharmony_ci return _Constify(eval(compile(parsed, orig, 'eval'))) 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cidef RewriteMetricsInTermsOfOthers(metrics: List[Tuple[str, str, Expression]] 57562306a36Sopenharmony_ci )-> Dict[Tuple[str, str], Expression]: 57662306a36Sopenharmony_ci """Shorten metrics by rewriting in terms of others. 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci Args: 57962306a36Sopenharmony_ci metrics (list): pmus, metric names and their expressions. 58062306a36Sopenharmony_ci Returns: 58162306a36Sopenharmony_ci Dict: mapping from a pmu, metric name pair to a shortened expression. 58262306a36Sopenharmony_ci """ 58362306a36Sopenharmony_ci updates: Dict[Tuple[str, str], Expression] = dict() 58462306a36Sopenharmony_ci for outer_pmu, outer_name, outer_expression in metrics: 58562306a36Sopenharmony_ci if outer_pmu is None: 58662306a36Sopenharmony_ci outer_pmu = 'cpu' 58762306a36Sopenharmony_ci updated = outer_expression 58862306a36Sopenharmony_ci while True: 58962306a36Sopenharmony_ci for inner_pmu, inner_name, inner_expression in metrics: 59062306a36Sopenharmony_ci if inner_pmu is None: 59162306a36Sopenharmony_ci inner_pmu = 'cpu' 59262306a36Sopenharmony_ci if inner_pmu.lower() != outer_pmu.lower(): 59362306a36Sopenharmony_ci continue 59462306a36Sopenharmony_ci if inner_name.lower() == outer_name.lower(): 59562306a36Sopenharmony_ci continue 59662306a36Sopenharmony_ci if (inner_pmu, inner_name) in updates: 59762306a36Sopenharmony_ci inner_expression = updates[(inner_pmu, inner_name)] 59862306a36Sopenharmony_ci updated = updated.Substitute(inner_name, inner_expression) 59962306a36Sopenharmony_ci if updated.Equals(outer_expression): 60062306a36Sopenharmony_ci break 60162306a36Sopenharmony_ci if (outer_pmu, outer_name) in updates and updated.Equals(updates[(outer_pmu, outer_name)]): 60262306a36Sopenharmony_ci break 60362306a36Sopenharmony_ci updates[(outer_pmu, outer_name)] = updated 60462306a36Sopenharmony_ci return updates 605