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