1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# Copyright (C) 2021 Huawei Device Co., Ltd. 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 sys 17import argparse 18import logging 19from ftrace_format_parser import FtraceEventCodeGenerator 20 21FTRACE_BUNDLE_PROTO = 'ftrace_event.proto' 22AUTO_GENERATED_GNI = 'autogenerated.gni' 23 24THIS_FILE = os.path.basename(__file__) 25logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', 26 level=logging.INFO) 27logger = logging.getLogger(THIS_FILE) 28 29PROTO_COMMON_HEADER = '''\ 30// THIS FILE IS GENERATED BY {}, PLEASE DON'T EDIT IT! 31// Copyright (c) 2021 Huawei Device Co., Ltd. 32// Licensed under the Apache License, Version 2.0 (the "License"); 33// you may not use this file except in compliance with the License. 34// You may obtain a copy of the License at 35// 36// http://www.apache.org/licenses/LICENSE-2.0 37// 38// Unless required by applicable law or agreed to in writing, software 39// distributed under the License is distributed on an "AS IS" BASIS, 40// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 41// See the License for the specific language governing permissions and 42// limitations under the License. 43// 44 45syntax = "proto3"; 46 47option optimize_for = LITE_RUNTIME; 48 49'''.format(THIS_FILE) 50 51GN_COPYRIGHT_HEADER = '''\ 52# THIS FILE IS GENERATE BY {}, PLEASE DON'T EDIT IT! 53# Copyright (C) 2021 Huawei Device Co., Ltd. 54# Licensed under the Apache License, Version 2.0 (the "License"); 55# you may not use this file except in compliance with the License. 56# You may obtain a copy of the License at 57# 58# http://www.apache.org/licenses/LICENSE-2.0 59# 60# Unless required by applicable law or agreed to in writing, software 61# distributed under the License is distributed on an "AS IS" BASIS, 62# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 63# See the License for the specific language governing permissions and 64# limitations under the License. 65'''.format(THIS_FILE) 66 67MAX_EVENTS_IN_CATEGORY = 100 68 69PROTO_MESSAGE_HEAD = '''\ 70message FtraceEvent { 71 uint64 timestamp = 1; 72 int32 tgid = 2; 73 string comm = 3; 74 75 message CommonFileds { 76 uint32 type = 1; 77 uint32 flags = 2; 78 uint32 preempt_count = 3; 79 int32 pid = 4; 80 }; 81 CommonFileds common_fields = 50; 82 83 oneof event { 84''' 85 86 87def to_camel_case(name): 88 return ''.join([p.capitalize() for p in name.split('_')]) 89 90 91def fix_field_name(name): 92 replace_map = { 93 'errno': 'error_code', 94 'sa_handler': 'sig_handler', 95 'sa_flags': 'sig_flags', 96 'new': 'seq_new' 97 } 98 if name in replace_map: 99 name = replace_map[name] 100 return str.lower(name) 101 102 103class FtraceEventProtoGenerator(FtraceEventCodeGenerator): 104 def __init__(self, events_dir, allow_list): 105 super().__init__(events_dir, allow_list) 106 107 def generate_code(self): 108 proto_file_list = [] 109 for category in self.grouped_event_formats: 110 proto_name = '{}.proto'.format(category) 111 proto_path = os.path.join(self.output_dir, proto_name) 112 proto_file_list.append(proto_path) 113 self.generate_event_proto(category, proto_path) 114 115 bundle_proto_head = [PROTO_COMMON_HEADER] 116 bundle_proto_body = [PROTO_MESSAGE_HEAD] 117 event_category_count = 0 118 for category in self.grouped_event_formats: 119 event_category_count += 1 120 proto_name = '{}.proto'.format(category) 121 bundle_proto_head.append('import "{}";\n'.format(proto_name)) 122 for i in range(len(self.grouped_event_formats[category])): 123 event_format = self.grouped_event_formats[category][i] 124 message_name = '{}_format'.format(event_format.name) 125 message_type = to_camel_case(message_name) 126 message_id = event_category_count * 100 + i 127 message_name = fix_field_name(message_name) 128 bundle_proto_body.append( 129 ' {} {} = {};\n'.format(message_type, 130 message_name, 131 message_id)) 132 bundle_proto_head.append('\n') 133 bundle_proto_body.append(' }\n') 134 bundle_proto_body.append('}\n') 135 136 bundle_proto_path = os.path.join(self.output_dir, FTRACE_BUNDLE_PROTO) 137 proto_file_list.append(bundle_proto_path) 138 logger.info('Generate {} ...'.format(bundle_proto_path)) 139 with open(bundle_proto_path, 'w+') as f: 140 f.writelines(bundle_proto_head + bundle_proto_body) 141 142 protos_gni_path = os.path.join(self.output_dir, AUTO_GENERATED_GNI) 143 logger.info('Generate {} ...'.format(protos_gni_path)) 144 with open(protos_gni_path, 'w+') as f: 145 f.write(GN_COPYRIGHT_HEADER) 146 # proto sources 147 f.write('auto_generated_ftrace_proto_sources = [\n') 148 for proto_path in proto_file_list: 149 f.write(' "{}",\n'.format(os.path.basename(proto_path))) 150 f.write(']\n') 151 152 def generate_event_proto(self, category, proto_path): 153 with open(proto_path, 'w+') as f: 154 f.write(PROTO_COMMON_HEADER) 155 f.write('// category: {}\n'.format(category)) 156 logger.info('Generate {} ...'.format(proto_path)) 157 for event_format in self.grouped_event_formats[category]: 158 logger.debug( 159 '{}/{}:'.format(event_format.category, event_format.name)) 160 message_name = '{}_format'.format(event_format.name) 161 message_type = to_camel_case(message_name) 162 device_format_file = '{}/{}/{}/format'.format( 163 '/sys/kernel/debug/tracing/events', 164 event_format.category, event_format.name) 165 166 field_count = 1 167 f.write('// {}\n'.format(device_format_file)) 168 f.write('message {} {{\n'.format(message_type)) 169 for field in event_format.remain_fields: 170 type_string = field.to_proto_type().to_string() 171 field_name = fix_field_name(field.name) 172 logger.debug(' {}: {}'.format(field, type_string)) 173 f.write(' {} {} = {};\n'.format(type_string, field_name, 174 field_count)) 175 field_count += 1 176 f.write('}\n') 177 f.write('\n') 178 179 180def main(): 181 parser = argparse.ArgumentParser( 182 description='FTrace proto code generator.') 183 parser.add_argument('-a', dest='allow_list', required=True, type=str, 184 help='event allow list file path') 185 parser.add_argument('-e', dest='events_dir', required=True, type=str, 186 help='event formats directory') 187 parser.add_argument('-o', dest='output_dir', required=True, type=str, 188 help='code file output directory') 189 190 args = parser.parse_args(sys.argv[1:]) 191 events_dir = args.events_dir 192 output_dir = args.output_dir 193 allow_list = args.allow_list 194 195 generator = FtraceEventProtoGenerator(events_dir, allow_list) 196 generator.generate(output_dir) 197 198 199if __name__ == '__main__': 200 main() 201