| #!/usr/bin/env python3 | |
| # Copyright (c) 2019, 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. | |
| from os import path | |
| import argparse | |
| import os | |
| import subprocess | |
| import sys | |
| import jdk | |
| import utils | |
| def parse_arguments(): | |
| parser = argparse.ArgumentParser( | |
| description='R8lib wrapper for retrace tool.') | |
| parser.add_argument('-c', | |
| '--commit-hash', | |
| '--commit_hash', | |
| help='Commit hash to download r8lib map file for.', | |
| default=None) | |
| parser.add_argument('--version', | |
| help='Version to download r8lib map file for.', | |
| default=None) | |
| parser.add_argument('--tag', | |
| help='Tag to download r8lib map file for.', | |
| default=None) | |
| parser.add_argument( | |
| '--exclude-deps', | |
| '--exclude_deps', | |
| default=None, | |
| action='store_true', | |
| help='Use the exclude-deps version of the mapping file.') | |
| parser.add_argument('--map', | |
| help='Path to r8lib map in text format.', | |
| default=None) | |
| parser.add_argument('--partition-map', | |
| '--partition_map', | |
| help='Path to r8lib map in ZIP partition format.', | |
| default=None) | |
| parser.add_argument('--r8jar', help='Path to r8 jar.', default=None) | |
| parser.add_argument('--no-r8lib', | |
| '--no_r8lib', | |
| default=False, | |
| action='store_true', | |
| help='Use r8.jar and not r8lib.jar.') | |
| parser.add_argument( | |
| '--stacktrace', | |
| help='Path to stacktrace file (read from stdin if not passed).', | |
| default=None) | |
| parser.add_argument('--quiet', | |
| default=None, | |
| action='store_true', | |
| help='Disables diagnostics printing to stdout.') | |
| parser.add_argument( | |
| '--debug-agent', | |
| '--debug_agent', | |
| default=None, | |
| action='store_true', | |
| help='Attach a debug-agent to the retracer java process.') | |
| parser.add_argument( | |
| '--regex', | |
| default=None, | |
| help='Sets a custom regular expression used for parsing') | |
| parser.add_argument('--verbose', | |
| default=None, | |
| action='store_true', | |
| help='Enables verbose retracing.') | |
| parser.add_argument('--disable-map-validation', | |
| default=None, | |
| action='store_true', | |
| help='Disable validation of map hash.') | |
| parser.add_argument( | |
| '--disable-partition-map', | |
| default=False, | |
| action='store_true', | |
| help='Disable use of partition map (unless one was explicitly specified' | |
| ' with --partition-map).') | |
| return parser.parse_args() | |
| def get_map_file(args, temp): | |
| # Default to using the specified map file. | |
| if args.map: | |
| return (False, args.map) | |
| if args.partition_map: | |
| return (True, args.partition_map) | |
| for use_partition_map in ([False] | |
| if args.disable_partition_map else [True, False]): | |
| # Try to extract map from the tag/version options. | |
| map_path = utils.find_cloud_storage_file_from_options( | |
| 'r8lib.jar_map.zip' if use_partition_map else 'r8lib.jar.map', args) | |
| if map_path: | |
| return (use_partition_map, map_path) | |
| # Try to extract map from the stack-trace source-file content. | |
| r8_source_file = get_r8_source_file_attribute(args.stacktrace) | |
| if r8_source_file: | |
| map_path = get_map_from_r8_source_file_attribute( | |
| args, r8_source_file, use_partition_map) | |
| if map_path: | |
| return (use_partition_map, map_path) | |
| # If no other map file was found, use the local mapping file. | |
| if args.r8jar: | |
| return (False, args.r8jar + ".map") | |
| return (False, utils.R8LIB_MAP) | |
| # Extract the R8 source file attribute from the stack trace if present. | |
| def get_r8_source_file_attribute(stacktrace): | |
| r8_source_file = None | |
| for line in open(stacktrace, 'r'): | |
| start = line.rfind("(R8_") | |
| if start > 0: | |
| end = line.find(":", start) | |
| content = line[start + 1:end] | |
| if r8_source_file: | |
| if content != r8_source_file: | |
| print( | |
| 'WARNING: there are multiple distinct R8 source files:') | |
| print(' ' + r8_source_file) | |
| print(' ' + content) | |
| else: | |
| r8_source_file = content | |
| return r8_source_file | |
| def get_map_from_r8_source_file_attribute(args, r8_source_file, | |
| use_partition_map): | |
| (header, r8_version_or_hash, maphash) = r8_source_file.split('_') | |
| # If the command-line specified --exclude-deps then assume it is as previous | |
| # versions will not be marked as such in the source-file line. | |
| is_excldeps = args.exclude_deps | |
| excldeps_start = r8_version_or_hash.find('+excldeps') | |
| if (excldeps_start > 0): | |
| is_excldeps = True | |
| r8_version_or_hash = r8_version_or_hash[0:excldeps_start] | |
| if len(r8_version_or_hash) < 40: | |
| args.version = r8_version_or_hash | |
| else: | |
| args.commit_hash = r8_version_or_hash | |
| map_path = None | |
| # First check local mapping file for map hash. | |
| if path.exists(utils.R8LIB_MAP) and get_hash_from_map_file( | |
| utils.R8LIB_MAP) == maphash: | |
| return utils.R8LIB_PARTITION_MAP if use_partition_map else utils.R8LIB_MAP | |
| try: | |
| # Look for mapping file on GCS based on the version or commit hash | |
| # found in the source file atttribute. | |
| map_path = utils.find_cloud_storage_file_from_options( | |
| 'r8lib' + ('-exclude-deps' if is_excldeps else '') + | |
| ('.jar_map.zip' if use_partition_map else '.jar.map'), args) | |
| if map_path: | |
| # Partition map is not validated. The mapping file header is | |
| # in the METADATA blob together with some other imformation. | |
| if not args.disable_map_validation and not use_partition_map: | |
| check_maphash(map_path, maphash, args) | |
| return map_path | |
| except Exception as e: | |
| print(e) | |
| print('WARNING: Falling back to using local mapping file.') | |
| # If no other map file was found, use the local mapping file. | |
| if args.r8jar: | |
| return args.r8jar + ('_map.zip' if use_partition_map else ".map") | |
| return utils.R8LIB_PARTITION_MAP if use_partition_map else utils.R8LIB_MAP | |
| def check_maphash(mapping_path, maphash, args): | |
| infile_maphash = get_hash_from_map_file(mapping_path) | |
| if infile_maphash != maphash: | |
| print('ERROR: The mapping file hash does not match the R8 line') | |
| print(' In mapping file: ' + infile_maphash) | |
| print(' In source file: ' + maphash) | |
| if (not args.exclude_deps): | |
| print( | |
| 'If this could be a version without internalized dependencies ' | |
| + 'try passing --exclude-deps') | |
| sys.exit(1) | |
| def get_hash_from_map_file(mapping_path): | |
| map_hash_header = "# pg_map_hash: SHA-256 " | |
| for line in open(mapping_path, 'r'): | |
| if line.startswith(map_hash_header): | |
| return line[len(map_hash_header):].strip() | |
| def main(): | |
| args = parse_arguments() | |
| with utils.TempDir() as temp: | |
| # Get the stack trace to retrace. | |
| if not args.stacktrace: | |
| if not args.quiet: | |
| print('Waiting for stack-trace input...') | |
| args.stacktrace = os.path.join(temp, 'stacktrace.txt') | |
| open(args.stacktrace, 'w').writelines(sys.stdin.readlines()) | |
| (is_partition_map, map_path) = get_map_file(args, temp) | |
| return run(map_path, | |
| is_partition_map, | |
| args.stacktrace, | |
| args.r8jar, | |
| args.no_r8lib, | |
| quiet=args.quiet, | |
| debug=args.debug_agent, | |
| regex=args.regex, | |
| verbose=args.verbose) | |
| def run(map_path, | |
| is_partition_map, | |
| stacktrace, | |
| r8jar, | |
| no_r8lib, | |
| quiet=False, | |
| debug=False, | |
| regex=None, | |
| verbose=False): | |
| retrace_args = [jdk.GetJavaExecutable(jdk.GetDefaultJdkHome())] | |
| if debug: | |
| retrace_args.append( | |
| '-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005' | |
| ) | |
| if not r8jar: | |
| r8jar = utils.R8_JAR if no_r8lib else utils.R8LIB_JAR | |
| retrace_args += ['-cp', r8jar, 'com.android.tools.r8.retrace.Retrace'] | |
| if is_partition_map: | |
| retrace_args.extend(['--partition-map', map_path]) | |
| else: | |
| retrace_args.append(map_path) | |
| if regex: | |
| retrace_args.append('--regex') | |
| retrace_args.append(regex) | |
| if quiet: | |
| retrace_args.append('--quiet') | |
| if stacktrace: | |
| retrace_args.append(stacktrace) | |
| if verbose: | |
| retrace_args.append('--verbose') | |
| utils.PrintCmd(retrace_args, quiet=quiet) | |
| return subprocess.call(retrace_args) | |
| if __name__ == '__main__': | |
| sys.exit(main()) |