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 = [] 18# List of event tables generated from "/sys" directories. 19_sys_event_tables = [] 20# List of regular metric tables. 21_metric_tables = [] 22# List of metric tables generated from "/sys" directories. 23_sys_metric_tables = [] 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 = {} 30# Events to write out when the table is closed 31_pending_events = [] 32# Name of events table to be written out 33_pending_events_tblname = None 34# Metrics to write out when the table is closed 35_pending_metrics = [] 36# Name of metrics table to be written out 37_pending_metrics_tblname = 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: str) -> 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: 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: 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: 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 0x3F7: 'frontend=', 253 } 254 return msrmap[int(num.split(',', 1)[0], 0)] 255 256 def real_event(name: str, event: str) -> Optional[str]: 257 """Convert well known event names to an event string otherwise use the event argument.""" 258 fixed = { 259 'inst_retired.any': 'event=0xc0,period=2000003', 260 'inst_retired.any_p': 'event=0xc0,period=2000003', 261 'cpu_clk_unhalted.ref': 'event=0x0,umask=0x03,period=2000003', 262 'cpu_clk_unhalted.thread': 'event=0x3c,period=2000003', 263 'cpu_clk_unhalted.core': 'event=0x3c,period=2000003', 264 'cpu_clk_unhalted.thread_any': 'event=0x3c,any=1,period=2000003', 265 } 266 if not name: 267 return None 268 if name.lower() in fixed: 269 return fixed[name.lower()] 270 return event 271 272 def unit_to_pmu(unit: str) -> Optional[str]: 273 """Convert a JSON Unit to Linux PMU name.""" 274 if not unit: 275 return 'default_core' 276 # Comment brought over from jevents.c: 277 # it's not realistic to keep adding these, we need something more scalable ... 278 table = { 279 'CBO': 'uncore_cbox', 280 'QPI LL': 'uncore_qpi', 281 'SBO': 'uncore_sbox', 282 'iMPH-U': 'uncore_arb', 283 'CPU-M-CF': 'cpum_cf', 284 'CPU-M-SF': 'cpum_sf', 285 'PAI-CRYPTO' : 'pai_crypto', 286 'PAI-EXT' : 'pai_ext', 287 'UPI LL': 'uncore_upi', 288 'hisi_sicl,cpa': 'hisi_sicl,cpa', 289 'hisi_sccl,ddrc': 'hisi_sccl,ddrc', 290 'hisi_sccl,hha': 'hisi_sccl,hha', 291 'hisi_sccl,l3c': 'hisi_sccl,l3c', 292 'imx8_ddr': 'imx8_ddr', 293 'imx9_ddr': 'imx9_ddr', 294 'L3PMC': 'amd_l3', 295 'DFPMC': 'amd_df', 296 'UMCPMC': 'amd_umc', 297 'cpu_core': 'cpu_core', 298 'cpu_atom': 'cpu_atom', 299 'ali_drw': 'ali_drw', 300 'arm_cmn': 'arm_cmn', 301 'software': 'software', 302 'tool': 'tool', 303 } 304 return table[unit] if unit in table else f'uncore_{unit.lower()}' 305 306 def is_zero(val: str) -> bool: 307 try: 308 if val.startswith('0x'): 309 return int(val, 16) == 0 310 else: 311 return int(val) == 0 312 except e: 313 return False 314 315 def canonicalize_value(val: str) -> str: 316 try: 317 if val.startswith('0x'): 318 return llx(int(val, 16)) 319 return str(int(val)) 320 except e: 321 return val 322 323 eventcode = 0 324 if 'EventCode' in jd: 325 eventcode = int(jd['EventCode'].split(',', 1)[0], 0) 326 if 'ExtSel' in jd: 327 eventcode |= int(jd['ExtSel']) << 8 328 configcode = int(jd['ConfigCode'], 0) if 'ConfigCode' in jd else None 329 eventidcode = int(jd['EventidCode'], 0) if 'EventidCode' in jd else None 330 legacy_hw_config = int(jd['LegacyConfigCode'], 0) if 'LegacyConfigCode' in jd else None 331 legacy_cache_config = int(jd['LegacyCacheCode'], 0) if 'LegacyCacheCode' in jd else None 332 self.name = jd['EventName'].lower() if 'EventName' in jd else None 333 self.topic = '' 334 self.compat = jd.get('Compat') 335 self.desc = fixdesc(jd.get('BriefDescription')) 336 self.long_desc = fixdesc(jd.get('PublicDescription')) 337 precise = jd.get('PEBS') 338 msr = lookup_msr(jd.get('MSRIndex')) 339 msrval = jd.get('MSRValue') 340 extra_desc = '' 341 if 'Data_LA' in jd: 342 extra_desc += ' Supports address when precise' 343 if 'Errata' in jd: 344 extra_desc += '.' 345 if 'Errata' in jd: 346 extra_desc += ' Spec update: ' + jd['Errata'] 347 self.pmu = unit_to_pmu(jd.get('Unit')) 348 filter = jd.get('Filter') 349 self.unit = jd.get('ScaleUnit') 350 self.perpkg = jd.get('PerPkg') 351 self.aggr_mode = convert_aggr_mode(jd.get('AggregationMode')) 352 self.deprecated = jd.get('Deprecated') 353 self.retirement_latency_mean = jd.get('RetirementLatencyMean') 354 self.retirement_latency_min = jd.get('RetirementLatencyMin') 355 self.retirement_latency_max = jd.get('RetirementLatencyMax') 356 self.metric_name = jd.get('MetricName') 357 self.metric_group = jd.get('MetricGroup') 358 self.metricgroup_no_group = jd.get('MetricgroupNoGroup') 359 self.default_metricgroup_name = jd.get('DefaultMetricgroupName') 360 self.event_grouping = convert_metric_constraint(jd.get('MetricConstraint')) 361 self.default_show_events = jd.get('DefaultShowEvents') 362 self.metric_expr = None 363 if 'MetricExpr' in jd: 364 self.metric_expr = metric.ParsePerfJson(jd['MetricExpr']).Simplify() 365 # Note, the metric formula for the threshold isn't parsed as the & 366 # and > have incorrect precedence. 367 self.metric_threshold = jd.get('MetricThreshold') 368 369 arch_std = jd.get('ArchStdEvent') 370 if precise and self.desc and '(Precise Event)' not in self.desc: 371 extra_desc += ' (Must be precise)' if precise == '2' else (' (Precise ' 372 'event)') 373 event = None 374 if configcode is not None: 375 event = f'config={llx(configcode)}' 376 elif eventidcode is not None: 377 event = f'eventid={llx(eventidcode)}' 378 elif legacy_hw_config is not None: 379 event = f'legacy-hardware-config={llx(legacy_hw_config)}' 380 elif legacy_cache_config is not None: 381 event = f'legacy-cache-config={llx(legacy_cache_config)}' 382 else: 383 event = f'event={llx(eventcode)}' 384 event_fields = [ 385 ('AnyThread', 'any='), 386 ('PortMask', 'ch_mask='), 387 ('CounterMask', 'cmask='), 388 ('EdgeDetect', 'edge='), 389 ('FCMask', 'fc_mask='), 390 ('Invert', 'inv='), 391 ('SampleAfterValue', 'period='), 392 ('UMask', 'umask='), 393 ('NodeType', 'type='), 394 ('RdWrMask', 'rdwrmask='), 395 ('EnAllCores', 'enallcores='), 396 ('EnAllSlices', 'enallslices='), 397 ('SliceId', 'sliceid='), 398 ('ThreadMask', 'threadmask='), 399 ] 400 for key, value in event_fields: 401 if key in jd and not is_zero(jd[key]): 402 event += f',{value}{canonicalize_value(jd[key])}' 403 if filter: 404 event += f',{filter}' 405 if msr: 406 event += f',{msr}{msrval}' 407 if self.desc and extra_desc: 408 self.desc += extra_desc 409 if self.long_desc and extra_desc: 410 self.long_desc += extra_desc 411 if self.desc and self.long_desc and self.desc == self.long_desc: 412 # Avoid duplicated descriptions. 413 self.long_desc = None 414 if arch_std: 415 if arch_std.lower() in _arch_std_events: 416 event = _arch_std_events[arch_std.lower()].event 417 # Copy from the architecture standard event to self for undefined fields. 418 for attr, value in _arch_std_events[arch_std.lower()].__dict__.items(): 419 if hasattr(self, attr) and not getattr(self, attr): 420 setattr(self, attr, value) 421 else: 422 raise argparse.ArgumentTypeError('Cannot find arch std event:', arch_std) 423 424 self.event = real_event(self.name, event) 425 426 def __repr__(self) -> str: 427 """String representation primarily for debugging.""" 428 s = '{\n' 429 for attr, value in self.__dict__.items(): 430 if value: 431 s += f'\t{attr} = {value},\n' 432 return s + '}' 433 434 def build_c_string(self, metric: bool) -> str: 435 s = '' 436 for attr in _json_metric_attributes if metric else _json_event_attributes: 437 x = getattr(self, attr) 438 if metric and x and attr == 'metric_expr': 439 # Convert parsed metric expressions into a string. Slashes 440 # must be doubled in the file. 441 x = x.ToPerfJson().replace('\\', '\\\\') 442 if metric and x and attr == 'metric_threshold': 443 x = x.replace('\\', '\\\\') 444 if attr in _json_enum_attributes: 445 s += x if x else '0' 446 else: 447 s += f'{x}\\000' if x else '\\000' 448 return s 449 450 def to_c_string(self, metric: bool) -> str: 451 """Representation of the event as a C struct initializer.""" 452 453 def make_comment(s: str) -> str: 454 s = s.replace('*/', r'\*\/') 455 return f'\t/* {s} */\n' if len(s) < 80 else f'\t/* {s[0:80]}... */\n' 456 457 s = self.build_c_string(metric) 458 return f'{make_comment(s)}\t{{ { _bcs.offsets[s] } }},\n' 459 460 461@lru_cache(maxsize=None) 462def read_json_events(path: str, topic: str) -> Sequence[JsonEvent]: 463 """Read json events from the specified file.""" 464 try: 465 events = json.load(open(path), object_hook=JsonEvent) 466 except BaseException as err: 467 print(f"Exception processing {path}") 468 raise 469 metrics: list[Tuple[str, str, metric.Expression]] = [] 470 for event in events: 471 event.topic = topic 472 if event.metric_name and '-' not in event.metric_name: 473 metrics.append((event.pmu, event.metric_name, event.metric_expr)) 474 updates = metric.RewriteMetricsInTermsOfOthers(metrics) 475 if updates: 476 for event in events: 477 if event.metric_name in updates: 478 # print(f'Updated {event.metric_name} from\n"{event.metric_expr}"\n' 479 # f'to\n"{updates[event.metric_name]}"') 480 event.metric_expr = updates[event.metric_name] 481 482 return events 483 484def preprocess_arch_std_files(archpath: str) -> None: 485 """Read in all architecture standard events.""" 486 global _arch_std_events 487 for item in os.scandir(archpath): 488 if not item.is_file() or not item.name.endswith('.json'): 489 continue 490 try: 491 for event in read_json_events(item.path, topic=''): 492 if event.name: 493 _arch_std_events[event.name.lower()] = event 494 if event.metric_name: 495 _arch_std_events[event.metric_name.lower()] = event 496 except Exception as e: 497 raise RuntimeError(f'Failure processing \'{item.name}\' in \'{archpath}\'') from e 498 499 500def add_events_table_entries(item: os.DirEntry, topic: str) -> None: 501 """Add contents of file to _pending_events table.""" 502 for e in read_json_events(item.path, topic): 503 if e.name: 504 _pending_events.append(e) 505 if e.metric_name and not any(e.metric_name == x.metric_name and 506 e.pmu == x.pmu for x in _pending_metrics): 507 _pending_metrics.append(e) 508 509 510def print_pending_events() -> None: 511 """Optionally close events table.""" 512 513 def event_cmp_key(j: JsonEvent) -> Tuple[str, str, bool, str, str]: 514 def fix_none(s: Optional[str]) -> str: 515 if s is None: 516 return '' 517 return s 518 519 return (fix_none(j.pmu).replace(',','_'), fix_none(j.name), j.desc is not None, fix_none(j.topic), 520 fix_none(j.metric_name)) 521 522 global _pending_events 523 if not _pending_events: 524 return 525 526 global _pending_events_tblname 527 if _pending_events_tblname.endswith('_sys'): 528 global _sys_event_tables 529 _sys_event_tables.append(_pending_events_tblname) 530 else: 531 global event_tables 532 _event_tables.append(_pending_events_tblname) 533 534 first = True 535 last_pmu = None 536 last_name = None 537 pmus = set() 538 for event in sorted(_pending_events, key=event_cmp_key): 539 if last_pmu and last_pmu == event.pmu: 540 assert event.name != last_name, f"Duplicate event: {last_pmu}/{last_name}/ in {_pending_events_tblname}" 541 if event.pmu != last_pmu: 542 if not first: 543 _args.output_file.write('};\n') 544 pmu_name = event.pmu.replace(',', '_') 545 _args.output_file.write( 546 f'static const struct compact_pmu_event {_pending_events_tblname}_{pmu_name}[] = {{\n') 547 first = False 548 last_pmu = event.pmu 549 pmus.add((event.pmu, pmu_name)) 550 551 _args.output_file.write(event.to_c_string(metric=False)) 552 last_name = event.name 553 _pending_events = [] 554 555 _args.output_file.write(f""" 556}}; 557 558static const struct pmu_table_entry {_pending_events_tblname}[] = {{ 559""") 560 for (pmu, tbl_pmu) in sorted(pmus): 561 pmu_name = f"{pmu}\\000" 562 _args.output_file.write(f"""\t{{ 563\t\t.entries = {_pending_events_tblname}_{tbl_pmu}, 564\t\t.num_entries = ARRAY_SIZE({_pending_events_tblname}_{tbl_pmu}), 565\t\t.pmu_name = {{ {_bcs.offsets[pmu_name]} /* {pmu_name} */ }}, 566\t}}, 567""") 568 _args.output_file.write('};\n\n') 569 570def print_pending_metrics() -> None: 571 """Optionally close metrics table.""" 572 573 def metric_cmp_key(j: JsonEvent) -> Tuple[bool, str, str]: 574 def fix_none(s: Optional[str]) -> str: 575 if s is None: 576 return '' 577 return s 578 579 return (j.desc is not None, fix_none(j.pmu), fix_none(j.metric_name)) 580 581 global _pending_metrics 582 if not _pending_metrics: 583 return 584 585 global _pending_metrics_tblname 586 if _pending_metrics_tblname.endswith('_sys'): 587 global _sys_metric_tables 588 _sys_metric_tables.append(_pending_metrics_tblname) 589 else: 590 global metric_tables 591 _metric_tables.append(_pending_metrics_tblname) 592 593 first = True 594 last_pmu = None 595 pmus = set() 596 for metric in sorted(_pending_metrics, key=metric_cmp_key): 597 if metric.pmu != last_pmu: 598 if not first: 599 _args.output_file.write('};\n') 600 pmu_name = metric.pmu.replace(',', '_') 601 _args.output_file.write( 602 f'static const struct compact_pmu_event {_pending_metrics_tblname}_{pmu_name}[] = {{\n') 603 first = False 604 last_pmu = metric.pmu 605 pmus.add((metric.pmu, pmu_name)) 606 607 _args.output_file.write(metric.to_c_string(metric=True)) 608 _pending_metrics = [] 609 610 _args.output_file.write(f""" 611}}; 612 613static const struct pmu_table_entry {_pending_metrics_tblname}[] = {{ 614""") 615 for (pmu, tbl_pmu) in sorted(pmus): 616 pmu_name = f"{pmu}\\000" 617 _args.output_file.write(f"""\t{{ 618\t\t.entries = {_pending_metrics_tblname}_{tbl_pmu}, 619\t\t.num_entries = ARRAY_SIZE({_pending_metrics_tblname}_{tbl_pmu}), 620\t\t.pmu_name = {{ {_bcs.offsets[pmu_name]} /* {pmu_name} */ }}, 621\t}}, 622""") 623 _args.output_file.write('};\n\n') 624 625def get_topic(topic: str) -> str: 626 if topic.endswith('metrics.json'): 627 return 'metrics' 628 return removesuffix(topic, '.json').replace('-', ' ') 629 630def preprocess_one_file(parents: Sequence[str], item: os.DirEntry) -> None: 631 632 if item.is_dir(): 633 return 634 635 # base dir or too deep 636 level = len(parents) 637 if level == 0 or level > 4: 638 return 639 640 # Ignore other directories. If the file name does not have a .json 641 # extension, ignore it. It could be a readme.txt for instance. 642 if not item.is_file() or not item.name.endswith('.json'): 643 return 644 645 if item.name.endswith('metricgroups.json'): 646 metricgroup_descriptions = json.load(open(item.path)) 647 for mgroup in metricgroup_descriptions: 648 assert len(mgroup) > 1, parents 649 description = f"{metricgroup_descriptions[mgroup]}\\000" 650 mgroup = f"{mgroup}\\000" 651 _bcs.add(mgroup, metric=True) 652 _bcs.add(description, metric=True) 653 _metricgroups[mgroup] = description 654 return 655 656 topic = get_topic(item.name) 657 for event in read_json_events(item.path, topic): 658 pmu_name = f"{event.pmu}\\000" 659 if event.name: 660 _bcs.add(pmu_name, metric=False) 661 _bcs.add(event.build_c_string(metric=False), metric=False) 662 if event.metric_name: 663 _bcs.add(pmu_name, metric=True) 664 _bcs.add(event.build_c_string(metric=True), metric=True) 665 666def process_one_file(parents: Sequence[str], item: os.DirEntry) -> None: 667 """Process a JSON file during the main walk.""" 668 def is_leaf_dir_ignoring_sys(path: str) -> bool: 669 for item in os.scandir(path): 670 if item.is_dir() and item.name != 'sys': 671 return False 672 return True 673 674 # Model directories are leaves (ignoring possible sys 675 # directories). The FTW will walk into the directory next. Flush 676 # pending events and metrics and update the table names for the new 677 # model directory. 678 if item.is_dir() and is_leaf_dir_ignoring_sys(item.path): 679 print_pending_events() 680 print_pending_metrics() 681 682 global _pending_events_tblname 683 _pending_events_tblname = file_name_to_table_name('pmu_events_', parents, item.name) 684 global _pending_metrics_tblname 685 _pending_metrics_tblname = file_name_to_table_name('pmu_metrics_', parents, item.name) 686 687 if item.name == 'sys': 688 _sys_event_table_to_metric_table_mapping[_pending_events_tblname] = _pending_metrics_tblname 689 return 690 691 # base dir or too deep 692 level = len(parents) 693 if level == 0 or level > 4: 694 return 695 696 # Ignore other directories. If the file name does not have a .json 697 # extension, ignore it. It could be a readme.txt for instance. 698 if not item.is_file() or not item.name.endswith('.json') or item.name.endswith('metricgroups.json'): 699 return 700 701 add_events_table_entries(item, get_topic(item.name)) 702 703 704def print_mapping_table(archs: Sequence[str]) -> None: 705 """Read the mapfile and generate the struct from cpuid string to event table.""" 706 _args.output_file.write(""" 707/* Struct used to make the PMU event table implementation opaque to callers. */ 708struct pmu_events_table { 709\tconst struct pmu_table_entry *pmus; 710\tuint32_t num_pmus; 711}; 712 713/* Struct used to make the PMU metric table implementation opaque to callers. */ 714struct pmu_metrics_table { 715\tconst char *name; 716\tconst struct pmu_table_entry *pmus; 717\tuint32_t num_pmus; 718}; 719 720/* 721 * Map a CPU to its table of PMU events. The CPU is identified by the 722 * cpuid field, which is an arch-specific identifier for the CPU. 723 * The identifier specified in tools/perf/pmu-events/arch/xxx/mapfile 724 * must match the get_cpuid_str() in tools/perf/arch/xxx/util/header.c) 725 * 726 * The cpuid can contain any character other than the comma. 727 */ 728struct pmu_events_map { 729\tconst char *arch; 730\tconst char *cpuid; 731\tstruct pmu_events_table event_table; 732\tstruct pmu_metrics_table metric_table; 733}; 734 735/* 736 * Global table mapping each known CPU for the architecture to its 737 * table of PMU events. 738 */ 739static const struct pmu_events_map pmu_events_map[] = { 740""") 741 for arch in archs: 742 if arch == 'test': 743 _args.output_file.write("""{ 744\t.arch = "testarch", 745\t.cpuid = "testcpu", 746\t.event_table = { 747\t\t.pmus = pmu_events__test_soc_cpu, 748\t\t.num_pmus = ARRAY_SIZE(pmu_events__test_soc_cpu), 749\t}, 750\t.metric_table = { 751\t\t.name = "test_soc_cpu", 752\t\t.pmus = pmu_metrics__test_soc_cpu, 753\t\t.num_pmus = ARRAY_SIZE(pmu_metrics__test_soc_cpu), 754\t} 755}, 756""") 757 elif arch == 'common': 758 _args.output_file.write("""{ 759\t.arch = "common", 760\t.cpuid = "common", 761\t.event_table = { 762\t\t.pmus = pmu_events__common, 763\t\t.num_pmus = ARRAY_SIZE(pmu_events__common), 764\t}, 765\t.metric_table = { 766\t\t.name = "common", 767\t\t.pmus = pmu_metrics__common, 768\t\t.num_pmus = ARRAY_SIZE(pmu_metrics__common), 769\t}, 770}, 771""") 772 else: 773 with open(f'{_args.starting_dir}/{arch}/mapfile.csv') as csvfile: 774 table = csv.reader(csvfile) 775 first = True 776 for row in table: 777 # Skip the first row or any row beginning with #. 778 if not first and len(row) > 0 and not row[0].startswith('#'): 779 event_tblname = file_name_to_table_name('pmu_events_', [], row[2].replace('/', '_')) 780 if event_tblname in _event_tables: 781 event_size = f'ARRAY_SIZE({event_tblname})' 782 else: 783 event_tblname = 'NULL' 784 event_size = '0' 785 metric_tblname = file_name_to_table_name('pmu_metrics_', [], row[2].replace('/', '_')) 786 if metric_tblname in _metric_tables: 787 metric_name = f'"{metric_tblname.replace("pmu_metrics__", "")}"' 788 metric_size = f'ARRAY_SIZE({metric_tblname})' 789 else: 790 metric_name = 'NULL' 791 metric_tblname = 'NULL' 792 metric_size = '0' 793 if event_size == '0' and metric_size == '0': 794 continue 795 cpuid = row[0].replace('\\', '\\\\') 796 _args.output_file.write(f"""{{ 797\t.arch = "{arch}", 798\t.cpuid = "{cpuid}", 799\t.event_table = {{ 800\t\t.pmus = {event_tblname}, 801\t\t.num_pmus = {event_size} 802\t}}, 803\t.metric_table = {{ 804\t\t.name = {metric_name}, 805\t\t.pmus = {metric_tblname}, 806\t\t.num_pmus = {metric_size} 807\t}} 808}}, 809""") 810 first = False 811 812 _args.output_file.write("""{ 813\t.arch = 0, 814\t.cpuid = 0, 815\t.event_table = { 0, 0 }, 816\t.metric_table = { 0 }, 817} 818}; 819""") 820 821 822def print_metric_table_functions() -> None: 823 _args.output_file.write(""" 824const char *pmu_metrics_table__name(const struct pmu_metrics_table *table) 825{ 826\treturn table ? table->name : NULL; 827} 828 829int pmu_metrics_table__iterate_tables(pmu_metrics_table_iter_t fn, void *data) 830{ 831\tsize_t i; 832\tint ret; 833 834\tfor (i = 0; pmu_events_map[i].cpuid; i++) { 835\t\tsize_t j; 836\t\tbool found = false; 837 838\t\tif (!pmu_events_map[i].metric_table.pmus) 839\t\t\tcontinue; 840\t\tfor (j = 0; j < i; j++) { 841\t\t\tif (pmu_events_map[j].metric_table.pmus == 842\t\t\t pmu_events_map[i].metric_table.pmus) { 843\t\t\t\tfound = true; 844\t\t\t\tbreak; 845\t\t\t} 846\t\t} 847\t\tif (found) 848\t\t\tcontinue; 849\t\tret = fn(&pmu_events_map[i].metric_table, data); 850\t\tif (ret) 851\t\t\treturn ret; 852\t} 853\tfor (i = 0; pmu_sys_event_tables[i].name; i++) { 854\t\tif (!pmu_sys_event_tables[i].metric_table.pmus) 855\t\t\tcontinue; 856\t\tret = fn(&pmu_sys_event_tables[i].metric_table, data); 857\t\tif (ret) 858\t\t\treturn ret; 859\t} 860\treturn 0; 861} 862""") 863 864 865def print_system_mapping_table() -> None: 866 """C struct mapping table array for tables from /sys directories.""" 867 _args.output_file.write(""" 868struct pmu_sys_events { 869\tconst char *name; 870\tstruct pmu_events_table event_table; 871\tstruct pmu_metrics_table metric_table; 872}; 873 874static const struct pmu_sys_events pmu_sys_event_tables[] = { 875""") 876 printed_metric_tables = [] 877 for tblname in _sys_event_tables: 878 _args.output_file.write(f"""\t{{ 879\t\t.event_table = {{ 880\t\t\t.pmus = {tblname}, 881\t\t\t.num_pmus = ARRAY_SIZE({tblname}) 882\t\t}},""") 883 metric_tblname = _sys_event_table_to_metric_table_mapping[tblname] 884 if metric_tblname in _sys_metric_tables: 885 _args.output_file.write(f""" 886\t\t.metric_table = {{ 887\t\t\t.name = "{metric_tblname.replace('pmu_metrics__', '')}", 888\t\t\t.pmus = {metric_tblname}, 889\t\t\t.num_pmus = ARRAY_SIZE({metric_tblname}) 890\t\t}},""") 891 printed_metric_tables.append(metric_tblname) 892 _args.output_file.write(f""" 893\t\t.name = \"{tblname}\", 894\t}}, 895""") 896 for tblname in _sys_metric_tables: 897 if tblname in printed_metric_tables: 898 continue 899 _args.output_file.write(f"""\t{{ 900\t\t.metric_table = {{ 901\t\t\t.name = "{tblname.replace('pmu_metrics__', '')}", 902\t\t\t.pmus = {tblname}, 903\t\t\t.num_pmus = ARRAY_SIZE({tblname}) 904\t\t}}, 905\t\t.name = \"{tblname}\", 906\t}}, 907""") 908 _args.output_file.write("""\t{ 909\t\t.event_table = { 0, 0 }, 910\t\t.metric_table = { 0 }, 911\t}, 912}; 913 914static void decompress_event(int offset, struct pmu_event *pe) 915{ 916\tconst char *p = &big_c_string[offset]; 917""") 918 for attr in _json_event_attributes: 919 _args.output_file.write(f'\n\tpe->{attr} = ') 920 if attr in _json_enum_attributes: 921 _args.output_file.write("*p - '0';\n") 922 else: 923 _args.output_file.write("(*p == '\\0' ? NULL : p);\n") 924 if attr == _json_event_attributes[-1]: 925 continue 926 if attr in _json_enum_attributes: 927 _args.output_file.write('\tp++;') 928 else: 929 _args.output_file.write('\twhile (*p++);') 930 _args.output_file.write("""} 931 932static void decompress_metric(int offset, struct pmu_metric *pm) 933{ 934\tconst char *p = &big_c_string[offset]; 935""") 936 for attr in _json_metric_attributes: 937 _args.output_file.write(f'\n\tpm->{attr} = ') 938 if attr in _json_enum_attributes: 939 _args.output_file.write("*p - '0';\n") 940 else: 941 _args.output_file.write("(*p == '\\0' ? NULL : p);\n") 942 if attr == _json_metric_attributes[-1]: 943 continue 944 if attr in _json_enum_attributes: 945 _args.output_file.write('\tp++;') 946 else: 947 _args.output_file.write('\twhile (*p++);') 948 _args.output_file.write("""} 949 950static int pmu_events_table__for_each_event_pmu(const struct pmu_events_table *table, 951\t\t\t\t\t\tconst struct pmu_table_entry *pmu, 952\t\t\t\t\t\tpmu_event_iter_fn fn, 953\t\t\t\t\t\tvoid *data) 954{ 955\tint ret; 956\tstruct pmu_event pe = { 957\t\t.pmu = &big_c_string[pmu->pmu_name.offset], 958\t}; 959 960\tfor (uint32_t i = 0; i < pmu->num_entries; i++) { 961\t\tdecompress_event(pmu->entries[i].offset, &pe); 962\t\tif (!pe.name) 963\t\t\tcontinue; 964\t\tret = fn(&pe, table, data); 965\t\tif (ret) 966\t\t\treturn ret; 967\t} 968\treturn 0; 969 } 970 971static int pmu_events_table__find_event_pmu(const struct pmu_events_table *table, 972\t\t\t\t\t const struct pmu_table_entry *pmu, 973\t\t\t\t\t const char *name, 974\t\t\t\t\t pmu_event_iter_fn fn, 975\t\t\t\t\t void *data) 976{ 977\tstruct pmu_event pe = { 978\t\t.pmu = &big_c_string[pmu->pmu_name.offset], 979\t}; 980\tint low = 0, high = pmu->num_entries - 1; 981 982\twhile (low <= high) { 983\t\tint cmp, mid = (low + high) / 2; 984 985\t\tdecompress_event(pmu->entries[mid].offset, &pe); 986 987\t\tif (!pe.name && !name) 988\t\t\tgoto do_call; 989 990\t\tif (!pe.name && name) { 991\t\t\tlow = mid + 1; 992\t\t\tcontinue; 993\t\t} 994\t\tif (pe.name && !name) { 995\t\t\thigh = mid - 1; 996\t\t\tcontinue; 997\t\t} 998 999\t\tcmp = strcasecmp(pe.name, name); 1000\t\tif (cmp < 0) { 1001\t\t\tlow = mid + 1; 1002\t\t\tcontinue; 1003\t\t} 1004\t\tif (cmp > 0) { 1005\t\t\thigh = mid - 1; 1006\t\t\tcontinue; 1007\t\t} 1008 do_call: 1009\t\treturn fn ? fn(&pe, table, data) : 0; 1010\t} 1011\treturn PMU_EVENTS__NOT_FOUND; 1012} 1013 1014int pmu_events_table__for_each_event(const struct pmu_events_table *table, 1015\t\t\t\t struct perf_pmu *pmu, 1016\t\t\t\t pmu_event_iter_fn fn, 1017\t\t\t\t void *data) 1018{ 1019\tif (!table) 1020\t\treturn 0; 1021\tfor (size_t i = 0; i < table->num_pmus; i++) { 1022\t\tconst struct pmu_table_entry *table_pmu = &table->pmus[i]; 1023\t\tconst char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; 1024\t\tint ret; 1025 1026\t\tif (pmu && !perf_pmu__name_wildcard_match(pmu, pmu_name)) 1027\t\t\tcontinue; 1028 1029\t\tret = pmu_events_table__for_each_event_pmu(table, table_pmu, fn, data); 1030\t\tif (ret) 1031\t\t\treturn ret; 1032\t} 1033\treturn 0; 1034} 1035 1036int pmu_events_table__find_event(const struct pmu_events_table *table, 1037\t\t\t\t struct perf_pmu *pmu, 1038\t\t\t\t const char *name, 1039\t\t\t\t pmu_event_iter_fn fn, 1040\t\t\t\t void *data) 1041{ 1042\tif (!table) 1043\t\treturn PMU_EVENTS__NOT_FOUND; 1044\tfor (size_t i = 0; i < table->num_pmus; i++) { 1045\t\tconst struct pmu_table_entry *table_pmu = &table->pmus[i]; 1046\t\tconst char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; 1047\t\tint ret; 1048 1049\t\tif (pmu && !perf_pmu__name_wildcard_match(pmu, pmu_name)) 1050\t\t\tcontinue; 1051 1052\t\tret = pmu_events_table__find_event_pmu(table, table_pmu, name, fn, data); 1053\t\tif (ret != PMU_EVENTS__NOT_FOUND) 1054\t\t\treturn ret; 1055\t} 1056\treturn PMU_EVENTS__NOT_FOUND; 1057} 1058 1059size_t pmu_events_table__num_events(const struct pmu_events_table *table, struct perf_pmu *pmu) 1060{ 1061\tsize_t count = 0; 1062 1063\tif (!table) 1064\t\treturn 0; 1065\tfor (size_t i = 0; i < table->num_pmus; i++) { 1066\t\tconst struct pmu_table_entry *table_pmu = &table->pmus[i]; 1067\t\tconst char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; 1068 1069\t\tif (perf_pmu__name_wildcard_match(pmu, pmu_name)) 1070\t\t\tcount += table_pmu->num_entries; 1071\t} 1072\treturn count; 1073} 1074 1075static int pmu_metrics_table__for_each_metric_pmu(const struct pmu_metrics_table *table, 1076\t\t\t\t\t\tconst struct pmu_table_entry *pmu, 1077\t\t\t\t\t\tpmu_metric_iter_fn fn, 1078\t\t\t\t\t\tvoid *data) 1079{ 1080\tint ret; 1081\tstruct pmu_metric pm = { 1082\t\t.pmu = &big_c_string[pmu->pmu_name.offset], 1083\t}; 1084 1085\tfor (uint32_t i = 0; i < pmu->num_entries; i++) { 1086\t\tdecompress_metric(pmu->entries[i].offset, &pm); 1087\t\tif (!pm.metric_expr) 1088\t\t\tcontinue; 1089\t\tret = fn(&pm, table, data); 1090\t\tif (ret) 1091\t\t\treturn ret; 1092\t} 1093\treturn 0; 1094} 1095 1096static int pmu_metrics_table__find_metric_pmu(const struct pmu_metrics_table *table, 1097\t\t\t\t\t const struct pmu_table_entry *pmu, 1098\t\t\t\t\t const char *metric, 1099\t\t\t\t\t pmu_metric_iter_fn fn, 1100\t\t\t\t\t void *data) 1101{ 1102\tstruct pmu_metric pm = { 1103\t\t.pmu = &big_c_string[pmu->pmu_name.offset], 1104\t}; 1105\tint low = 0, high = pmu->num_entries - 1; 1106 1107\twhile (low <= high) { 1108\t\tint cmp, mid = (low + high) / 2; 1109 1110\t\tdecompress_metric(pmu->entries[mid].offset, &pm); 1111 1112\t\tif (!pm.metric_name && !metric) 1113\t\t\tgoto do_call; 1114 1115\t\tif (!pm.metric_name && metric) { 1116\t\t\tlow = mid + 1; 1117\t\t\tcontinue; 1118\t\t} 1119\t\tif (pm.metric_name && !metric) { 1120\t\t\thigh = mid - 1; 1121\t\t\tcontinue; 1122\t\t} 1123 1124\t\tcmp = strcmp(pm.metric_name, metric); 1125\t\tif (cmp < 0) { 1126\t\t\tlow = mid + 1; 1127\t\t\tcontinue; 1128\t\t} 1129\t\tif (cmp > 0) { 1130\t\t\thigh = mid - 1; 1131\t\t\tcontinue; 1132\t\t} 1133 do_call: 1134\t\treturn fn ? fn(&pm, table, data) : 0; 1135\t} 1136\treturn PMU_METRICS__NOT_FOUND; 1137} 1138 1139int pmu_metrics_table__for_each_metric(const struct pmu_metrics_table *table, 1140\t\t\t\t pmu_metric_iter_fn fn, 1141\t\t\t\t void *data) 1142{ 1143\tif (!table) 1144\t\treturn 0; 1145\tfor (size_t i = 0; i < table->num_pmus; i++) { 1146\t\tint ret = pmu_metrics_table__for_each_metric_pmu(table, &table->pmus[i], fn, data); 1147 1148\t\tif (ret) 1149\t\t\treturn ret; 1150\t} 1151\treturn 0; 1152} 1153 1154int pmu_metrics_table__find_metric(const struct pmu_metrics_table *table, 1155\t\t\t\t struct perf_pmu *pmu, 1156\t\t\t\t const char *metric, 1157\t\t\t\t pmu_metric_iter_fn fn, 1158\t\t\t\t void *data) 1159{ 1160\tif (!table) 1161\t\treturn 0; 1162\tfor (size_t i = 0; i < table->num_pmus; i++) { 1163\t\tconst struct pmu_table_entry *table_pmu = &table->pmus[i]; 1164\t\tconst char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; 1165\t\tint ret; 1166 1167\t\tif (pmu && !perf_pmu__name_wildcard_match(pmu, pmu_name)) 1168\t\t\tcontinue; 1169 1170\t\tret = pmu_metrics_table__find_metric_pmu(table, table_pmu, metric, fn, data); 1171\t\tif (ret != PMU_METRICS__NOT_FOUND) 1172\t\t\treturn ret; 1173\t} 1174\treturn PMU_METRICS__NOT_FOUND; 1175} 1176 1177static const struct pmu_events_map *map_for_cpu(struct perf_cpu cpu) 1178{ 1179\tstatic struct { 1180\t\tconst struct pmu_events_map *map; 1181\t\tstruct perf_cpu cpu; 1182\t} last_result; 1183\tstatic struct { 1184\t\tconst struct pmu_events_map *map; 1185\t\tchar *cpuid; 1186\t} last_map_search; 1187\tstatic bool has_last_result, has_last_map_search; 1188\tconst struct pmu_events_map *map = NULL; 1189\tchar *cpuid = NULL; 1190\tsize_t i; 1191 1192\tif (has_last_result && last_result.cpu.cpu == cpu.cpu) 1193\t\treturn last_result.map; 1194 1195\tcpuid = get_cpuid_allow_env_override(cpu); 1196 1197\t/* 1198\t * On some platforms which uses cpus map, cpuid can be NULL for 1199\t * PMUs other than CORE PMUs. 1200\t */ 1201\tif (!cpuid) 1202\t\tgoto out_update_last_result; 1203 1204\tif (has_last_map_search && !strcmp(last_map_search.cpuid, cpuid)) { 1205\t\tmap = last_map_search.map; 1206\t\tfree(cpuid); 1207\t} else { 1208\t\ti = 0; 1209\t\tfor (;;) { 1210\t\t\tmap = &pmu_events_map[i++]; 1211 1212\t\t\tif (!map->arch) { 1213\t\t\t\tmap = NULL; 1214\t\t\t\tbreak; 1215\t\t\t} 1216 1217\t\t\tif (!strcmp_cpuid_str(map->cpuid, cpuid)) 1218\t\t\t\tbreak; 1219\t\t} 1220\t\tfree(last_map_search.cpuid); 1221\t\tlast_map_search.cpuid = cpuid; 1222\t\tlast_map_search.map = map; 1223\t\thas_last_map_search = true; 1224\t} 1225out_update_last_result: 1226\tlast_result.cpu = cpu; 1227\tlast_result.map = map; 1228\thas_last_result = true; 1229\treturn map; 1230} 1231 1232static const struct pmu_events_map *map_for_pmu(struct perf_pmu *pmu) 1233{ 1234\tstruct perf_cpu cpu = { -1 }; 1235 1236\tif (pmu) { 1237\t\tfor (size_t i = 0; i < ARRAY_SIZE(pmu_events__common); i++) { 1238\t\t\tconst char *pmu_name = &big_c_string[pmu_events__common[i].pmu_name.offset]; 1239 1240\t\t\tif (!strcmp(pmu_name, pmu->name)) { 1241\t\t\t\tconst struct pmu_events_map *map = &pmu_events_map[0]; 1242 1243\t\t\t\twhile (strcmp("common", map->arch)) 1244\t\t\t\t\tmap++; 1245\t\t\t\treturn map; 1246\t\t\t} 1247\t\t} 1248\t\tcpu = perf_cpu_map__min(pmu->cpus); 1249\t} 1250\treturn map_for_cpu(cpu); 1251} 1252 1253const struct pmu_events_table *perf_pmu__find_events_table(struct perf_pmu *pmu) 1254{ 1255\tconst struct pmu_events_map *map = map_for_pmu(pmu); 1256 1257\tif (!map) 1258\t\treturn NULL; 1259 1260\tif (!pmu) 1261\t\treturn &map->event_table; 1262 1263\tfor (size_t i = 0; i < map->event_table.num_pmus; i++) { 1264\t\tconst struct pmu_table_entry *table_pmu = &map->event_table.pmus[i]; 1265\t\tconst char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; 1266 1267\t\tif (perf_pmu__name_wildcard_match(pmu, pmu_name)) 1268\t\t\treturn &map->event_table; 1269\t} 1270\treturn NULL; 1271} 1272 1273const struct pmu_events_table *perf_pmu__default_core_events_table(void) 1274{ 1275\tint i = 0; 1276 1277\tfor (;;) { 1278\t\tconst struct pmu_events_map *map = &pmu_events_map[i++]; 1279 1280\t\tif (!map->arch) 1281\t\t\tbreak; 1282 1283\t\tif (!strcmp(map->cpuid, "common")) 1284\t\t\treturn &map->event_table; 1285\t} 1286\treturn NULL; 1287} 1288 1289const struct pmu_metrics_table *pmu_metrics_table__find(void) 1290{ 1291\tstruct perf_cpu cpu = { -1 }; 1292\tconst struct pmu_events_map *map = map_for_cpu(cpu); 1293 1294\treturn map ? &map->metric_table : NULL; 1295} 1296 1297const struct pmu_metrics_table *pmu_metrics_table__default(void) 1298{ 1299\tint i = 0; 1300 1301\tfor (;;) { 1302\t\tconst struct pmu_events_map *map = &pmu_events_map[i++]; 1303 1304\t\tif (!map->arch) 1305\t\t\tbreak; 1306 1307\t\tif (!strcmp(map->cpuid, "common")) 1308\t\t\treturn &map->metric_table; 1309\t} 1310\treturn NULL; 1311} 1312 1313const struct pmu_events_table *find_core_events_table(const char *arch, const char *cpuid) 1314{ 1315\tfor (const struct pmu_events_map *tables = &pmu_events_map[0]; 1316\t tables->arch; 1317\t tables++) { 1318\t\tif (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid)) 1319\t\t\treturn &tables->event_table; 1320\t} 1321\treturn NULL; 1322} 1323 1324const struct pmu_metrics_table *find_core_metrics_table(const char *arch, const char *cpuid) 1325{ 1326\tfor (const struct pmu_events_map *tables = &pmu_events_map[0]; 1327\t tables->arch; 1328\t tables++) { 1329\t\tif (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid)) 1330\t\t\treturn &tables->metric_table; 1331\t} 1332\treturn NULL; 1333} 1334 1335int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data) 1336{ 1337\tfor (const struct pmu_events_map *tables = &pmu_events_map[0]; 1338\t tables->arch; 1339\t tables++) { 1340\t\tint ret = pmu_events_table__for_each_event(&tables->event_table, 1341\t\t\t\t\t\t\t /*pmu=*/NULL, fn, data); 1342 1343\t\tif (ret) 1344\t\t\treturn ret; 1345\t} 1346\treturn 0; 1347} 1348 1349int pmu_for_each_core_metric(pmu_metric_iter_fn fn, void *data) 1350{ 1351\tfor (const struct pmu_events_map *tables = &pmu_events_map[0]; 1352\t tables->arch; 1353\t tables++) { 1354\t\tint ret = pmu_metrics_table__for_each_metric(&tables->metric_table, fn, data); 1355 1356\t\tif (ret) 1357\t\t\treturn ret; 1358\t} 1359\treturn 0; 1360} 1361 1362const struct pmu_events_table *find_sys_events_table(const char *name) 1363{ 1364\tfor (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; 1365\t tables->name; 1366\t tables++) { 1367\t\tif (!strcmp(tables->name, name)) 1368\t\t\treturn &tables->event_table; 1369\t} 1370\treturn NULL; 1371} 1372 1373int pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data) 1374{ 1375\tfor (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; 1376\t tables->name; 1377\t tables++) { 1378\t\tint ret = pmu_events_table__for_each_event(&tables->event_table, 1379\t\t\t\t\t\t\t /*pmu=*/NULL, fn, data); 1380 1381\t\tif (ret) 1382\t\t\treturn ret; 1383\t} 1384\treturn 0; 1385} 1386 1387int pmu_for_each_sys_metric(pmu_metric_iter_fn fn, void *data) 1388{ 1389\tfor (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; 1390\t tables->name; 1391\t tables++) { 1392\t\tint ret = pmu_metrics_table__for_each_metric(&tables->metric_table, fn, data); 1393 1394\t\tif (ret) 1395\t\t\treturn ret; 1396\t} 1397\treturn 0; 1398} 1399""") 1400 1401def print_metricgroups() -> None: 1402 _args.output_file.write(""" 1403static const int metricgroups[][2] = { 1404""") 1405 for mgroup in sorted(_metricgroups): 1406 description = _metricgroups[mgroup] 1407 _args.output_file.write( 1408 f'\t{{ {_bcs.offsets[mgroup]}, {_bcs.offsets[description]} }}, /* {mgroup} => {description} */\n' 1409 ) 1410 _args.output_file.write(""" 1411}; 1412 1413const char *describe_metricgroup(const char *group) 1414{ 1415\tint low = 0, high = (int)ARRAY_SIZE(metricgroups) - 1; 1416 1417\twhile (low <= high) { 1418\t\tint mid = (low + high) / 2; 1419\t\tconst char *mgroup = &big_c_string[metricgroups[mid][0]]; 1420\t\tint cmp = strcmp(mgroup, group); 1421 1422\t\tif (cmp == 0) 1423\t\t\treturn &big_c_string[metricgroups[mid][1]]; 1424\t\telse if (cmp < 0) 1425\t\t\tlow = mid + 1; 1426\t\telse 1427\t\t\thigh = mid - 1; 1428\t} 1429\treturn NULL; 1430} 1431""") 1432 1433def main() -> None: 1434 global _args 1435 1436 def dir_path(path: str) -> str: 1437 """Validate path is a directory for argparse.""" 1438 if os.path.isdir(path): 1439 return path 1440 raise argparse.ArgumentTypeError(f'\'{path}\' is not a valid directory') 1441 1442 def ftw(path: str, parents: Sequence[str], 1443 action: Callable[[Sequence[str], os.DirEntry], None]) -> None: 1444 """Replicate the directory/file walking behavior of C's file tree walk.""" 1445 for item in sorted(os.scandir(path), key=lambda e: e.name): 1446 if _args.model != 'all' and item.is_dir(): 1447 # Check if the model matches one in _args.model. 1448 if len(parents) == _args.model.split(',')[0].count('/'): 1449 # We're testing the correct directory. 1450 item_path = '/'.join(parents) + ('/' if len(parents) > 0 else '') + item.name 1451 if 'test' not in item_path and 'common' not in item_path and item_path not in _args.model.split(','): 1452 continue 1453 try: 1454 action(parents, item) 1455 except Exception as e: 1456 raise RuntimeError(f'Action failure for \'{item.name}\' in {parents}') from e 1457 if item.is_dir(): 1458 ftw(item.path, parents + [item.name], action) 1459 1460 ap = argparse.ArgumentParser() 1461 ap.add_argument('arch', help='Architecture name like x86') 1462 ap.add_argument('model', help='''Select a model such as skylake to 1463reduce the code size. Normally set to "all". For architectures like 1464ARM64 with an implementor/model, the model must include the implementor 1465such as "arm/cortex-a34".''', 1466 default='all') 1467 ap.add_argument( 1468 'starting_dir', 1469 type=dir_path, 1470 help='Root of tree containing architecture directories containing json files' 1471 ) 1472 ap.add_argument( 1473 'output_file', type=argparse.FileType('w', encoding='utf-8'), nargs='?', default=sys.stdout) 1474 ap.add_argument( 1475 'output_string_file', type=argparse.FileType('w', encoding='utf-8'), nargs='?', default=None) 1476 _args = ap.parse_args() 1477 1478 _args.output_file.write(f"""/* SPDX-License-Identifier: GPL-2.0 */ 1479/* THIS FILE WAS AUTOGENERATED BY `jevents.py arch={_args.arch} model={_args.model}` ! */ 1480""") 1481 _args.output_file.write(""" 1482#include <pmu-events/pmu-events.h> 1483#include "util/header.h" 1484#include "util/pmu.h" 1485#include <string.h> 1486#include <stddef.h> 1487 1488struct compact_pmu_event { 1489\tint offset; 1490}; 1491 1492struct pmu_table_entry { 1493\tconst struct compact_pmu_event *entries; 1494\tuint32_t num_entries; 1495\tstruct compact_pmu_event pmu_name; 1496}; 1497 1498""") 1499 archs = [] 1500 for item in os.scandir(_args.starting_dir): 1501 if not item.is_dir(): 1502 continue 1503 if item.name == _args.arch or _args.arch == 'all' or item.name == 'test' or item.name == 'common': 1504 archs.append(item.name) 1505 1506 if len(archs) < 2 and _args.arch != 'none': 1507 raise IOError(f'Missing architecture directory \'{_args.arch}\'') 1508 1509 archs.sort() 1510 for arch in archs: 1511 arch_path = f'{_args.starting_dir}/{arch}' 1512 preprocess_arch_std_files(arch_path) 1513 ftw(arch_path, [], preprocess_one_file) 1514 1515 _bcs.compute() 1516 _args.output_file.write('/* clang-format off */\n') 1517 if not _args.output_string_file: 1518 _args.output_file.write('static const char *const big_c_string =\n') 1519 for s in _bcs.big_string: 1520 _args.output_file.write(s) 1521 _args.output_file.write(';\n\n') 1522 else: 1523 _args.output_string_file.write('/* SPDX-License-Identifier: GPL-2.0 */\n') 1524 _args.output_string_file.write('/* Autogenerated by jevents.py */\n') 1525 _args.output_string_file.write('extern const char big_c_string[];\n') 1526 _args.output_string_file.write('const char big_c_string[] =\n') 1527 for s in _bcs.big_string: 1528 _args.output_string_file.write(s) 1529 _args.output_string_file.write(';\n') 1530 _args.output_file.write('extern const char big_c_string[];\n\n') 1531 for arch in archs: 1532 arch_path = f'{_args.starting_dir}/{arch}' 1533 ftw(arch_path, [], process_one_file) 1534 print_pending_events() 1535 print_pending_metrics() 1536 1537 print_mapping_table(archs) 1538 print_system_mapping_table() 1539 _args.output_file.write('/* clang-format on */\n') 1540 print_metric_table_functions() 1541 print_metricgroups() 1542 _args.output_file.close() 1543 if _args.output_string_file: 1544 _args.output_string_file.close() 1545 1546if __name__ == '__main__': 1547 main() 1548