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# Order specific JsonEvent attributes will be visited. 41_json_event_attributes = [ 42 # cmp_sevent related attributes. 43 'name', 'pmu', 'topic', 'desc', 44 # Seems useful, put it early. 45 'event', 46 # Short things in alphabetical order. 47 'aggr_mode', 'compat', 'deprecated', 'perpkg', 'unit', 48 # Longer things (the last won't be iterated over during decompress). 49 'long_desc' 50] 51 52# Attributes that are in pmu_metric rather than pmu_event. 53_json_metric_attributes = [ 54 'metric_name', 'metric_group', 'metric_constraint', 'metric_expr', 'desc', 55 'long_desc', 'unit', 'compat', 'aggr_mode' 56] 57 58def removesuffix(s: str, suffix: str) -> str: 59 """Remove the suffix from a string 60 61 The removesuffix function is added to str in Python 3.9. We aim for 3.6 62 compatibility and so provide our own function here. 63 """ 64 return s[0:-len(suffix)] if s.endswith(suffix) else s 65 66 67def file_name_to_table_name(prefix: str, parents: Sequence[str], 68 dirname: str) -> str: 69 """Generate a C table name from directory names.""" 70 tblname = prefix 71 for p in parents: 72 tblname += '_' + p 73 tblname += '_' + dirname 74 return tblname.replace('-', '_') 75 76 77def c_len(s: str) -> int: 78 """Return the length of s a C string 79 80 This doesn't handle all escape characters properly. It first assumes 81 all \ are for escaping, it then adjusts as it will have over counted 82 \\. The code uses \000 rather than \0 as a terminator as an adjacent 83 number would be folded into a string of \0 (ie. "\0" + "5" doesn't 84 equal a terminator followed by the number 5 but the escape of 85 \05). The code adjusts for \000 but not properly for all octal, hex 86 or unicode values. 87 """ 88 try: 89 utf = s.encode(encoding='utf-8',errors='strict') 90 except: 91 print(f'broken string {s}') 92 raise 93 return len(utf) - utf.count(b'\\') + utf.count(b'\\\\') - (utf.count(b'\\000') * 2) 94 95class BigCString: 96 """A class to hold many strings concatenated together. 97 98 Generating a large number of stand-alone C strings creates a large 99 number of relocations in position independent code. The BigCString 100 is a helper for this case. It builds a single string which within it 101 are all the other C strings (to avoid memory issues the string 102 itself is held as a list of strings). The offsets within the big 103 string are recorded and when stored to disk these don't need 104 relocation. To reduce the size of the string further, identical 105 strings are merged. If a longer string ends-with the same value as a 106 shorter string, these entries are also merged. 107 """ 108 strings: Set[str] 109 big_string: Sequence[str] 110 offsets: Dict[str, int] 111 112 def __init__(self): 113 self.strings = set() 114 115 def add(self, s: str) -> None: 116 """Called to add to the big string.""" 117 self.strings.add(s) 118 119 def compute(self) -> None: 120 """Called once all strings are added to compute the string and offsets.""" 121 122 folded_strings = {} 123 # Determine if two strings can be folded, ie. let 1 string use the 124 # end of another. First reverse all strings and sort them. 125 sorted_reversed_strings = sorted([x[::-1] for x in self.strings]) 126 127 # Strings 'xyz' and 'yz' will now be [ 'zy', 'zyx' ]. Scan forward 128 # for each string to see if there is a better candidate to fold it 129 # into, in the example rather than using 'yz' we can use'xyz' at 130 # an offset of 1. We record which string can be folded into which 131 # in folded_strings, we don't need to record the offset as it is 132 # trivially computed from the string lengths. 133 for pos,s in enumerate(sorted_reversed_strings): 134 best_pos = pos 135 for check_pos in range(pos + 1, len(sorted_reversed_strings)): 136 if sorted_reversed_strings[check_pos].startswith(s): 137 best_pos = check_pos 138 else: 139 break 140 if pos != best_pos: 141 folded_strings[s[::-1]] = sorted_reversed_strings[best_pos][::-1] 142 143 # Compute reverse mappings for debugging. 144 fold_into_strings = collections.defaultdict(set) 145 for key, val in folded_strings.items(): 146 if key != val: 147 fold_into_strings[val].add(key) 148 149 # big_string_offset is the current location within the C string 150 # being appended to - comments, etc. don't count. big_string is 151 # the string contents represented as a list. Strings are immutable 152 # in Python and so appending to one causes memory issues, while 153 # lists are mutable. 154 big_string_offset = 0 155 self.big_string = [] 156 self.offsets = {} 157 158 # Emit all strings that aren't folded in a sorted manner. 159 for s in sorted(self.strings): 160 if s not in folded_strings: 161 self.offsets[s] = big_string_offset 162 self.big_string.append(f'/* offset={big_string_offset} */ "') 163 self.big_string.append(s) 164 self.big_string.append('"') 165 if s in fold_into_strings: 166 self.big_string.append(' /* also: ' + ', '.join(fold_into_strings[s]) + ' */') 167 self.big_string.append('\n') 168 big_string_offset += c_len(s) 169 continue 170 171 # Compute the offsets of the folded strings. 172 for s in folded_strings.keys(): 173 assert s not in self.offsets 174 folded_s = folded_strings[s] 175 self.offsets[s] = self.offsets[folded_s] + c_len(folded_s) - c_len(s) 176 177_bcs = BigCString() 178 179class JsonEvent: 180 """Representation of an event loaded from a json file dictionary.""" 181 182 def __init__(self, jd: dict): 183 """Constructor passed the dictionary of parsed json values.""" 184 185 def llx(x: int) -> str: 186 """Convert an int to a string similar to a printf modifier of %#llx.""" 187 return '0' if x == 0 else hex(x) 188 189 def fixdesc(s: str) -> str: 190 """Fix formatting issue for the desc string.""" 191 if s is None: 192 return None 193 return removesuffix(removesuffix(removesuffix(s, '. '), 194 '. '), '.').replace('\n', '\\n').replace( 195 '\"', '\\"').replace('\r', '\\r') 196 197 def convert_aggr_mode(aggr_mode: str) -> Optional[str]: 198 """Returns the aggr_mode_class enum value associated with the JSON string.""" 199 if not aggr_mode: 200 return None 201 aggr_mode_to_enum = { 202 'PerChip': '1', 203 'PerCore': '2', 204 } 205 return aggr_mode_to_enum[aggr_mode] 206 207 def lookup_msr(num: str) -> Optional[str]: 208 """Converts the msr number, or first in a list to the appropriate event field.""" 209 if not num: 210 return None 211 msrmap = { 212 0x3F6: 'ldlat=', 213 0x1A6: 'offcore_rsp=', 214 0x1A7: 'offcore_rsp=', 215 0x3F7: 'frontend=', 216 } 217 return msrmap[int(num.split(',', 1)[0], 0)] 218 219 def real_event(name: str, event: str) -> Optional[str]: 220 """Convert well known event names to an event string otherwise use the event argument.""" 221 fixed = { 222 'inst_retired.any': 'event=0xc0,period=2000003', 223 'inst_retired.any_p': 'event=0xc0,period=2000003', 224 'cpu_clk_unhalted.ref': 'event=0x0,umask=0x03,period=2000003', 225 'cpu_clk_unhalted.thread': 'event=0x3c,period=2000003', 226 'cpu_clk_unhalted.core': 'event=0x3c,period=2000003', 227 'cpu_clk_unhalted.thread_any': 'event=0x3c,any=1,period=2000003', 228 } 229 if not name: 230 return None 231 if name.lower() in fixed: 232 return fixed[name.lower()] 233 return event 234 235 def unit_to_pmu(unit: str) -> Optional[str]: 236 """Convert a JSON Unit to Linux PMU name.""" 237 if not unit: 238 return None 239 # Comment brought over from jevents.c: 240 # it's not realistic to keep adding these, we need something more scalable ... 241 table = { 242 'CBO': 'uncore_cbox', 243 'QPI LL': 'uncore_qpi', 244 'SBO': 'uncore_sbox', 245 'iMPH-U': 'uncore_arb', 246 'CPU-M-CF': 'cpum_cf', 247 'CPU-M-SF': 'cpum_sf', 248 'PAI-CRYPTO' : 'pai_crypto', 249 'UPI LL': 'uncore_upi', 250 'hisi_sicl,cpa': 'hisi_sicl,cpa', 251 'hisi_sccl,ddrc': 'hisi_sccl,ddrc', 252 'hisi_sccl,hha': 'hisi_sccl,hha', 253 'hisi_sccl,l3c': 'hisi_sccl,l3c', 254 'imx8_ddr': 'imx8_ddr', 255 'L3PMC': 'amd_l3', 256 'DFPMC': 'amd_df', 257 'cpu_core': 'cpu_core', 258 'cpu_atom': 'cpu_atom', 259 } 260 return table[unit] if unit in table else f'uncore_{unit.lower()}' 261 262 eventcode = 0 263 if 'EventCode' in jd: 264 eventcode = int(jd['EventCode'].split(',', 1)[0], 0) 265 if 'ExtSel' in jd: 266 eventcode |= int(jd['ExtSel']) << 8 267 configcode = int(jd['ConfigCode'], 0) if 'ConfigCode' in jd else None 268 self.name = jd['EventName'].lower() if 'EventName' in jd else None 269 self.topic = '' 270 self.compat = jd.get('Compat') 271 self.desc = fixdesc(jd.get('BriefDescription')) 272 self.long_desc = fixdesc(jd.get('PublicDescription')) 273 precise = jd.get('PEBS') 274 msr = lookup_msr(jd.get('MSRIndex')) 275 msrval = jd.get('MSRValue') 276 extra_desc = '' 277 if 'Data_LA' in jd: 278 extra_desc += ' Supports address when precise' 279 if 'Errata' in jd: 280 extra_desc += '.' 281 if 'Errata' in jd: 282 extra_desc += ' Spec update: ' + jd['Errata'] 283 self.pmu = unit_to_pmu(jd.get('Unit')) 284 filter = jd.get('Filter') 285 self.unit = jd.get('ScaleUnit') 286 self.perpkg = jd.get('PerPkg') 287 self.aggr_mode = convert_aggr_mode(jd.get('AggregationMode')) 288 self.deprecated = jd.get('Deprecated') 289 self.metric_name = jd.get('MetricName') 290 self.metric_group = jd.get('MetricGroup') 291 self.metric_constraint = jd.get('MetricConstraint') 292 self.metric_expr = None 293 if 'MetricExpr' in jd: 294 self.metric_expr = metric.ParsePerfJson(jd['MetricExpr']).Simplify() 295 296 arch_std = jd.get('ArchStdEvent') 297 if precise and self.desc and '(Precise Event)' not in self.desc: 298 extra_desc += ' (Must be precise)' if precise == '2' else (' (Precise ' 299 'event)') 300 event = f'config={llx(configcode)}' if configcode is not None else f'event={llx(eventcode)}' 301 event_fields = [ 302 ('AnyThread', 'any='), 303 ('PortMask', 'ch_mask='), 304 ('CounterMask', 'cmask='), 305 ('EdgeDetect', 'edge='), 306 ('FCMask', 'fc_mask='), 307 ('Invert', 'inv='), 308 ('SampleAfterValue', 'period='), 309 ('UMask', 'umask='), 310 ] 311 for key, value in event_fields: 312 if key in jd and jd[key] != '0': 313 event += ',' + value + jd[key] 314 if filter: 315 event += f',{filter}' 316 if msr: 317 event += f',{msr}{msrval}' 318 if self.desc and extra_desc: 319 self.desc += extra_desc 320 if self.long_desc and extra_desc: 321 self.long_desc += extra_desc 322 if self.pmu: 323 if self.desc and not self.desc.endswith('. '): 324 self.desc += '. ' 325 self.desc = (self.desc if self.desc else '') + ('Unit: ' + self.pmu + ' ') 326 if arch_std and arch_std.lower() in _arch_std_events: 327 event = _arch_std_events[arch_std.lower()].event 328 # Copy from the architecture standard event to self for undefined fields. 329 for attr, value in _arch_std_events[arch_std.lower()].__dict__.items(): 330 if hasattr(self, attr) and not getattr(self, attr): 331 setattr(self, attr, value) 332 333 self.event = real_event(self.name, event) 334 335 def __repr__(self) -> str: 336 """String representation primarily for debugging.""" 337 s = '{\n' 338 for attr, value in self.__dict__.items(): 339 if value: 340 s += f'\t{attr} = {value},\n' 341 return s + '}' 342 343 def build_c_string(self, metric: bool) -> str: 344 s = '' 345 for attr in _json_metric_attributes if metric else _json_event_attributes: 346 x = getattr(self, attr) 347 if metric and x and attr == 'metric_expr': 348 # Convert parsed metric expressions into a string. Slashes 349 # must be doubled in the file. 350 x = x.ToPerfJson().replace('\\', '\\\\') 351 s += f'{x}\\000' if x else '\\000' 352 return s 353 354 def to_c_string(self, metric: bool) -> str: 355 """Representation of the event as a C struct initializer.""" 356 357 s = self.build_c_string(metric) 358 return f'{{ { _bcs.offsets[s] } }}, /* {s} */\n' 359 360 361@lru_cache(maxsize=None) 362def read_json_events(path: str, topic: str) -> Sequence[JsonEvent]: 363 """Read json events from the specified file.""" 364 try: 365 events = json.load(open(path), object_hook=JsonEvent) 366 except BaseException as err: 367 print(f"Exception processing {path}") 368 raise 369 metrics: list[Tuple[str, metric.Expression]] = [] 370 for event in events: 371 event.topic = topic 372 if event.metric_name and '-' not in event.metric_name: 373 metrics.append((event.metric_name, event.metric_expr)) 374 updates = metric.RewriteMetricsInTermsOfOthers(metrics) 375 if updates: 376 for event in events: 377 if event.metric_name in updates: 378 # print(f'Updated {event.metric_name} from\n"{event.metric_expr}"\n' 379 # f'to\n"{updates[event.metric_name]}"') 380 event.metric_expr = updates[event.metric_name] 381 382 return events 383 384def preprocess_arch_std_files(archpath: str) -> None: 385 """Read in all architecture standard events.""" 386 global _arch_std_events 387 for item in os.scandir(archpath): 388 if item.is_file() and item.name.endswith('.json'): 389 for event in read_json_events(item.path, topic=''): 390 if event.name: 391 _arch_std_events[event.name.lower()] = event 392 if event.metric_name: 393 _arch_std_events[event.metric_name.lower()] = event 394 395 396def add_events_table_entries(item: os.DirEntry, topic: str) -> None: 397 """Add contents of file to _pending_events table.""" 398 for e in read_json_events(item.path, topic): 399 if e.name: 400 _pending_events.append(e) 401 if e.metric_name: 402 _pending_metrics.append(e) 403 404 405def print_pending_events() -> None: 406 """Optionally close events table.""" 407 408 def event_cmp_key(j: JsonEvent) -> Tuple[bool, str, str, str, str]: 409 def fix_none(s: Optional[str]) -> str: 410 if s is None: 411 return '' 412 return s 413 414 return (j.desc is not None, fix_none(j.topic), fix_none(j.name), fix_none(j.pmu), 415 fix_none(j.metric_name)) 416 417 global _pending_events 418 if not _pending_events: 419 return 420 421 global _pending_events_tblname 422 if _pending_events_tblname.endswith('_sys'): 423 global _sys_event_tables 424 _sys_event_tables.append(_pending_events_tblname) 425 else: 426 global event_tables 427 _event_tables.append(_pending_events_tblname) 428 429 _args.output_file.write( 430 f'static const struct compact_pmu_event {_pending_events_tblname}[] = {{\n') 431 432 for event in sorted(_pending_events, key=event_cmp_key): 433 _args.output_file.write(event.to_c_string(metric=False)) 434 _pending_events = [] 435 436 _args.output_file.write('};\n\n') 437 438def print_pending_metrics() -> None: 439 """Optionally close metrics table.""" 440 441 def metric_cmp_key(j: JsonEvent) -> Tuple[bool, str, str]: 442 def fix_none(s: Optional[str]) -> str: 443 if s is None: 444 return '' 445 return s 446 447 return (j.desc is not None, fix_none(j.pmu), fix_none(j.metric_name)) 448 449 global _pending_metrics 450 if not _pending_metrics: 451 return 452 453 global _pending_metrics_tblname 454 if _pending_metrics_tblname.endswith('_sys'): 455 global _sys_metric_tables 456 _sys_metric_tables.append(_pending_metrics_tblname) 457 else: 458 global metric_tables 459 _metric_tables.append(_pending_metrics_tblname) 460 461 _args.output_file.write( 462 f'static const struct compact_pmu_event {_pending_metrics_tblname}[] = {{\n') 463 464 for metric in sorted(_pending_metrics, key=metric_cmp_key): 465 _args.output_file.write(metric.to_c_string(metric=True)) 466 _pending_metrics = [] 467 468 _args.output_file.write('};\n\n') 469 470def get_topic(topic: str) -> str: 471 if topic.endswith('metrics.json'): 472 return 'metrics' 473 return removesuffix(topic, '.json').replace('-', ' ') 474 475def preprocess_one_file(parents: Sequence[str], item: os.DirEntry) -> None: 476 477 if item.is_dir(): 478 return 479 480 # base dir or too deep 481 level = len(parents) 482 if level == 0 or level > 4: 483 return 484 485 # Ignore other directories. If the file name does not have a .json 486 # extension, ignore it. It could be a readme.txt for instance. 487 if not item.is_file() or not item.name.endswith('.json'): 488 return 489 490 topic = get_topic(item.name) 491 for event in read_json_events(item.path, topic): 492 if event.name: 493 _bcs.add(event.build_c_string(metric=False)) 494 if event.metric_name: 495 _bcs.add(event.build_c_string(metric=True)) 496 497def process_one_file(parents: Sequence[str], item: os.DirEntry) -> None: 498 """Process a JSON file during the main walk.""" 499 def is_leaf_dir(path: str) -> bool: 500 for item in os.scandir(path): 501 if item.is_dir(): 502 return False 503 return True 504 505 # model directory, reset topic 506 if item.is_dir() and is_leaf_dir(item.path): 507 print_pending_events() 508 print_pending_metrics() 509 510 global _pending_events_tblname 511 _pending_events_tblname = file_name_to_table_name('pmu_events_', parents, item.name) 512 global _pending_metrics_tblname 513 _pending_metrics_tblname = file_name_to_table_name('pmu_metrics_', parents, item.name) 514 515 if item.name == 'sys': 516 _sys_event_table_to_metric_table_mapping[_pending_events_tblname] = _pending_metrics_tblname 517 return 518 519 # base dir or too deep 520 level = len(parents) 521 if level == 0 or level > 4: 522 return 523 524 # Ignore other directories. If the file name does not have a .json 525 # extension, ignore it. It could be a readme.txt for instance. 526 if not item.is_file() or not item.name.endswith('.json'): 527 return 528 529 add_events_table_entries(item, get_topic(item.name)) 530 531 532def print_mapping_table(archs: Sequence[str]) -> None: 533 """Read the mapfile and generate the struct from cpuid string to event table.""" 534 _args.output_file.write(""" 535/* Struct used to make the PMU event table implementation opaque to callers. */ 536struct pmu_events_table { 537 const struct compact_pmu_event *entries; 538 size_t length; 539}; 540 541/* Struct used to make the PMU metric table implementation opaque to callers. */ 542struct pmu_metrics_table { 543 const struct compact_pmu_event *entries; 544 size_t length; 545}; 546 547/* 548 * Map a CPU to its table of PMU events. The CPU is identified by the 549 * cpuid field, which is an arch-specific identifier for the CPU. 550 * The identifier specified in tools/perf/pmu-events/arch/xxx/mapfile 551 * must match the get_cpuid_str() in tools/perf/arch/xxx/util/header.c) 552 * 553 * The cpuid can contain any character other than the comma. 554 */ 555struct pmu_events_map { 556 const char *arch; 557 const char *cpuid; 558 struct pmu_events_table event_table; 559 struct pmu_metrics_table metric_table; 560}; 561 562/* 563 * Global table mapping each known CPU for the architecture to its 564 * table of PMU events. 565 */ 566const struct pmu_events_map pmu_events_map[] = { 567""") 568 for arch in archs: 569 if arch == 'test': 570 _args.output_file.write("""{ 571\t.arch = "testarch", 572\t.cpuid = "testcpu", 573\t.event_table = { 574\t\t.entries = pmu_events__test_soc_cpu, 575\t\t.length = ARRAY_SIZE(pmu_events__test_soc_cpu), 576\t}, 577\t.metric_table = { 578\t\t.entries = pmu_metrics__test_soc_cpu, 579\t\t.length = ARRAY_SIZE(pmu_metrics__test_soc_cpu), 580\t} 581}, 582""") 583 else: 584 with open(f'{_args.starting_dir}/{arch}/mapfile.csv') as csvfile: 585 table = csv.reader(csvfile) 586 first = True 587 for row in table: 588 # Skip the first row or any row beginning with #. 589 if not first and len(row) > 0 and not row[0].startswith('#'): 590 event_tblname = file_name_to_table_name('pmu_events_', [], row[2].replace('/', '_')) 591 if event_tblname in _event_tables: 592 event_size = f'ARRAY_SIZE({event_tblname})' 593 else: 594 event_tblname = 'NULL' 595 event_size = '0' 596 metric_tblname = file_name_to_table_name('pmu_metrics_', [], row[2].replace('/', '_')) 597 if metric_tblname in _metric_tables: 598 metric_size = f'ARRAY_SIZE({metric_tblname})' 599 else: 600 metric_tblname = 'NULL' 601 metric_size = '0' 602 if event_size == '0' and metric_size == '0': 603 continue 604 cpuid = row[0].replace('\\', '\\\\') 605 _args.output_file.write(f"""{{ 606\t.arch = "{arch}", 607\t.cpuid = "{cpuid}", 608\t.event_table = {{ 609\t\t.entries = {event_tblname}, 610\t\t.length = {event_size} 611\t}}, 612\t.metric_table = {{ 613\t\t.entries = {metric_tblname}, 614\t\t.length = {metric_size} 615\t}} 616}}, 617""") 618 first = False 619 620 _args.output_file.write("""{ 621\t.arch = 0, 622\t.cpuid = 0, 623\t.event_table = { 0, 0 }, 624\t.metric_table = { 0, 0 }, 625} 626}; 627""") 628 629 630def print_system_mapping_table() -> None: 631 """C struct mapping table array for tables from /sys directories.""" 632 _args.output_file.write(""" 633struct pmu_sys_events { 634\tconst char *name; 635\tstruct pmu_events_table event_table; 636\tstruct pmu_metrics_table metric_table; 637}; 638 639static const struct pmu_sys_events pmu_sys_event_tables[] = { 640""") 641 printed_metric_tables = [] 642 for tblname in _sys_event_tables: 643 _args.output_file.write(f"""\t{{ 644\t\t.event_table = {{ 645\t\t\t.entries = {tblname}, 646\t\t\t.length = ARRAY_SIZE({tblname}) 647\t\t}},""") 648 metric_tblname = _sys_event_table_to_metric_table_mapping[tblname] 649 if metric_tblname in _sys_metric_tables: 650 _args.output_file.write(f""" 651\t\t.metric_table = {{ 652\t\t\t.entries = {metric_tblname}, 653\t\t\t.length = ARRAY_SIZE({metric_tblname}) 654\t\t}},""") 655 printed_metric_tables.append(metric_tblname) 656 _args.output_file.write(f""" 657\t\t.name = \"{tblname}\", 658\t}}, 659""") 660 for tblname in _sys_metric_tables: 661 if tblname in printed_metric_tables: 662 continue 663 _args.output_file.write(f"""\t{{ 664\t\t.metric_table = {{ 665\t\t\t.entries = {tblname}, 666\t\t\t.length = ARRAY_SIZE({tblname}) 667\t\t}}, 668\t\t.name = \"{tblname}\", 669\t}}, 670""") 671 _args.output_file.write("""\t{ 672\t\t.event_table = { 0, 0 }, 673\t\t.metric_table = { 0, 0 }, 674\t}, 675}; 676 677static void decompress_event(int offset, struct pmu_event *pe) 678{ 679\tconst char *p = &big_c_string[offset]; 680""") 681 for attr in _json_event_attributes: 682 _args.output_file.write(f""" 683\tpe->{attr} = (*p == '\\0' ? NULL : p); 684""") 685 if attr == _json_event_attributes[-1]: 686 continue 687 _args.output_file.write('\twhile (*p++);') 688 _args.output_file.write("""} 689 690static void decompress_metric(int offset, struct pmu_metric *pm) 691{ 692\tconst char *p = &big_c_string[offset]; 693""") 694 for attr in _json_metric_attributes: 695 _args.output_file.write(f""" 696\tpm->{attr} = (*p == '\\0' ? NULL : p); 697""") 698 if attr == _json_metric_attributes[-1]: 699 continue 700 _args.output_file.write('\twhile (*p++);') 701 _args.output_file.write("""} 702 703int pmu_events_table_for_each_event(const struct pmu_events_table *table, 704 pmu_event_iter_fn fn, 705 void *data) 706{ 707 for (size_t i = 0; i < table->length; i++) { 708 struct pmu_event pe; 709 int ret; 710 711 decompress_event(table->entries[i].offset, &pe); 712 if (!pe.name) 713 continue; 714 ret = fn(&pe, table, data); 715 if (ret) 716 return ret; 717 } 718 return 0; 719} 720 721int pmu_metrics_table_for_each_metric(const struct pmu_metrics_table *table, 722 pmu_metric_iter_fn fn, 723 void *data) 724{ 725 for (size_t i = 0; i < table->length; i++) { 726 struct pmu_metric pm; 727 int ret; 728 729 decompress_metric(table->entries[i].offset, &pm); 730 if (!pm.metric_expr) 731 continue; 732 ret = fn(&pm, table, data); 733 if (ret) 734 return ret; 735 } 736 return 0; 737} 738 739const struct pmu_events_table *perf_pmu__find_events_table(struct perf_pmu *pmu) 740{ 741 const struct pmu_events_table *table = NULL; 742 char *cpuid = perf_pmu__getcpuid(pmu); 743 int i; 744 745 /* on some platforms which uses cpus map, cpuid can be NULL for 746 * PMUs other than CORE PMUs. 747 */ 748 if (!cpuid) 749 return NULL; 750 751 i = 0; 752 for (;;) { 753 const struct pmu_events_map *map = &pmu_events_map[i++]; 754 if (!map->arch) 755 break; 756 757 if (!strcmp_cpuid_str(map->cpuid, cpuid)) { 758 table = &map->event_table; 759 break; 760 } 761 } 762 free(cpuid); 763 return table; 764} 765 766const struct pmu_metrics_table *perf_pmu__find_metrics_table(struct perf_pmu *pmu) 767{ 768 const struct pmu_metrics_table *table = NULL; 769 char *cpuid = perf_pmu__getcpuid(pmu); 770 int i; 771 772 /* on some platforms which uses cpus map, cpuid can be NULL for 773 * PMUs other than CORE PMUs. 774 */ 775 if (!cpuid) 776 return NULL; 777 778 i = 0; 779 for (;;) { 780 const struct pmu_events_map *map = &pmu_events_map[i++]; 781 if (!map->arch) 782 break; 783 784 if (!strcmp_cpuid_str(map->cpuid, cpuid)) { 785 table = &map->metric_table; 786 break; 787 } 788 } 789 free(cpuid); 790 return table; 791} 792 793const struct pmu_events_table *find_core_events_table(const char *arch, const char *cpuid) 794{ 795 for (const struct pmu_events_map *tables = &pmu_events_map[0]; 796 tables->arch; 797 tables++) { 798 if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid)) 799 return &tables->event_table; 800 } 801 return NULL; 802} 803 804const struct pmu_metrics_table *find_core_metrics_table(const char *arch, const char *cpuid) 805{ 806 for (const struct pmu_events_map *tables = &pmu_events_map[0]; 807 tables->arch; 808 tables++) { 809 if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid)) 810 return &tables->metric_table; 811 } 812 return NULL; 813} 814 815int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data) 816{ 817 for (const struct pmu_events_map *tables = &pmu_events_map[0]; 818 tables->arch; 819 tables++) { 820 int ret = pmu_events_table_for_each_event(&tables->event_table, fn, data); 821 822 if (ret) 823 return ret; 824 } 825 return 0; 826} 827 828int pmu_for_each_core_metric(pmu_metric_iter_fn fn, void *data) 829{ 830 for (const struct pmu_events_map *tables = &pmu_events_map[0]; 831 tables->arch; 832 tables++) { 833 int ret = pmu_metrics_table_for_each_metric(&tables->metric_table, fn, data); 834 835 if (ret) 836 return ret; 837 } 838 return 0; 839} 840 841const struct pmu_events_table *find_sys_events_table(const char *name) 842{ 843 for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; 844 tables->name; 845 tables++) { 846 if (!strcmp(tables->name, name)) 847 return &tables->event_table; 848 } 849 return NULL; 850} 851 852int pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data) 853{ 854 for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; 855 tables->name; 856 tables++) { 857 int ret = pmu_events_table_for_each_event(&tables->event_table, fn, data); 858 859 if (ret) 860 return ret; 861 } 862 return 0; 863} 864 865int pmu_for_each_sys_metric(pmu_metric_iter_fn fn, void *data) 866{ 867 for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; 868 tables->name; 869 tables++) { 870 int ret = pmu_metrics_table_for_each_metric(&tables->metric_table, fn, data); 871 872 if (ret) 873 return ret; 874 } 875 return 0; 876} 877""") 878 879 880def main() -> None: 881 global _args 882 883 def dir_path(path: str) -> str: 884 """Validate path is a directory for argparse.""" 885 if os.path.isdir(path): 886 return path 887 raise argparse.ArgumentTypeError(f'\'{path}\' is not a valid directory') 888 889 def ftw(path: str, parents: Sequence[str], 890 action: Callable[[Sequence[str], os.DirEntry], None]) -> None: 891 """Replicate the directory/file walking behavior of C's file tree walk.""" 892 for item in os.scandir(path): 893 if _args.model != 'all' and item.is_dir(): 894 # Check if the model matches one in _args.model. 895 if len(parents) == _args.model.split(',')[0].count('/'): 896 # We're testing the correct directory. 897 item_path = '/'.join(parents) + ('/' if len(parents) > 0 else '') + item.name 898 if 'test' not in item_path and item_path not in _args.model.split(','): 899 continue 900 action(parents, item) 901 if item.is_dir(): 902 ftw(item.path, parents + [item.name], action) 903 904 ap = argparse.ArgumentParser() 905 ap.add_argument('arch', help='Architecture name like x86') 906 ap.add_argument('model', help='''Select a model such as skylake to 907reduce the code size. Normally set to "all". For architectures like 908ARM64 with an implementor/model, the model must include the implementor 909such as "arm/cortex-a34".''', 910 default='all') 911 ap.add_argument( 912 'starting_dir', 913 type=dir_path, 914 help='Root of tree containing architecture directories containing json files' 915 ) 916 ap.add_argument( 917 'output_file', type=argparse.FileType('w', encoding='utf-8'), nargs='?', default=sys.stdout) 918 _args = ap.parse_args() 919 920 _args.output_file.write(""" 921#include "pmu-events/pmu-events.h" 922#include "util/header.h" 923#include "util/pmu.h" 924#include <string.h> 925#include <stddef.h> 926 927struct compact_pmu_event { 928 int offset; 929}; 930 931""") 932 archs = [] 933 for item in os.scandir(_args.starting_dir): 934 if not item.is_dir(): 935 continue 936 if item.name == _args.arch or _args.arch == 'all' or item.name == 'test': 937 archs.append(item.name) 938 939 if len(archs) < 2: 940 raise IOError(f'Missing architecture directory \'{_args.arch}\'') 941 942 archs.sort() 943 for arch in archs: 944 arch_path = f'{_args.starting_dir}/{arch}' 945 preprocess_arch_std_files(arch_path) 946 ftw(arch_path, [], preprocess_one_file) 947 948 _bcs.compute() 949 _args.output_file.write('static const char *const big_c_string =\n') 950 for s in _bcs.big_string: 951 _args.output_file.write(s) 952 _args.output_file.write(';\n\n') 953 for arch in archs: 954 arch_path = f'{_args.starting_dir}/{arch}' 955 ftw(arch_path, [], process_one_file) 956 print_pending_events() 957 print_pending_metrics() 958 959 print_mapping_table(archs) 960 print_system_mapping_table() 961 962 963if __name__ == '__main__': 964 main() 965