Home | History | Annotate | Download | only in tools
      1 #!/usr/bin/env python
      2 # Copyright (C) 2018 The Android Open Source Project
      3 #
      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.
     15 
     16 from __future__ import absolute_import
     17 from __future__ import division
     18 from __future__ import print_function
     19 import os
     20 import re
     21 import sys
     22 
     23 COMMON_PROTOS = (
     24   'protos/perfetto/common/android_log_constants.proto',
     25   'protos/perfetto/common/sys_stats_counters.proto',
     26 )
     27 
     28 CONFIG_PROTOS = (
     29   'protos/perfetto/config/android/android_log_config.proto',
     30   'protos/perfetto/config/chrome/chrome_config.proto',
     31   'protos/perfetto/config/data_source_config.proto',
     32   'protos/perfetto/config/ftrace/ftrace_config.proto',
     33   'protos/perfetto/config/inode_file/inode_file_config.proto',
     34   'protos/perfetto/config/power/android_power_config.proto',
     35   'protos/perfetto/config/process_stats/process_stats_config.proto',
     36   'protos/perfetto/config/sys_stats/sys_stats_config.proto',
     37   'protos/perfetto/config/test_config.proto',
     38   'protos/perfetto/config/trace_config.proto',
     39   'protos/perfetto/config/profiling/heapprofd_config.proto',
     40   'protos/perfetto/config/android/packages_list_config.proto',
     41 )
     42 
     43 MERGED_CONFIG_PROTO = 'protos/perfetto/config/perfetto_config.proto'
     44 
     45 TRACE_PROTOS = (
     46   'protos/perfetto/common/trace_stats.proto', # only referenced by trace protos
     47   'protos/perfetto/trace/android/android_log.proto',
     48   'protos/perfetto/trace/android/packages_list.proto',
     49   'protos/perfetto/trace/clock_snapshot.proto',
     50   'protos/perfetto/trace/filesystem/inode_file_map.proto',
     51   'protos/perfetto/trace/ftrace/binder.proto',
     52   'protos/perfetto/trace/ftrace/block.proto',
     53   'protos/perfetto/trace/ftrace/clk.proto',
     54   'protos/perfetto/trace/ftrace/ext4.proto',
     55   'protos/perfetto/trace/ftrace/f2fs.proto',
     56   'protos/perfetto/trace/ftrace/filemap.proto',
     57   'protos/perfetto/trace/ftrace/ftrace.proto',
     58   'protos/perfetto/trace/ftrace/ftrace_event.proto',
     59   'protos/perfetto/trace/ftrace/ftrace_event_bundle.proto',
     60   'protos/perfetto/trace/ftrace/ftrace_stats.proto',
     61   'protos/perfetto/trace/ftrace/generic.proto',
     62   'protos/perfetto/trace/ftrace/kmem.proto',
     63   'protos/perfetto/trace/ftrace/lowmemorykiller.proto',
     64   'protos/perfetto/trace/ftrace/mm_event.proto',
     65   'protos/perfetto/trace/ftrace/power.proto',
     66   'protos/perfetto/trace/ftrace/raw_syscalls.proto',
     67   'protos/perfetto/trace/ftrace/sched.proto',
     68   'protos/perfetto/trace/ftrace/signal.proto',
     69   'protos/perfetto/trace/ftrace/systrace.proto',
     70   'protos/perfetto/trace/ftrace/task.proto',
     71   'protos/perfetto/trace/ftrace/vmscan.proto',
     72   'protos/perfetto/trace/interned_data/interned_data.proto',
     73   'protos/perfetto/trace/power/battery_counters.proto',
     74   'protos/perfetto/trace/power/power_rails.proto',
     75   'protos/perfetto/trace/profiling/profile_packet.proto',
     76   'protos/perfetto/trace/ps/process_stats.proto',
     77   'protos/perfetto/trace/ps/process_tree.proto',
     78   'protos/perfetto/trace/sys_stats/sys_stats.proto',
     79   'protos/perfetto/trace/system_info.proto',
     80   'protos/perfetto/trace/trace.proto',
     81   'protos/perfetto/trace/trace_packet.proto',
     82   'protos/perfetto/trace/track_event/debug_annotation.proto',
     83   'protos/perfetto/trace/track_event/process_descriptor.proto',
     84   'protos/perfetto/trace/track_event/task_execution.proto',
     85   'protos/perfetto/trace/track_event/thread_descriptor.proto',
     86   'protos/perfetto/trace/track_event/track_event.proto',
     87   'protos/perfetto/trace/trigger.proto',
     88 )
     89 
     90 MERGED_TRACE_PROTO = 'protos/perfetto/trace/perfetto_trace.proto'
     91 
     92 REPLACEMENT_HEADER = '''
     93 // AUTOGENERATED - DO NOT EDIT
     94 // ---------------------------
     95 // This file has been generated by
     96 // AOSP://external/perfetto/%s
     97 // merging the perfetto config protos.
     98 // This fused proto is intended to be copied in:
     99 //  - Android tree, for statsd.
    100 //  - Google internal repos.
    101 
    102 syntax = "proto2";
    103 
    104 package perfetto.protos;
    105 '''
    106 
    107 def merge_protos(proto_paths, output_path):
    108   root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
    109   merged_content = ''
    110   for proto in proto_paths:
    111     path = os.path.join(root_dir, proto)
    112     with open(path) as f:
    113       content = f.read()
    114 
    115     # Remove header
    116     header = re.match(r'\/(\*|\/)(?:.|\s)*?package .*;\n', content)
    117     header = header.group(0)
    118     content = content[len(header):]
    119     if merged_content == '':
    120       merged_content += REPLACEMENT_HEADER.lstrip() % __file__
    121     content = re.sub(r'^import.*?\n\n?', '', content, flags=re.MULTILINE)
    122     merged_content += '\n// Begin of %s\n' % proto
    123     merged_content += content
    124     merged_content += '\n// End of %s\n' % proto
    125 
    126   definitions_re = r'^ *(?:message|enum) ([A-Z][A-Za-z0-9].*) {'
    127   definitions = re.finditer(definitions_re, merged_content, re.MULTILINE)
    128   types = set((match.group(1) for match in definitions))
    129 
    130   uses_re = r'^( +)(?:repeated)?(?:optional)?\s'\
    131             r'?([A-Z]\w+.*)\s+[a-z]\w+\s*=\s*(\d+);'
    132   uses = re.finditer(uses_re, merged_content, re.MULTILINE)
    133   substitutions = []
    134   for use in uses:
    135     everything = use.group(0)
    136     indentation = use.group(1)
    137     used_type = use.group(2)
    138     field_number = use.group(3)
    139     if used_type not in types:
    140       replacement = '{}// removed field with id {}'.format(
    141           indentation, field_number)
    142       substitutions.append((everything, replacement))
    143 
    144   for before, after in substitutions:
    145     merged_content = merged_content.replace(before, after)
    146 
    147   out_path = os.path.join(root_dir, output_path)
    148 
    149   prev_content = None
    150   if os.path.exists(out_path):
    151     with open(out_path, 'rb') as fprev:
    152       prev_content = fprev.read()
    153 
    154   if prev_content == merged_content:
    155     return True
    156 
    157   if '--check-only' in sys.argv:
    158     return False
    159 
    160   print('Updating {}'.format(output_path))
    161   with open(out_path, 'wb') as fout:
    162     fout.write(merged_content)
    163   return True
    164 
    165 def main():
    166   config_result = merge_protos(COMMON_PROTOS + CONFIG_PROTOS,
    167                                MERGED_CONFIG_PROTO)
    168   trace_result = merge_protos(COMMON_PROTOS + TRACE_PROTOS + CONFIG_PROTOS,
    169                               MERGED_TRACE_PROTO)
    170   return 0 if config_result and trace_result else 1
    171 
    172 if __name__ == '__main__':
    173   sys.exit(main())
    174