1#!/usr/bin/env python3 2# 3# Copyright (C) 2015 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18import dataclasses 19from dataclasses import dataclass 20import json 21import sys 22from typing import List 23 24 25def gen_event_type_entry_str(event_type_name, event_type, event_config, description='', 26 limited_arch=''): 27 return '{"%s", %s, %s, "%s", "%s"},\n' % ( 28 event_type_name, event_type, event_config, description, limited_arch) 29 30 31def gen_hardware_events(): 32 hardware_configs = ["cpu-cycles", 33 "instructions", 34 "cache-references", 35 "cache-misses", 36 "branch-instructions", 37 "branch-misses", 38 "bus-cycles", 39 "stalled-cycles-frontend", 40 "stalled-cycles-backend", 41 ] 42 generated_str = "" 43 for config in hardware_configs: 44 event_type_name = config 45 event_config = "PERF_COUNT_HW_" + config.replace('-', '_').upper() 46 47 generated_str += gen_event_type_entry_str( 48 event_type_name, "PERF_TYPE_HARDWARE", event_config) 49 50 return generated_str 51 52 53def gen_software_events(): 54 software_configs = ["cpu-clock", 55 "task-clock", 56 "page-faults", 57 "context-switches", 58 "cpu-migrations", 59 ["minor-faults", "PERF_COUNT_SW_PAGE_FAULTS_MIN"], 60 ["major-faults", "PERF_COUNT_SW_PAGE_FAULTS_MAJ"], 61 "alignment-faults", 62 "emulation-faults", 63 ] 64 generated_str = "" 65 for config in software_configs: 66 if isinstance(config, list): 67 event_type_name = config[0] 68 event_config = config[1] 69 else: 70 event_type_name = config 71 event_config = "PERF_COUNT_SW_" + config.replace('-', '_').upper() 72 73 generated_str += gen_event_type_entry_str( 74 event_type_name, "PERF_TYPE_SOFTWARE", event_config) 75 76 return generated_str 77 78 79def gen_hw_cache_events(): 80 hw_cache_types = [["L1-dcache", "PERF_COUNT_HW_CACHE_L1D"], 81 ["L1-icache", "PERF_COUNT_HW_CACHE_L1I"], 82 ["LLC", "PERF_COUNT_HW_CACHE_LL"], 83 ["dTLB", "PERF_COUNT_HW_CACHE_DTLB"], 84 ["iTLB", "PERF_COUNT_HW_CACHE_ITLB"], 85 ["branch", "PERF_COUNT_HW_CACHE_BPU"], 86 ["node", "PERF_COUNT_HW_CACHE_NODE"], 87 ] 88 hw_cache_ops = [["loads", "load", "PERF_COUNT_HW_CACHE_OP_READ"], 89 ["stores", "store", "PERF_COUNT_HW_CACHE_OP_WRITE"], 90 ["prefetches", "prefetch", 91 "PERF_COUNT_HW_CACHE_OP_PREFETCH"], 92 ] 93 hw_cache_op_results = [["accesses", "PERF_COUNT_HW_CACHE_RESULT_ACCESS"], 94 ["misses", "PERF_COUNT_HW_CACHE_RESULT_MISS"], 95 ] 96 generated_str = "" 97 for (type_name, type_config) in hw_cache_types: 98 for (op_name_access, op_name_miss, op_config) in hw_cache_ops: 99 for (result_name, result_config) in hw_cache_op_results: 100 if result_name == "accesses": 101 event_type_name = type_name + '-' + op_name_access 102 else: 103 event_type_name = type_name + '-' + \ 104 op_name_miss + '-' + result_name 105 event_config = "((%s) | (%s << 8) | (%s << 16))" % ( 106 type_config, op_config, result_config) 107 generated_str += gen_event_type_entry_str( 108 event_type_name, "PERF_TYPE_HW_CACHE", event_config) 109 110 return generated_str 111 112 113@dataclass 114class RawEvent: 115 number: int 116 name: str 117 desc: str 118 limited_arch: str 119 120 121@dataclass 122class CpuModel: 123 name: str 124 implementer: int 125 partnum: int 126 mvendorid: int 127 marchid: str 128 mimpid: str 129 supported_raw_events: list[int] = dataclasses.field(default_factory=list) 130 131 132class ArchData: 133 def __init__(self, arch: str): 134 self.arch = arch 135 self.events: List[RawEvent] = [] 136 self.cpus: List[CpuModel] = [] 137 138 def load_from_json_data(self, data) -> None: 139 # Load common events 140 for event in data['events']: 141 number = int(event[0], 16) 142 name = 'raw-' + event[1].lower().replace('_', '-') 143 desc = event[2] 144 self.events.append(RawEvent(number, name, desc, self.arch)) 145 for cpu in data['cpus']: 146 cpu_name = cpu['name'].lower().replace('_', '-') 147 cpu_model = CpuModel( 148 cpu['name'], 149 int(cpu.get('implementer', '0'), 16), 150 int(cpu.get('partnum', '0'), 16), 151 int(cpu.get('mvendorid', '0'), 16), 152 cpu.get('marchid', '0'), 153 cpu.get('mimpid', '0'), 154 [] 155 ) 156 cpu_index = len(self.cpus) 157 158 self.cpus.append(cpu_model) 159 # Load common events supported in this cpu model. 160 for number in cpu['common_events']: 161 number = int(number, 16) 162 event = self.get_event(number) 163 cpu_model.supported_raw_events.append(number) 164 165 # Load cpu specific events supported in this cpu model. 166 if 'implementation_defined_events' in cpu: 167 for event in cpu['implementation_defined_events']: 168 number = int(event[0], 16) 169 name = ('raw-' + cpu_name + '-' + event[1]).lower().replace('_', '-') 170 desc = event[2] 171 limited_arch = self.arch + ':' + cpu['name'] 172 self.events.append(RawEvent(number, name, desc, limited_arch)) 173 cpu_model.supported_raw_events.append(number) 174 175 def get_event(self, event_number: int) -> RawEvent: 176 for event in self.events: 177 if event.number == event_number: 178 return event 179 raise Exception(f'no event for event number {event_number}') 180 181 182class X86ArchData: 183 def __init__(self, arch: str): 184 self.arch = arch 185 self.events: List[RawEvent] = [] 186 187 def load_from_json_data(self, data) -> None: 188 for event in data['events']: 189 number = int(event[0], 16) 190 name = event[1] 191 desc = event[2] 192 limited_arch = self.arch 193 if len(event) > 3: 194 limited_arch += ":" + event[3] 195 self.events.append(RawEvent(number, name, desc, limited_arch)) 196 197 198class RawEventGenerator: 199 def __init__(self, event_table_file: str): 200 with open(event_table_file, 'r') as fh: 201 event_table = json.load(fh) 202 self.arm64_data = ArchData('arm64') 203 self.arm64_data.load_from_json_data(event_table['arm64']) 204 self.riscv64_data = ArchData('riscv64') 205 self.riscv64_data.load_from_json_data(event_table['riscv64']) 206 self.x86_intel_data = X86ArchData('x86-intel') 207 self.x86_intel_data.load_from_json_data(event_table['x86-intel']) 208 self.x86_amd_data = X86ArchData('x86-amd') 209 self.x86_amd_data.load_from_json_data(event_table['x86-amd']) 210 211 def generate_raw_events(self) -> str: 212 def generate_event_entries(events, guard) -> list: 213 lines = [] 214 for event in events: 215 lines.append(gen_event_type_entry_str(event.name, 'PERF_TYPE_RAW', '0x%x' % 216 event.number, event.desc, event.limited_arch)) 217 return guard(''.join(lines)) 218 219 lines_arm64 = generate_event_entries(self.arm64_data.events, self.add_arm_guard) 220 lines_riscv64 = generate_event_entries(self.riscv64_data.events, self.add_riscv_guard) 221 lines_x86_intel = generate_event_entries(self.x86_intel_data.events, self.add_x86_guard) 222 lines_x86_amd = generate_event_entries(self.x86_amd_data.events, self.add_x86_guard) 223 224 return lines_arm64 + lines_riscv64 + lines_x86_intel + lines_x86_amd 225 226 def generate_cpu_support_events(self) -> str: 227 def generate_cpu_events(data, guard) -> str: 228 lines = [] 229 for cpu in data: 230 event_list = ', '.join('0x%x' % number for number in cpu.supported_raw_events) 231 lines.append('{"%s", {%s}},' % (cpu.name, event_list)) 232 return guard('\n'.join(lines)) 233 234 text = f""" 235 // Map from cpu model to raw events supported on that cpu. 236 std::unordered_map<std::string, std::unordered_set<int>> cpu_supported_raw_events = {{ 237 {generate_cpu_events(self.arm64_data.cpus, self.add_arm_guard)} 238 {generate_cpu_events(self.riscv64_data.cpus, self.add_riscv_guard)} 239 }};\n 240 """ 241 242 return text 243 244 def generate_cpu_models(self) -> str: 245 def generate_model(data, map_type, map_key_type, id_func) -> str: 246 lines = [f'std::{map_type}<{map_key_type}, std::string> cpuid_to_name = {{'] 247 for cpu in data: 248 cpu_id = id_func(cpu) 249 lines.append(f'{{{cpu_id}, "{cpu.name}"}},') 250 lines.append('};') 251 return '\n'.join(lines) 252 253 arm64_model = generate_model( 254 self.arm64_data.cpus, 255 "unordered_map", 256 "uint64_t", 257 lambda cpu: f"0x{((cpu.implementer << 32) | cpu.partnum):x}ull" 258 ) 259 260 riscv64_model = generate_model( 261 self.riscv64_data.cpus, 262 "map", 263 "std::tuple<uint64_t, std::string, std::string>", 264 lambda cpu: f'{{0x{cpu.mvendorid:x}ull, "{cpu.marchid}", "{cpu.mimpid}"}}' 265 ) 266 267 return self.add_arm_guard(arm64_model) + "\n" + self.add_riscv_guard(riscv64_model) 268 269 def add_arm_guard(self, data: str) -> str: 270 return f'#if defined(__aarch64__) || defined(__arm__)\n{data}\n#endif\n' 271 272 def add_riscv_guard(self, data: str) -> str: 273 return f'#if defined(__riscv)\n{data}\n#endif\n' 274 275 def add_x86_guard(self, data: str) -> str: 276 return f'#if defined(__i386__) || defined(__x86_64__)\n{data}\n#endif\n' 277 278 279def gen_events(event_table_file: str): 280 generated_str = """ 281 #include <unordered_map> 282 #include <unordered_set> 283 #include <map> 284 #include <string_view> 285 286 #include "event_type.h" 287 288 namespace simpleperf { 289 290 // A constexpr-constructible version of EventType for the built-in table. 291 struct BuiltinEventType { 292 std::string_view name; 293 uint32_t type; 294 uint64_t config; 295 std::string_view description; 296 std::string_view limited_arch; 297 298 explicit operator EventType() const { 299 return {std::string(name), type, config, std::string(description), std::string(limited_arch)}; 300 } 301 }; 302 303 static constexpr BuiltinEventType kBuiltinEventTypes[] = { 304 """ 305 generated_str += gen_hardware_events() + '\n' 306 generated_str += gen_software_events() + '\n' 307 generated_str += gen_hw_cache_events() + '\n' 308 raw_event_generator = RawEventGenerator(event_table_file) 309 generated_str += raw_event_generator.generate_raw_events() + '\n' 310 generated_str += """ 311 }; 312 313 void LoadBuiltinEventTypes(std::set<EventType>& set) { 314 for (const auto& event_type : kBuiltinEventTypes) { 315 set.insert(static_cast<EventType>(event_type)); 316 } 317 } 318 319 320 """ 321 generated_str += raw_event_generator.generate_cpu_support_events() 322 generated_str += raw_event_generator.generate_cpu_models() 323 324 generated_str += """ 325 } // namespace simpleperf 326 """ 327 return generated_str 328 329 330def main(): 331 event_table_file = sys.argv[1] 332 output_file = sys.argv[2] 333 generated_str = gen_events(event_table_file) 334 with open(output_file, 'w') as fh: 335 fh.write(generated_str) 336 337 338if __name__ == '__main__': 339 main() 340