1 #!/usr/bin/env python3 2 # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2- 3 """Convert directories of JSON events to C cod 4 import argparse 5 import csv 6 from functools import lru_cache 7 import json 8 import metric 9 import os 10 import sys 11 from typing import (Callable, Dict, Optional, 12 import 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" d 19 _sys_event_tables = [] 20 # List of regular metric tables. 21 _metric_tables = [] 22 # List of metric tables generated from "/sys" 23 _sys_metric_tables = [] 24 # Mapping between sys event table names and sy 25 _sys_event_table_to_metric_table_mapping = {} 26 # Map from an event name to an architecture st 27 # JsonEvent. Architecture standard events are 28 # f'{_args.starting_dir}/{_args.arch}' directo 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 close 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 des 41 _metricgroups = {} 42 # Order specific JsonEvent attributes will be 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 # Longer things (the last won't be iterate 51 'long_desc' 52 ] 53 54 # Attributes that are in pmu_metric rather tha 55 _json_metric_attributes = [ 56 'metric_name', 'metric_group', 'metric_exp 57 'desc', 'long_desc', 'unit', 'compat', 'me 58 'default_metricgroup_name', 'aggr_mode', ' 59 ] 60 # Attributes that are bools or enum int values 61 _json_enum_attributes = ['aggr_mode', 'depreca 62 63 def removesuffix(s: str, suffix: str) -> str: 64 """Remove the suffix from a string 65 66 The removesuffix function is added to str in 67 compatibility and so provide our own functio 68 """ 69 return s[0:-len(suffix)] if s.endswith(suffi 70 71 72 def file_name_to_table_name(prefix: str, paren 73 dirname: str) -> s 74 """Generate a C table name from directory na 75 tblname = prefix 76 for p in parents: 77 tblname += '_' + p 78 tblname += '_' + dirname 79 return tblname.replace('-', '_') 80 81 82 def c_len(s: str) -> int: 83 """Return the length of s a C string 84 85 This doesn't handle all escape characters pr 86 all \\ are for escaping, it then adjusts as 87 \\. The code uses \000 rather than \0 as a t 88 number would be folded into a string of \0 ( 89 equal a terminator followed by the number 5 90 \05). The code adjusts for \000 but not prop 91 or unicode values. 92 """ 93 try: 94 utf = s.encode(encoding='utf-8',errors='st 95 except: 96 print(f'broken string {s}') 97 raise 98 return len(utf) - utf.count(b'\\') + utf.cou 99 100 class BigCString: 101 """A class to hold many strings concatenated 102 103 Generating a large number of stand-alone C s 104 number of relocations in position independen 105 is a helper for this case. It builds a singl 106 are all the other C strings (to avoid memory 107 itself is held as a list of strings). The of 108 string are recorded and when stored to disk 109 relocation. To reduce the size of the string 110 strings are merged. If a longer string ends- 111 shorter string, these entries are also merge 112 """ 113 strings: Set[str] 114 big_string: Sequence[str] 115 offsets: Dict[str, int] 116 insert_number: int 117 insert_point: Dict[str, int] 118 metrics: Set[str] 119 120 def __init__(self): 121 self.strings = set() 122 self.insert_number = 0; 123 self.insert_point = {} 124 self.metrics = set() 125 126 def add(self, s: str, metric: bool) -> None: 127 """Called to add to the big string.""" 128 if s not in self.strings: 129 self.strings.add(s) 130 self.insert_point[s] = self.insert_numbe 131 self.insert_number += 1 132 if metric: 133 self.metrics.add(s) 134 135 def compute(self) -> None: 136 """Called once all strings are added to co 137 138 folded_strings = {} 139 # Determine if two strings can be folded, 140 # end of another. First reverse all string 141 sorted_reversed_strings = sorted([x[::-1] 142 143 # Strings 'xyz' and 'yz' will now be [ 'zy 144 # for each string to see if there is a bet 145 # into, in the example rather than using ' 146 # an offset of 1. We record which string c 147 # in folded_strings, we don't need to reco 148 # trivially computed from the string lengt 149 for pos,s in enumerate(sorted_reversed_str 150 best_pos = pos 151 for check_pos in range(pos + 1, len(sort 152 if sorted_reversed_strings[check_pos]. 153 best_pos = check_pos 154 else: 155 break 156 if pos != best_pos: 157 folded_strings[s[::-1]] = sorted_rever 158 159 # Compute reverse mappings for debugging. 160 fold_into_strings = collections.defaultdic 161 for key, val in folded_strings.items(): 162 if key != val: 163 fold_into_strings[val].add(key) 164 165 # big_string_offset is the current locatio 166 # being appended to - comments, etc. don't 167 # the string contents represented as a lis 168 # in Python and so appending to one causes 169 # lists are mutable. 170 big_string_offset = 0 171 self.big_string = [] 172 self.offsets = {} 173 174 def string_cmp_key(s: str) -> Tuple[bool, 175 return (s in self.metrics, self.insert_p 176 177 # Emit all strings that aren't folded in a 178 for s in sorted(self.strings, key=string_c 179 if s not in folded_strings: 180 self.offsets[s] = big_string_offset 181 self.big_string.append(f'/* offset={bi 182 self.big_string.append(s) 183 self.big_string.append('"') 184 if s in fold_into_strings: 185 self.big_string.append(' /* also: ' 186 self.big_string.append('\n') 187 big_string_offset += c_len(s) 188 continue 189 190 # Compute the offsets of the folded string 191 for s in folded_strings.keys(): 192 assert s not in self.offsets 193 folded_s = folded_strings[s] 194 self.offsets[s] = self.offsets[folded_s] 195 196 _bcs = BigCString() 197 198 class JsonEvent: 199 """Representation of an event loaded from a 200 201 def __init__(self, jd: dict): 202 """Constructor passed the dictionary of pa 203 204 def llx(x: int) -> str: 205 """Convert an int to a string similar to 206 return str(x) if x >= 0 and x < 10 else 207 208 def fixdesc(s: str) -> str: 209 """Fix formatting issue for the desc str 210 if s is None: 211 return None 212 return removesuffix(removesuffix(removes 213 '. '), 214 '\" 215 216 def convert_aggr_mode(aggr_mode: str) -> O 217 """Returns the aggr_mode_class enum valu 218 if not aggr_mode: 219 return None 220 aggr_mode_to_enum = { 221 'PerChip': '1', 222 'PerCore': '2', 223 } 224 return aggr_mode_to_enum[aggr_mode] 225 226 def convert_metric_constraint(metric_const 227 """Returns the metric_event_groups enum 228 if not metric_constraint: 229 return None 230 metric_constraint_to_enum = { 231 'NO_GROUP_EVENTS': '1', 232 'NO_GROUP_EVENTS_NMI': '2', 233 'NO_NMI_WATCHDOG': '2', 234 'NO_GROUP_EVENTS_SMT': '3', 235 } 236 return metric_constraint_to_enum[metric_ 237 238 def lookup_msr(num: str) -> Optional[str]: 239 """Converts the msr number, or first in 240 if not num: 241 return None 242 msrmap = { 243 0x3F6: 'ldlat=', 244 0x1A6: 'offcore_rsp=', 245 0x1A7: 'offcore_rsp=', 246 0x3F7: 'frontend=', 247 } 248 return msrmap[int(num.split(',', 1)[0], 249 250 def real_event(name: str, event: str) -> O 251 """Convert well known event names to an 252 fixed = { 253 'inst_retired.any': 'event=0xc0,peri 254 'inst_retired.any_p': 'event=0xc0,pe 255 'cpu_clk_unhalted.ref': 'event=0x0,u 256 'cpu_clk_unhalted.thread': 'event=0x 257 'cpu_clk_unhalted.core': 'event=0x3c 258 'cpu_clk_unhalted.thread_any': 'even 259 } 260 if not name: 261 return None 262 if name.lower() in fixed: 263 return fixed[name.lower()] 264 return event 265 266 def unit_to_pmu(unit: str) -> Optional[str 267 """Convert a JSON Unit to Linux PMU name 268 if not unit: 269 return 'default_core' 270 # Comment brought over from jevents.c: 271 # it's not realistic to keep adding thes 272 table = { 273 'CBO': 'uncore_cbox', 274 'QPI LL': 'uncore_qpi', 275 'SBO': 'uncore_sbox', 276 'iMPH-U': 'uncore_arb', 277 'CPU-M-CF': 'cpum_cf', 278 'CPU-M-SF': 'cpum_sf', 279 'PAI-CRYPTO' : 'pai_crypto', 280 'PAI-EXT' : 'pai_ext', 281 'UPI LL': 'uncore_upi', 282 'hisi_sicl,cpa': 'hisi_sicl,cpa', 283 'hisi_sccl,ddrc': 'hisi_sccl,ddrc', 284 'hisi_sccl,hha': 'hisi_sccl,hha', 285 'hisi_sccl,l3c': 'hisi_sccl,l3c', 286 'imx8_ddr': 'imx8_ddr', 287 'imx9_ddr': 'imx9_ddr', 288 'L3PMC': 'amd_l3', 289 'DFPMC': 'amd_df', 290 'UMCPMC': 'amd_umc', 291 'cpu_core': 'cpu_core', 292 'cpu_atom': 'cpu_atom', 293 'ali_drw': 'ali_drw', 294 'arm_cmn': 'arm_cmn', 295 } 296 return table[unit] if unit in table else 297 298 def is_zero(val: str) -> bool: 299 try: 300 if val.startswith('0x'): 301 return int(val, 16) == 0 302 else: 303 return int(val) == 0 304 except e: 305 return False 306 307 def canonicalize_value(val: str) -> str: 308 try: 309 if val.startswith('0x'): 310 return llx(int(val, 16)) 311 return str(int(val)) 312 except e: 313 return val 314 315 eventcode = 0 316 if 'EventCode' in jd: 317 eventcode = int(jd['EventCode'].split(', 318 if 'ExtSel' in jd: 319 eventcode |= int(jd['ExtSel']) << 8 320 configcode = int(jd['ConfigCode'], 0) if ' 321 eventidcode = int(jd['EventidCode'], 0) if 322 self.name = jd['EventName'].lower() if 'Ev 323 self.topic = '' 324 self.compat = jd.get('Compat') 325 self.desc = fixdesc(jd.get('BriefDescripti 326 self.long_desc = fixdesc(jd.get('PublicDes 327 precise = jd.get('PEBS') 328 msr = lookup_msr(jd.get('MSRIndex')) 329 msrval = jd.get('MSRValue') 330 extra_desc = '' 331 if 'Data_LA' in jd: 332 extra_desc += ' Supports address when p 333 if 'Errata' in jd: 334 extra_desc += '.' 335 if 'Errata' in jd: 336 extra_desc += ' Spec update: ' + jd['Er 337 self.pmu = unit_to_pmu(jd.get('Unit')) 338 filter = jd.get('Filter') 339 self.unit = jd.get('ScaleUnit') 340 self.perpkg = jd.get('PerPkg') 341 self.aggr_mode = convert_aggr_mode(jd.get( 342 self.deprecated = jd.get('Deprecated') 343 self.metric_name = jd.get('MetricName') 344 self.metric_group = jd.get('MetricGroup') 345 self.metricgroup_no_group = jd.get('Metric 346 self.default_metricgroup_name = jd.get('De 347 self.event_grouping = convert_metric_const 348 self.metric_expr = None 349 if 'MetricExpr' in jd: 350 self.metric_expr = metric.ParsePerfJson( 351 # Note, the metric formula for the thresho 352 # and > have incorrect precedence. 353 self.metric_threshold = jd.get('MetricThre 354 355 arch_std = jd.get('ArchStdEvent') 356 if precise and self.desc and '(Precise Eve 357 extra_desc += ' (Must be precise)' if pr 358 359 event = None 360 if configcode is not None: 361 event = f'config={llx(configcode)}' 362 elif eventidcode is not None: 363 event = f'eventid={llx(eventidcode)}' 364 else: 365 event = f'event={llx(eventcode)}' 366 event_fields = [ 367 ('AnyThread', 'any='), 368 ('PortMask', 'ch_mask='), 369 ('CounterMask', 'cmask='), 370 ('EdgeDetect', 'edge='), 371 ('FCMask', 'fc_mask='), 372 ('Invert', 'inv='), 373 ('SampleAfterValue', 'period='), 374 ('UMask', 'umask='), 375 ('NodeType', 'type='), 376 ('RdWrMask', 'rdwrmask='), 377 ('EnAllCores', 'enallcores='), 378 ('EnAllSlices', 'enallslices='), 379 ('SliceId', 'sliceid='), 380 ('ThreadMask', 'threadmask='), 381 ] 382 for key, value in event_fields: 383 if key in jd and not is_zero(jd[key]): 384 event += f',{value}{canonicalize_value 385 if filter: 386 event += f',{filter}' 387 if msr: 388 event += f',{msr}{msrval}' 389 if self.desc and extra_desc: 390 self.desc += extra_desc 391 if self.long_desc and extra_desc: 392 self.long_desc += extra_desc 393 if arch_std: 394 if arch_std.lower() in _arch_std_events: 395 event = _arch_std_events[arch_std.lowe 396 # Copy from the architecture standard 397 for attr, value in _arch_std_events[ar 398 if hasattr(self, attr) and not getat 399 setattr(self, attr, value) 400 else: 401 raise argparse.ArgumentTypeError('Cann 402 403 self.event = real_event(self.name, event) 404 405 def __repr__(self) -> str: 406 """String representation primarily for deb 407 s = '{\n' 408 for attr, value in self.__dict__.items(): 409 if value: 410 s += f'\t{attr} = {value},\n' 411 return s + '}' 412 413 def build_c_string(self, metric: bool) -> st 414 s = '' 415 for attr in _json_metric_attributes if met 416 x = getattr(self, attr) 417 if metric and x and attr == 'metric_expr 418 # Convert parsed metric expressions in 419 # must be doubled in the file. 420 x = x.ToPerfJson().replace('\\', '\\\\ 421 if metric and x and attr == 'metric_thre 422 x = x.replace('\\', '\\\\') 423 if attr in _json_enum_attributes: 424 s += x if x else '0' 425 else: 426 s += f'{x}\\000' if x else '\\000' 427 return s 428 429 def to_c_string(self, metric: bool) -> str: 430 """Representation of the event as a C stru 431 432 s = self.build_c_string(metric) 433 return f'{{ { _bcs.offsets[s] } }}, /* {s} 434 435 436 @lru_cache(maxsize=None) 437 def read_json_events(path: str, topic: str) -> 438 """Read json events from the specified file. 439 try: 440 events = json.load(open(path), object_hook 441 except BaseException as err: 442 print(f"Exception processing {path}") 443 raise 444 metrics: list[Tuple[str, str, metric.Express 445 for event in events: 446 event.topic = topic 447 if event.metric_name and '-' not in event. 448 metrics.append((event.pmu, event.metric_ 449 updates = metric.RewriteMetricsInTermsOfOthe 450 if updates: 451 for event in events: 452 if event.metric_name in updates: 453 # print(f'Updated {event.metric_name} 454 # f'to\n"{updates[event.metric_n 455 event.metric_expr = updates[event.metr 456 457 return events 458 459 def preprocess_arch_std_files(archpath: str) - 460 """Read in all architecture standard events. 461 global _arch_std_events 462 for item in os.scandir(archpath): 463 if item.is_file() and item.name.endswith(' 464 for event in read_json_events(item.path, 465 if event.name: 466 _arch_std_events[event.name.lower()] 467 if event.metric_name: 468 _arch_std_events[event.metric_name.l 469 470 471 def add_events_table_entries(item: os.DirEntry 472 """Add contents of file to _pending_events t 473 for e in read_json_events(item.path, topic): 474 if e.name: 475 _pending_events.append(e) 476 if e.metric_name: 477 _pending_metrics.append(e) 478 479 480 def print_pending_events() -> None: 481 """Optionally close events table.""" 482 483 def event_cmp_key(j: JsonEvent) -> Tuple[str 484 def fix_none(s: Optional[str]) -> str: 485 if s is None: 486 return '' 487 return s 488 489 return (fix_none(j.pmu).replace(',','_'), 490 fix_none(j.metric_name)) 491 492 global _pending_events 493 if not _pending_events: 494 return 495 496 global _pending_events_tblname 497 if _pending_events_tblname.endswith('_sys'): 498 global _sys_event_tables 499 _sys_event_tables.append(_pending_events_t 500 else: 501 global event_tables 502 _event_tables.append(_pending_events_tblna 503 504 first = True 505 last_pmu = None 506 last_name = None 507 pmus = set() 508 for event in sorted(_pending_events, key=eve 509 if last_pmu and last_pmu == event.pmu: 510 assert event.name != last_name, f"Duplic 511 if event.pmu != last_pmu: 512 if not first: 513 _args.output_file.write('};\n') 514 pmu_name = event.pmu.replace(',', '_') 515 _args.output_file.write( 516 f'static const struct compact_pmu_ev 517 first = False 518 last_pmu = event.pmu 519 pmus.add((event.pmu, pmu_name)) 520 521 _args.output_file.write(event.to_c_string( 522 last_name = event.name 523 _pending_events = [] 524 525 _args.output_file.write(f""" 526 }}; 527 528 const struct pmu_table_entry {_pending_events_ 529 """) 530 for (pmu, tbl_pmu) in sorted(pmus): 531 pmu_name = f"{pmu}\\000" 532 _args.output_file.write(f"""{{ 533 .entries = {_pending_events_tblname}_{tbl 534 .num_entries = ARRAY_SIZE({_pending_event 535 .pmu_name = {{ {_bcs.offsets[pmu_name]} / 536 }}, 537 """) 538 _args.output_file.write('};\n\n') 539 540 def print_pending_metrics() -> None: 541 """Optionally close metrics table.""" 542 543 def metric_cmp_key(j: JsonEvent) -> Tuple[bo 544 def fix_none(s: Optional[str]) -> str: 545 if s is None: 546 return '' 547 return s 548 549 return (j.desc is not None, fix_none(j.pmu 550 551 global _pending_metrics 552 if not _pending_metrics: 553 return 554 555 global _pending_metrics_tblname 556 if _pending_metrics_tblname.endswith('_sys') 557 global _sys_metric_tables 558 _sys_metric_tables.append(_pending_metrics 559 else: 560 global metric_tables 561 _metric_tables.append(_pending_metrics_tbl 562 563 first = True 564 last_pmu = None 565 pmus = set() 566 for metric in sorted(_pending_metrics, key=m 567 if metric.pmu != last_pmu: 568 if not first: 569 _args.output_file.write('};\n') 570 pmu_name = metric.pmu.replace(',', '_') 571 _args.output_file.write( 572 f'static const struct compact_pmu_ev 573 first = False 574 last_pmu = metric.pmu 575 pmus.add((metric.pmu, pmu_name)) 576 577 _args.output_file.write(metric.to_c_string 578 _pending_metrics = [] 579 580 _args.output_file.write(f""" 581 }}; 582 583 const struct pmu_table_entry {_pending_metrics 584 """) 585 for (pmu, tbl_pmu) in sorted(pmus): 586 pmu_name = f"{pmu}\\000" 587 _args.output_file.write(f"""{{ 588 .entries = {_pending_metrics_tblname}_{tb 589 .num_entries = ARRAY_SIZE({_pending_metri 590 .pmu_name = {{ {_bcs.offsets[pmu_name]} / 591 }}, 592 """) 593 _args.output_file.write('};\n\n') 594 595 def get_topic(topic: str) -> str: 596 if topic.endswith('metrics.json'): 597 return 'metrics' 598 return removesuffix(topic, '.json').replace( 599 600 def preprocess_one_file(parents: Sequence[str] 601 602 if item.is_dir(): 603 return 604 605 # base dir or too deep 606 level = len(parents) 607 if level == 0 or level > 4: 608 return 609 610 # Ignore other directories. If the file name 611 # extension, ignore it. It could be a readme 612 if not item.is_file() or not item.name.endsw 613 return 614 615 if item.name == 'metricgroups.json': 616 metricgroup_descriptions = json.load(open( 617 for mgroup in metricgroup_descriptions: 618 assert len(mgroup) > 1, parents 619 description = f"{metricgroup_description 620 mgroup = f"{mgroup}\\000" 621 _bcs.add(mgroup, metric=True) 622 _bcs.add(description, metric=True) 623 _metricgroups[mgroup] = description 624 return 625 626 topic = get_topic(item.name) 627 for event in read_json_events(item.path, top 628 pmu_name = f"{event.pmu}\\000" 629 if event.name: 630 _bcs.add(pmu_name, metric=False) 631 _bcs.add(event.build_c_string(metric=Fal 632 if event.metric_name: 633 _bcs.add(pmu_name, metric=True) 634 _bcs.add(event.build_c_string(metric=Tru 635 636 def process_one_file(parents: Sequence[str], i 637 """Process a JSON file during the main walk. 638 def is_leaf_dir_ignoring_sys(path: str) -> b 639 for item in os.scandir(path): 640 if item.is_dir() and item.name != 'sys': 641 return False 642 return True 643 644 # Model directories are leaves (ignoring pos 645 # directories). The FTW will walk into the d 646 # pending events and metrics and update the 647 # model directory. 648 if item.is_dir() and is_leaf_dir_ignoring_sy 649 print_pending_events() 650 print_pending_metrics() 651 652 global _pending_events_tblname 653 _pending_events_tblname = file_name_to_tab 654 global _pending_metrics_tblname 655 _pending_metrics_tblname = file_name_to_ta 656 657 if item.name == 'sys': 658 _sys_event_table_to_metric_table_mapping 659 return 660 661 # base dir or too deep 662 level = len(parents) 663 if level == 0 or level > 4: 664 return 665 666 # Ignore other directories. If the file name 667 # extension, ignore it. It could be a readme 668 if not item.is_file() or not item.name.endsw 669 return 670 671 add_events_table_entries(item, get_topic(ite 672 673 674 def print_mapping_table(archs: Sequence[str]) 675 """Read the mapfile and generate the struct 676 _args.output_file.write(""" 677 /* Struct used to make the PMU event table imp 678 struct pmu_events_table { 679 const struct pmu_table_entry *pmus; 680 uint32_t num_pmus; 681 }; 682 683 /* Struct used to make the PMU metric table im 684 struct pmu_metrics_table { 685 const struct pmu_table_entry *pmus; 686 uint32_t num_pmus; 687 }; 688 689 /* 690 * Map a CPU to its table of PMU events. The C 691 * cpuid field, which is an arch-specific iden 692 * The identifier specified in tools/perf/pmu- 693 * must match the get_cpuid_str() in tools/per 694 * 695 * The cpuid can contain any character other 696 */ 697 struct pmu_events_map { 698 const char *arch; 699 const char *cpuid; 700 struct pmu_events_table event_table; 701 struct pmu_metrics_table metric_table; 702 }; 703 704 /* 705 * Global table mapping each known CPU for the 706 * table of PMU events. 707 */ 708 const struct pmu_events_map pmu_events_map[] = 709 """) 710 for arch in archs: 711 if arch == 'test': 712 _args.output_file.write("""{ 713 \t.arch = "testarch", 714 \t.cpuid = "testcpu", 715 \t.event_table = { 716 \t\t.pmus = pmu_events__test_soc_cpu, 717 \t\t.num_pmus = ARRAY_SIZE(pmu_events__test_so 718 \t}, 719 \t.metric_table = { 720 \t\t.pmus = pmu_metrics__test_soc_cpu, 721 \t\t.num_pmus = ARRAY_SIZE(pmu_metrics__test_s 722 \t} 723 }, 724 """) 725 else: 726 with open(f'{_args.starting_dir}/{arch}/ 727 table = csv.reader(csvfile) 728 first = True 729 for row in table: 730 # Skip the first row or any row begi 731 if not first and len(row) > 0 and no 732 event_tblname = file_name_to_table 733 if event_tblname in _event_tables: 734 event_size = f'ARRAY_SIZE({event 735 else: 736 event_tblname = 'NULL' 737 event_size = '0' 738 metric_tblname = file_name_to_tabl 739 if metric_tblname in _metric_table 740 metric_size = f'ARRAY_SIZE({metr 741 else: 742 metric_tblname = 'NULL' 743 metric_size = '0' 744 if event_size == '0' and metric_si 745 continue 746 cpuid = row[0].replace('\\', '\\\\ 747 _args.output_file.write(f"""{{ 748 \t.arch = "{arch}", 749 \t.cpuid = "{cpuid}", 750 \t.event_table = {{ 751 \t\t.pmus = {event_tblname}, 752 \t\t.num_pmus = {event_size} 753 \t}}, 754 \t.metric_table = {{ 755 \t\t.pmus = {metric_tblname}, 756 \t\t.num_pmus = {metric_size} 757 \t}} 758 }}, 759 """) 760 first = False 761 762 _args.output_file.write("""{ 763 \t.arch = 0, 764 \t.cpuid = 0, 765 \t.event_table = { 0, 0 }, 766 \t.metric_table = { 0, 0 }, 767 } 768 }; 769 """) 770 771 772 def print_system_mapping_table() -> None: 773 """C struct mapping table array for tables f 774 _args.output_file.write(""" 775 struct pmu_sys_events { 776 \tconst char *name; 777 \tstruct pmu_events_table event_table; 778 \tstruct pmu_metrics_table metric_table; 779 }; 780 781 static const struct pmu_sys_events pmu_sys_eve 782 """) 783 printed_metric_tables = [] 784 for tblname in _sys_event_tables: 785 _args.output_file.write(f"""\t{{ 786 \t\t.event_table = {{ 787 \t\t\t.pmus = {tblname}, 788 \t\t\t.num_pmus = ARRAY_SIZE({tblname}) 789 \t\t}},""") 790 metric_tblname = _sys_event_table_to_metri 791 if metric_tblname in _sys_metric_tables: 792 _args.output_file.write(f""" 793 \t\t.metric_table = {{ 794 \t\t\t.pmus = {metric_tblname}, 795 \t\t\t.num_pmus = ARRAY_SIZE({metric_tblname}) 796 \t\t}},""") 797 printed_metric_tables.append(metric_tbln 798 _args.output_file.write(f""" 799 \t\t.name = \"{tblname}\", 800 \t}}, 801 """) 802 for tblname in _sys_metric_tables: 803 if tblname in printed_metric_tables: 804 continue 805 _args.output_file.write(f"""\t{{ 806 \t\t.metric_table = {{ 807 \t\t\t.pmus = {tblname}, 808 \t\t\t.num_pmus = ARRAY_SIZE({tblname}) 809 \t\t}}, 810 \t\t.name = \"{tblname}\", 811 \t}}, 812 """) 813 _args.output_file.write("""\t{ 814 \t\t.event_table = { 0, 0 }, 815 \t\t.metric_table = { 0, 0 }, 816 \t}, 817 }; 818 819 static void decompress_event(int offset, struc 820 { 821 \tconst char *p = &big_c_string[offset]; 822 """) 823 for attr in _json_event_attributes: 824 _args.output_file.write(f'\n\tpe->{attr} = 825 if attr in _json_enum_attributes: 826 _args.output_file.write("*p - '0';\n") 827 else: 828 _args.output_file.write("(*p == '\\0' ? 829 if attr == _json_event_attributes[-1]: 830 continue 831 if attr in _json_enum_attributes: 832 _args.output_file.write('\tp++;') 833 else: 834 _args.output_file.write('\twhile (*p++); 835 _args.output_file.write("""} 836 837 static void decompress_metric(int offset, stru 838 { 839 \tconst char *p = &big_c_string[offset]; 840 """) 841 for attr in _json_metric_attributes: 842 _args.output_file.write(f'\n\tpm->{attr} = 843 if attr in _json_enum_attributes: 844 _args.output_file.write("*p - '0';\n") 845 else: 846 _args.output_file.write("(*p == '\\0' ? 847 if attr == _json_metric_attributes[-1]: 848 continue 849 if attr in _json_enum_attributes: 850 _args.output_file.write('\tp++;') 851 else: 852 _args.output_file.write('\twhile (*p++); 853 _args.output_file.write("""} 854 855 static int pmu_events_table__for_each_event_pm 856 857 858 859 { 860 int ret; 861 struct pmu_event pe = { 862 .pmu = &big_c_string[pmu->pmu_ 863 }; 864 865 for (uint32_t i = 0; i < pmu->num_entr 866 decompress_event(pmu->entries[ 867 if (!pe.name) 868 continue; 869 ret = fn(&pe, table, data); 870 if (ret) 871 return ret; 872 } 873 return 0; 874 } 875 876 static int pmu_events_table__find_event_pmu(co 877 co 878 co 879 pm 880 vo 881 { 882 struct pmu_event pe = { 883 .pmu = &big_c_string[pmu->pmu_ 884 }; 885 int low = 0, high = pmu->num_entries - 886 887 while (low <= high) { 888 int cmp, mid = (low + high) / 889 890 decompress_event(pmu->entries[ 891 892 if (!pe.name && !name) 893 goto do_call; 894 895 if (!pe.name && name) { 896 low = mid + 1; 897 continue; 898 } 899 if (pe.name && !name) { 900 high = mid - 1; 901 continue; 902 } 903 904 cmp = strcasecmp(pe.name, name 905 if (cmp < 0) { 906 low = mid + 1; 907 continue; 908 } 909 if (cmp > 0) { 910 high = mid - 1; 911 continue; 912 } 913 do_call: 914 return fn ? fn(&pe, table, dat 915 } 916 return PMU_EVENTS__NOT_FOUND; 917 } 918 919 int pmu_events_table__for_each_event(const str 920 struct per 921 pmu_event_ 922 void *data 923 { 924 for (size_t i = 0; i < table->num_pmus 925 const struct pmu_table_entry * 926 const char *pmu_name = &big_c_ 927 int ret; 928 929 if (pmu && !pmu__name_match(pm 930 continue; 931 932 ret = pmu_events_table__for_ea 933 if (pmu || ret) 934 return ret; 935 } 936 return 0; 937 } 938 939 int pmu_events_table__find_event(const struct 940 struct perf_p 941 const char *n 942 pmu_event_ite 943 void *data) 944 { 945 for (size_t i = 0; i < table->num_pmus 946 const struct pmu_table_entry * 947 const char *pmu_name = &big_c_ 948 int ret; 949 950 if (!pmu__name_match(pmu, pmu_ 951 continue; 952 953 ret = pmu_events_table__find_e 954 if (ret != PMU_EVENTS__NOT_FOU 955 return ret; 956 } 957 return PMU_EVENTS__NOT_FOUND; 958 } 959 960 size_t pmu_events_table__num_events(const stru 961 struct per 962 { 963 size_t count = 0; 964 965 for (size_t i = 0; i < table->num_pmus 966 const struct pmu_table_entry * 967 const char *pmu_name = &big_c_ 968 969 if (pmu__name_match(pmu, pmu_n 970 count += table_pmu->nu 971 } 972 return count; 973 } 974 975 static int pmu_metrics_table__for_each_metric_ 976 977 978 979 { 980 int ret; 981 struct pmu_metric pm = { 982 .pmu = &big_c_string[pmu->pmu_ 983 }; 984 985 for (uint32_t i = 0; i < pmu->num_entr 986 decompress_metric(pmu->entries 987 if (!pm.metric_expr) 988 continue; 989 ret = fn(&pm, table, data); 990 if (ret) 991 return ret; 992 } 993 return 0; 994 } 995 996 int pmu_metrics_table__for_each_metric(const s 997 pmu_metri 998 void *dat 999 { 1000 for (size_t i = 0; i < table->num_pmu 1001 int ret = pmu_metrics_table__ 1002 1003 1004 if (ret) 1005 return ret; 1006 } 1007 return 0; 1008 } 1009 1010 static const struct pmu_events_map *map_for_p 1011 { 1012 static struct { 1013 const struct pmu_events_map * 1014 struct perf_pmu *pmu; 1015 } last_result; 1016 static struct { 1017 const struct pmu_events_map * 1018 char *cpuid; 1019 } last_map_search; 1020 static bool has_last_result, has_last 1021 const struct pmu_events_map *map = NU 1022 char *cpuid = NULL; 1023 size_t i; 1024 1025 if (has_last_result && last_result.pm 1026 return last_result.map; 1027 1028 cpuid = perf_pmu__getcpuid(pmu); 1029 1030 /* 1031 * On some platforms which uses cpus 1032 * PMUs other than CORE PMUs. 1033 */ 1034 if (!cpuid) 1035 goto out_update_last_result; 1036 1037 if (has_last_map_search && !strcmp(la 1038 map = last_map_search.map; 1039 free(cpuid); 1040 } else { 1041 i = 0; 1042 for (;;) { 1043 map = &pmu_events_map 1044 1045 if (!map->arch) { 1046 map = NULL; 1047 break; 1048 } 1049 1050 if (!strcmp_cpuid_str 1051 break; 1052 } 1053 free(last_map_search.cpuid); 1054 last_map_search.cpuid = cpuid; 1055 last_map_search.map = map; 1056 has_last_map_search = true; 1057 } 1058 out_update_last_result: 1059 last_result.pmu = pmu; 1060 last_result.map = map; 1061 has_last_result = true; 1062 return map; 1063 } 1064 1065 const struct pmu_events_table *perf_pmu__find 1066 { 1067 const struct pmu_events_map *map = ma 1068 1069 if (!map) 1070 return NULL; 1071 1072 if (!pmu) 1073 return &map->event_table; 1074 1075 for (size_t i = 0; i < map->event_tab 1076 const struct pmu_table_entry 1077 const char *pmu_name = &big_c 1078 1079 if (pmu__name_match(pmu, pmu_ 1080 return &map->event_t 1081 } 1082 return NULL; 1083 } 1084 1085 const struct pmu_metrics_table *perf_pmu__fin 1086 { 1087 const struct pmu_events_map *map = ma 1088 1089 if (!map) 1090 return NULL; 1091 1092 if (!pmu) 1093 return &map->metric_table; 1094 1095 for (size_t i = 0; i < map->metric_ta 1096 const struct pmu_table_entry 1097 const char *pmu_name = &big_c 1098 1099 if (pmu__name_match(pmu, pmu_ 1100 return &map->metri 1101 } 1102 return NULL; 1103 } 1104 1105 const struct pmu_events_table *find_core_even 1106 { 1107 for (const struct pmu_events_map *tab 1108 tables->arch; 1109 tables++) { 1110 if (!strcmp(tables->arch, arc 1111 return &tables->event 1112 } 1113 return NULL; 1114 } 1115 1116 const struct pmu_metrics_table *find_core_met 1117 { 1118 for (const struct pmu_events_map *tab 1119 tables->arch; 1120 tables++) { 1121 if (!strcmp(tables->arch, arc 1122 return &tables->metri 1123 } 1124 return NULL; 1125 } 1126 1127 int pmu_for_each_core_event(pmu_event_iter_fn 1128 { 1129 for (const struct pmu_events_map *tab 1130 tables->arch; 1131 tables++) { 1132 int ret = pmu_events_table__f 1133 1134 1135 if (ret) 1136 return ret; 1137 } 1138 return 0; 1139 } 1140 1141 int pmu_for_each_core_metric(pmu_metric_iter_ 1142 { 1143 for (const struct pmu_events_map *tab 1144 tables->arch; 1145 tables++) { 1146 int ret = pmu_metrics_table__ 1147 1148 if (ret) 1149 return ret; 1150 } 1151 return 0; 1152 } 1153 1154 const struct pmu_events_table *find_sys_event 1155 { 1156 for (const struct pmu_sys_events *tab 1157 tables->name; 1158 tables++) { 1159 if (!strcmp(tables->name, nam 1160 return &tables->event 1161 } 1162 return NULL; 1163 } 1164 1165 int pmu_for_each_sys_event(pmu_event_iter_fn 1166 { 1167 for (const struct pmu_sys_events *tab 1168 tables->name; 1169 tables++) { 1170 int ret = pmu_events_table__f 1171 1172 1173 if (ret) 1174 return ret; 1175 } 1176 return 0; 1177 } 1178 1179 int pmu_for_each_sys_metric(pmu_metric_iter_f 1180 { 1181 for (const struct pmu_sys_events *tab 1182 tables->name; 1183 tables++) { 1184 int ret = pmu_metrics_table__ 1185 1186 if (ret) 1187 return ret; 1188 } 1189 return 0; 1190 } 1191 """) 1192 1193 def print_metricgroups() -> None: 1194 _args.output_file.write(""" 1195 static const int metricgroups[][2] = { 1196 """) 1197 for mgroup in sorted(_metricgroups): 1198 description = _metricgroups[mgroup] 1199 _args.output_file.write( 1200 f'\t{{ {_bcs.offsets[mgroup]}, {_bcs. 1201 ) 1202 _args.output_file.write(""" 1203 }; 1204 1205 const char *describe_metricgroup(const char * 1206 { 1207 int low = 0, high = (int)ARRAY_SIZE(m 1208 1209 while (low <= high) { 1210 int mid = (low + high) / 2; 1211 const char *mgroup = &big_c_s 1212 int cmp = strcmp(mgroup, grou 1213 1214 if (cmp == 0) { 1215 return &big_c_string[ 1216 } else if (cmp < 0) { 1217 low = mid + 1; 1218 } else { 1219 high = mid - 1; 1220 } 1221 } 1222 return NULL; 1223 } 1224 """) 1225 1226 def main() -> None: 1227 global _args 1228 1229 def dir_path(path: str) -> str: 1230 """Validate path is a directory for argpa 1231 if os.path.isdir(path): 1232 return path 1233 raise argparse.ArgumentTypeError(f'\'{pat 1234 1235 def ftw(path: str, parents: Sequence[str], 1236 action: Callable[[Sequence[str], os 1237 """Replicate the directory/file walking b 1238 for item in sorted(os.scandir(path), key= 1239 if _args.model != 'all' and item.is_dir 1240 # Check if the model matches one in _ 1241 if len(parents) == _args.model.split( 1242 # We're testing the correct directo 1243 item_path = '/'.join(parents) + ('/ 1244 if 'test' not in item_path and item 1245 continue 1246 action(parents, item) 1247 if item.is_dir(): 1248 ftw(item.path, parents + [item.name], 1249 1250 ap = argparse.ArgumentParser() 1251 ap.add_argument('arch', help='Architecture 1252 ap.add_argument('model', help='''Select a m 1253 reduce the code size. Normally set to "all". 1254 ARM64 with an implementor/model, the model mu 1255 such as "arm/cortex-a34".''', 1256 default='all') 1257 ap.add_argument( 1258 'starting_dir', 1259 type=dir_path, 1260 help='Root of tree containing architect 1261 ) 1262 ap.add_argument( 1263 'output_file', type=argparse.FileType(' 1264 _args = ap.parse_args() 1265 1266 _args.output_file.write(f""" 1267 /* SPDX-License-Identifier: GPL-2.0 */ 1268 /* THIS FILE WAS AUTOGENERATED BY jevents.py 1269 """) 1270 _args.output_file.write(""" 1271 #include <pmu-events/pmu-events.h> 1272 #include "util/header.h" 1273 #include "util/pmu.h" 1274 #include <string.h> 1275 #include <stddef.h> 1276 1277 struct compact_pmu_event { 1278 int offset; 1279 }; 1280 1281 struct pmu_table_entry { 1282 const struct compact_pmu_event *entri 1283 uint32_t num_entries; 1284 struct compact_pmu_event pmu_name; 1285 }; 1286 1287 """) 1288 archs = [] 1289 for item in os.scandir(_args.starting_dir): 1290 if not item.is_dir(): 1291 continue 1292 if item.name == _args.arch or _args.arch 1293 archs.append(item.name) 1294 1295 if len(archs) < 2 and _args.arch != 'none': 1296 raise IOError(f'Missing architecture dire 1297 1298 archs.sort() 1299 for arch in archs: 1300 arch_path = f'{_args.starting_dir}/{arch} 1301 preprocess_arch_std_files(arch_path) 1302 ftw(arch_path, [], preprocess_one_file) 1303 1304 _bcs.compute() 1305 _args.output_file.write('static const char 1306 for s in _bcs.big_string: 1307 _args.output_file.write(s) 1308 _args.output_file.write(';\n\n') 1309 for arch in archs: 1310 arch_path = f'{_args.starting_dir}/{arch} 1311 ftw(arch_path, [], process_one_file) 1312 print_pending_events() 1313 print_pending_metrics() 1314 1315 print_mapping_table(archs) 1316 print_system_mapping_table() 1317 print_metricgroups() 1318 1319 if __name__ == '__main__': 1320 main()
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.