1ffc606adSIan Rogers#!/usr/bin/env python3 2ffc606adSIan Rogers# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 3ffc606adSIan Rogers"""Convert directories of JSON events to C code.""" 4ffc606adSIan Rogersimport argparse 5ffc606adSIan Rogersimport csv 6ffc606adSIan Rogersimport json 7*40769665SIan Rogersimport metric 8ffc606adSIan Rogersimport os 9ffc606adSIan Rogersimport sys 109118259cSIan Rogersfrom typing import (Callable, Dict, Optional, Sequence, Set, Tuple) 119118259cSIan Rogersimport collections 12ffc606adSIan Rogers 13ffc606adSIan Rogers# Global command line arguments. 14ffc606adSIan Rogers_args = None 15ffc606adSIan Rogers# List of event tables generated from "/sys" directories. 16ffc606adSIan Rogers_sys_event_tables = [] 17ffc606adSIan Rogers# Map from an event name to an architecture standard 18ffc606adSIan Rogers# JsonEvent. Architecture standard events are in json files in the top 19ffc606adSIan Rogers# f'{_args.starting_dir}/{_args.arch}' directory. 20ffc606adSIan Rogers_arch_std_events = {} 21ffc606adSIan Rogers# Track whether an events table is currently being defined and needs closing. 22ffc606adSIan Rogers_close_table = False 237b2f844cSIan Rogers# Events to write out when the table is closed 247b2f844cSIan Rogers_pending_events = [] 259118259cSIan Rogers# Global BigCString shared by all structures. 269118259cSIan Rogers_bcs = None 279118259cSIan Rogers# Order specific JsonEvent attributes will be visited. 289118259cSIan Rogers_json_event_attributes = [ 299118259cSIan Rogers # cmp_sevent related attributes. 309118259cSIan Rogers 'name', 'pmu', 'topic', 'desc', 'metric_name', 'metric_group', 319118259cSIan Rogers # Seems useful, put it early. 329118259cSIan Rogers 'event', 339118259cSIan Rogers # Short things in alphabetical order. 349118259cSIan Rogers 'aggr_mode', 'compat', 'deprecated', 'perpkg', 'unit', 359118259cSIan Rogers # Longer things (the last won't be iterated over during decompress). 369118259cSIan Rogers 'metric_constraint', 'metric_expr', 'long_desc' 379118259cSIan Rogers] 38ffc606adSIan Rogers 39ffc606adSIan Rogers 40ffc606adSIan Rogersdef removesuffix(s: str, suffix: str) -> str: 41ffc606adSIan Rogers """Remove the suffix from a string 42ffc606adSIan Rogers 43ffc606adSIan Rogers The removesuffix function is added to str in Python 3.9. We aim for 3.6 44ffc606adSIan Rogers compatibility and so provide our own function here. 45ffc606adSIan Rogers """ 46ffc606adSIan Rogers return s[0:-len(suffix)] if s.endswith(suffix) else s 47ffc606adSIan Rogers 48ffc606adSIan Rogers 49ffc606adSIan Rogersdef file_name_to_table_name(parents: Sequence[str], dirname: str) -> str: 50ffc606adSIan Rogers """Generate a C table name from directory names.""" 51ffc606adSIan Rogers tblname = 'pme' 52ffc606adSIan Rogers for p in parents: 53ffc606adSIan Rogers tblname += '_' + p 54ffc606adSIan Rogers tblname += '_' + dirname 55ffc606adSIan Rogers return tblname.replace('-', '_') 56ffc606adSIan Rogers 579118259cSIan Rogersdef c_len(s: str) -> int: 589118259cSIan Rogers """Return the length of s a C string 599118259cSIan Rogers 609118259cSIan Rogers This doesn't handle all escape characters properly. It first assumes 619118259cSIan Rogers all \ are for escaping, it then adjusts as it will have over counted 629118259cSIan Rogers \\. The code uses \000 rather than \0 as a terminator as an adjacent 639118259cSIan Rogers number would be folded into a string of \0 (ie. "\0" + "5" doesn't 649118259cSIan Rogers equal a terminator followed by the number 5 but the escape of 659118259cSIan Rogers \05). The code adjusts for \000 but not properly for all octal, hex 669118259cSIan Rogers or unicode values. 679118259cSIan Rogers """ 689118259cSIan Rogers try: 699118259cSIan Rogers utf = s.encode(encoding='utf-8',errors='strict') 709118259cSIan Rogers except: 719118259cSIan Rogers print(f'broken string {s}') 729118259cSIan Rogers raise 739118259cSIan Rogers return len(utf) - utf.count(b'\\') + utf.count(b'\\\\') - (utf.count(b'\\000') * 2) 749118259cSIan Rogers 759118259cSIan Rogersclass BigCString: 769118259cSIan Rogers """A class to hold many strings concatenated together. 779118259cSIan Rogers 789118259cSIan Rogers Generating a large number of stand-alone C strings creates a large 799118259cSIan Rogers number of relocations in position independent code. The BigCString 809118259cSIan Rogers is a helper for this case. It builds a single string which within it 819118259cSIan Rogers are all the other C strings (to avoid memory issues the string 829118259cSIan Rogers itself is held as a list of strings). The offsets within the big 839118259cSIan Rogers string are recorded and when stored to disk these don't need 84d0313e62SIan Rogers relocation. To reduce the size of the string further, identical 85d0313e62SIan Rogers strings are merged. If a longer string ends-with the same value as a 86d0313e62SIan Rogers shorter string, these entries are also merged. 879118259cSIan Rogers """ 889118259cSIan Rogers strings: Set[str] 899118259cSIan Rogers big_string: Sequence[str] 909118259cSIan Rogers offsets: Dict[str, int] 919118259cSIan Rogers 929118259cSIan Rogers def __init__(self): 939118259cSIan Rogers self.strings = set() 949118259cSIan Rogers 959118259cSIan Rogers def add(self, s: str) -> None: 969118259cSIan Rogers """Called to add to the big string.""" 979118259cSIan Rogers self.strings.add(s) 989118259cSIan Rogers 999118259cSIan Rogers def compute(self) -> None: 1009118259cSIan Rogers """Called once all strings are added to compute the string and offsets.""" 1019118259cSIan Rogers 102d0313e62SIan Rogers folded_strings = {} 103d0313e62SIan Rogers # Determine if two strings can be folded, ie. let 1 string use the 104d0313e62SIan Rogers # end of another. First reverse all strings and sort them. 105d0313e62SIan Rogers sorted_reversed_strings = sorted([x[::-1] for x in self.strings]) 106d0313e62SIan Rogers 107d0313e62SIan Rogers # Strings 'xyz' and 'yz' will now be [ 'zy', 'zyx' ]. Scan forward 108d0313e62SIan Rogers # for each string to see if there is a better candidate to fold it 109d0313e62SIan Rogers # into, in the example rather than using 'yz' we can use'xyz' at 110d0313e62SIan Rogers # an offset of 1. We record which string can be folded into which 111d0313e62SIan Rogers # in folded_strings, we don't need to record the offset as it is 112d0313e62SIan Rogers # trivially computed from the string lengths. 113d0313e62SIan Rogers for pos,s in enumerate(sorted_reversed_strings): 114d0313e62SIan Rogers best_pos = pos 115d0313e62SIan Rogers for check_pos in range(pos + 1, len(sorted_reversed_strings)): 116d0313e62SIan Rogers if sorted_reversed_strings[check_pos].startswith(s): 117d0313e62SIan Rogers best_pos = check_pos 118d0313e62SIan Rogers else: 119d0313e62SIan Rogers break 120d0313e62SIan Rogers if pos != best_pos: 121d0313e62SIan Rogers folded_strings[s[::-1]] = sorted_reversed_strings[best_pos][::-1] 122d0313e62SIan Rogers 123d0313e62SIan Rogers # Compute reverse mappings for debugging. 124d0313e62SIan Rogers fold_into_strings = collections.defaultdict(set) 125d0313e62SIan Rogers for key, val in folded_strings.items(): 126d0313e62SIan Rogers if key != val: 127d0313e62SIan Rogers fold_into_strings[val].add(key) 128d0313e62SIan Rogers 1299118259cSIan Rogers # big_string_offset is the current location within the C string 1309118259cSIan Rogers # being appended to - comments, etc. don't count. big_string is 1319118259cSIan Rogers # the string contents represented as a list. Strings are immutable 1329118259cSIan Rogers # in Python and so appending to one causes memory issues, while 1339118259cSIan Rogers # lists are mutable. 1349118259cSIan Rogers big_string_offset = 0 1359118259cSIan Rogers self.big_string = [] 1369118259cSIan Rogers self.offsets = {} 137d0313e62SIan Rogers 138d0313e62SIan Rogers # Emit all strings that aren't folded in a sorted manner. 1399118259cSIan Rogers for s in sorted(self.strings): 140d0313e62SIan Rogers if s not in folded_strings: 1419118259cSIan Rogers self.offsets[s] = big_string_offset 1429118259cSIan Rogers self.big_string.append(f'/* offset={big_string_offset} */ "') 1439118259cSIan Rogers self.big_string.append(s) 144d0313e62SIan Rogers self.big_string.append('"') 145d0313e62SIan Rogers if s in fold_into_strings: 146d0313e62SIan Rogers self.big_string.append(' /* also: ' + ', '.join(fold_into_strings[s]) + ' */') 147d0313e62SIan Rogers self.big_string.append('\n') 1489118259cSIan Rogers big_string_offset += c_len(s) 149d0313e62SIan Rogers continue 150d0313e62SIan Rogers 151d0313e62SIan Rogers # Compute the offsets of the folded strings. 152d0313e62SIan Rogers for s in folded_strings.keys(): 153d0313e62SIan Rogers assert s not in self.offsets 154d0313e62SIan Rogers folded_s = folded_strings[s] 155d0313e62SIan Rogers self.offsets[s] = self.offsets[folded_s] + c_len(folded_s) - c_len(s) 1569118259cSIan Rogers 1579118259cSIan Rogers_bcs = BigCString() 158ffc606adSIan Rogers 159ffc606adSIan Rogersclass JsonEvent: 160ffc606adSIan Rogers """Representation of an event loaded from a json file dictionary.""" 161ffc606adSIan Rogers 162ffc606adSIan Rogers def __init__(self, jd: dict): 163ffc606adSIan Rogers """Constructor passed the dictionary of parsed json values.""" 164ffc606adSIan Rogers 165ffc606adSIan Rogers def llx(x: int) -> str: 166ffc606adSIan Rogers """Convert an int to a string similar to a printf modifier of %#llx.""" 167ffc606adSIan Rogers return '0' if x == 0 else hex(x) 168ffc606adSIan Rogers 169ffc606adSIan Rogers def fixdesc(s: str) -> str: 170ffc606adSIan Rogers """Fix formatting issue for the desc string.""" 171ffc606adSIan Rogers if s is None: 172ffc606adSIan Rogers return None 173ffc606adSIan Rogers return removesuffix(removesuffix(removesuffix(s, '. '), 174ffc606adSIan Rogers '. '), '.').replace('\n', '\\n').replace( 175ffc606adSIan Rogers '\"', '\\"').replace('\r', '\\r') 176ffc606adSIan Rogers 177e1e19d05SIan Rogers def convert_aggr_mode(aggr_mode: str) -> Optional[str]: 178ffc606adSIan Rogers """Returns the aggr_mode_class enum value associated with the JSON string.""" 179ffc606adSIan Rogers if not aggr_mode: 180ffc606adSIan Rogers return None 181ffc606adSIan Rogers aggr_mode_to_enum = { 182ffc606adSIan Rogers 'PerChip': '1', 183ffc606adSIan Rogers 'PerCore': '2', 184ffc606adSIan Rogers } 185ffc606adSIan Rogers return aggr_mode_to_enum[aggr_mode] 186ffc606adSIan Rogers 187e1e19d05SIan Rogers def lookup_msr(num: str) -> Optional[str]: 188ffc606adSIan Rogers """Converts the msr number, or first in a list to the appropriate event field.""" 189ffc606adSIan Rogers if not num: 190ffc606adSIan Rogers return None 191ffc606adSIan Rogers msrmap = { 192ffc606adSIan Rogers 0x3F6: 'ldlat=', 193ffc606adSIan Rogers 0x1A6: 'offcore_rsp=', 194ffc606adSIan Rogers 0x1A7: 'offcore_rsp=', 195ffc606adSIan Rogers 0x3F7: 'frontend=', 196ffc606adSIan Rogers } 197ffc606adSIan Rogers return msrmap[int(num.split(',', 1)[0], 0)] 198ffc606adSIan Rogers 199e1e19d05SIan Rogers def real_event(name: str, event: str) -> Optional[str]: 200ffc606adSIan Rogers """Convert well known event names to an event string otherwise use the event argument.""" 201ffc606adSIan Rogers fixed = { 202ffc606adSIan Rogers 'inst_retired.any': 'event=0xc0,period=2000003', 203ffc606adSIan Rogers 'inst_retired.any_p': 'event=0xc0,period=2000003', 204ffc606adSIan Rogers 'cpu_clk_unhalted.ref': 'event=0x0,umask=0x03,period=2000003', 205ffc606adSIan Rogers 'cpu_clk_unhalted.thread': 'event=0x3c,period=2000003', 206ffc606adSIan Rogers 'cpu_clk_unhalted.core': 'event=0x3c,period=2000003', 207ffc606adSIan Rogers 'cpu_clk_unhalted.thread_any': 'event=0x3c,any=1,period=2000003', 208ffc606adSIan Rogers } 209ffc606adSIan Rogers if not name: 210ffc606adSIan Rogers return None 211ffc606adSIan Rogers if name.lower() in fixed: 212ffc606adSIan Rogers return fixed[name.lower()] 213ffc606adSIan Rogers return event 214ffc606adSIan Rogers 215e1e19d05SIan Rogers def unit_to_pmu(unit: str) -> Optional[str]: 216ffc606adSIan Rogers """Convert a JSON Unit to Linux PMU name.""" 217ffc606adSIan Rogers if not unit: 218ffc606adSIan Rogers return None 219ffc606adSIan Rogers # Comment brought over from jevents.c: 220ffc606adSIan Rogers # it's not realistic to keep adding these, we need something more scalable ... 221ffc606adSIan Rogers table = { 222ffc606adSIan Rogers 'CBO': 'uncore_cbox', 223ffc606adSIan Rogers 'QPI LL': 'uncore_qpi', 224ffc606adSIan Rogers 'SBO': 'uncore_sbox', 225ffc606adSIan Rogers 'iMPH-U': 'uncore_arb', 226ffc606adSIan Rogers 'CPU-M-CF': 'cpum_cf', 227ffc606adSIan Rogers 'CPU-M-SF': 'cpum_sf', 228e0b23af8SThomas Richter 'PAI-CRYPTO' : 'pai_crypto', 229ffc606adSIan Rogers 'UPI LL': 'uncore_upi', 230ffc606adSIan Rogers 'hisi_sicl,cpa': 'hisi_sicl,cpa', 231ffc606adSIan Rogers 'hisi_sccl,ddrc': 'hisi_sccl,ddrc', 232ffc606adSIan Rogers 'hisi_sccl,hha': 'hisi_sccl,hha', 233ffc606adSIan Rogers 'hisi_sccl,l3c': 'hisi_sccl,l3c', 234ffc606adSIan Rogers 'imx8_ddr': 'imx8_ddr', 235ffc606adSIan Rogers 'L3PMC': 'amd_l3', 236ffc606adSIan Rogers 'DFPMC': 'amd_df', 237ffc606adSIan Rogers 'cpu_core': 'cpu_core', 238ffc606adSIan Rogers 'cpu_atom': 'cpu_atom', 239ffc606adSIan Rogers } 240ffc606adSIan Rogers return table[unit] if unit in table else f'uncore_{unit.lower()}' 241ffc606adSIan Rogers 242ffc606adSIan Rogers eventcode = 0 243ffc606adSIan Rogers if 'EventCode' in jd: 244ffc606adSIan Rogers eventcode = int(jd['EventCode'].split(',', 1)[0], 0) 245ffc606adSIan Rogers if 'ExtSel' in jd: 246ffc606adSIan Rogers eventcode |= int(jd['ExtSel']) << 8 247ffc606adSIan Rogers configcode = int(jd['ConfigCode'], 0) if 'ConfigCode' in jd else None 248ffc606adSIan Rogers self.name = jd['EventName'].lower() if 'EventName' in jd else None 2497b2f844cSIan Rogers self.topic = '' 250ffc606adSIan Rogers self.compat = jd.get('Compat') 251ffc606adSIan Rogers self.desc = fixdesc(jd.get('BriefDescription')) 252ffc606adSIan Rogers self.long_desc = fixdesc(jd.get('PublicDescription')) 253ffc606adSIan Rogers precise = jd.get('PEBS') 254ffc606adSIan Rogers msr = lookup_msr(jd.get('MSRIndex')) 255ffc606adSIan Rogers msrval = jd.get('MSRValue') 256ffc606adSIan Rogers extra_desc = '' 257ffc606adSIan Rogers if 'Data_LA' in jd: 258ffc606adSIan Rogers extra_desc += ' Supports address when precise' 259ffc606adSIan Rogers if 'Errata' in jd: 260ffc606adSIan Rogers extra_desc += '.' 261ffc606adSIan Rogers if 'Errata' in jd: 262ffc606adSIan Rogers extra_desc += ' Spec update: ' + jd['Errata'] 263ffc606adSIan Rogers self.pmu = unit_to_pmu(jd.get('Unit')) 264ffc606adSIan Rogers filter = jd.get('Filter') 265ffc606adSIan Rogers self.unit = jd.get('ScaleUnit') 266ffc606adSIan Rogers self.perpkg = jd.get('PerPkg') 267ffc606adSIan Rogers self.aggr_mode = convert_aggr_mode(jd.get('AggregationMode')) 268ffc606adSIan Rogers self.deprecated = jd.get('Deprecated') 269ffc606adSIan Rogers self.metric_name = jd.get('MetricName') 270ffc606adSIan Rogers self.metric_group = jd.get('MetricGroup') 271ffc606adSIan Rogers self.metric_constraint = jd.get('MetricConstraint') 272*40769665SIan Rogers self.metric_expr = None 273*40769665SIan Rogers if 'MetricExpr' in jd: 274*40769665SIan Rogers self.metric_expr = metric.ParsePerfJson(jd['MetricExpr']).Simplify() 275*40769665SIan Rogers 276ffc606adSIan Rogers arch_std = jd.get('ArchStdEvent') 277e1e19d05SIan Rogers if precise and self.desc and '(Precise Event)' not in self.desc: 278ffc606adSIan Rogers extra_desc += ' (Must be precise)' if precise == '2' else (' (Precise ' 279ffc606adSIan Rogers 'event)') 280ffc606adSIan Rogers event = f'config={llx(configcode)}' if configcode is not None else f'event={llx(eventcode)}' 281ffc606adSIan Rogers event_fields = [ 282ffc606adSIan Rogers ('AnyThread', 'any='), 283ffc606adSIan Rogers ('PortMask', 'ch_mask='), 284ffc606adSIan Rogers ('CounterMask', 'cmask='), 285ffc606adSIan Rogers ('EdgeDetect', 'edge='), 286ffc606adSIan Rogers ('FCMask', 'fc_mask='), 287ffc606adSIan Rogers ('Invert', 'inv='), 288ffc606adSIan Rogers ('SampleAfterValue', 'period='), 289ffc606adSIan Rogers ('UMask', 'umask='), 290ffc606adSIan Rogers ] 291ffc606adSIan Rogers for key, value in event_fields: 292ffc606adSIan Rogers if key in jd and jd[key] != '0': 293ffc606adSIan Rogers event += ',' + value + jd[key] 294ffc606adSIan Rogers if filter: 295ffc606adSIan Rogers event += f',{filter}' 296ffc606adSIan Rogers if msr: 297ffc606adSIan Rogers event += f',{msr}{msrval}' 298ffc606adSIan Rogers if self.desc and extra_desc: 299ffc606adSIan Rogers self.desc += extra_desc 300ffc606adSIan Rogers if self.long_desc and extra_desc: 301ffc606adSIan Rogers self.long_desc += extra_desc 302ffc606adSIan Rogers if self.pmu: 303ffc606adSIan Rogers if self.desc and not self.desc.endswith('. '): 304ffc606adSIan Rogers self.desc += '. ' 305ffc606adSIan Rogers self.desc = (self.desc if self.desc else '') + ('Unit: ' + self.pmu + ' ') 306ffc606adSIan Rogers if arch_std and arch_std.lower() in _arch_std_events: 307ffc606adSIan Rogers event = _arch_std_events[arch_std.lower()].event 308ffc606adSIan Rogers # Copy from the architecture standard event to self for undefined fields. 309ffc606adSIan Rogers for attr, value in _arch_std_events[arch_std.lower()].__dict__.items(): 310ffc606adSIan Rogers if hasattr(self, attr) and not getattr(self, attr): 311ffc606adSIan Rogers setattr(self, attr, value) 312ffc606adSIan Rogers 313ffc606adSIan Rogers self.event = real_event(self.name, event) 314ffc606adSIan Rogers 315ffc606adSIan Rogers def __repr__(self) -> str: 316ffc606adSIan Rogers """String representation primarily for debugging.""" 317ffc606adSIan Rogers s = '{\n' 318ffc606adSIan Rogers for attr, value in self.__dict__.items(): 319ffc606adSIan Rogers if value: 320ffc606adSIan Rogers s += f'\t{attr} = {value},\n' 321ffc606adSIan Rogers return s + '}' 322ffc606adSIan Rogers 3239118259cSIan Rogers def build_c_string(self) -> str: 3249118259cSIan Rogers s = '' 3259118259cSIan Rogers for attr in _json_event_attributes: 3269118259cSIan Rogers x = getattr(self, attr) 327*40769665SIan Rogers if x and attr == 'metric_expr': 328*40769665SIan Rogers # Convert parsed metric expressions into a string. Slashes 329*40769665SIan Rogers # must be doubled in the file. 330*40769665SIan Rogers x = x.ToPerfJson().replace('\\', '\\\\') 3319118259cSIan Rogers s += f'{x}\\000' if x else '\\000' 3329118259cSIan Rogers return s 3339118259cSIan Rogers 3347b2f844cSIan Rogers def to_c_string(self) -> str: 335ffc606adSIan Rogers """Representation of the event as a C struct initializer.""" 336ffc606adSIan Rogers 3379118259cSIan Rogers s = self.build_c_string() 3389118259cSIan Rogers return f'{{ { _bcs.offsets[s] } }}, /* {s} */\n' 339ffc606adSIan Rogers 340ffc606adSIan Rogers 3417b2f844cSIan Rogersdef read_json_events(path: str, topic: str) -> Sequence[JsonEvent]: 342ffc606adSIan Rogers """Read json events from the specified file.""" 343ee2ce6fdSIan Rogers 344ee2ce6fdSIan Rogers try: 3457b2f844cSIan Rogers result = json.load(open(path), object_hook=JsonEvent) 346ee2ce6fdSIan Rogers except BaseException as err: 347ee2ce6fdSIan Rogers print(f"Exception processing {path}") 348ee2ce6fdSIan Rogers raise 3497b2f844cSIan Rogers for event in result: 3507b2f844cSIan Rogers event.topic = topic 3517b2f844cSIan Rogers return result 352ffc606adSIan Rogers 353ffc606adSIan Rogersdef preprocess_arch_std_files(archpath: str) -> None: 354ffc606adSIan Rogers """Read in all architecture standard events.""" 355ffc606adSIan Rogers global _arch_std_events 356ffc606adSIan Rogers for item in os.scandir(archpath): 357ffc606adSIan Rogers if item.is_file() and item.name.endswith('.json'): 3587b2f844cSIan Rogers for event in read_json_events(item.path, topic=''): 359ffc606adSIan Rogers if event.name: 360ffc606adSIan Rogers _arch_std_events[event.name.lower()] = event 361ffc606adSIan Rogers 362ffc606adSIan Rogers 363ffc606adSIan Rogersdef print_events_table_prefix(tblname: str) -> None: 364ffc606adSIan Rogers """Called when a new events table is started.""" 365ffc606adSIan Rogers global _close_table 366ffc606adSIan Rogers if _close_table: 367ffc606adSIan Rogers raise IOError('Printing table prefix but last table has no suffix') 3689118259cSIan Rogers _args.output_file.write(f'static const struct compact_pmu_event {tblname}[] = {{\n') 369ffc606adSIan Rogers _close_table = True 370ffc606adSIan Rogers 371ffc606adSIan Rogers 3727b2f844cSIan Rogersdef add_events_table_entries(item: os.DirEntry, topic: str) -> None: 3737b2f844cSIan Rogers """Add contents of file to _pending_events table.""" 374ffc606adSIan Rogers if not _close_table: 375ffc606adSIan Rogers raise IOError('Table entries missing prefix') 3767b2f844cSIan Rogers for e in read_json_events(item.path, topic): 3777b2f844cSIan Rogers _pending_events.append(e) 378ffc606adSIan Rogers 379ffc606adSIan Rogers 380ffc606adSIan Rogersdef print_events_table_suffix() -> None: 381ffc606adSIan Rogers """Optionally close events table.""" 3827b2f844cSIan Rogers 3839118259cSIan Rogers def event_cmp_key(j: JsonEvent) -> Tuple[bool, str, str, str, str]: 3849118259cSIan Rogers def fix_none(s: Optional[str]) -> str: 3857b2f844cSIan Rogers if s is None: 3867b2f844cSIan Rogers return '' 3877b2f844cSIan Rogers return s 3887b2f844cSIan Rogers 3899118259cSIan Rogers return (j.desc is not None, fix_none(j.topic), fix_none(j.name), fix_none(j.pmu), 3907b2f844cSIan Rogers fix_none(j.metric_name)) 3917b2f844cSIan Rogers 392ffc606adSIan Rogers global _close_table 3937b2f844cSIan Rogers if not _close_table: 3947b2f844cSIan Rogers return 3957b2f844cSIan Rogers 3967b2f844cSIan Rogers global _pending_events 3977b2f844cSIan Rogers for event in sorted(_pending_events, key=event_cmp_key): 3987b2f844cSIan Rogers _args.output_file.write(event.to_c_string()) 3997b2f844cSIan Rogers _pending_events = [] 4007b2f844cSIan Rogers 4019118259cSIan Rogers _args.output_file.write('};\n\n') 402ffc606adSIan Rogers _close_table = False 403ffc606adSIan Rogers 4049118259cSIan Rogersdef get_topic(topic: str) -> str: 4059118259cSIan Rogers if topic.endswith('metrics.json'): 4069118259cSIan Rogers return 'metrics' 4079118259cSIan Rogers return removesuffix(topic, '.json').replace('-', ' ') 4089118259cSIan Rogers 4099118259cSIan Rogersdef preprocess_one_file(parents: Sequence[str], item: os.DirEntry) -> None: 4109118259cSIan Rogers 4119118259cSIan Rogers if item.is_dir(): 4129118259cSIan Rogers return 4139118259cSIan Rogers 4149118259cSIan Rogers # base dir or too deep 4159118259cSIan Rogers level = len(parents) 4169118259cSIan Rogers if level == 0 or level > 4: 4179118259cSIan Rogers return 4189118259cSIan Rogers 4199118259cSIan Rogers # Ignore other directories. If the file name does not have a .json 4209118259cSIan Rogers # extension, ignore it. It could be a readme.txt for instance. 4219118259cSIan Rogers if not item.is_file() or not item.name.endswith('.json'): 4229118259cSIan Rogers return 4239118259cSIan Rogers 4249118259cSIan Rogers topic = get_topic(item.name) 4259118259cSIan Rogers for event in read_json_events(item.path, topic): 4269118259cSIan Rogers _bcs.add(event.build_c_string()) 427ffc606adSIan Rogers 428ffc606adSIan Rogersdef process_one_file(parents: Sequence[str], item: os.DirEntry) -> None: 429ffc606adSIan Rogers """Process a JSON file during the main walk.""" 430ffc606adSIan Rogers global _sys_event_tables 431ffc606adSIan Rogers 432ffc606adSIan Rogers def is_leaf_dir(path: str) -> bool: 433ffc606adSIan Rogers for item in os.scandir(path): 434ffc606adSIan Rogers if item.is_dir(): 435ffc606adSIan Rogers return False 436ffc606adSIan Rogers return True 437ffc606adSIan Rogers 438ffc606adSIan Rogers # model directory, reset topic 439ffc606adSIan Rogers if item.is_dir() and is_leaf_dir(item.path): 440ffc606adSIan Rogers print_events_table_suffix() 441ffc606adSIan Rogers 442ffc606adSIan Rogers tblname = file_name_to_table_name(parents, item.name) 443ffc606adSIan Rogers if item.name == 'sys': 444ffc606adSIan Rogers _sys_event_tables.append(tblname) 445ffc606adSIan Rogers print_events_table_prefix(tblname) 446ffc606adSIan Rogers return 447ffc606adSIan Rogers 448ffc606adSIan Rogers # base dir or too deep 449ffc606adSIan Rogers level = len(parents) 450ffc606adSIan Rogers if level == 0 or level > 4: 451ffc606adSIan Rogers return 452ffc606adSIan Rogers 453ffc606adSIan Rogers # Ignore other directories. If the file name does not have a .json 454ffc606adSIan Rogers # extension, ignore it. It could be a readme.txt for instance. 455ffc606adSIan Rogers if not item.is_file() or not item.name.endswith('.json'): 456ffc606adSIan Rogers return 457ffc606adSIan Rogers 4587b2f844cSIan Rogers add_events_table_entries(item, get_topic(item.name)) 459ffc606adSIan Rogers 460ffc606adSIan Rogers 461099b157cSIan Rogersdef print_mapping_table(archs: Sequence[str]) -> None: 462ffc606adSIan Rogers """Read the mapfile and generate the struct from cpuid string to event table.""" 46329be2fe0SIan Rogers _args.output_file.write(""" 4641ba3752aSIan Rogers/* Struct used to make the PMU event table implementation opaque to callers. */ 4651ba3752aSIan Rogersstruct pmu_events_table { 4669118259cSIan Rogers const struct compact_pmu_event *entries; 4679118259cSIan Rogers size_t length; 4681ba3752aSIan Rogers}; 4691ba3752aSIan Rogers 47029be2fe0SIan Rogers/* 47129be2fe0SIan Rogers * Map a CPU to its table of PMU events. The CPU is identified by the 47229be2fe0SIan Rogers * cpuid field, which is an arch-specific identifier for the CPU. 47329be2fe0SIan Rogers * The identifier specified in tools/perf/pmu-events/arch/xxx/mapfile 47429be2fe0SIan Rogers * must match the get_cpuid_str() in tools/perf/arch/xxx/util/header.c) 47529be2fe0SIan Rogers * 47629be2fe0SIan Rogers * The cpuid can contain any character other than the comma. 47729be2fe0SIan Rogers */ 47829be2fe0SIan Rogersstruct pmu_events_map { 47929be2fe0SIan Rogers const char *arch; 48029be2fe0SIan Rogers const char *cpuid; 4811ba3752aSIan Rogers struct pmu_events_table table; 48229be2fe0SIan Rogers}; 48329be2fe0SIan Rogers 48429be2fe0SIan Rogers/* 48529be2fe0SIan Rogers * Global table mapping each known CPU for the architecture to its 48629be2fe0SIan Rogers * table of PMU events. 48729be2fe0SIan Rogers */ 48829be2fe0SIan Rogersconst struct pmu_events_map pmu_events_map[] = { 48929be2fe0SIan Rogers""") 490099b157cSIan Rogers for arch in archs: 491099b157cSIan Rogers if arch == 'test': 492ffc606adSIan Rogers _args.output_file.write("""{ 493099b157cSIan Rogers\t.arch = "testarch", 494ffc606adSIan Rogers\t.cpuid = "testcpu", 4959118259cSIan Rogers\t.table = { 4969118259cSIan Rogers\t.entries = pme_test_soc_cpu, 4979118259cSIan Rogers\t.length = ARRAY_SIZE(pme_test_soc_cpu), 4989118259cSIan Rogers\t} 499ffc606adSIan Rogers}, 500099b157cSIan Rogers""") 501099b157cSIan Rogers else: 502099b157cSIan Rogers with open(f'{_args.starting_dir}/{arch}/mapfile.csv') as csvfile: 503099b157cSIan Rogers table = csv.reader(csvfile) 504099b157cSIan Rogers first = True 505099b157cSIan Rogers for row in table: 506099b157cSIan Rogers # Skip the first row or any row beginning with #. 507099b157cSIan Rogers if not first and len(row) > 0 and not row[0].startswith('#'): 508099b157cSIan Rogers tblname = file_name_to_table_name([], row[2].replace('/', '_')) 509099b157cSIan Rogers cpuid = row[0].replace('\\', '\\\\') 510099b157cSIan Rogers _args.output_file.write(f"""{{ 511099b157cSIan Rogers\t.arch = "{arch}", 512099b157cSIan Rogers\t.cpuid = "{cpuid}", 5139118259cSIan Rogers\t.table = {{ 5149118259cSIan Rogers\t\t.entries = {tblname}, 5159118259cSIan Rogers\t\t.length = ARRAY_SIZE({tblname}) 5169118259cSIan Rogers\t}} 517099b157cSIan Rogers}}, 518099b157cSIan Rogers""") 519099b157cSIan Rogers first = False 520099b157cSIan Rogers 521099b157cSIan Rogers _args.output_file.write("""{ 522099b157cSIan Rogers\t.arch = 0, 523ffc606adSIan Rogers\t.cpuid = 0, 5249118259cSIan Rogers\t.table = { 0, 0 }, 525099b157cSIan Rogers} 526ffc606adSIan Rogers}; 527ffc606adSIan Rogers""") 528ffc606adSIan Rogers 529ffc606adSIan Rogers 530ffc606adSIan Rogersdef print_system_mapping_table() -> None: 531ffc606adSIan Rogers """C struct mapping table array for tables from /sys directories.""" 5322519db2aSIan Rogers _args.output_file.write(""" 5332519db2aSIan Rogersstruct pmu_sys_events { 5342519db2aSIan Rogers\tconst char *name; 5351ba3752aSIan Rogers\tstruct pmu_events_table table; 5362519db2aSIan Rogers}; 5372519db2aSIan Rogers 5382519db2aSIan Rogersstatic const struct pmu_sys_events pmu_sys_event_tables[] = { 5392519db2aSIan Rogers""") 540ffc606adSIan Rogers for tblname in _sys_event_tables: 541ffc606adSIan Rogers _args.output_file.write(f"""\t{{ 5429118259cSIan Rogers\t\t.table = {{ 5439118259cSIan Rogers\t\t\t.entries = {tblname}, 5449118259cSIan Rogers\t\t\t.length = ARRAY_SIZE({tblname}) 5459118259cSIan Rogers\t\t}}, 546ffc606adSIan Rogers\t\t.name = \"{tblname}\", 547ffc606adSIan Rogers\t}}, 548ffc606adSIan Rogers""") 549ffc606adSIan Rogers _args.output_file.write("""\t{ 5509118259cSIan Rogers\t\t.table = { 0, 0 } 551ffc606adSIan Rogers\t}, 552ffc606adSIan Rogers}; 5532519db2aSIan Rogers 5549118259cSIan Rogersstatic void decompress(int offset, struct pmu_event *pe) 5559118259cSIan Rogers{ 5569118259cSIan Rogers\tconst char *p = &big_c_string[offset]; 5579118259cSIan Rogers""") 5589118259cSIan Rogers for attr in _json_event_attributes: 5599118259cSIan Rogers _args.output_file.write(f""" 5609118259cSIan Rogers\tpe->{attr} = (*p == '\\0' ? NULL : p); 5619118259cSIan Rogers""") 5629118259cSIan Rogers if attr == _json_event_attributes[-1]: 5639118259cSIan Rogers continue 5649118259cSIan Rogers _args.output_file.write('\twhile (*p++);') 5659118259cSIan Rogers _args.output_file.write("""} 5669118259cSIan Rogers 5679118259cSIan Rogersint pmu_events_table_for_each_event(const struct pmu_events_table *table, 5689118259cSIan Rogers pmu_event_iter_fn fn, 569660842e4SIan Rogers void *data) 570660842e4SIan Rogers{ 5719118259cSIan Rogers for (size_t i = 0; i < table->length; i++) { 5729118259cSIan Rogers struct pmu_event pe; 5739118259cSIan Rogers int ret; 574660842e4SIan Rogers 5759118259cSIan Rogers decompress(table->entries[i].offset, &pe); 5769118259cSIan Rogers ret = fn(&pe, table, data); 577660842e4SIan Rogers if (ret) 578660842e4SIan Rogers return ret; 579660842e4SIan Rogers } 580660842e4SIan Rogers return 0; 581660842e4SIan Rogers} 582660842e4SIan Rogers 5831ba3752aSIan Rogersconst struct pmu_events_table *perf_pmu__find_table(struct perf_pmu *pmu) 58429be2fe0SIan Rogers{ 5851ba3752aSIan Rogers const struct pmu_events_table *table = NULL; 58629be2fe0SIan Rogers char *cpuid = perf_pmu__getcpuid(pmu); 58729be2fe0SIan Rogers int i; 58829be2fe0SIan Rogers 58929be2fe0SIan Rogers /* on some platforms which uses cpus map, cpuid can be NULL for 59029be2fe0SIan Rogers * PMUs other than CORE PMUs. 59129be2fe0SIan Rogers */ 59229be2fe0SIan Rogers if (!cpuid) 59329be2fe0SIan Rogers return NULL; 59429be2fe0SIan Rogers 59529be2fe0SIan Rogers i = 0; 59629be2fe0SIan Rogers for (;;) { 59729be2fe0SIan Rogers const struct pmu_events_map *map = &pmu_events_map[i++]; 5981ba3752aSIan Rogers if (!map->arch) 59929be2fe0SIan Rogers break; 60029be2fe0SIan Rogers 60129be2fe0SIan Rogers if (!strcmp_cpuid_str(map->cpuid, cpuid)) { 6021ba3752aSIan Rogers table = &map->table; 60329be2fe0SIan Rogers break; 60429be2fe0SIan Rogers } 60529be2fe0SIan Rogers } 60629be2fe0SIan Rogers free(cpuid); 60729be2fe0SIan Rogers return table; 60829be2fe0SIan Rogers} 60929be2fe0SIan Rogers 6101ba3752aSIan Rogersconst struct pmu_events_table *find_core_events_table(const char *arch, const char *cpuid) 61129be2fe0SIan Rogers{ 61229be2fe0SIan Rogers for (const struct pmu_events_map *tables = &pmu_events_map[0]; 6131ba3752aSIan Rogers tables->arch; 61429be2fe0SIan Rogers tables++) { 61529be2fe0SIan Rogers if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid)) 6161ba3752aSIan Rogers return &tables->table; 61729be2fe0SIan Rogers } 61829be2fe0SIan Rogers return NULL; 61929be2fe0SIan Rogers} 62029be2fe0SIan Rogers 62129be2fe0SIan Rogersint pmu_for_each_core_event(pmu_event_iter_fn fn, void *data) 62229be2fe0SIan Rogers{ 62329be2fe0SIan Rogers for (const struct pmu_events_map *tables = &pmu_events_map[0]; 6241ba3752aSIan Rogers tables->arch; 62529be2fe0SIan Rogers tables++) { 6261ba3752aSIan Rogers int ret = pmu_events_table_for_each_event(&tables->table, fn, data); 62729be2fe0SIan Rogers 62829be2fe0SIan Rogers if (ret) 62929be2fe0SIan Rogers return ret; 63029be2fe0SIan Rogers } 63129be2fe0SIan Rogers return 0; 63229be2fe0SIan Rogers} 63329be2fe0SIan Rogers 6341ba3752aSIan Rogersconst struct pmu_events_table *find_sys_events_table(const char *name) 6352519db2aSIan Rogers{ 6362519db2aSIan Rogers for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; 6372519db2aSIan Rogers tables->name; 6382519db2aSIan Rogers tables++) { 6392519db2aSIan Rogers if (!strcmp(tables->name, name)) 6401ba3752aSIan Rogers return &tables->table; 6412519db2aSIan Rogers } 6422519db2aSIan Rogers return NULL; 6432519db2aSIan Rogers} 6442519db2aSIan Rogers 6452519db2aSIan Rogersint pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data) 6462519db2aSIan Rogers{ 6472519db2aSIan Rogers for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; 6482519db2aSIan Rogers tables->name; 6492519db2aSIan Rogers tables++) { 6501ba3752aSIan Rogers int ret = pmu_events_table_for_each_event(&tables->table, fn, data); 6512519db2aSIan Rogers 6522519db2aSIan Rogers if (ret) 6532519db2aSIan Rogers return ret; 6542519db2aSIan Rogers } 6552519db2aSIan Rogers return 0; 6562519db2aSIan Rogers} 657ffc606adSIan Rogers""") 658ffc606adSIan Rogers 659ffc606adSIan Rogers 660ffc606adSIan Rogersdef main() -> None: 661ffc606adSIan Rogers global _args 662ffc606adSIan Rogers 663ffc606adSIan Rogers def dir_path(path: str) -> str: 664ffc606adSIan Rogers """Validate path is a directory for argparse.""" 665ffc606adSIan Rogers if os.path.isdir(path): 666ffc606adSIan Rogers return path 667ffc606adSIan Rogers raise argparse.ArgumentTypeError(f'\'{path}\' is not a valid directory') 668ffc606adSIan Rogers 669ffc606adSIan Rogers def ftw(path: str, parents: Sequence[str], 670ffc606adSIan Rogers action: Callable[[Sequence[str], os.DirEntry], None]) -> None: 671ffc606adSIan Rogers """Replicate the directory/file walking behavior of C's file tree walk.""" 672ffc606adSIan Rogers for item in os.scandir(path): 673ffc606adSIan Rogers action(parents, item) 674ffc606adSIan Rogers if item.is_dir(): 675ffc606adSIan Rogers ftw(item.path, parents + [item.name], action) 676ffc606adSIan Rogers 677ffc606adSIan Rogers ap = argparse.ArgumentParser() 678ffc606adSIan Rogers ap.add_argument('arch', help='Architecture name like x86') 679ffc606adSIan Rogers ap.add_argument( 680ffc606adSIan Rogers 'starting_dir', 681ffc606adSIan Rogers type=dir_path, 682ffc606adSIan Rogers help='Root of tree containing architecture directories containing json files' 683ffc606adSIan Rogers ) 684ffc606adSIan Rogers ap.add_argument( 6859118259cSIan Rogers 'output_file', type=argparse.FileType('w', encoding='utf-8'), nargs='?', default=sys.stdout) 686ffc606adSIan Rogers _args = ap.parse_args() 687ffc606adSIan Rogers 6882519db2aSIan Rogers _args.output_file.write(""" 6892519db2aSIan Rogers#include "pmu-events/pmu-events.h" 69029be2fe0SIan Rogers#include "util/header.h" 69129be2fe0SIan Rogers#include "util/pmu.h" 6922519db2aSIan Rogers#include <string.h> 6932519db2aSIan Rogers#include <stddef.h> 6942519db2aSIan Rogers 6959118259cSIan Rogersstruct compact_pmu_event { 6969118259cSIan Rogers int offset; 6979118259cSIan Rogers}; 6989118259cSIan Rogers 6992519db2aSIan Rogers""") 700099b157cSIan Rogers archs = [] 701099b157cSIan Rogers for item in os.scandir(_args.starting_dir): 702099b157cSIan Rogers if not item.is_dir(): 703099b157cSIan Rogers continue 704099b157cSIan Rogers if item.name == _args.arch or _args.arch == 'all' or item.name == 'test': 705099b157cSIan Rogers archs.append(item.name) 706099b157cSIan Rogers 707099b157cSIan Rogers if len(archs) < 2: 708099b157cSIan Rogers raise IOError(f'Missing architecture directory \'{_args.arch}\'') 709099b157cSIan Rogers 710099b157cSIan Rogers archs.sort() 711099b157cSIan Rogers for arch in archs: 712099b157cSIan Rogers arch_path = f'{_args.starting_dir}/{arch}' 713ffc606adSIan Rogers preprocess_arch_std_files(arch_path) 7149118259cSIan Rogers ftw(arch_path, [], preprocess_one_file) 7159118259cSIan Rogers 7169118259cSIan Rogers _bcs.compute() 7179118259cSIan Rogers _args.output_file.write('static const char *const big_c_string =\n') 7189118259cSIan Rogers for s in _bcs.big_string: 7199118259cSIan Rogers _args.output_file.write(s) 7209118259cSIan Rogers _args.output_file.write(';\n\n') 7219118259cSIan Rogers for arch in archs: 7229118259cSIan Rogers arch_path = f'{_args.starting_dir}/{arch}' 723ffc606adSIan Rogers ftw(arch_path, [], process_one_file) 724ffc606adSIan Rogers print_events_table_suffix() 725ffc606adSIan Rogers 726099b157cSIan Rogers print_mapping_table(archs) 727ffc606adSIan Rogers print_system_mapping_table() 728ffc606adSIan Rogers 729ffc606adSIan Rogers 730ffc606adSIan Rogersif __name__ == '__main__': 731ffc606adSIan Rogers main() 732