1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# Copyright (c) Huawei Technologies Co., Ltd. 2021. All rights reserved. 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15import os 16import re 17import logging 18 19 20class Common: 21 this_file = os.path.basename(__file__) 22 logging.basicConfig( 23 format="%(asctime)s %(levelname)s %(message)s", level=logging.INFO 24 ) 25 logger = logging.getLogger(this_file) 26 27 28def enumerate_files(top_dir): 29 files = [] 30 stack = [top_dir] 31 while len(stack) > 0: 32 current = stack.pop() 33 for entry in os.listdir(current): 34 if entry.startswith("."): 35 continue 36 path = os.path.join(current, entry) 37 if os.path.isfile(path): 38 files.append(path) # append to result list. 39 if os.path.isdir(path): 40 stack.append(path) # continue to do depth first search. 41 files.sort() 42 return files 43 44 45class FtraceEvent(object): 46 def __init__(self, format_path, event_category, event_name): 47 self.format_path = format_path 48 self.event_category = event_category 49 self.event_name = event_name 50 51 def __str__(self): 52 return '{{ P: "{}", C: "{}", N: {} }}'.format( 53 self.format_path, self.event_category, self.event_name 54 ) 55 56 def __repr__(self): 57 return self.__str__() 58 59 60class ProtoType(object): 61 INVALID, INTEGER, STRING, BYTES, ARRAY = 0, 1, 2, 3, 4 62 63 def __init__(self, tid, size=0, signed=False): 64 self.tid = tid # type id 65 self.size = size 66 self.signed = signed 67 68 def __str__(self): 69 return "{{ T: {}, SZ: {}, SN: {} }}".format(self.tid, self.size, self.signed) 70 71 def __repr__(self): 72 return self.__str__() 73 74 def to_string(self): 75 if self.tid != ProtoType.INVALID: 76 if self.tid == ProtoType.STRING: 77 return "string" 78 elif self.tid == ProtoType.BYTES: 79 return "string" 80 elif self.tid == ProtoType.ARRAY: 81 return "repeated uint64" 82 elif self.tid == ProtoType.INTEGER: 83 t = "" 84 if not self.signed: 85 t = "{}{}".format(t, "u") 86 if self.size <= 4: 87 t = "{}{}".format(t, "int32") 88 elif self.size <= 8: 89 t = "{}{}".format(t, "int64") 90 return t 91 92 93class FtraceEventField(object): 94 def __init__(self, event, field, name, offset, size, signed): 95 self.event = event 96 self.field = field 97 self.name = name 98 self.offset = offset 99 self.size = size 100 self.signed = signed 101 102 def __str__(self): 103 return '{{ E: {}, F: "{}", N: "{}", O: {}, SZ: {}, SN: {} }}'.format( 104 self.event, self.field, self.name, self.offset, self.size, self.signed 105 ) 106 107 def __repr__(self): 108 return self.__str__() 109 110 def to_proto_type_special_case(self): 111 if self.event == ("ftrace", "print") and self.name == "buf": 112 # ftrace/print: char buf[]; 113 return ProtoType(ProtoType.STRING) 114 if self.size == 0: 115 Common.logger.fatal("zero size field {}!".format(self)) 116 return ProtoType.INVALID 117 118 def is_string_type(self): 119 return ( 120 re.match(r"char\s+\w+\[\d*\]", self.field) 121 or re.match(r"char\s*\*\s*\w+", self.field) 122 or re.findall(r"char\s*\*\s*", self.field) 123 or re.findall(r"char\[\]", self.field) 124 or re.findall(r"char\s*\w+\[.*\]", self.field) 125 or re.match(r"__u8\s+\w+\[\d+\]", self.field) 126 or re.match(r"__u8\s+\w+\[.*]", self.field) 127 or re.match(r"u32\s+\w+\[\d+\]", self.field) 128 ) 129 130 def is_array_type(self): 131 return re.match(r"unsigned long\s+\w+\[\d*\]", self.field) 132 133 def is_uintptr_type(self): 134 return ( 135 self.field.startswith("ino_t ") 136 or self.field.startswith("i_ino ") 137 or self.field.startswith("dev_t ") 138 or self.field.startswith("loff_t ") 139 or self.field.startswith("sector_t ") 140 or self.field.startswith("ext4_fsblk_t ") 141 or "long" in self.field 142 or "size_t" in self.field 143 or "intptr_t" in self.field 144 ) 145 146 def to_proto_type(self): 147 t = self.to_proto_type_special_case() 148 if t is not ProtoType.INVALID: 149 return t 150 151 if self.is_string_type(): 152 return ProtoType(ProtoType.STRING) 153 if self.is_array_type(): 154 return ProtoType(ProtoType.ARRAY) 155 elif self.is_uintptr_type(): 156 if (self.field.startswith("ino_t ") and self.size == 4): 157 return ProtoType(ProtoType.INTEGER, 4, False) 158 return ProtoType(ProtoType.INTEGER, 8, False) 159 elif 0 < self.size <= 4: 160 if self.size in [1, 2, 4]: 161 if "*" in self.field: 162 return ProtoType(ProtoType.INTEGER, 8, False) 163 return ProtoType(ProtoType.INTEGER, 4, self.signed) 164 elif 4 < self.size <= 8: 165 if self.size in [8]: 166 return ProtoType(ProtoType.INTEGER, 8, self.signed) 167 else: 168 Common.logger.fatal("can not convert {} to proto type.".format(str(self))) 169 170 171class FtraceEventFormat(object): 172 def __init__(self): 173 self.category = "" 174 self.path = "" 175 self.name = "" 176 self.event_id = "" 177 self.common_fields = [] 178 self.remain_fields = [] 179 self.print_fmt = "" 180 181 def __str__(self): 182 return '{{ name: "{}", ID: {}, common: {}, remain: {}" }}'.format( 183 self.name, self.event_id, self.common_fields, self.remain_fields 184 ) 185 186 def __repr__(self): 187 return self.__str__() 188 189 190class FtraceEventFormatParser(object): 191 def __init__(self): 192 self.text = [] 193 self.line = None 194 195 def _parse_name(self, type_name): 196 if len(type_name) > 0: 197 start_idx = type_name.rfind(" ") 198 remain = type_name[start_idx + 1 :] 199 if "[" not in remain: 200 return remain 201 return remain[: remain.rfind("[")] 202 203 def _parse_field(self, event): 204 fields = [f.strip().split(":") for f in self.line.split(";")] 205 if fields[0][0] == "field" and fields[1][0] == "offset": 206 type_name, offset = fields[0][1], int(fields[1][1]) 207 name = self._parse_name(type_name) 208 if fields[2][0] == "size" and fields[3][0] == "signed": 209 size, signed = int(fields[2][1]), int(fields[3][1]) == 1 210 return FtraceEventField(event, type_name, name, offset, size, signed) 211 212 def parse(self, ftrace_info): 213 self.text = [] 214 with open(ftrace_info.format_path) as f: 215 self.text = f.readlines() 216 event_format = FtraceEventFormat() 217 event_format.category = ftrace_info.event_category 218 event_format.path = ftrace_info.format_path 219 event = (ftrace_info.event_category, ftrace_info.event_name) 220 cursor_line = 0 221 field_list = [] 222 while cursor_line < len(self.text): 223 self.line = self.text[cursor_line].strip() 224 if self.line.startswith("name:"): 225 event_format.name = self.line.split(":")[1].strip() 226 cursor_line += 1 227 elif self.line.startswith("ID:"): 228 event_format.event_id = int(self.line.split(":")[1]) 229 cursor_line += 1 230 elif self.line.startswith("format:"): 231 field_list = event_format.common_fields 232 cursor_line += 1 233 elif self.line.startswith("field:"): 234 field_list.append(self._parse_field(event)) 235 cursor_line += 1 236 elif self.line.startswith("print fmt:"): 237 event_format.print_fmt = self.line[len("print fmt:") :] 238 cursor_line += 1 239 elif len(self.line) == 0: 240 field_list = event_format.remain_fields 241 cursor_line += 1 242 else: 243 Common.logger.warning( 244 "ignore unknow line at {}:{}".format( 245 ftrace_info.format_path, cursor_line 246 ) 247 ) 248 cursor_line += 1 249 return event_format 250 251 252class FtraceEventCodeGenerator(object): 253 def __init__(self, events_dir, allow_list): 254 self.events_dir = events_dir 255 self.allow_list = allow_list 256 self.output_dir = None 257 self.allowed_events = set() 258 self.available_events = set() 259 self.category_to_info = {} 260 self.grouped_event_formats = {} 261 self.target_event_list = [] 262 self.target_event_formats = [] 263 self.parser = FtraceEventFormatParser() 264 self.text = None 265 266 def _load_allow_list(self): 267 self.text = [] 268 with open(self.allow_list) as f: 269 self.text = [line.strip() for line in f.readlines()] 270 for i in self.text: 271 if i.startswith("removed") or i.startswith("#"): 272 continue 273 class_type = tuple(i.split("/")) # event class and event type 274 if len(class_type) == 2: 275 self.allowed_events.add(class_type) 276 277 def _get_platform_event_list(self): 278 files = enumerate_files(self.events_dir) 279 is_format = lambda p: os.path.basename(p) == "format" 280 format_files = list(filter(is_format, files)) 281 event_names = set() 282 for path in format_files: 283 parts = path.split(os.path.sep) 284 if parts[-1] == "format": 285 category_type = tuple(parts[-3:-1]) # category/type 286 self.available_events.add(category_type) 287 event_category, event_name = parts[-3], parts[-2] 288 event_info = FtraceEvent(path, event_category, event_name) 289 self.category_to_info[category_type] = event_info 290 event_names.add(parts[-2]) 291 Common.logger.info("platform events: %d", len(self.available_events)) 292 Common.logger.info("allowed events: %d", len(self.allowed_events)) 293 294 def _get_target_event_list(self): 295 targets = list(self.allowed_events & self.available_events) 296 Common.logger.info("target events: %d", len(targets)) 297 targets.sort() 298 self.target_event_list = [self.category_to_info.get(c) for c in targets] 299 Common.logger.info("target_event_list: %d", len(self.target_event_list)) 300 301 def _parse_ftrace_formats(self): 302 for info in self.target_event_list: 303 event_format = self.parser.parse(info) 304 event_category = event_format.category 305 if event_category not in self.grouped_event_formats: 306 self.grouped_event_formats[event_category] = [] 307 self.grouped_event_formats[event_category].append(event_format) 308 self.target_event_formats.append(event_format) 309 310 def generate_code(self): 311 print("FATAL: subclass must implements generate_code_files method!") 312 os.abort() 313 314 def generate(self, output_dir): 315 self.output_dir = output_dir 316 self._load_allow_list() 317 self._get_platform_event_list() 318 self._get_target_event_list() 319 self._parse_ftrace_formats() 320 self.generate_code() 321