diff --git a/scripts/create_perf_json.py b/scripts/create_perf_json.py index 5023229c..66c5c15b 100755 --- a/scripts/create_perf_json.py +++ b/scripts/create_perf_json.py @@ -572,8 +572,8 @@ def cstate_json(self): (['KBL'], [3, 6, 7], [2, 3, 6, 7]), (['CNL'], [1, 3, 6, 7], [2, 3, 6, 7, 8, 9, 10]), (['ICL', 'TGL', 'RKL'], [6, 7], [2, 3, 6, 7, 8, 9, 10]), - (['ICX', 'SPR'], [1, 6], [2, 6]), - (['ADL', 'GRT', 'ADLN'], [1, 6, 7], [2, 3, 6, 8, 10]), + (['ICX', 'SPR', 'EMR', 'GNR'], [1, 6], [2, 6]), + (['ADL', 'GRT', 'ADLN', 'GRR', 'SRF'], [1, 6, 7], [2, 3, 6, 8, 10]), (['MTL'], [1, 6, 7], [2, 3, 6, 7, 8, 9, 10]), (['SLM'], [1, 6], [6]), (['KNL', 'KNM'], [6], [2, 3, 6]), @@ -658,6 +658,7 @@ def smi_json(self) -> metric.MetricGroup: threshold=(metric.Event('smi_cycles') > 0.10)) ]) + @staticmethod def extract_pebs_formula(formula: str) -> str: """ @@ -696,8 +697,297 @@ def extract_pebs_formula(formula: str) -> str: return new_formula + + def save_form(self, name: str, group: str, form: str, desc: str, locate: str, + scale_unit: Optional[str], threshold: Optional[str], + issues: list[str], pmu_prefix: str, events: Dict[str, PerfmonJsonEvent], + infoname : Dict[str, str], aux : Dict[str, str], + issue_to_metrics: Dict[str, Set[str]], + saved_formulas: list[Dict[str, str]]): + if self.shortname == 'BDW-DE': + if name in ['tma_false_sharing']: + # Uncore events missing for BDW-DE, so drop. + _verboseprint3(f'Dropping metric {name}') + return + + # Make 'TmaL1' group names more consistent with the 'tma_' + # prefix and '_group' suffix. + if group: + if 'TmaL1' in group and 'tma_L1_group' not in group: + group += ';tma_L1_group' + if 'TmaL2' in group and 'tma_L2_group' not in group: + group += ';tma_L2_group' + _verboseprint3(f'Checking metric {name}: {form}') + for v, _ in re.findall(r'(([A-Z_a-z0-9.]|\\-)+)', form): + if v.isdigit() or re.match(r'\d+\.\d+', v) is not None or \ + re.match('0x[a-fA-F0-9]+', v) is not None or \ + re.match(r'\d+e\d+', v) is not None: + continue + if v in ['if', 'then', 'else', 'min', 'max', 'core_wide', + 'SMT_on', 'duration_time', 'cmask', 'umask', + 'u', 'k', 'cpu', 'cpu_atom', 'cpu_core', 'edge', + 'inv', 'TSC', 'filter_opc', 'cha_0', 'event', + 'imc_0', 'uncore_cha_0', 'cbox_0', 'arb', 'cbox', + 'num_packages', 'num_cores', 'SYSTEM_TSC_FREQ', + 'filter_tid', 'TSC', 'cha', 'config1', + 'source_count', 'slots', 'thresh', 'has_pmem', + 'num_dies', 'num_cpus_online', 'PEBS', 'R', + 'offcore_rsp']: + continue + if v.startswith('tma_') or v.startswith('topdown\\-'): + continue + assert v in events or v.upper() in events or v in infoname or v in aux, \ + f'Expected {v} to be an event in "{name}": "{form}" on {self.shortname}' + + assert f'{pmu_prefix}@UNC' not in form, form + if group: + group = ';'.join(sorted(set(group.split(';')))) + # Check for duplicate metrics. Note, done after + # verifying the events. + parsed_threshold = None + dups = [m for m in saved_formulas if m['MetricName'] == name] + if len(dups) > 0: + assert len(dups) == 1 + m = dups[0] + if form != m['MetricExpr']: + _verboseprint2(f'duplicate metric {name} forms differ' + f'\n\tnew: {form}' + f'\n\texisting: {m["MetricExpr"]}') + if not locate and ' Sample with: ' not in desc: + if 'PublicDescription' in m: + d = m['PublicDescription'] + else: + d = m['BriefDescription'] + if ' Sample with: ' in d: + locate = re.sub(r'.* Sample with: (.*)', r'\1', d) + if not threshold: + parsed_threshold = m.get('MetricThreshold') + group = m['MetricGroup'] + saved_formulas.remove(m) + + desc = desc.strip() + def append_to_desc(s: str): + nonlocal desc + if desc[-1] != '.': + desc += '.' + desc = f'{desc} {s}' + + if locate: + append_to_desc(f'Sample with: {locate}') + + if issues: + related = set() + for issue in issues: + related.update(issue_to_metrics[issue]) + related.remove(name) + append_to_desc(f'Related metrics: {", ".join(sorted(related))}') + + try: + if "$PEBS" in form: + form = self.extract_pebs_formula(form) + formula = { + 'MetricName': name, + 'MetricExpr': metric.ParsePerfJson(form).Simplify().ToPerfJson(), + } + except SyntaxError as e: + raise SyntaxError(f'Parsing metric {name} for {self.longname}') from e + + if group and len(group) > 0: + formula['MetricGroup'] = group + if '.' in desc: + sdesc = re.sub(r'(? bool: aux[aux_name] = form _verboseprint3(f'Adding aux {aux_name}: {form}') - jo = [] for i in info: if i.name in ignore: _verboseprint2(f'Skipping {i.name}') @@ -1247,283 +1536,6 @@ def resolve(v: str) -> str: form = fixup(form) return form - def save_form(name, group, form, desc, locate, scale_unit, threshold, - issues): - if self.shortname == 'BDW-DE': - if name in ['tma_false_sharing']: - # Uncore events missing for BDW-DE, so drop. - _verboseprint3(f'Dropping metric {name}') - return - - # Make 'TmaL1' group names more consistent with the 'tma_' - # prefix and '_group' suffix. - if group: - if 'TmaL1' in group and 'tma_L1_group' not in group: - group += ';tma_L1_group' - if 'TmaL2' in group and 'tma_L2_group' not in group: - group += ';tma_L2_group' - _verboseprint3(f'Checking metric {name}: {form}') - for v, _ in re.findall(r'(([A-Z_a-z0-9.]|\\-)+)', form): - if v.isdigit() or re.match(r'\d+\.\d+', v) is not None or \ - re.match('0x[a-fA-F0-9]+', v) is not None or \ - re.match(r'\d+e\d+', v) is not None: - continue - if v in ['if', 'then', 'else', 'min', 'max', 'core_wide', - 'SMT_on', 'duration_time', 'cmask', 'umask', - 'u', 'k', 'cpu', 'cpu_atom', 'cpu_core', 'edge', - 'inv', 'TSC', 'filter_opc', 'cha_0', 'event', - 'imc_0', 'uncore_cha_0', 'cbox_0', 'arb', 'cbox', - 'num_packages', 'num_cores', 'SYSTEM_TSC_FREQ', - 'filter_tid', 'TSC', 'cha', 'config1', - 'source_count', 'slots', 'thresh', 'has_pmem', - 'num_dies', 'num_cpus_online', 'PEBS']: - continue - if v.startswith('tma_') or v.startswith('topdown\\-'): - continue - assert v in events or v.upper() in events or v in infoname or v in aux, \ - f'Expected {v} to be an event in "{name}": "{form}" on {self.shortname}' - - assert f'{pmu_prefix}@UNC' not in form, form - if group: - group = ';'.join(sorted(set(group.split(';')))) - # Check for duplicate metrics. Note, done after - # verifying the events. - parsed_threshold = None - dups = [m for m in jo if m['MetricName'] == name] - if len(dups) > 0: - assert len(dups) == 1 - m = dups[0] - if form != m['MetricExpr']: - _verboseprint2(f'duplicate metric {name} forms differ' - f'\n\tnew: {form}' - f'\n\texisting: {m["MetricExpr"]}') - if not locate and ' Sample with: ' not in desc: - if 'PublicDescription' in m: - d = m['PublicDescription'] - else: - d = m['BriefDescription'] - if ' Sample with: ' in d: - locate = re.sub(r'.* Sample with: (.*)', r'\1', d) - if not threshold: - parsed_threshold = m.get('MetricThreshold') - group = m['MetricGroup'] - jo.remove(m) - - desc = desc.strip() - def append_to_desc(s: str): - nonlocal desc - if desc[-1] != '.': - desc += '.' - desc = f'{desc} {s}' - - if locate: - append_to_desc(f'Sample with: {locate}') - - if issues: - related = set() - for issue in issues: - related.update(issue_to_metrics[issue]) - related.remove(name) - append_to_desc(f'Related metrics: {", ".join(sorted(related))}') - - try: - if "$PEBS" in form: - form = self.extract_pebs_formula(form) - j = { - 'MetricName': name, - 'MetricExpr': metric.ParsePerfJson(form).Simplify().ToPerfJson(), - } - except SyntaxError as e: - raise SyntaxError(f'Parsing metric {name} for {self.longname}') from e - - if group and len(group) > 0: - j['MetricGroup'] = group - if '.' in desc: - sdesc = re.sub(r'(? {threshold}') - save_form(i.name, i.groups, form, i.desc, i.locate, i.scale_unit, - threshold, i.issues) - - if 'Socket_CLKS' in infoname: - form = 'Socket_CLKS / #num_dies / duration_time / 1000000000' - form = resolve_all(form, expand_metrics=False) - if form: - formula = metric.ParsePerfJson(form) - save_form('UNCORE_FREQ', 'SoC', formula.ToPerfJson(), - 'Uncore frequency per die [GHZ]', None, None, None, []) + self.save_form(i.name, i.groups, form, i.desc, i.locate, i.scale_unit, + threshold, i.issues, pmu_prefix, events, infoname, aux, + issue_to_metrics, saved_formulas) + def extract_extra_metrics(self, pmu_prefix: str, events: Dict[str, PerfmonJsonEvent], + saved_formulas: list[Dict[str, str]]): if 'extra metrics' in self.files: with open(self.files['extra metrics'], 'r') as extra_json: + ignore = { + 'tma_info_system_mux': 'MUX', + 'tma_info_system_power': 'Power', + 'tma_info_system_time': 'Time', + } broken_metrics = { - 'ICX': { - # Missing event: UNC_CHA_TOR_INSERTS.IA_MISS_LLCPREFDRD - 'llc_data_read_mpi_demand_plus_prefetch', - # Missing event: UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_DRAM - 'llc_demand_data_read_miss_to_dram_latency', - # Missing event: UNC_CHA_TOR_INSERTS.IO_HIT_RDCUR - 'io_bandwidth_read', + 'EMR': { + # Missing event: UNC_UPI_TXL_FLITS.ALL_DATA + 'tma_info_system_upi_data_transmit_bw', }, - 'SPR': { - # Missing ')' - 'tma_int_operations' - } } skip = broken_metrics.get(self.shortname) if not skip: @@ -1593,15 +1597,26 @@ def append_to_desc(s: str): if em['MetricName'] in ignore: _verboseprint2(f"Skipping {em['MetricName']}") continue - dups = [m for m in jo if m['MetricName'] == em['MetricName']] + dups = [m for m in saved_formulas if m['MetricName'] == em['MetricName']] if dups: _verboseprint3(f'Not replacing:\n\t{dups[0]["MetricExpr"]}\nwith:\n\t{em["MetricExpr"]}') continue - save_form(em['MetricName'], em['MetricGroup'], em['MetricExpr'], - em['BriefDescription'], None, em.get('ScaleUnit'), - em.get('MetricThreshold'), []) + self.save_form(em['MetricName'], em['MetricGroup'], em['MetricExpr'], + em['BriefDescription'], None, em.get('ScaleUnit'), + em.get('MetricThreshold'), [], pmu_prefix, events, + infoname={}, aux={}, issue_to_metrics={}, + saved_formulas=saved_formulas) + + if any(m['MetricName'] == 'tma_info_system_socket_clks' for m in saved_formulas): + form = 'tma_info_system_socket_clks / #num_dies / duration_time / 1000000000' + #form = resolve_all(form, expand_metrics=False) + if form: + formula = metric.ParsePerfJson(form) + self.save_form('UNCORE_FREQ', 'SoC', formula.ToPerfJson(), + 'Uncore frequency per die [GHZ]', None, None, None, [], + pmu_prefix, events, infoname={}, aux={}, + issue_to_metrics={}, saved_formulas=saved_formulas) - return jo def count_counters(self, event_type, pmon_events): """ @@ -1798,7 +1813,9 @@ def to_perf_json(self, outdir: Path): continue pmu_prefix = unit if 'atom' in self.files else 'cpu' with open(self.files[metric_csv_key], 'r') as metric_csv: - csv_metrics = self.extract_tma_metrics(metric_csv, pmu_prefix, events) + csv_metrics = [] + self.extract_tma_metrics(metric_csv, pmu_prefix, events, csv_metrics) + self.extract_extra_metrics(pmu_prefix, events, csv_metrics) csv_metrics = sorted(csv_metrics, key=lambda m: (m['Unit'] if 'Unit' in m else 'cpu', m['MetricName']) @@ -1961,7 +1978,7 @@ def __init__(self, base_path: Path): _verboseprint2('Parsed models:\n' + str(self)) def __str__(self): - return ''.join(str(model) for model in self.archs) + return '\n'.join(str(model) for model in self.archs) def to_perf_json(self, outdir: Path): """ diff --git a/scripts/metric.py b/scripts/metric.py index f8c73a88..66881c89 100644 --- a/scripts/metric.py +++ b/scripts/metric.py @@ -54,6 +54,12 @@ def __lt__(self, other: Union[int, float, 'Expression']) -> 'Operator': def __gt__(self, other: Union[int, float, 'Expression']) -> 'Operator': return Operator('>', self, other) + def __le__(self, other: Union[int, float, 'Expression']) -> 'Operator': + return _Constify(other).__gt__(self) + + def __ge__(self, other: Union[int, float, 'Expression']) -> 'Operator': + return _Constify(other).__lt__(self) + def __add__(self, other: Union[int, float, 'Expression']) -> 'Operator': return Operator('+', self, other) @@ -163,7 +169,7 @@ def Simplify(self) -> Expression: lhs = self.lhs.Simplify() rhs = self.rhs.Simplify() if isinstance(lhs, Constant) and isinstance(rhs, Constant): - return Constant(ast.literal_eval(lhs + self.operator + rhs)) + return Constant(ast.literal_eval(str(lhs) + self.operator + str(rhs))) if isinstance(self.lhs, Constant): if self.operator in ('+', '|') and lhs.value == '0': @@ -185,7 +191,7 @@ def Simplify(self) -> Expression: if self.operator == '*' and rhs.value == '0': return Constant(0) - if self.operator == '*' and self.rhs.value == '1': + if self.operator == '*' and rhs.value == '1': return lhs return Operator(self.operator, lhs, rhs) @@ -200,9 +206,7 @@ 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) + rhs = self.rhs.Substitute(name, expression) return Operator(self.operator, lhs, rhs) @@ -262,7 +266,7 @@ def __init__(self, rhs: Optional[Union[int, float, Expression]] = None): self.fn = fn self.lhs = _Constify(lhs) - self.rhs = _Constify(rhs) + self.rhs = None if rhs is None else _Constify(rhs) def ToPerfJson(self): if self.rhs: @@ -502,7 +506,7 @@ def AddToMetricGroup(self, group): def Flatten(self) -> Set[Metric]: """Returns a set of all leaf metrics.""" - result = set() + result: Set[Metric] = set() for x in self.metric_list: result = result.union(x.Flatten())