| #!/usr/bin/env python3 |
| # Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file |
| # for details. All rights reserved. Use of this source code is governed by a |
| # BSD-style license that can be found in the LICENSE file. |
| |
| import argparse |
| import sys |
| |
| COMPANION_CLASS_SUFFIX = '$-CC' |
| EXTERNAL_SYNTHETIC_SUFFIX = '$$ExternalSynthetic' |
| SYNTHETIC_PREFIX = 'S' |
| |
| |
| # Parses a list of class and method descriptors, prefixed with one or more flags |
| # 'H' (hot), 'S' (startup), 'P' (post startup). |
| # |
| # Example: |
| # |
| # HSPLandroidx/compose/runtime/ComposerImpl;->updateValue(Ljava/lang/Object;)V |
| # HSPLandroidx/compose/runtime/ComposerImpl;->updatedNodeCount(I)I |
| # HLandroidx/compose/runtime/ComposerImpl;->validateNodeExpected()V |
| # PLandroidx/compose/runtime/CompositionImpl;->applyChanges()V |
| # HLandroidx/compose/runtime/ComposerKt;->findLocation(Ljava/util/List;I)I |
| # Landroidx/compose/runtime/ComposerImpl; |
| # |
| # See also https://developer.android.com/studio/profile/baselineprofiles. |
| def parse_art_profile(lines): |
| art_profile = {} |
| flags_to_name = {'H': 'hot', 'S': 'startup', 'P': 'post_startup'} |
| for line in lines: |
| line = line.strip() |
| if not line: |
| continue |
| flags = {'hot': False, 'startup': False, 'post_startup': False} |
| while line[0] in flags_to_name: |
| flag_abbreviation = line[0] |
| flag_name = flags_to_name.get(flag_abbreviation) |
| flags[flag_name] = True |
| line = line[1:] |
| while line.startswith('['): |
| line = line[1:] |
| assert line.startswith('L'), line |
| descriptor = line |
| art_profile[descriptor] = flags |
| return art_profile |
| |
| |
| def transform_art_profile_to_r8_startup_list(art_profile, |
| generalize_synthetics=False): |
| r8_startup_list = {} |
| for startup_descriptor, flags in art_profile.items(): |
| transformed_startup_descriptor = transform_synthetic_descriptor( |
| startup_descriptor) if generalize_synthetics else startup_descriptor |
| r8_startup_list[transformed_startup_descriptor] = { |
| 'conditional_startup': False, |
| 'hot': flags['hot'], |
| 'startup': flags['startup'], |
| 'post_startup': flags['post_startup'] |
| } |
| return r8_startup_list |
| |
| |
| def transform_synthetic_descriptor(descriptor): |
| companion_class_index = descriptor.find(COMPANION_CLASS_SUFFIX) |
| if companion_class_index >= 0: |
| return SYNTHETIC_PREFIX + descriptor[0:companion_class_index] + ';' |
| external_synthetic_index = descriptor.find(EXTERNAL_SYNTHETIC_SUFFIX) |
| if external_synthetic_index >= 0: |
| return SYNTHETIC_PREFIX + descriptor[0:external_synthetic_index] + ';' |
| return descriptor |
| |
| |
| def filter_r8_startup_list(r8_startup_list, options): |
| filtered_r8_startup_list = {} |
| for startup_descriptor, flags in r8_startup_list.items(): |
| if not options.include_post_startup \ |
| and flags.get('post_startup') \ |
| and not flags.get('startup'): |
| continue |
| filtered_r8_startup_list[startup_descriptor] = flags |
| return filtered_r8_startup_list |
| |
| |
| def parse_options(argv): |
| result = argparse.ArgumentParser( |
| description='Utilities for converting an ART profile into an R8 startup ' |
| 'list.') |
| result.add_argument('--art-profile', help='Path to the ART profile') |
| result.add_argument( |
| '--include-post-startup', |
| help='Include post startup classes and methods in the R8 ' |
| 'startup list', |
| action='store_true', |
| default=False) |
| result.add_argument('--out', help='Where to store the R8 startup list') |
| options, args = result.parse_known_args(argv) |
| return options, args |
| |
| |
| def main(argv): |
| (options, args) = parse_options(argv) |
| with open(options.art_profile, 'r') as f: |
| art_profile = parse_art_profile(f.read().splitlines()) |
| r8_startup_list = transform_art_profile_to_r8_startup_list(art_profile) |
| filtered_r8_startup_list = filter_r8_startup_list(r8_startup_list, options) |
| if options.out is not None: |
| with open(options.out, 'w') as f: |
| for startup_descriptor, flags in filtered_r8_startup_list.items(): |
| f.write(startup_descriptor) |
| f.write('\n') |
| else: |
| for startup_descriptor, flags in filtered_r8_startup_list.items(): |
| print(startup_descriptor) |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main(sys.argv[1:])) |