xref: /openbmc/linux/tools/perf/pmu-events/jevents.py (revision 40769665)
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