Update retrace.py to automatically find r8lib mappings.

Bug: 201269335
Change-Id: I00f944937a80a6bc3993d759a9eecd59ba687c07
diff --git a/tools/retrace.py b/tools/retrace.py
index 94ec1b3..0aa5af2 100755
--- a/tools/retrace.py
+++ b/tools/retrace.py
@@ -1,13 +1,14 @@
-#!/usr/bin/env python
+#!/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.
 
 import argparse
-import jdk
+import os
 import subprocess
 import sys
 
+import jdk
 import utils
 
 
@@ -30,7 +31,7 @@
   parser.add_argument(
       '--map',
       help='Path to r8lib map.',
-      default=utils.R8LIB_JAR + '.map')
+      default=None)
   parser.add_argument(
       '--no-r8lib',
       default=False,
@@ -38,7 +39,7 @@
       help='Use r8.jar and not r8lib.jar.')
   parser.add_argument(
       '--stacktrace',
-      help='Path to stacktrace file.',
+      help='Path to stacktrace file (read from stdin if not passed).',
       default=None)
   parser.add_argument(
       '--quiet',
@@ -63,11 +64,75 @@
   return parser.parse_args()
 
 
+def get_map_file(args, temp):
+  # default to using the specified map file.
+  if args.map:
+    return args.map
+
+  # next try to extract it from the tag/version options.
+  map_path = utils.find_cloud_storage_file_from_options('r8lib.jar.map', args)
+  if map_path:
+    return map_path
+
+  # next try to extract it from the stack-trace source-file content.
+  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())
+
+  r8_source_file = None
+  for line in open(args.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
+
+  if r8_source_file:
+    (header, r8_version_or_hash, maphash) = r8_source_file.split('_')
+    if len(r8_version_or_hash) < 40:
+      args.version = r8_version_or_hash
+    else:
+      args.commit_hash = r8_version_or_hash
+    map_path = None
+    try:
+      map_path = utils.find_cloud_storage_file_from_options('r8lib.jar.map', args)
+    except Exception as e:
+      print(e)
+      print('WARNING: Falling back to using local mapping file.')
+
+    if map_path:
+      check_maphash(map_path, maphash)
+      return map_path
+
+  # If no other map file was found, use the local mapping file.
+  return utils.R8LIB_JAR + '.map'
+
+
+def check_maphash(mapping_path, maphash):
+  map_hash_header = "# pg_map_hash: SHA-256 "
+  for line in open(mapping_path, 'r'):
+    if line.startswith(map_hash_header):
+      infile_maphash = line[len(map_hash_header):].strip()
+      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)
+        sys.exit(1)
+
+
 def main():
   args = parse_arguments()
-  map_path = utils.find_cloud_storage_file_from_options(
-      'r8lib.jar.map', args, orElse=args.map)
-  return run(
+  with utils.TempDir() as temp:
+    map_path = get_map_file(args, temp)
+    return run(
       map_path,
       args.stacktrace,
       args.no_r8lib,
@@ -76,6 +141,7 @@
       regex=args.regex,
       verbose=args.verbose)
 
+
 def run(map_path, stacktrace, no_r8lib, quiet=False, debug=False, regex=None, verbose=False):
   retrace_args = [jdk.GetJavaExecutable()]