#!/usr/bin/env python3 # # Copyright (C) 2015 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # import dataclasses from dataclasses import dataclass import json import sys from typing import List def gen_event_type_entry_str(event_type_name, event_type, event_config, description='', limited_arch=''): return '{"%s", %s, %s, "%s", "%s"},\n' % ( event_type_name, event_type, event_config, description, limited_arch) def gen_hardware_events(): hardware_configs = ["cpu-cycles", "instructions", "cache-references", "cache-misses", "branch-instructions", "branch-misses", "bus-cycles", "stalled-cycles-frontend", "stalled-cycles-backend", ] generated_str = "" for config in hardware_configs: event_type_name = config event_config = "PERF_COUNT_HW_" + config.replace('-', '_').upper() generated_str += gen_event_type_entry_str( event_type_name, "PERF_TYPE_HARDWARE", event_config) return generated_str def gen_software_events(): software_configs = ["cpu-clock", "task-clock", "page-faults", "context-switches", "cpu-migrations", ["minor-faults", "PERF_COUNT_SW_PAGE_FAULTS_MIN"], ["major-faults", "PERF_COUNT_SW_PAGE_FAULTS_MAJ"], "alignment-faults", "emulation-faults", ] generated_str = "" for config in software_configs: if isinstance(config, list): event_type_name = config[0] event_config = config[1] else: event_type_name = config event_config = "PERF_COUNT_SW_" + config.replace('-', '_').upper() generated_str += gen_event_type_entry_str( event_type_name, "PERF_TYPE_SOFTWARE", event_config) return generated_str def gen_hw_cache_events(): hw_cache_types = [["L1-dcache", "PERF_COUNT_HW_CACHE_L1D"], ["L1-icache", "PERF_COUNT_HW_CACHE_L1I"], ["LLC", "PERF_COUNT_HW_CACHE_LL"], ["dTLB", "PERF_COUNT_HW_CACHE_DTLB"], ["iTLB", "PERF_COUNT_HW_CACHE_ITLB"], ["branch", "PERF_COUNT_HW_CACHE_BPU"], ["node", "PERF_COUNT_HW_CACHE_NODE"], ] hw_cache_ops = [["loads", "load", "PERF_COUNT_HW_CACHE_OP_READ"], ["stores", "store", "PERF_COUNT_HW_CACHE_OP_WRITE"], ["prefetches", "prefetch", "PERF_COUNT_HW_CACHE_OP_PREFETCH"], ] hw_cache_op_results = [["accesses", "PERF_COUNT_HW_CACHE_RESULT_ACCESS"], ["misses", "PERF_COUNT_HW_CACHE_RESULT_MISS"], ] generated_str = "" for (type_name, type_config) in hw_cache_types: for (op_name_access, op_name_miss, op_config) in hw_cache_ops: for (result_name, result_config) in hw_cache_op_results: if result_name == "accesses": event_type_name = type_name + '-' + op_name_access else: event_type_name = type_name + '-' + \ op_name_miss + '-' + result_name event_config = "((%s) | (%s << 8) | (%s << 16))" % ( type_config, op_config, result_config) generated_str += gen_event_type_entry_str( event_type_name, "PERF_TYPE_HW_CACHE", event_config) return generated_str @dataclass class RawEvent: number: int name: str desc: str limited_arch: str @dataclass class CpuModel: name: str implementer: int partnum: int mvendorid: int marchid: str mimpid: str supported_raw_events: list[int] = dataclasses.field(default_factory=list) class ArchData: def __init__(self, arch: str): self.arch = arch self.events: List[RawEvent] = [] self.cpus: List[CpuModel] = [] def load_from_json_data(self, data) -> None: # Load common events for event in data['events']: number = int(event[0], 16) name = 'raw-' + event[1].lower().replace('_', '-') desc = event[2] self.events.append(RawEvent(number, name, desc, self.arch)) for cpu in data['cpus']: cpu_name = cpu['name'].lower().replace('_', '-') cpu_model = CpuModel( cpu['name'], int(cpu.get('implementer', '0'), 16), int(cpu.get('partnum', '0'), 16), int(cpu.get('mvendorid', '0'), 16), cpu.get('marchid', '0'), cpu.get('mimpid', '0'), [] ) cpu_index = len(self.cpus) self.cpus.append(cpu_model) # Load common events supported in this cpu model. for number in cpu['common_events']: number = int(number, 16) event = self.get_event(number) cpu_model.supported_raw_events.append(number) # Load cpu specific events supported in this cpu model. if 'implementation_defined_events' in cpu: for event in cpu['implementation_defined_events']: number = int(event[0], 16) name = ('raw-' + cpu_name + '-' + event[1]).lower().replace('_', '-') desc = event[2] limited_arch = self.arch + ':' + cpu['name'] self.events.append(RawEvent(number, name, desc, limited_arch)) cpu_model.supported_raw_events.append(number) def get_event(self, event_number: int) -> RawEvent: for event in self.events: if event.number == event_number: return event raise Exception(f'no event for event number {event_number}') class X86ArchData: def __init__(self, arch: str): self.arch = arch self.events: List[RawEvent] = [] def load_from_json_data(self, data) -> None: for event in data['events']: number = int(event[0], 16) name = event[1] desc = event[2] self.events.append(RawEvent(number, name, desc, self.arch)) class RawEventGenerator: def __init__(self, event_table_file: str): with open(event_table_file, 'r') as fh: event_table = json.load(fh) self.arm64_data = ArchData('arm64') self.arm64_data.load_from_json_data(event_table['arm64']) self.riscv64_data = ArchData('riscv64') self.riscv64_data.load_from_json_data(event_table['riscv64']) self.x86_intel_data = X86ArchData('x86-intel') self.x86_intel_data.load_from_json_data(event_table['x86-intel']) self.x86_amd_data = X86ArchData('x86-amd') self.x86_amd_data.load_from_json_data(event_table['x86-amd']) def generate_raw_events(self) -> str: def generate_event_entries(events, guard) -> list: lines = [] for event in events: lines.append(gen_event_type_entry_str(event.name, 'PERF_TYPE_RAW', '0x%x' % event.number, event.desc, event.limited_arch)) return guard(''.join(lines)) lines_arm64 = generate_event_entries(self.arm64_data.events, self.add_arm_guard) lines_riscv64 = generate_event_entries(self.riscv64_data.events, self.add_riscv_guard) lines_x86_intel = generate_event_entries(self.x86_intel_data.events, self.add_x86_guard) lines_x86_amd = generate_event_entries(self.x86_amd_data.events, self.add_x86_guard) return lines_arm64 + lines_riscv64 + lines_x86_intel + lines_x86_amd def generate_cpu_support_events(self) -> str: def generate_cpu_events(data, guard) -> str: lines = [] for cpu in data: event_list = ', '.join('0x%x' % number for number in cpu.supported_raw_events) lines.append('{"%s", {%s}},' % (cpu.name, event_list)) return guard('\n'.join(lines)) text = f""" // Map from cpu model to raw events supported on that cpu. std::unordered_map> cpu_supported_raw_events = {{ {generate_cpu_events(self.arm64_data.cpus, self.add_arm_guard)} {generate_cpu_events(self.riscv64_data.cpus, self.add_riscv_guard)} }};\n """ return text def generate_cpu_models(self) -> str: def generate_model(data, map_type, map_key_type, id_func) -> str: lines = [f'std::{map_type}<{map_key_type}, std::string> cpuid_to_name = {{'] for cpu in data: cpu_id = id_func(cpu) lines.append(f'{{{cpu_id}, "{cpu.name}"}},') lines.append('};') return '\n'.join(lines) arm64_model = generate_model( self.arm64_data.cpus, "unordered_map", "uint64_t", lambda cpu: f"0x{((cpu.implementer << 32) | cpu.partnum):x}ull" ) riscv64_model = generate_model( self.riscv64_data.cpus, "map", "std::tuple", lambda cpu: f'{{0x{cpu.mvendorid:x}ull, "{cpu.marchid}", "{cpu.mimpid}"}}' ) return self.add_arm_guard(arm64_model) + "\n" + self.add_riscv_guard(riscv64_model) def add_arm_guard(self, data: str) -> str: return f'#if defined(__aarch64__) || defined(__arm__)\n{data}\n#endif\n' def add_riscv_guard(self, data: str) -> str: return f'#if defined(__riscv)\n{data}\n#endif\n' def add_x86_guard(self, data: str) -> str: return f'#if defined(__i386__) || defined(__x86_64__)\n{data}\n#endif\n' def gen_events(event_table_file: str): generated_str = """ #include #include #include #include #include "event_type.h" namespace simpleperf { // A constexpr-constructible version of EventType for the built-in table. struct BuiltinEventType { std::string_view name; uint32_t type; uint64_t config; std::string_view description; std::string_view limited_arch; explicit operator EventType() const { return {std::string(name), type, config, std::string(description), std::string(limited_arch)}; } }; static constexpr BuiltinEventType kBuiltinEventTypes[] = { """ generated_str += gen_hardware_events() + '\n' generated_str += gen_software_events() + '\n' generated_str += gen_hw_cache_events() + '\n' raw_event_generator = RawEventGenerator(event_table_file) generated_str += raw_event_generator.generate_raw_events() + '\n' generated_str += """ }; void LoadBuiltinEventTypes(std::set& set) { for (const auto& event_type : kBuiltinEventTypes) { set.insert(static_cast(event_type)); } } """ generated_str += raw_event_generator.generate_cpu_support_events() generated_str += raw_event_generator.generate_cpu_models() generated_str += """ } // namespace simpleperf """ return generated_str def main(): event_table_file = sys.argv[1] output_file = sys.argv[2] generated_str = gen_events(event_table_file) with open(output_file, 'w') as fh: fh.write(generated_str) if __name__ == '__main__': main()