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 self.events.append(RawEvent(number, name, desc, self.arch)) 193 194 195class RawEventGenerator: 196 def __init__(self, event_table_file: str): 197 with open(event_table_file, 'r') as fh: 198 event_table = json.load(fh) 199 self.arm64_data = ArchData('arm64') 200 self.arm64_data.load_from_json_data(event_table['arm64']) 201 self.riscv64_data = ArchData('riscv64') 202 self.riscv64_data.load_from_json_data(event_table['riscv64']) 203 self.x86_intel_data = X86ArchData('x86-intel') 204 self.x86_intel_data.load_from_json_data(event_table['x86-intel']) 205 self.x86_amd_data = X86ArchData('x86-amd') 206 self.x86_amd_data.load_from_json_data(event_table['x86-amd']) 207 208 def generate_raw_events(self) -> str: 209 def generate_event_entries(events, guard) -> list: 210 lines = [] 211 for event in events: 212 lines.append(gen_event_type_entry_str(event.name, 'PERF_TYPE_RAW', '0x%x' % 213 event.number, event.desc, event.limited_arch)) 214 return guard(''.join(lines)) 215 216 lines_arm64 = generate_event_entries(self.arm64_data.events, self.add_arm_guard) 217 lines_riscv64 = generate_event_entries(self.riscv64_data.events, self.add_riscv_guard) 218 lines_x86_intel = generate_event_entries(self.x86_intel_data.events, self.add_x86_guard) 219 lines_x86_amd = generate_event_entries(self.x86_amd_data.events, self.add_x86_guard) 220 221 return lines_arm64 + lines_riscv64 + lines_x86_intel + lines_x86_amd 222 223 def generate_cpu_support_events(self) -> str: 224 def generate_cpu_events(data, guard) -> str: 225 lines = [] 226 for cpu in data: 227 event_list = ', '.join('0x%x' % number for number in cpu.supported_raw_events) 228 lines.append('{"%s", {%s}},' % (cpu.name, event_list)) 229 return guard('\n'.join(lines)) 230 231 text = f""" 232 // Map from cpu model to raw events supported on that cpu. 233 std::unordered_map<std::string, std::unordered_set<int>> cpu_supported_raw_events = {{ 234 {generate_cpu_events(self.arm64_data.cpus, self.add_arm_guard)} 235 {generate_cpu_events(self.riscv64_data.cpus, self.add_riscv_guard)} 236 }};\n 237 """ 238 239 return text 240 241 def generate_cpu_models(self) -> str: 242 def generate_model(data, map_type, map_key_type, id_func) -> str: 243 lines = [f'std::{map_type}<{map_key_type}, std::string> cpuid_to_name = {{'] 244 for cpu in data: 245 cpu_id = id_func(cpu) 246 lines.append(f'{{{cpu_id}, "{cpu.name}"}},') 247 lines.append('};') 248 return '\n'.join(lines) 249 250 arm64_model = generate_model( 251 self.arm64_data.cpus, 252 "unordered_map", 253 "uint64_t", 254 lambda cpu: f"0x{((cpu.implementer << 32) | cpu.partnum):x}ull" 255 ) 256 257 riscv64_model = generate_model( 258 self.riscv64_data.cpus, 259 "map", 260 "std::tuple<uint64_t, std::string, std::string>", 261 lambda cpu: f'{{0x{cpu.mvendorid:x}ull, "{cpu.marchid}", "{cpu.mimpid}"}}' 262 ) 263 264 return self.add_arm_guard(arm64_model) + "\n" + self.add_riscv_guard(riscv64_model) 265 266 def add_arm_guard(self, data: str) -> str: 267 return f'#if defined(__aarch64__) || defined(__arm__)\n{data}\n#endif\n' 268 269 def add_riscv_guard(self, data: str) -> str: 270 return f'#if defined(__riscv)\n{data}\n#endif\n' 271 272 def add_x86_guard(self, data: str) -> str: 273 return f'#if defined(__i386__) || defined(__x86_64__)\n{data}\n#endif\n' 274 275 276def gen_events(event_table_file: str): 277 generated_str = """ 278 #include <unordered_map> 279 #include <unordered_set> 280 #include <map> 281 #include <string_view> 282 283 #include "event_type.h" 284 285 namespace simpleperf { 286 287 // A constexpr-constructible version of EventType for the built-in table. 288 struct BuiltinEventType { 289 std::string_view name; 290 uint32_t type; 291 uint64_t config; 292 std::string_view description; 293 std::string_view limited_arch; 294 295 explicit operator EventType() const { 296 return {std::string(name), type, config, std::string(description), std::string(limited_arch)}; 297 } 298 }; 299 300 static constexpr BuiltinEventType kBuiltinEventTypes[] = { 301 """ 302 generated_str += gen_hardware_events() + '\n' 303 generated_str += gen_software_events() + '\n' 304 generated_str += gen_hw_cache_events() + '\n' 305 raw_event_generator = RawEventGenerator(event_table_file) 306 generated_str += raw_event_generator.generate_raw_events() + '\n' 307 generated_str += """ 308 }; 309 310 void LoadBuiltinEventTypes(std::set<EventType>& set) { 311 for (const auto& event_type : kBuiltinEventTypes) { 312 set.insert(static_cast<EventType>(event_type)); 313 } 314 } 315 316 317 """ 318 generated_str += raw_event_generator.generate_cpu_support_events() 319 generated_str += raw_event_generator.generate_cpu_models() 320 321 generated_str += """ 322 } // namespace simpleperf 323 """ 324 return generated_str 325 326 327def main(): 328 event_table_file = sys.argv[1] 329 output_file = sys.argv[2] 330 generated_str = gen_events(event_table_file) 331 with open(output_file, 'w') as fh: 332 fh.write(generated_str) 333 334 335if __name__ == '__main__': 336 main() 337