tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

tracereferences.py (6526B)


      1 #!/usr/bin/env python3
      2 # Copyright 2025 The Chromium Authors
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 """Runs R8's TraceReferences tool to ensure DEX files are valid."""
      6 
      7 import argparse
      8 import json
      9 import logging
     10 import os
     11 import pathlib
     12 import re
     13 import sys
     14 
     15 from util import build_utils
     16 from util import server_utils
     17 import action_helpers  # build_utils adds //build to sys.path.
     18 
     19 _DUMP_DIR_NAME = 'r8inputs_tracerefs'
     20 
     21 _SUPPRESSION_PATTERN = '|'.join([
     22    # Summary contains warning count, which our filtering makes wrong.
     23    r'Warning: Tracereferences found',
     24    r'dalvik\.system',
     25    r'libcore\.io',
     26    r'sun\.misc\.Unsafe',
     27 
     28    # Explicictly guarded by try (NoClassDefFoundError) in Flogger's
     29    # PlatformProvider.
     30    r'com\.google\.common\.flogger\.backend\.google\.GooglePlatform',
     31    r'com\.google\.common\.flogger\.backend\.system\.DefaultPlatform',
     32 
     33    # TODO(agrieve): Exclude these only when use_jacoco_coverage=true.
     34    r'java\.lang\.instrument\.ClassFileTransformer',
     35    r'java\.lang\.instrument\.IllegalClassFormatException',
     36    r'java\.lang\.instrument\.Instrumentation',
     37    r'java\.lang\.management\.ManagementFactory',
     38    r'javax\.management\.MBeanServer',
     39    r'javax\.management\.ObjectInstance',
     40    r'javax\.management\.ObjectName',
     41    r'javax\.management\.StandardMBean',
     42 
     43    # Explicitly guarded by try (NoClassDefFoundError) in Firebase's
     44    # KotlinDetector: com.google.firebase.platforminfo.KotlinDetector.
     45    r'kotlin\.KotlinVersion',
     46 
     47    # Not sure why these two are missing, but they do not seem important.
     48    r'ResultIgnorabilityUnspecified',
     49    r'kotlin\.DeprecationLevel',
     50 
     51    # Assume missing android.* / java.* references are OS APIs that are not in
     52    # android.jar. Not in the above list so as to not match parameter types.
     53    # E.g. Missing method void android.media.MediaRouter2$RouteCallback
     54    # E.g. Missing class android.util.StatsEvent$Builder
     55    r'Missing method \S+ android\.',
     56    r'Missing class android\.',
     57 
     58    # The follow classes are from Android XR system libraries and used on
     59    # immersive environment.
     60    r'Missing class com.google.ar.imp.core\.',
     61 ])
     62 
     63 
     64 def _RunTraceReferences(error_title, r8jar, libs, dex_files, options):
     65  cmd = build_utils.JavaCmd(xmx='2G')
     66 
     67  if options.dump_inputs:
     68    cmd += [f'-Dcom.android.tools.r8.dumpinputtodirectory={_DUMP_DIR_NAME}']
     69 
     70  cmd += [
     71      '-cp', r8jar, 'com.android.tools.r8.tracereferences.TraceReferences',
     72      '--map-diagnostics:MissingDefinitionsDiagnostic', 'error', 'warning',
     73      '--check'
     74  ]
     75 
     76  for path in libs:
     77    cmd += ['--lib', path]
     78  for path in dex_files:
     79    cmd += ['--source', path]
     80 
     81  failed_holder = [False]
     82 
     83  def stderr_filter(stderr):
     84 
     85    had_unfiltered_items = '  ' in stderr
     86    stderr = build_utils.FilterLines(stderr, _SUPPRESSION_PATTERN)
     87    if stderr:
     88      if 'Missing' in stderr:
     89        failed_holder[0] = True
     90        stderr = 'TraceReferences failed: ' + error_title + """
     91 Tip: Build with:
     92        is_java_debug=false
     93        treat_warnings_as_errors=false
     94        enable_proguard_obfuscation=false
     95     and then use dexdump to see which class(s) reference them.
     96 
     97     E.g.:
     98       third_party/android_sdk/public/build-tools/*/dexdump -d \
     99 out/Release/apks/YourApk.apk > dex.txt
    100 """ + stderr
    101      elif had_unfiltered_items:
    102        # Left only with empty headings. All indented items filtered out.
    103        stderr = ''
    104    return stderr
    105 
    106  try:
    107    if options.verbose:
    108      stderr_filter = None
    109    build_utils.CheckOutput(cmd,
    110                            print_stdout=True,
    111                            stderr_filter=stderr_filter,
    112                            fail_on_output=options.warnings_as_errors)
    113  except build_utils.CalledProcessError as e:
    114    # Do not output command line because it is massive and makes the actual
    115    # error message hard to find.
    116    sys.stderr.write(e.output)
    117    sys.exit(1)
    118  return failed_holder[0]
    119 
    120 
    121 def main():
    122  build_utils.InitLogging('TRACEREFS_DEBUG')
    123 
    124  parser = argparse.ArgumentParser()
    125  parser.add_argument('--tracerefs-json')
    126  parser.add_argument('--use-build-server',
    127                      action='store_true',
    128                      help='Always use the build server.')
    129  parser.add_argument('--stamp')
    130  parser.add_argument('--depfile')
    131  parser.add_argument('--warnings-as-errors',
    132                      action='store_true',
    133                      help='Treat all warnings as errors.')
    134  parser.add_argument('--dump-inputs',
    135                      action='store_true',
    136                      help='Use when filing R8 bugs to capture inputs.'
    137                      ' Stores inputs to r8inputs.zip')
    138  parser.add_argument('--verbose',
    139                      action='store_true',
    140                      help='Do not filter output')
    141  args = parser.parse_args()
    142 
    143  with open(args.tracerefs_json) as f:
    144    spec = json.load(f)
    145  r8jar = spec['r8jar']
    146  libs = spec['libs']
    147 
    148  # No need for r8jar here because GN knows about it already.
    149  depfile_deps = []
    150  depfile_deps += libs
    151  for job in spec['jobs']:
    152    depfile_deps += job['jars']
    153 
    154  action_helpers.write_depfile(args.depfile, args.stamp, depfile_deps)
    155 
    156  if server_utils.MaybeRunCommand(name=args.stamp,
    157                                  argv=sys.argv,
    158                                  stamp_file=args.stamp,
    159                                  use_build_server=args.use_build_server):
    160    return
    161 
    162  if args.dump_inputs:
    163    # Dumping inputs causes output to be emitted, avoid failing due to stdout.
    164    args.warnings_as_errors = False
    165 
    166    # Use dumpinputtodirectory instead of dumpinputtofile to avoid failing the
    167    # build and keep running tracereferences.
    168    dump_dir_name = _DUMP_DIR_NAME
    169    dump_dir_path = pathlib.Path(dump_dir_name)
    170    if dump_dir_path.exists():
    171      shutil.rmtree(dump_dir_path)
    172    # The directory needs to exist before r8 adds the zip files in it.
    173    dump_dir_path.mkdir()
    174 
    175  logging.debug('Running TraceReferences')
    176  error_title = 'DEX contains references to non-existent symbols after R8.'
    177  for job in spec['jobs']:
    178    name = job['name']
    179    dex_files = job['jars']
    180    if _RunTraceReferences(error_title, r8jar, libs, dex_files, args):
    181      # Failed but didn't raise due to warnings_as_errors=False
    182      break
    183    error_title = (f'DEX within module "{name}" contains reference(s) to '
    184                   'symbols within child splits')
    185 
    186  logging.info('Checks completed.')
    187  server_utils.MaybeTouch(args.stamp)
    188 
    189 
    190 if __name__ == '__main__':
    191  main()