|  | #!/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_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()] | 
|  |  | 
|  | 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()) |