#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (C) 2021 Huawei Device Co., Ltd. # 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 os import sys import argparse import logging from ftrace_format_parser import FtraceEventCodeGenerator from ftrace_format_parser import ProtoType AUTO_GENERATED_GNI = 'autogenerated.gni' THIS_FILE = os.path.basename(__file__) logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', level=logging.INFO) logger = logging.getLogger(THIS_FILE) CPP_COPYRIGHT_HEADER = '''\ /* THIS FILE IS GENERATE BY {}, PLEASE DON'T EDIT IT! * Copyright (c) 2021 Huawei Device Co., Ltd. * 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. */ '''.format(THIS_FILE) GN_COPYRIGHT_HEADER = '''\ # THIS FILE IS GENERATE BY {}, PLEASE DON'T EDIT IT! # Copyright (C) 2021 Huawei Device Co., Ltd. # 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. '''.format(THIS_FILE) PARSE_FUNCTION_ARGS = '({}, {}, {}, {})'.format('FtraceEvent& ftraceEvent', 'uint8_t data[]', 'size_t size', 'const EventFormat& format') PARSE_REGISTER_MACRO = 'REGISTER_FTRACE_EVENT_PARSE_FUNCTION' CHECK_FUNCTION_ARGS = '(const FtraceEvent& event) -> bool' FORMAT_FUNCTION_ARGS = '(const FtraceEvent& event) -> std::string' FORMAT_REGISTER_MACRO = 'REGISTER_FTRACE_EVENT_FORMATTER' def to_camel_case(name): return ''.join([p.capitalize() for p in name.split('_')]) def fix_field_name(name): replace_map = { 'errno': 'error_code', 'sa_handler': 'sig_handler', 'sa_flags': 'sig_flags' } if name in replace_map: name = replace_map[name] return str.lower(name) def ensure_dir_exists(file_path): file_dir = os.path.dirname(file_path) if not os.path.exists(file_dir): os.mkdir(file_dir) class EventParserCodeGenerator(FtraceEventCodeGenerator): def __init__(self, events_dir, allow_list): super().__init__(events_dir, allow_list) def parser_file_path(self, category): file_name = 'ftrace_{}_event_parser.cpp'.format(category) return os.path.join(self.output_dir, file_name) def generate_code(self): generated_cpp_sources = [] for event in self.target_event_formats: type_name = '{}/{}'.format(event.category, event.name) logger.info('ftrace_events: "{}"'.format(type_name)) # generate sub event parser code for category in self.grouped_event_formats: parser_src_file = self.parser_file_path(category) generated_cpp_sources.append(parser_src_file) logger.info('Generate {} ...'.format(parser_src_file)) ensure_dir_exists(parser_src_file) with open(parser_src_file, 'w', encoding='utf-8') as f: f.write(CPP_COPYRIGHT_HEADER) f.write('#include "sub_event_parser.h"\n') f.write('\n') f.write("FTRACE_NS_BEGIN\n") f.write("namespace {\n") self.generate_parse_functions(category, f) f.write("} // namespace\n") f.write("FTRACE_NS_END\n") f.write('\n') # generate .gni generated_cpp_gni = os.path.join(self.output_dir, AUTO_GENERATED_GNI) logger.info('Generate {} ...'.format(generated_cpp_gni)) with open(generated_cpp_gni, 'w', encoding='utf-8') as f: f.write(GN_COPYRIGHT_HEADER) f.write('\n') f.write('auto_generated_cpp_sources = [\n') for path in generated_cpp_sources: src = '{}'.format(os.path.basename(path)) f.write(' "{}",\n'.format(src)) f.write(']\n') def generate_parse_functions(self, category, f): count = 0 for event in self.grouped_event_formats[category]: count += 1 if count > 1: f.write('\n') f.write('{}({},\n'.format(PARSE_REGISTER_MACRO, event.name)) f.write('[] {} {{\n'.format(PARSE_FUNCTION_ARGS)) f.write(' int i = 0;\n') f.write(' auto msg = ftraceEvent.mutable_{}_format();\n'.format( str.lower(event.name))) for i in range(len(event.remain_fields)): self.generate_parse_field_lines(event, f, i) f.write("});\n") @staticmethod def generate_parse_field_lines(event, f, i): field_info = event.remain_fields[i] field_name = fix_field_name(field_info.name) type_info = field_info.to_proto_type() parse_func = None if type_info.tid == ProtoType.STRING: parse_func = 'ParseStrField' elif type_info.tid == ProtoType.INTEGER: assert type_info.size in [4, 8] c_type = None if type_info.size == 4: c_type = 'int32_t' if type_info.signed else 'uint32_t' elif type_info.size == 8: c_type = 'int64_t' if type_info.signed else 'uint64_t' parse_func = 'ParseIntField<{}>'.format(c_type) else: logger.warning('WARNING: unkown proto type:{} {}'.format( event.name, field_name)) assert parse_func f.write(' msg->set_{}(FtraceFieldParser::'.format(field_name)) f.write('{}(format.fields, i++, data, size));\n'.format(parse_func)) class EventFormatterCodeGenerator(FtraceEventCodeGenerator): def __init__(self, events_dir, allow_list): super().__init__(events_dir, allow_list) def formatter_file_path(self, category): file_name = 'ftrace_{}_event_formatter.cpp'.format(category) return os.path.join(self.output_dir, file_name) def generate_code(self): generated_cpp_sources = [] # generate sub event parser code for category in self.grouped_event_formats: formatter_src_file = self.formatter_file_path(category) generated_cpp_sources.append(formatter_src_file) logger.info('Generate {} ...'.format(formatter_src_file)) ensure_dir_exists(formatter_src_file) with open(formatter_src_file, 'w', encoding='utf-8') as f: f.write(CPP_COPYRIGHT_HEADER) f.write('#include "event_formatter.h"\n') f.write('#include \n') f.write('\n') f.write("FTRACE_NS_BEGIN\n") f.write("namespace {\n") self.generate_format_functions(category, f) f.write("} // namespace\n") f.write("FTRACE_NS_END\n") f.write('\n') # generate .gni generated_cpp_gni = os.path.join(self.output_dir, AUTO_GENERATED_GNI) logger.info('Generate {} ...'.format(generated_cpp_gni)) with open(generated_cpp_gni, 'w', encoding='utf-8') as f: f.write(GN_COPYRIGHT_HEADER) f.write('\n') f.write('auto_generated_cpp_sources = [\n') for path in generated_cpp_sources: src = '{}'.format(os.path.basename(path)) f.write(' "{}",\n'.format(src)) f.write(']\n') def generate_format_functions(self, category, f): count = 0 for event in self.grouped_event_formats[category]: count += 1 if count > 1: f.write('\n') f.write('{}({},\n'.format(FORMAT_REGISTER_MACRO, event.name)) f.write('[] {} {{\n'.format(CHECK_FUNCTION_ARGS, )) f.write(' return event.has_{}_format();'.format( str.lower(event.name))) f.write('},') # end of check function f.write('[] {} {{\n'.format(FORMAT_FUNCTION_ARGS)) f.write(' auto msg = event.{}_format();\n'.format( str.lower(event.name))) f.write(" std::stringstream sout;\n") f.write(' sout << "{}:";\n'.format(event.name)) for field_info in event.remain_fields: field_name = fix_field_name(field_info.name) f.write(' sout << " {}=" << msg.{}();\n'.format(field_name, field_name)) f.write(" return sout.str();\n") f.write("});\n") # end of format function def main(): parser = argparse.ArgumentParser( description='FTrace C++ code generator.') parser.add_argument('-a', dest='allow_list', required=True, type=str, help='event allow list file path') parser.add_argument('-e', dest='events_dir', required=True, type=str, help='event formats directory') parser.add_argument('-p', dest='parser_out', required=False, type=str, help='parser code output directory') parser.add_argument('-f', dest='formatter_out', required=False, type=str, help='formaater code output directory') args = parser.parse_args(sys.argv[1:]) allow_list = args.allow_list events_dir = args.events_dir parser_out = args.parser_out formatter_out = args.formatter_out # check arguments if not os.path.isfile(allow_list): parser.print_usage() exit(1) if not os.path.isdir(events_dir): parser.print_usage() exit(2) if parser_out: if not os.path.isdir(parser_out): parser.print_usage() exit(3) parser_gen = EventParserCodeGenerator(events_dir, allow_list) parser_gen.generate(os.path.join(parser_out)) if formatter_out: if not os.path.isdir(formatter_out): parser.print_usage() exit(4) fmtter_gen = EventFormatterCodeGenerator(events_dir, allow_list) fmtter_gen.generate(formatter_out) if __name__ == '__main__': main()