1#!/usr/bin/env python3 2# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 3"""Convert directories of JSON events to C code.""" 4import argparse 5import csv 6from functools import lru_cache 7import json 8import metric 9import os 10import sys 11from typing import (Callable, Dict, Optional, Sequence, Set, Tuple) 12import collections 13 14# Global command line arguments. 15_args = None 16# List of regular event tables. 17_event_tables: list[str] = [] 18# List of event tables generated from "/sys" directories. 19_sys_event_tables: list[str] = [] 20# List of regular metric tables. 21_metric_tables: list[str] = [] 22# List of metric tables generated from "/sys" directories. 23_sys_metric_tables: list[str] = [] 24# Mapping between sys event table names and sys metric table names. 25_sys_event_table_to_metric_table_mapping = {} 26# Map from an event name to an architecture standard 27# JsonEvent. Architecture standard events are in json files in the top 28# f'{_args.starting_dir}/{_args.arch}' directory. 29_arch_std_events: Dict[str, "JsonEvent"] = {} 30# Events to write out when the table is closed 31_pending_events: list["JsonEvent"] = [] 32# Name of events table to be written out 33_pending_events_tblname: Optional[str] = None 34# Metrics to write out when the table is closed 35_pending_metrics: list["JsonEvent"] = [] 36# Name of metrics table to be written out 37_pending_metrics_tblname: Optional[str] = None 38# Global BigCString shared by all structures. 39_bcs = None 40# Map from the name of a metric group to a description of the group. 41_metricgroups = {} 42# Order specific JsonEvent attributes will be visited. 43_json_event_attributes = [ 44 # cmp_sevent related attributes. 45 'name', 'topic', 'desc', 46 # Seems useful, put it early. 47 'event', 48 # Short things in alphabetical order. 49 'compat', 'deprecated', 'perpkg', 'unit', 50 # Retirement latency specific to Intel granite rapids currently. 51 'retirement_latency_mean', 'retirement_latency_min', 52 'retirement_latency_max', 53 # Longer things (the last won't be iterated over during decompress). 54 'long_desc' 55] 56 57# Attributes that are in pmu_metric rather than pmu_event. 58_json_metric_attributes = [ 59 'metric_name', 'metric_group', 'metric_expr', 'metric_threshold', 60 'desc', 'long_desc', 'unit', 'compat', 'metricgroup_no_group', 61 'default_metricgroup_name', 'aggr_mode', 'event_grouping', 62 'default_show_events' 63] 64# Attributes that are bools or enum int values, encoded as '0', '1',... 65_json_enum_attributes = ['aggr_mode', 'deprecated', 'event_grouping', 'perpkg', 66 'default_show_events'] 67 68def removesuffix(s: str, suffix: str) -> str: 69 """Remove the suffix from a string 70 71 The removesuffix function is added to str in Python 3.9. We aim for 3.6 72 compatibility and so provide our own function here. 73 """ 74 return s[0:-len(suffix)] if s.endswith(suffix) else s 75 76 77def file_name_to_table_name(prefix: str, parents: Sequence[str], 78 dirname: str) -> str: 79 """Generate a C table name from directory names.""" 80 tblname = prefix 81 for p in parents: 82 tblname += '_' + p 83 tblname += '_' + dirname 84 return tblname.replace('-', '_') 85 86 87def c_len(s: str) -> int: 88 """Return the length of s a C string 89 90 This doesn't handle all escape characters properly. It first assumes 91 all \\ are for escaping, it then adjusts as it will have over counted 92 \\. The code uses \000 rather than \0 as a terminator as an adjacent 93 number would be folded into a string of \0 (ie. "\0" + "5" doesn't 94 equal a terminator followed by the number 5 but the escape of 95 \05). The code adjusts for \000 but not properly for all octal, hex 96 or unicode values. 97 """ 98 try: 99 utf = s.encode(encoding='utf-8',errors='strict') 100 except: 101 print(f'broken string {s}') 102 raise 103 return len(utf) - utf.count(b'\\') + utf.count(b'\\\\') - (utf.count(b'\\000') * 2) 104 105class BigCString: 106 """A class to hold many strings concatenated together. 107 108 Generating a large number of stand-alone C strings creates a large 109 number of relocations in position independent code. The BigCString 110 is a helper for this case. It builds a single string which within it 111 are all the other C strings (to avoid memory issues the string 112 itself is held as a list of strings). The offsets within the big 113 string are recorded and when stored to disk these don't need 114 relocation. To reduce the size of the string further, identical 115 strings are merged. If a longer string ends-with the same value as a 116 shorter string, these entries are also merged. 117 """ 118 strings: Set[str] 119 big_string: Sequence[str] 120 offsets: Dict[str, int] 121 insert_number: int 122 insert_point: Dict[str, int] 123 metrics: Set[str] 124 125 def __init__(self): 126 self.strings = set() 127 self.insert_number = 0; 128 self.insert_point = {} 129 self.metrics = set() 130 131 def add(self, s: str, metric: bool) -> None: 132 """Called to add to the big string.""" 133 if s not in self.strings: 134 self.strings.add(s) 135 self.insert_point[s] = self.insert_number 136 self.insert_number += 1 137 if metric: 138 self.metrics.add(s) 139 140 def compute(self) -> None: 141 """Called once all strings are added to compute the string and offsets.""" 142 143 folded_strings = {} 144 # Determine if two strings can be folded, ie. let 1 string use the 145 # end of another. First reverse all strings and sort them. 146 sorted_reversed_strings = sorted([x[::-1] for x in self.strings]) 147 148 # Strings 'xyz' and 'yz' will now be [ 'zy', 'zyx' ]. Scan forward 149 # for each string to see if there is a better candidate to fold it 150 # into, in the example rather than using 'yz' we can use'xyz' at 151 # an offset of 1. We record which string can be folded into which 152 # in folded_strings, we don't need to record the offset as it is 153 # trivially computed from the string lengths. 154 for pos,s in enumerate(sorted_reversed_strings): 155 best_pos = pos 156 for check_pos in range(pos + 1, len(sorted_reversed_strings)): 157 if sorted_reversed_strings[check_pos].startswith(s): 158 best_pos = check_pos 159 else: 160 break 161 if pos != best_pos: 162 folded_strings[s[::-1]] = sorted_reversed_strings[best_pos][::-1] 163 164 # Compute reverse mappings for debugging. 165 fold_into_strings = collections.defaultdict(set) 166 for key, val in folded_strings.items(): 167 if key != val: 168 fold_into_strings[val].add(key) 169 170 # big_string_offset is the current location within the C string 171 # being appended to - comments, etc. don't count. big_string is 172 # the string contents represented as a list. Strings are immutable 173 # in Python and so appending to one causes memory issues, while 174 # lists are mutable. 175 big_string_offset = 0 176 self.big_string = [] 177 self.offsets = {} 178 179 def string_cmp_key(s: str) -> Tuple[bool, int, str]: 180 return (s in self.metrics, self.insert_point[s], s) 181 182 # Emit all strings that aren't folded in a sorted manner. 183 for s in sorted(self.strings, key=string_cmp_key): 184 if s not in folded_strings: 185 self.offsets[s] = big_string_offset 186 self.big_string.append(f'/* offset={big_string_offset} */\n"') 187 self.big_string.append(s) 188 self.big_string.append('"') 189 if s in fold_into_strings: 190 self.big_string.append(' /* also: ' + ', '.join(fold_into_strings[s]) + ' */') 191 self.big_string.append('\n') 192 big_string_offset += c_len(s) 193 continue 194 195 # Compute the offsets of the folded strings. 196 for s in folded_strings.keys(): 197 assert s not in self.offsets 198 folded_s = folded_strings[s] 199 self.offsets[s] = self.offsets[folded_s] + c_len(folded_s) - c_len(s) 200 201_bcs = BigCString() 202 203class JsonEvent: 204 """Representation of an event loaded from a json file dictionary.""" 205 206 def __init__(self, jd: dict): 207 """Constructor passed the dictionary of parsed json values.""" 208 209 def llx(x: int) -> str: 210 """Convert an int to a string similar to a printf modifier of %#llx.""" 211 return str(x) if x >= 0 and x < 10 else hex(x) 212 213 def fixdesc(s: Optional[str]) -> Optional[str]: 214 """Fix formatting issue for the desc string.""" 215 if s is None: 216 return None 217 return removesuffix(removesuffix(removesuffix(s, '. '), 218 '. '), '.').replace('\n', '\\n').replace( 219 '\"', '\\"').replace('\r', '\\r') 220 221 def convert_aggr_mode(aggr_mode: Optional[str]) -> Optional[str]: 222 """Returns the aggr_mode_class enum value associated with the JSON string.""" 223 if not aggr_mode: 224 return None 225 aggr_mode_to_enum = { 226 'PerChip': '1', 227 'PerCore': '2', 228 } 229 return aggr_mode_to_enum[aggr_mode] 230 231 def convert_metric_constraint(metric_constraint: Optional[str]) -> Optional[str]: 232 """Returns the metric_event_groups enum value associated with the JSON string.""" 233 if not metric_constraint: 234 return None 235 metric_constraint_to_enum = { 236 'NO_GROUP_EVENTS': '1', 237 'NO_GROUP_EVENTS_NMI': '2', 238 'NO_NMI_WATCHDOG': '2', 239 'NO_GROUP_EVENTS_SMT': '3', 240 'NO_THRESHOLD_AND_NMI': '4', 241 } 242 return metric_constraint_to_enum[metric_constraint] 243 244 def lookup_msr(num: Optional[str]) -> Optional[str]: 245 """Converts the msr number, or first in a list to the appropriate event field.""" 246 if not num: 247 return None 248 msrmap = { 249 0x3F6: 'ldlat=', 250 0x1A6: 'offcore_rsp=', 251 0x1A7: 'offcore_rsp=', 252 0x3E0: 'offcore_rsp=', 253 0x3E1: 'offcore_rsp=', 254 0x3E2: 'offcore_rsp=', 255 0x3E3: 'offcore_rsp=', 256 0x3F7: 'frontend=', 257 } 258 return msrmap[int(num.split(',', 1)[0], 0)] 259 260 def real_event(name: Optional[str], event: Optional[str]) -> Optional[str]: 261 """Convert well known event names to an event string otherwise use the event argument.""" 262 fixed = { 263 'inst_retired.any': 'event=0xc0,period=2000003', 264 'inst_retired.any_p': 'event=0xc0,period=2000003', 265 'cpu_clk_unhalted.ref': 'event=0x0,umask=0x03,period=2000003', 266 'cpu_clk_unhalted.thread': 'event=0x3c,period=2000003', 267 'cpu_clk_unhalted.core': 'event=0x3c,period=2000003', 268 'cpu_clk_unhalted.thread_any': 'event=0x3c,any=1,period=2000003', 269 } 270 if not name: 271 return None 272 if name.lower() in fixed: 273 return fixed[name.lower()] 274 return event 275 276 def unit_to_pmu(unit: Optional[str]) -> Optional[str]: 277 """Convert a JSON Unit to Linux PMU name.""" 278 if not unit: 279 return 'default_core' 280 # Comment brought over from jevents.c: 281 # it's not realistic to keep adding these, we need something more scalable ... 282 table = { 283 'CBO': 'uncore_cbox', 284 'QPI LL': 'uncore_qpi', 285 'SBO': 'uncore_sbox', 286 'iMPH-U': 'uncore_arb', 287 'CPU-M-CF': 'cpum_cf', 288 'CPU-M-SF': 'cpum_sf', 289 'PAI-CRYPTO' : 'pai_crypto', 290 'PAI-EXT' : 'pai_ext', 291 'UPI LL': 'uncore_upi', 292 'hisi_sicl,cpa': 'hisi_sicl,cpa', 293 'hisi_sccl,ddrc': 'hisi_sccl,ddrc', 294 'hisi_sccl,hha': 'hisi_sccl,hha', 295 'hisi_sccl,l3c': 'hisi_sccl,l3c', 296 'imx8_ddr': 'imx8_ddr', 297 'imx9_ddr': 'imx9_ddr', 298 'L3PMC': 'amd_l3', 299 'DFPMC': 'amd_df', 300 'UMCPMC': 'amd_umc', 301 'cpu_core': 'cpu_core', 302 'cpu_atom': 'cpu_atom', 303 'ali_drw': 'ali_drw', 304 'arm_cmn': 'arm_cmn', 305 'software': 'software', 306 'tool': 'tool', 307 } 308 return table[unit] if unit in table else f'uncore_{unit.lower()}' 309 310 def is_zero(val: str) -> bool: 311 try: 312 if val.startswith('0x'): 313 return int(val, 16) == 0 314 else: 315 return int(val) == 0 316 except: 317 return False 318 319 def canonicalize_value(val: str) -> str: 320 try: 321 if val.startswith('0x'): 322 return llx(int(val, 16)) 323 return str(int(val)) 324 except: 325 return val 326 327 eventcode = 0 328 if 'EventCode' in jd: 329 eventcode = int(jd['EventCode'].split(',', 1)[0], 0) 330 if 'ExtSel' in jd: 331 eventcode |= int(jd['ExtSel']) << 8 332 configcode = int(jd['ConfigCode'], 0) if 'ConfigCode' in jd else None 333 eventidcode = int(jd['EventidCode'], 0) if 'EventidCode' in jd else None 334 legacy_hw_config = int(jd['LegacyConfigCode'], 0) if 'LegacyConfigCode' in jd else None 335 legacy_cache_config = int(jd['LegacyCacheCode'], 0) if 'LegacyCacheCode' in jd else None 336 self.name = jd['EventName'].lower() if 'EventName' in jd else None 337 self.topic = '' 338 self.compat = jd.get('Compat') 339 self.desc = fixdesc(jd.get('BriefDescription')) 340 self.long_desc = fixdesc(jd.get('PublicDescription')) 341 precise = jd.get('PEBS') 342 msr = lookup_msr(jd.get('MSRIndex')) 343 msrval = jd.get('MSRValue') 344 extra_desc = '' 345 if 'Data_LA' in jd: 346 extra_desc += ' Supports address when precise' 347 if 'Errata' in jd: 348 extra_desc += '.' 349 if 'Errata' in jd: 350 extra_desc += ' Spec update: ' + jd['Errata'] 351 self.pmu = unit_to_pmu(jd.get('Unit')) 352 filter = jd.get('Filter') 353 self.unit = jd.get('ScaleUnit') 354 self.perpkg = jd.get('PerPkg') 355 self.aggr_mode = convert_aggr_mode(jd.get('AggregationMode')) 356 self.deprecated = jd.get('Deprecated') 357 self.retirement_latency_mean = jd.get('RetirementLatencyMean') 358 self.retirement_latency_min = jd.get('RetirementLatencyMin') 359 self.retirement_latency_max = jd.get('RetirementLatencyMax') 360 self.metric_name = jd.get('MetricName') 361 self.metric_group = jd.get('MetricGroup') 362 self.metricgroup_no_group = jd.get('MetricgroupNoGroup') 363 self.default_metricgroup_name = jd.get('DefaultMetricgroupName') 364 self.event_grouping = convert_metric_constraint(jd.get('MetricConstraint')) 365 self.default_show_events = jd.get('DefaultShowEvents') 366 self.metric_expr = None 367 if 'MetricExpr' in jd: 368 self.metric_expr = metric.ParsePerfJson(jd['MetricExpr']).Simplify() 369 # Note, the metric formula for the threshold isn't parsed as the & 370 # and > have incorrect precedence. 371 self.metric_threshold = jd.get('MetricThreshold') 372 373 arch_std = jd.get('ArchStdEvent') 374 if precise and self.desc and '(Precise Event)' not in self.desc: 375 extra_desc += ' (Must be precise)' if precise == '2' else (' (Precise ' 376 'event)') 377 self.event: Optional[str] = None 378 event: Optional[str] = None 379 if configcode is not None: 380 event = f'config={llx(configcode)}' 381 elif eventidcode is not None: 382 event = f'eventid={llx(eventidcode)}' 383 elif legacy_hw_config is not None: 384 event = f'legacy-hardware-config={llx(legacy_hw_config)}' 385 elif legacy_cache_config is not None: 386 event = f'legacy-cache-config={llx(legacy_cache_config)}' 387 else: 388 event = f'event={llx(eventcode)}' 389 event_fields = [ 390 ('AnyThread', 'any='), 391 ('PortMask', 'ch_mask='), 392 ('CounterMask', 'cmask='), 393 ('EdgeDetect', 'edge='), 394 ('FCMask', 'fc_mask='), 395 ('Invert', 'inv='), 396 ('SampleAfterValue', 'period='), 397 ('UMask', 'umask='), 398 ('NodeType', 'type='), 399 ('RdWrMask', 'rdwrmask='), 400 ('EnAllCores', 'enallcores='), 401 ('EnAllSlices', 'enallslices='), 402 ('SliceId', 'sliceid='), 403 ('ThreadMask', 'threadmask='), 404 ] 405 for key, value in event_fields: 406 if key in jd and not is_zero(jd[key]): 407 event += f',{value}{canonicalize_value(jd[key])}' 408 if filter: 409 event += f',{filter}' 410 if msr: 411 event += f',{msr}{msrval}' 412 if self.desc and extra_desc: 413 self.desc += extra_desc 414 if self.long_desc and extra_desc: 415 self.long_desc += extra_desc 416 if self.desc and self.long_desc and self.desc == self.long_desc: 417 # Avoid duplicated descriptions. 418 self.long_desc = None 419 if arch_std: 420 if arch_std.lower() in _arch_std_events: 421 event = _arch_std_events[arch_std.lower()].event 422 # Copy from the architecture standard event to self for undefined fields. 423 for attr, value in _arch_std_events[arch_std.lower()].__dict__.items(): 424 if hasattr(self, attr) and not getattr(self, attr): 425 setattr(self, attr, value) 426 else: 427 raise argparse.ArgumentTypeError('Cannot find arch std event:', arch_std) 428 429 self.event = real_event(self.name, event) 430 431 def __repr__(self) -> str: 432 """String representation primarily for debugging.""" 433 s = '{\n' 434 for attr, value in self.__dict__.items(): 435 if value: 436 s += f'\t{attr} = {value},\n' 437 return s + '}' 438 439 def build_c_string(self, metric: bool) -> str: 440 s = '' 441 for attr in _json_metric_attributes if metric else _json_event_attributes: 442 x = getattr(self, attr) 443 if metric and x and attr == 'metric_expr': 444 # Convert parsed metric expressions into a string. Slashes 445 # must be doubled in the file. 446 x = x.ToPerfJson().replace('\\', '\\\\') 447 if metric and x and attr == 'metric_threshold': 448 x = x.replace('\\', '\\\\') 449 if attr in _json_enum_attributes: 450 s += x if x else '0' 451 else: 452 s += f'{x}\\000' if x else '\\000' 453 return s 454 455 def to_c_string(self, metric: bool) -> str: 456 """Representation of the event as a C struct initializer.""" 457 458 def make_comment(s: str) -> str: 459 s = s.replace('*/', r'\*\/') 460 return f'\t/* {s} */\n' if len(s) < 80 else f'\t/* {s[0:80]}... */\n' 461 462 s = self.build_c_string(metric) 463 assert _bcs is not None 464 return f'{make_comment(s)}\t{{ { _bcs.offsets[s] } }},\n' 465 466 467_json_cache = {} 468def _read_json_events_impl(path: str, topic: str) -> Sequence[JsonEvent]: 469 """Read json events from the specified file.""" 470 try: 471 events = json.load(open(path), object_hook=JsonEvent) 472 except BaseException as err: 473 print(f"Exception processing {path}") 474 raise 475 metrics: list[Tuple[str, str, metric.Expression]] = [] 476 for event in events: 477 event.topic = topic 478 if event.metric_name and '-' not in event.metric_name: 479 metrics.append((event.pmu, event.metric_name, event.metric_expr)) 480 updates = metric.RewriteMetricsInTermsOfOthers(metrics) 481 if updates: 482 for event in events: 483 if event.metric_name in updates: 484 event.metric_expr = updates[event.metric_name] 485 486 return events 487 488def read_json_events(path: str, topic: str) -> Sequence[JsonEvent]: 489 key = (path, topic) 490 if key not in _json_cache: 491 _json_cache[key] = _read_json_events_impl(path, topic) 492 return _json_cache[key] 493 494def preprocess_arch_std_files(archpath: str) -> None: 495 """Read in all architecture standard events.""" 496 global _arch_std_events 497 for item in os.scandir(archpath): 498 if not item.is_file() or not item.name.endswith('.json'): 499 continue 500 try: 501 for event in read_json_events(item.path, topic=''): 502 if event.name: 503 _arch_std_events[event.name.lower()] = event 504 if event.metric_name: 505 _arch_std_events[event.metric_name.lower()] = event 506 except Exception as e: 507 raise RuntimeError(f'Failure processing \'{item.name}\' in \'{archpath}\'') from e 508 509 510def add_events_table_entries(item: os.DirEntry, topic: str) -> None: 511 """Add contents of file to _pending_events table.""" 512 for e in read_json_events(item.path, topic): 513 if e.name: 514 _pending_events.append(e) 515 if e.metric_name and not any(e.metric_name == x.metric_name and 516 e.pmu == x.pmu for x in _pending_metrics): 517 _pending_metrics.append(e) 518 519 520def print_pending_events() -> None: 521 """Optionally close events table.""" 522 523 def event_cmp_key(j: JsonEvent) -> Tuple[str, str, bool, str, str]: 524 def fix_none(s: Optional[str]) -> str: 525 if s is None: 526 return '' 527 return s 528 529 return (fix_none(j.pmu).replace(',','_'), fix_none(j.name), j.desc is not None, fix_none(j.topic), 530 fix_none(j.metric_name)) 531 532 global _pending_events 533 if not _pending_events: 534 return 535 536 global _pending_events_tblname 537 assert _pending_events_tblname is not None 538 if _pending_events_tblname.endswith('_sys'): 539 global _sys_event_tables 540 _sys_event_tables.append(_pending_events_tblname) 541 else: 542 global event_tables 543 _event_tables.append(_pending_events_tblname) 544 545 first = True 546 last_pmu = None 547 last_name = None 548 pmus: Set[Tuple[str, str]] = set() 549 assert _args is not None 550 for event in sorted(_pending_events, key=event_cmp_key): 551 if last_pmu and last_pmu == event.pmu: 552 assert event.name != last_name, f"Duplicate event: {last_pmu}/{last_name}/ in {_pending_events_tblname}" 553 if event.pmu != last_pmu: 554 if not first: 555 _args.output_file.write('};\n') 556 pmu_name = event.pmu.replace(',', '_') 557 _args.output_file.write( 558 f'static const struct compact_pmu_event {_pending_events_tblname}_{pmu_name}[] = {{\n') 559 first = False 560 last_pmu = event.pmu 561 pmus.add((event.pmu, pmu_name)) 562 563 _args.output_file.write(event.to_c_string(metric=False)) 564 last_name = event.name 565 _pending_events = [] 566 567 _args.output_file.write(f""" 568}}; 569 570static const struct pmu_table_entry {_pending_events_tblname}[] = {{ 571""") 572 for (pmu, tbl_pmu) in sorted(pmus): 573 pmu_name = f"{pmu}\\000" 574 _args.output_file.write(f"""\t{{ 575\t\t.entries = {_pending_events_tblname}_{tbl_pmu}, 576\t\t.num_entries = ARRAY_SIZE({_pending_events_tblname}_{tbl_pmu}), 577\t\t.pmu_name = {{ {_bcs.offsets[pmu_name]} /* {pmu_name} */ }}, 578\t}}, 579""") 580 _args.output_file.write('};\n\n') 581 582def print_pending_metrics() -> None: 583 """Optionally close metrics table.""" 584 585 def metric_cmp_key(j: JsonEvent) -> Tuple[str, str, str, str]: 586 def fix_none(s: Optional[str]) -> str: 587 if s is None: 588 return '' 589 return s 590 591 assert j.metric_expr is not None 592 return (fix_none(j.pmu), fix_none(j.metric_name), j.metric_expr.ToPerfJson(), 593 fix_none(j.desc)) 594 595 global _pending_metrics 596 if not _pending_metrics: 597 return 598 599 global _pending_metrics_tblname 600 assert _pending_metrics_tblname is not None 601 if _pending_metrics_tblname.endswith('_sys'): 602 global _sys_metric_tables 603 _sys_metric_tables.append(_pending_metrics_tblname) 604 else: 605 global metric_tables 606 _metric_tables.append(_pending_metrics_tblname) 607 608 first = True 609 last_pmu = None 610 pmus: Set[Tuple[str, str]] = set() 611 assert _args is not None 612 for metric in sorted(_pending_metrics, key=metric_cmp_key): 613 if metric.pmu != last_pmu: 614 if not first: 615 _args.output_file.write('};\n') 616 pmu_name = metric.pmu.replace(',', '_') 617 _args.output_file.write( 618 f'static const struct compact_pmu_event {_pending_metrics_tblname}_{pmu_name}[] = {{\n') 619 first = False 620 last_pmu = metric.pmu 621 pmus.add((metric.pmu, pmu_name)) 622 623 _args.output_file.write(metric.to_c_string(metric=True)) 624 _pending_metrics = [] 625 626 _args.output_file.write(f""" 627}}; 628 629static const struct pmu_table_entry {_pending_metrics_tblname}[] = {{ 630""") 631 for (pmu, tbl_pmu) in sorted(pmus): 632 pmu_name = f"{pmu}\\000" 633 _args.output_file.write(f"""\t{{ 634\t\t.entries = {_pending_metrics_tblname}_{tbl_pmu}, 635\t\t.num_entries = ARRAY_SIZE({_pending_metrics_tblname}_{tbl_pmu}), 636\t\t.pmu_name = {{ {_bcs.offsets[pmu_name]} /* {pmu_name} */ }}, 637\t}}, 638""") 639 _args.output_file.write('};\n\n') 640 641def get_topic(topic: str) -> str: 642 if topic.endswith('metrics.json'): 643 return 'metrics' 644 return removesuffix(topic, '.json').replace('-', ' ') 645 646def preprocess_one_file(parents: Sequence[str], item: os.DirEntry) -> None: 647 if item.is_dir(): 648 return 649 650 # base dir or too deep 651 level = len(parents) 652 if level == 0 or level > 4: 653 return 654 655 # Ignore other directories. If the file name does not have a .json 656 # extension, ignore it. It could be a readme.txt for instance. 657 if not item.is_file() or not item.name.endswith('.json'): 658 return 659 660 assert _bcs is not None 661 if item.name.endswith('metricgroups.json'): 662 metricgroup_descriptions = json.load(open(item.path)) 663 for mgroup in metricgroup_descriptions: 664 assert len(mgroup) > 1, parents 665 description = f"{metricgroup_descriptions[mgroup]}\\000" 666 mgroup = f"{mgroup}\\000" 667 _bcs.add(mgroup, metric=True) 668 _bcs.add(description, metric=True) 669 _metricgroups[mgroup] = description 670 return 671 672 topic = get_topic(item.name) 673 for event in read_json_events(item.path, topic): 674 pmu_name = f"{event.pmu}\\000" 675 if event.name: 676 _bcs.add(pmu_name, metric=False) 677 _bcs.add(event.build_c_string(metric=False), metric=False) 678 if event.metric_name: 679 _bcs.add(pmu_name, metric=True) 680 _bcs.add(event.build_c_string(metric=True), metric=True) 681 682def process_one_file(parents: Sequence[str], item: os.DirEntry) -> None: 683 """Process a JSON file during the main walk.""" 684 def is_leaf_dir_ignoring_sys(path: str) -> bool: 685 for item in os.scandir(path): 686 if item.is_dir() and item.name != 'sys': 687 return False 688 return True 689 690 # Model directories are leaves (ignoring possible sys 691 # directories). The FTW will walk into the directory next. Flush 692 # pending events and metrics and update the table names for the new 693 # model directory. 694 if item.is_dir() and is_leaf_dir_ignoring_sys(item.path): 695 print_pending_events() 696 print_pending_metrics() 697 698 global _pending_events_tblname 699 _pending_events_tblname = file_name_to_table_name('pmu_events_', parents, item.name) 700 global _pending_metrics_tblname 701 _pending_metrics_tblname = file_name_to_table_name('pmu_metrics_', parents, item.name) 702 703 if item.name == 'sys': 704 _sys_event_table_to_metric_table_mapping[_pending_events_tblname] = _pending_metrics_tblname 705 return 706 707 # base dir or too deep 708 level = len(parents) 709 if level == 0 or level > 4: 710 return 711 712 # Ignore other directories. If the file name does not have a .json 713 # extension, ignore it. It could be a readme.txt for instance. 714 if not item.is_file() or not item.name.endswith('.json') or item.name.endswith('metricgroups.json'): 715 return 716 717 add_events_table_entries(item, get_topic(item.name)) 718 719 720def print_mapping_table(archs: Sequence[str]) -> None: 721 """Read the mapfile and generate the struct from cpuid string to event table.""" 722 assert _args is not None 723 _args.output_file.write(""" 724/* Struct used to make the PMU event table implementation opaque to callers. */ 725struct pmu_events_table { 726\tconst struct pmu_table_entry *pmus; 727\tuint32_t num_pmus; 728}; 729 730/* Struct used to make the PMU metric table implementation opaque to callers. */ 731struct pmu_metrics_table { 732\tconst char *name; 733\tconst struct pmu_table_entry *pmus; 734\tuint32_t num_pmus; 735}; 736 737/* 738 * Map a CPU to its table of PMU events. The CPU is identified by the 739 * cpuid field, which is an arch-specific identifier for the CPU. 740 * The identifier specified in tools/perf/pmu-events/arch/xxx/mapfile 741 * must match the get_cpuid_str() in tools/perf/arch/xxx/util/header.c) 742 * 743 * The cpuid can contain any character other than the comma. 744 */ 745struct pmu_events_map { 746\tconst char *arch; 747\tconst char *cpuid; 748\tstruct pmu_events_table event_table; 749\tstruct pmu_metrics_table metric_table; 750}; 751 752/* 753 * Global table mapping each known CPU for the architecture to its 754 * table of PMU events. 755 */ 756static const struct pmu_events_map pmu_events_map[] = { 757""") 758 for arch in archs: 759 if arch == 'test': 760 _args.output_file.write("""{ 761\t.arch = "testarch", 762\t.cpuid = "testcpu", 763\t.event_table = { 764\t\t.pmus = pmu_events__test_soc_cpu, 765\t\t.num_pmus = ARRAY_SIZE(pmu_events__test_soc_cpu), 766\t}, 767\t.metric_table = { 768\t\t.name = "test_soc_cpu", 769\t\t.pmus = pmu_metrics__test_soc_cpu, 770\t\t.num_pmus = ARRAY_SIZE(pmu_metrics__test_soc_cpu), 771\t} 772}, 773""") 774 elif arch == 'common': 775 _args.output_file.write("""{ 776\t.arch = "common", 777\t.cpuid = "common", 778\t.event_table = { 779\t\t.pmus = pmu_events__common, 780\t\t.num_pmus = ARRAY_SIZE(pmu_events__common), 781\t}, 782\t.metric_table = { 783\t\t.name = "common", 784\t\t.pmus = pmu_metrics__common, 785\t\t.num_pmus = ARRAY_SIZE(pmu_metrics__common), 786\t}, 787}, 788""") 789 else: 790 with open(f'{_args.starting_dir}/{arch}/mapfile.csv') as csvfile: 791 table = csv.reader(csvfile) 792 first = True 793 for row in table: 794 # Skip the first row or any row beginning with #. 795 if not first and len(row) > 0 and not row[0].startswith('#'): 796 event_tblname = file_name_to_table_name('pmu_events_', [], row[2].replace('/', '_')) 797 if event_tblname in _event_tables: 798 event_size = f'ARRAY_SIZE({event_tblname})' 799 else: 800 event_tblname = 'NULL' 801 event_size = '0' 802 metric_tblname = file_name_to_table_name('pmu_metrics_', [], row[2].replace('/', '_')) 803 if metric_tblname in _metric_tables: 804 metric_name = f'"{metric_tblname.replace("pmu_metrics__", "")}"' 805 metric_size = f'ARRAY_SIZE({metric_tblname})' 806 else: 807 metric_name = 'NULL' 808 metric_tblname = 'NULL' 809 metric_size = '0' 810 if event_size == '0' and metric_size == '0': 811 continue 812 cpuid = row[0].replace('\\', '\\\\') 813 _args.output_file.write(f"""{{ 814\t.arch = "{arch}", 815\t.cpuid = "{cpuid}", 816\t.event_table = {{ 817\t\t.pmus = {event_tblname}, 818\t\t.num_pmus = {event_size} 819\t}}, 820\t.metric_table = {{ 821\t\t.name = {metric_name}, 822\t\t.pmus = {metric_tblname}, 823\t\t.num_pmus = {metric_size} 824\t}} 825}}, 826""") 827 first = False 828 829 _args.output_file.write("""{ 830\t.arch = 0, 831\t.cpuid = 0, 832\t.event_table = { 0, 0 }, 833\t.metric_table = { 0 }, 834} 835}; 836""") 837 838 839def print_metric_table_functions() -> None: 840 assert _args is not None 841 _args.output_file.write(""" 842const char *pmu_metrics_table__name(const struct pmu_metrics_table *table) 843{ 844\treturn table ? table->name : NULL; 845} 846 847int pmu_metrics_table__iterate_tables(pmu_metrics_table_iter_t fn, void *data) 848{ 849\tsize_t i; 850\tint ret; 851 852\tfor (i = 0; pmu_events_map[i].cpuid; i++) { 853\t\tsize_t j; 854\t\tbool found = false; 855 856\t\tif (!pmu_events_map[i].metric_table.pmus) 857\t\t\tcontinue; 858\t\tfor (j = 0; j < i; j++) { 859\t\t\tif (pmu_events_map[j].metric_table.pmus == 860\t\t\t pmu_events_map[i].metric_table.pmus) { 861\t\t\t\tfound = true; 862\t\t\t\tbreak; 863\t\t\t} 864\t\t} 865\t\tif (found) 866\t\t\tcontinue; 867\t\tret = fn(&pmu_events_map[i].metric_table, data); 868\t\tif (ret) 869\t\t\treturn ret; 870\t} 871\tfor (i = 0; pmu_sys_event_tables[i].name; i++) { 872\t\tif (!pmu_sys_event_tables[i].metric_table.pmus) 873\t\t\tcontinue; 874\t\tret = fn(&pmu_sys_event_tables[i].metric_table, data); 875\t\tif (ret) 876\t\t\treturn ret; 877\t} 878\treturn 0; 879} 880""") 881 882 883def print_system_mapping_table() -> None: 884 assert _args is not None 885 """C struct mapping table array for tables from /sys directories.""" 886 _args.output_file.write(""" 887struct pmu_sys_events { 888\tconst char *name; 889\tstruct pmu_events_table event_table; 890\tstruct pmu_metrics_table metric_table; 891}; 892 893static const struct pmu_sys_events pmu_sys_event_tables[] = { 894""") 895 printed_metric_tables = [] 896 for tblname in _sys_event_tables: 897 _args.output_file.write(f"""\t{{ 898\t\t.event_table = {{ 899\t\t\t.pmus = {tblname}, 900\t\t\t.num_pmus = ARRAY_SIZE({tblname}) 901\t\t}},""") 902 metric_tblname = _sys_event_table_to_metric_table_mapping[tblname] 903 if metric_tblname in _sys_metric_tables: 904 _args.output_file.write(f""" 905\t\t.metric_table = {{ 906\t\t\t.name = "{metric_tblname.replace('pmu_metrics__', '')}", 907\t\t\t.pmus = {metric_tblname}, 908\t\t\t.num_pmus = ARRAY_SIZE({metric_tblname}) 909\t\t}},""") 910 printed_metric_tables.append(metric_tblname) 911 _args.output_file.write(f""" 912\t\t.name = \"{tblname}\", 913\t}}, 914""") 915 for tblname in _sys_metric_tables: 916 if tblname in printed_metric_tables: 917 continue 918 _args.output_file.write(f"""\t{{ 919\t\t.metric_table = {{ 920\t\t\t.name = "{tblname.replace('pmu_metrics__', '')}", 921\t\t\t.pmus = {tblname}, 922\t\t\t.num_pmus = ARRAY_SIZE({tblname}) 923\t\t}}, 924\t\t.name = \"{tblname}\", 925\t}}, 926""") 927 _args.output_file.write("""\t{ 928\t\t.event_table = { 0, 0 }, 929\t\t.metric_table = { 0 }, 930\t}, 931}; 932 933static void decompress_event(int offset, struct pmu_event *pe) 934{ 935\tconst char *p = &big_c_string[offset]; 936""") 937 for attr in _json_event_attributes: 938 _args.output_file.write(f'\n\tpe->{attr} = ') 939 if attr in _json_enum_attributes: 940 _args.output_file.write("*p - '0';\n") 941 else: 942 _args.output_file.write("(*p == '\\0' ? NULL : p);\n") 943 if attr == _json_event_attributes[-1]: 944 continue 945 if attr in _json_enum_attributes: 946 _args.output_file.write('\tp++;') 947 else: 948 _args.output_file.write('\twhile (*p++);') 949 _args.output_file.write("""} 950 951static void decompress_metric(int offset, struct pmu_metric *pm) 952{ 953\tconst char *p = &big_c_string[offset]; 954""") 955 for attr in _json_metric_attributes: 956 _args.output_file.write(f'\n\tpm->{attr} = ') 957 if attr in _json_enum_attributes: 958 _args.output_file.write("*p - '0';\n") 959 else: 960 _args.output_file.write("(*p == '\\0' ? NULL : p);\n") 961 if attr == _json_metric_attributes[-1]: 962 continue 963 if attr in _json_enum_attributes: 964 _args.output_file.write('\tp++;') 965 else: 966 _args.output_file.write('\twhile (*p++);') 967 _args.output_file.write("""} 968 969static int pmu_events_table__for_each_event_pmu(const struct pmu_events_table *table, 970\t\t\t\t\t\tconst struct pmu_table_entry *pmu, 971\t\t\t\t\t\tpmu_event_iter_fn fn, 972\t\t\t\t\t\tvoid *data) 973{ 974\tint ret; 975\tstruct pmu_event pe = { 976\t\t.pmu = &big_c_string[pmu->pmu_name.offset], 977\t}; 978 979\tfor (uint32_t i = 0; i < pmu->num_entries; i++) { 980\t\tdecompress_event(pmu->entries[i].offset, &pe); 981\t\tif (!pe.name) 982\t\t\tcontinue; 983\t\tret = fn(&pe, table, data); 984\t\tif (ret) 985\t\t\treturn ret; 986\t} 987\treturn 0; 988 } 989 990static int pmu_events_table__find_event_pmu(const struct pmu_events_table *table, 991\t\t\t\t\t const struct pmu_table_entry *pmu, 992\t\t\t\t\t const char *name, 993\t\t\t\t\t pmu_event_iter_fn fn, 994\t\t\t\t\t void *data) 995{ 996\tstruct pmu_event pe = { 997\t\t.pmu = &big_c_string[pmu->pmu_name.offset], 998\t}; 999\tint low = 0, high = pmu->num_entries - 1; 1000 1001\twhile (low <= high) { 1002\t\tint cmp, mid = (low + high) / 2; 1003 1004\t\tdecompress_event(pmu->entries[mid].offset, &pe); 1005 1006\t\tif (!pe.name && !name) 1007\t\t\tgoto do_call; 1008 1009\t\tif (!pe.name && name) { 1010\t\t\tlow = mid + 1; 1011\t\t\tcontinue; 1012\t\t} 1013\t\tif (pe.name && !name) { 1014\t\t\thigh = mid - 1; 1015\t\t\tcontinue; 1016\t\t} 1017 1018\t\tcmp = strcasecmp(pe.name, name); 1019\t\tif (cmp < 0) { 1020\t\t\tlow = mid + 1; 1021\t\t\tcontinue; 1022\t\t} 1023\t\tif (cmp > 0) { 1024\t\t\thigh = mid - 1; 1025\t\t\tcontinue; 1026\t\t} 1027 do_call: 1028\t\treturn fn ? fn(&pe, table, data) : 0; 1029\t} 1030\treturn PMU_EVENTS__NOT_FOUND; 1031} 1032 1033int pmu_events_table__for_each_event(const struct pmu_events_table *table, 1034\t\t\t\t struct perf_pmu *pmu, 1035\t\t\t\t pmu_event_iter_fn fn, 1036\t\t\t\t void *data) 1037{ 1038\tif (!table) 1039\t\treturn 0; 1040\tfor (size_t i = 0; i < table->num_pmus; i++) { 1041\t\tconst struct pmu_table_entry *table_pmu = &table->pmus[i]; 1042\t\tconst char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; 1043\t\tint ret; 1044 1045\t\tif (pmu && !perf_pmu__name_wildcard_match(pmu, pmu_name)) 1046\t\t\tcontinue; 1047 1048\t\tret = pmu_events_table__for_each_event_pmu(table, table_pmu, fn, data); 1049\t\tif (ret) 1050\t\t\treturn ret; 1051\t} 1052\treturn 0; 1053} 1054 1055int pmu_events_table__find_event(const struct pmu_events_table *table, 1056\t\t\t\t struct perf_pmu *pmu, 1057\t\t\t\t const char *name, 1058\t\t\t\t pmu_event_iter_fn fn, 1059\t\t\t\t void *data) 1060{ 1061\tif (!table) 1062\t\treturn PMU_EVENTS__NOT_FOUND; 1063\tfor (size_t i = 0; i < table->num_pmus; i++) { 1064\t\tconst struct pmu_table_entry *table_pmu = &table->pmus[i]; 1065\t\tconst char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; 1066\t\tint ret; 1067 1068\t\tif (pmu && !perf_pmu__name_wildcard_match(pmu, pmu_name)) 1069\t\t\tcontinue; 1070 1071\t\tret = pmu_events_table__find_event_pmu(table, table_pmu, name, fn, data); 1072\t\tif (ret != PMU_EVENTS__NOT_FOUND) 1073\t\t\treturn ret; 1074\t} 1075\treturn PMU_EVENTS__NOT_FOUND; 1076} 1077 1078size_t pmu_events_table__num_events(const struct pmu_events_table *table, struct perf_pmu *pmu) 1079{ 1080\tsize_t count = 0; 1081 1082\tif (!table) 1083\t\treturn 0; 1084\tfor (size_t i = 0; i < table->num_pmus; i++) { 1085\t\tconst struct pmu_table_entry *table_pmu = &table->pmus[i]; 1086\t\tconst char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; 1087 1088\t\tif (perf_pmu__name_wildcard_match(pmu, pmu_name)) 1089\t\t\tcount += table_pmu->num_entries; 1090\t} 1091\treturn count; 1092} 1093 1094static int pmu_metrics_table__for_each_metric_pmu(const struct pmu_metrics_table *table, 1095\t\t\t\t\t\tconst struct pmu_table_entry *pmu, 1096\t\t\t\t\t\tpmu_metric_iter_fn fn, 1097\t\t\t\t\t\tvoid *data) 1098{ 1099\tint ret; 1100\tstruct pmu_metric pm = { 1101\t\t.pmu = &big_c_string[pmu->pmu_name.offset], 1102\t}; 1103 1104\tfor (uint32_t i = 0; i < pmu->num_entries; i++) { 1105\t\tdecompress_metric(pmu->entries[i].offset, &pm); 1106\t\tif (!pm.metric_expr) 1107\t\t\tcontinue; 1108\t\tret = fn(&pm, table, data); 1109\t\tif (ret) 1110\t\t\treturn ret; 1111\t} 1112\treturn 0; 1113} 1114 1115static int pmu_metrics_table__find_metric_pmu(const struct pmu_metrics_table *table, 1116\t\t\t\t\t const struct pmu_table_entry *pmu, 1117\t\t\t\t\t const char *metric, 1118\t\t\t\t\t pmu_metric_iter_fn fn, 1119\t\t\t\t\t void *data) 1120{ 1121\tstruct pmu_metric pm = { 1122\t\t.pmu = &big_c_string[pmu->pmu_name.offset], 1123\t}; 1124\tint low = 0, high = pmu->num_entries - 1; 1125 1126\twhile (low <= high) { 1127\t\tint cmp, mid = (low + high) / 2; 1128 1129\t\tdecompress_metric(pmu->entries[mid].offset, &pm); 1130 1131\t\tif (!pm.metric_name && !metric) 1132\t\t\tgoto do_call; 1133 1134\t\tif (!pm.metric_name && metric) { 1135\t\t\tlow = mid + 1; 1136\t\t\tcontinue; 1137\t\t} 1138\t\tif (pm.metric_name && !metric) { 1139\t\t\thigh = mid - 1; 1140\t\t\tcontinue; 1141\t\t} 1142 1143\t\tcmp = strcmp(pm.metric_name, metric); 1144\t\tif (cmp < 0) { 1145\t\t\tlow = mid + 1; 1146\t\t\tcontinue; 1147\t\t} 1148\t\tif (cmp > 0) { 1149\t\t\thigh = mid - 1; 1150\t\t\tcontinue; 1151\t\t} 1152 do_call: 1153\t\treturn fn ? fn(&pm, table, data) : 0; 1154\t} 1155\treturn PMU_METRICS__NOT_FOUND; 1156} 1157 1158int pmu_metrics_table__for_each_metric(const struct pmu_metrics_table *table, 1159\t\t\t\t pmu_metric_iter_fn fn, 1160\t\t\t\t void *data) 1161{ 1162\tif (!table) 1163\t\treturn 0; 1164\tfor (size_t i = 0; i < table->num_pmus; i++) { 1165\t\tint ret = pmu_metrics_table__for_each_metric_pmu(table, &table->pmus[i], fn, data); 1166 1167\t\tif (ret) 1168\t\t\treturn ret; 1169\t} 1170\treturn 0; 1171} 1172 1173int pmu_metrics_table__find_metric(const struct pmu_metrics_table *table, 1174\t\t\t\t struct perf_pmu *pmu, 1175\t\t\t\t const char *metric, 1176\t\t\t\t pmu_metric_iter_fn fn, 1177\t\t\t\t void *data) 1178{ 1179\tif (!table) 1180\t\treturn 0; 1181\tfor (size_t i = 0; i < table->num_pmus; i++) { 1182\t\tconst struct pmu_table_entry *table_pmu = &table->pmus[i]; 1183\t\tconst char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; 1184\t\tint ret; 1185 1186\t\tif (pmu && !perf_pmu__name_wildcard_match(pmu, pmu_name)) 1187\t\t\tcontinue; 1188 1189\t\tret = pmu_metrics_table__find_metric_pmu(table, table_pmu, metric, fn, data); 1190\t\tif (ret != PMU_METRICS__NOT_FOUND) 1191\t\t\treturn ret; 1192\t} 1193\treturn PMU_METRICS__NOT_FOUND; 1194} 1195 1196static const struct pmu_events_map *map_for_cpu(struct perf_cpu cpu) 1197{ 1198\tstatic struct { 1199\t\tconst struct pmu_events_map *map; 1200\t\tstruct perf_cpu cpu; 1201\t} last_result; 1202\tstatic struct { 1203\t\tconst struct pmu_events_map *map; 1204\t\tchar *cpuid; 1205\t} last_map_search; 1206\tstatic bool has_last_result, has_last_map_search; 1207\tconst struct pmu_events_map *map = NULL; 1208\tchar *cpuid = NULL; 1209\tsize_t i; 1210 1211\tif (has_last_result && last_result.cpu.cpu == cpu.cpu) 1212\t\treturn last_result.map; 1213 1214\tcpuid = get_cpuid_allow_env_override(cpu); 1215 1216\t/* 1217\t * On some platforms which uses cpus map, cpuid can be NULL for 1218\t * PMUs other than CORE PMUs. 1219\t */ 1220\tif (!cpuid) 1221\t\tgoto out_update_last_result; 1222 1223\tif (has_last_map_search && !strcmp(last_map_search.cpuid, cpuid)) { 1224\t\tmap = last_map_search.map; 1225\t\tfree(cpuid); 1226\t} else { 1227\t\ti = 0; 1228\t\tfor (;;) { 1229\t\t\tmap = &pmu_events_map[i++]; 1230 1231\t\t\tif (!map->arch) { 1232\t\t\t\tmap = NULL; 1233\t\t\t\tbreak; 1234\t\t\t} 1235 1236\t\t\tif (!strcmp_cpuid_str(map->cpuid, cpuid)) 1237\t\t\t\tbreak; 1238\t\t} 1239\t\tfree(last_map_search.cpuid); 1240\t\tlast_map_search.cpuid = cpuid; 1241\t\tlast_map_search.map = map; 1242\t\thas_last_map_search = true; 1243\t} 1244out_update_last_result: 1245\tlast_result.cpu = cpu; 1246\tlast_result.map = map; 1247\thas_last_result = true; 1248\treturn map; 1249} 1250 1251static const struct pmu_events_map *map_for_pmu(struct perf_pmu *pmu) 1252{ 1253\tstruct perf_cpu cpu = { -1 }; 1254 1255\tif (pmu) { 1256\t\tfor (size_t i = 0; i < ARRAY_SIZE(pmu_events__common); i++) { 1257\t\t\tconst char *pmu_name = &big_c_string[pmu_events__common[i].pmu_name.offset]; 1258 1259\t\t\tif (!strcmp(pmu_name, pmu->name)) { 1260\t\t\t\tconst struct pmu_events_map *map = &pmu_events_map[0]; 1261 1262\t\t\t\twhile (strcmp("common", map->arch)) 1263\t\t\t\t\tmap++; 1264\t\t\t\treturn map; 1265\t\t\t} 1266\t\t} 1267\t\tcpu = perf_cpu_map__min(pmu->cpus); 1268\t} 1269\treturn map_for_cpu(cpu); 1270} 1271 1272const struct pmu_events_table *perf_pmu__find_events_table(struct perf_pmu *pmu) 1273{ 1274\tconst struct pmu_events_map *map = map_for_pmu(pmu); 1275 1276\tif (!map) 1277\t\treturn NULL; 1278 1279\tif (!pmu) 1280\t\treturn &map->event_table; 1281 1282\tfor (size_t i = 0; i < map->event_table.num_pmus; i++) { 1283\t\tconst struct pmu_table_entry *table_pmu = &map->event_table.pmus[i]; 1284\t\tconst char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; 1285 1286\t\tif (perf_pmu__name_wildcard_match(pmu, pmu_name)) 1287\t\t\treturn &map->event_table; 1288\t} 1289\treturn NULL; 1290} 1291 1292const struct pmu_events_table *perf_pmu__default_core_events_table(void) 1293{ 1294\tint i = 0; 1295 1296\tfor (;;) { 1297\t\tconst struct pmu_events_map *map = &pmu_events_map[i++]; 1298 1299\t\tif (!map->arch) 1300\t\t\tbreak; 1301 1302\t\tif (!strcmp(map->cpuid, "common")) 1303\t\t\treturn &map->event_table; 1304\t} 1305\treturn NULL; 1306} 1307 1308const struct pmu_metrics_table *pmu_metrics_table__find(void) 1309{ 1310\tstruct perf_cpu cpu = { -1 }; 1311\tconst struct pmu_events_map *map = map_for_cpu(cpu); 1312 1313\treturn map ? &map->metric_table : NULL; 1314} 1315 1316const struct pmu_metrics_table *pmu_metrics_table__default(void) 1317{ 1318\tint i = 0; 1319 1320\tfor (;;) { 1321\t\tconst struct pmu_events_map *map = &pmu_events_map[i++]; 1322 1323\t\tif (!map->arch) 1324\t\t\tbreak; 1325 1326\t\tif (!strcmp(map->cpuid, "common")) 1327\t\t\treturn &map->metric_table; 1328\t} 1329\treturn NULL; 1330} 1331 1332const struct pmu_events_table *find_core_events_table(const char *arch, const char *cpuid) 1333{ 1334\tfor (const struct pmu_events_map *tables = &pmu_events_map[0]; 1335\t tables->arch; 1336\t tables++) { 1337\t\tif (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid)) 1338\t\t\treturn &tables->event_table; 1339\t} 1340\treturn NULL; 1341} 1342 1343const struct pmu_metrics_table *find_core_metrics_table(const char *arch, const char *cpuid) 1344{ 1345\tfor (const struct pmu_events_map *tables = &pmu_events_map[0]; 1346\t tables->arch; 1347\t tables++) { 1348\t\tif (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid)) 1349\t\t\treturn &tables->metric_table; 1350\t} 1351\treturn NULL; 1352} 1353 1354int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data) 1355{ 1356\tfor (const struct pmu_events_map *tables = &pmu_events_map[0]; 1357\t tables->arch; 1358\t tables++) { 1359\t\tint ret = pmu_events_table__for_each_event(&tables->event_table, 1360\t\t\t\t\t\t\t /*pmu=*/NULL, fn, data); 1361 1362\t\tif (ret) 1363\t\t\treturn ret; 1364\t} 1365\treturn 0; 1366} 1367 1368int pmu_for_each_core_metric(pmu_metric_iter_fn fn, void *data) 1369{ 1370\tfor (const struct pmu_events_map *tables = &pmu_events_map[0]; 1371\t tables->arch; 1372\t tables++) { 1373\t\tint ret = pmu_metrics_table__for_each_metric(&tables->metric_table, fn, data); 1374 1375\t\tif (ret) 1376\t\t\treturn ret; 1377\t} 1378\treturn 0; 1379} 1380 1381const struct pmu_events_table *find_sys_events_table(const char *name) 1382{ 1383\tfor (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; 1384\t tables->name; 1385\t tables++) { 1386\t\tif (!strcmp(tables->name, name)) 1387\t\t\treturn &tables->event_table; 1388\t} 1389\treturn NULL; 1390} 1391 1392int pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data) 1393{ 1394\tfor (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; 1395\t tables->name; 1396\t tables++) { 1397\t\tint ret = pmu_events_table__for_each_event(&tables->event_table, 1398\t\t\t\t\t\t\t /*pmu=*/NULL, fn, data); 1399 1400\t\tif (ret) 1401\t\t\treturn ret; 1402\t} 1403\treturn 0; 1404} 1405 1406int pmu_for_each_sys_metric(pmu_metric_iter_fn fn, void *data) 1407{ 1408\tfor (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; 1409\t tables->name; 1410\t tables++) { 1411\t\tint ret = pmu_metrics_table__for_each_metric(&tables->metric_table, fn, data); 1412 1413\t\tif (ret) 1414\t\t\treturn ret; 1415\t} 1416\treturn 0; 1417} 1418""") 1419 1420def print_metricgroups() -> None: 1421 assert _args is not None 1422 _args.output_file.write(""" 1423static const int metricgroups[][2] = { 1424""") 1425 for mgroup in sorted(_metricgroups): 1426 description = _metricgroups[mgroup] 1427 _args.output_file.write( 1428 f'\t{{ {_bcs.offsets[mgroup]}, {_bcs.offsets[description]} }}, /* {mgroup} => {description} */\n' 1429 ) 1430 _args.output_file.write(""" 1431}; 1432 1433const char *describe_metricgroup(const char *group) 1434{ 1435\tint low = 0, high = (int)ARRAY_SIZE(metricgroups) - 1; 1436 1437\twhile (low <= high) { 1438\t\tint mid = (low + high) / 2; 1439\t\tconst char *mgroup = &big_c_string[metricgroups[mid][0]]; 1440\t\tint cmp = strcmp(mgroup, group); 1441 1442\t\tif (cmp == 0) 1443\t\t\treturn &big_c_string[metricgroups[mid][1]]; 1444\t\telse if (cmp < 0) 1445\t\t\tlow = mid + 1; 1446\t\telse 1447\t\t\thigh = mid - 1; 1448\t} 1449\treturn NULL; 1450} 1451""") 1452 1453def _parallel_read_json_events(task: Tuple[str, str]) -> Tuple[str, str, Sequence[JsonEvent]]: 1454 path, topic = task 1455 return path, topic, _read_json_events_impl(path, topic) 1456 1457def _init_worker(std_events: dict) -> None: 1458 global _arch_std_events 1459 _arch_std_events = std_events 1460 1461def main() -> None: 1462 global _args 1463 1464 def dir_path(path: str) -> str: 1465 """Validate path is a directory for argparse.""" 1466 if os.path.isdir(path): 1467 return path 1468 raise argparse.ArgumentTypeError(f'\'{path}\' is not a valid directory') 1469 1470 def ftw(path: str, parents: Sequence[str], 1471 action: Callable[[Sequence[str], os.DirEntry], None]) -> None: 1472 """Replicate the directory/file walking behavior of C's file tree walk.""" 1473 assert _args is not None 1474 for item in sorted(os.scandir(path), key=lambda e: e.name): 1475 if _args.model != 'all' and item.is_dir(): 1476 # Check if the model matches one in _args.model. 1477 if len(parents) == _args.model.split(',')[0].count('/'): 1478 # We're testing the correct directory. 1479 item_path = '/'.join(parents) + ('/' if len(parents) > 0 else '') + item.name 1480 if 'test' not in item_path and 'common' not in item_path and item_path not in _args.model.split(','): 1481 continue 1482 try: 1483 action(parents, item) 1484 except Exception as e: 1485 raise RuntimeError(f'Action failure for \'{item.name}\' in {parents}') from e 1486 if item.is_dir(): 1487 ftw(item.path, parents + [item.name], action) 1488 1489 ap = argparse.ArgumentParser() 1490 ap.add_argument('arch', help='Architecture name like x86') 1491 ap.add_argument('model', help='''Select a model such as skylake to 1492reduce the code size. Normally set to "all". For architectures like 1493ARM64 with an implementor/model, the model must include the implementor 1494such as "arm/cortex-a34".''', 1495 default='all') 1496 ap.add_argument( 1497 'starting_dir', 1498 type=dir_path, 1499 help='Root of tree containing architecture directories containing json files' 1500 ) 1501 ap.add_argument( 1502 'output_file', type=argparse.FileType('w', encoding='utf-8'), nargs='?', default=sys.stdout) 1503 ap.add_argument( 1504 'output_string_file', type=argparse.FileType('w', encoding='utf-8'), nargs='?', default=None) 1505 _args = ap.parse_args() 1506 1507 _args.output_file.write(f"""/* SPDX-License-Identifier: GPL-2.0 */ 1508/* THIS FILE WAS AUTOGENERATED BY `jevents.py arch={_args.arch} model={_args.model}` ! */ 1509""") 1510 _args.output_file.write(""" 1511#include <pmu-events/pmu-events.h> 1512#include "util/header.h" 1513#include "util/pmu.h" 1514#include <string.h> 1515#include <stddef.h> 1516 1517struct compact_pmu_event { 1518\tint offset; 1519}; 1520 1521struct pmu_table_entry { 1522\tconst struct compact_pmu_event *entries; 1523\tuint32_t num_entries; 1524\tstruct compact_pmu_event pmu_name; 1525}; 1526 1527""") 1528 archs = [] 1529 for item in os.scandir(_args.starting_dir): 1530 if not item.is_dir(): 1531 continue 1532 if item.name == _args.arch or _args.arch == 'all' or item.name == 'test' or item.name == 'common': 1533 archs.append(item.name) 1534 1535 if len(archs) < 2 and _args.arch != 'none': 1536 raise IOError(f'Missing architecture directory \'{_args.arch}\'') 1537 1538 archs.sort() 1539 import concurrent.futures 1540 tasks = [] 1541 def collect_json(parents: Sequence[str], item: os.DirEntry) -> None: 1542 if len(parents) == 0: 1543 return 1544 if item.is_file() and item.name.endswith('.json') and not item.name.endswith('metricgroups.json'): 1545 tasks.append((item.path, get_topic(item.name))) 1546 1547 for arch in archs: 1548 arch_path = f'{_args.starting_dir}/{arch}' 1549 preprocess_arch_std_files(arch_path) 1550 ftw(arch_path, [], collect_json) 1551 1552 with concurrent.futures.ProcessPoolExecutor(initializer=_init_worker, initargs=(_arch_std_events,)) as executor: 1553 for path, topic, events in executor.map(_parallel_read_json_events, tasks): 1554 _json_cache[(path, topic)] = events 1555 1556 for arch in archs: 1557 arch_path = f'{_args.starting_dir}/{arch}' 1558 ftw(arch_path, [], preprocess_one_file) 1559 1560 assert _bcs is not None 1561 _bcs.compute() 1562 _args.output_file.write('/* clang-format off */\n') 1563 if not _args.output_string_file: 1564 _args.output_file.write('static const char *const big_c_string =\n') 1565 for s in _bcs.big_string: 1566 _args.output_file.write(s) 1567 _args.output_file.write(';\n\n') 1568 else: 1569 _args.output_string_file.write('/* SPDX-License-Identifier: GPL-2.0 */\n') 1570 _args.output_string_file.write('/* Autogenerated by jevents.py */\n') 1571 _args.output_string_file.write('extern const char big_c_string[];\n') 1572 _args.output_string_file.write('const char big_c_string[] =\n') 1573 for s in _bcs.big_string: 1574 _args.output_string_file.write(s) 1575 _args.output_string_file.write(';\n') 1576 _args.output_file.write('extern const char big_c_string[];\n\n') 1577 for arch in archs: 1578 arch_path = f'{_args.starting_dir}/{arch}' 1579 ftw(arch_path, [], process_one_file) 1580 print_pending_events() 1581 print_pending_metrics() 1582 1583 print_mapping_table(archs) 1584 print_system_mapping_table() 1585 _args.output_file.write('/* clang-format on */\n') 1586 print_metric_table_functions() 1587 print_metricgroups() 1588 _args.output_file.close() 1589 if _args.output_string_file: 1590 _args.output_string_file.close() 1591 1592if __name__ == '__main__': 1593 main() 1594