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