diff options
Diffstat (limited to 'chromium/build/android')
109 files changed, 3223 insertions, 282 deletions
diff --git a/chromium/build/android/BUILD.gn b/chromium/build/android/BUILD.gn index 523636d137a..f6c9ab41ff5 100644 --- a/chromium/build/android/BUILD.gn +++ b/chromium/build/android/BUILD.gn @@ -54,6 +54,12 @@ if (enable_java_templates) { "android_ndk_root=" + rebase_path(android_ndk_root, root_build_dir) + CR _data += "android_tool_prefix=" + rebase_path(android_tool_prefix, root_build_dir) + CR + if (defined(android_secondary_abi_cpu)) { + _secondary_label_info = + get_label_info(":foo($android_secondary_abi_toolchain)", "root_out_dir") + _data += "android_secondary_abi_toolchain=" + + rebase_path(_secondary_label_info, root_build_dir) + CR + } write_file(android_build_vars, _data) } @@ -71,8 +77,8 @@ copy("cpplib_unstripped") { action("cpplib_stripped") { _strip_bin = "${android_tool_prefix}strip" _soname = "libc++_shared.so" - _input_so = "${root_shlib_dir}/lib.unstripped/${_soname}" - _output_so = "${root_shlib_dir}/${_soname}" + _input_so = "${root_out_dir}/lib.unstripped/${_soname}" + _output_so = "${root_out_dir}/${_soname}" deps = [ ":cpplib_unstripped", @@ -117,8 +123,14 @@ python_library("test_runner_py") { "${android_sdk_root}/platform-tools/adb", "//third_party/catapult/third_party/gsutil/", "//third_party/catapult/devil/devil/devil_dependencies.json", - "//third_party/proguard/lib/proguard.jar", ] + + # Proguard is needed only when using apks (rather than native executables). + if (enable_java_templates) { + deps = [ + "//third_party/proguard:proguard603_java", + ] + } } python_library("logdog_wrapper_py") { diff --git a/chromium/build/android/PRESUBMIT.py b/chromium/build/android/PRESUBMIT.py index 65d37d17a76..9617b5197cc 100644 --- a/chromium/build/android/PRESUBMIT.py +++ b/chromium/build/android/PRESUBMIT.py @@ -74,6 +74,7 @@ def CommonChecks(input_api, output_api): J('pylib', 'results', 'json_results_test.py'), J('pylib', 'symbols', 'apk_native_libs_unittest.py'), J('pylib', 'symbols', 'elf_symbolizer_unittest.py'), + J('pylib', 'symbols', 'symbol_utils_unittest.py'), J('pylib', 'utils', 'decorators_test.py'), J('pylib', 'utils', 'device_dependencies_test.py'), J('pylib', 'utils', 'dexdump_test.py'), diff --git a/chromium/build/android/apk_operations.py b/chromium/build/android/apk_operations.py index 33c3ef05559..2b17c19cdd4 100755 --- a/chromium/build/android/apk_operations.py +++ b/chromium/build/android/apk_operations.py @@ -16,6 +16,7 @@ import posixpath import random import re import shlex +import shutil import sys import tempfile import textwrap @@ -39,7 +40,11 @@ from incremental_install import installer from pylib import constants from pylib.symbols import deobfuscator from pylib.utils import simpleperf +from pylib.utils import app_bundle_utils +with devil_env.SysPath(os.path.join(os.path.dirname(__file__), '..', '..', + 'build', 'android', 'gyp')): + import bundletool # Matches messages only on pre-L (Dalvik) that are spammy and unimportant. _DALVIK_IGNORE_PATTERN = re.compile('|'.join([ @@ -74,6 +79,55 @@ def _InstallApk(devices, apk, install_dict): device_utils.DeviceUtils.parallel(devices).pMap(install) +# A named tuple containing the information needed to convert a bundle into +# an installable .apks archive. +# Fields: +# bundle_path: Path to input bundle file. +# bundle_apk_path: Path to output bundle .apks archive file. +# aapt2_path: Path to aapt2 tool. +# keystore_path: Path to keystore file. +# keystore_password: Password for the keystore file. +# keystore_alias: Signing key name alias within the keystore file. +BundleGenerationInfo = collections.namedtuple( + 'BundleGenerationInfo', + 'bundle_path,bundle_apks_path,aapt2_path,keystore_path,keystore_password,' + 'keystore_alias') + + +def _GenerateBundleApks(info): + """Generate an .apks archive from a bundle on demand. + + Args: + info: A BundleGenerationInfo instance. + Returns: + Path of output .apks archive. + """ + app_bundle_utils.GenerateBundleApks( + info.bundle_path, + info.bundle_apks_path, + info.aapt2_path, + info.keystore_path, + info.keystore_password, + info.keystore_alias) + return info.bundle_apks_path + + +def _InstallBundle(devices, bundle_apks): + def install(device): + # NOTE: For now, installation requires running 'bundletool install-apks'. + # TODO(digit): Add proper support for bundles to devil instead, then use it. + cmd_args = [ + 'install-apks', + '--apks=' + bundle_apks, + '--adb=' + adb_wrapper.AdbWrapper.GetAdbPath(), + '--device-id=' + device.serial + ] + bundletool.RunBundleTool(cmd_args) + + logging.info('Installing bundle.') + device_utils.DeviceUtils.parallel(devices).pMap(install) + + def _UninstallApk(devices, install_dict, package_name): def uninstall(device): if install_dict: @@ -735,21 +789,27 @@ class _Command(object): supports_incremental = False accepts_command_line_flags = False accepts_args = False + need_device_args = True all_devices_by_default = False calls_exec = False supports_multiple_devices = True - def __init__(self, from_wrapper_script): + def __init__(self, from_wrapper_script, is_bundle): self._parser = None self._from_wrapper_script = from_wrapper_script self.args = None self.apk_helper = None self.install_dict = None self.devices = None - # Do not support incremental install outside the context of wrapper scripts. - if not from_wrapper_script: + self.is_bundle = is_bundle + self.bundle_generation_info = None + # Only support incremental install from APK wrapper scripts. + if is_bundle or not from_wrapper_script: self.supports_incremental = False + def RegisterBundleGenerationInfo(self, bundle_generation_info): + self.bundle_generation_info = bundle_generation_info + def _RegisterExtraArgs(self, subp): pass @@ -760,17 +820,18 @@ class _Command(object): formatter_class=argparse.RawDescriptionHelpFormatter) self._parser = subp subp.set_defaults(command=self) - subp.add_argument('--all', - action='store_true', - default=self.all_devices_by_default, - help='Operate on all connected devices.',) - subp.add_argument('-d', - '--device', - action='append', - default=[], - dest='devices', - help='Target device for script to work on. Enter ' - 'multiple times for multiple devices.') + if self.need_device_args: + subp.add_argument('--all', + action='store_true', + default=self.all_devices_by_default, + help='Operate on all connected devices.',) + subp.add_argument('-d', + '--device', + action='append', + default=[], + dest='devices', + help='Target device for script to work on. Enter ' + 'multiple times for multiple devices.') subp.add_argument('-v', '--verbose', action='count', @@ -780,15 +841,29 @@ class _Command(object): group = subp.add_argument_group('%s arguments' % self.name) if self.needs_package_name: - # Always gleaned from apk when using wrapper scripts. - group.add_argument('--package-name', - help=argparse.SUPPRESS if self._from_wrapper_script else ( - "App's package name.")) + # Three cases to consider here, since later code assumes + # self.args.package_name always exists, even if None: + # + # - Called from a bundle wrapper script, the package_name is already + # set through parser.set_defaults(), so don't call add_argument() + # to avoid overriding its value. + # + # - Called from an apk wrapper script. The --package-name argument + # should not appear, but self.args.package_name will be gleaned from + # the --apk-path file later. + # + # - Called directly, then --package-name is required on the command-line. + # + if not self.is_bundle: + group.add_argument( + '--package-name', + help=argparse.SUPPRESS if self._from_wrapper_script else ( + "App's package name.")) if self.needs_apk_path or self.needs_package_name: # Adding this argument to the subparser would override the set_defaults() # value set by on the parent parser (even if None). - if not self._from_wrapper_script: + if not self._from_wrapper_script and not self.is_bundle: group.add_argument('--apk-path', required=self.needs_apk_path, help='Path to .apk') @@ -822,26 +897,27 @@ class _Command(object): self._RegisterExtraArgs(group) def ProcessArgs(self, args): - devices = device_utils.DeviceUtils.HealthyDevices( - device_arg=args.devices, - enable_device_files_cache=bool(args.output_directory), - default_retries=0) self.args = args - self.devices = devices + self.devices = [] + if self.need_device_args: + self.devices = device_utils.DeviceUtils.HealthyDevices( + device_arg=args.devices, + enable_device_files_cache=bool(args.output_directory), + default_retries=0) # TODO(agrieve): Device cache should not depend on output directory. # Maybe put int /tmp? - _LoadDeviceCaches(devices, args.output_directory) + _LoadDeviceCaches(self.devices, args.output_directory) # Ensure these keys always exist. They are set by wrapper scripts, but not # always added when not using wrapper scripts. args.__dict__.setdefault('apk_path', None) args.__dict__.setdefault('incremental_json', None) try: - if len(devices) > 1: + if len(self.devices) > 1: if not self.supports_multiple_devices: - self._parser.error(device_errors.MultipleDevicesError(devices)) + self._parser.error(device_errors.MultipleDevicesError(self.devices)) if not args.all and not args.devices: - self._parser.error(_GenerateMissingAllFlagMessage(devices)) + self._parser.error(_GenerateMissingAllFlagMessage(self.devices)) incremental_apk_exists = False @@ -869,7 +945,8 @@ class _Command(object): self._parser.error('Both incremental and non-incremental apks exist. ' 'Select using --incremental or --non-incremental') - if self.needs_apk_path or args.apk_path or args.incremental_json: + if ((self.needs_apk_path and not self.is_bundle) or args.apk_path + or args.incremental_json): if args.incremental_json: if incremental_apk_exists: self.install_dict = install_dict @@ -893,9 +970,9 @@ class _Command(object): # Save cache now if command will not get a chance to afterwards. if self.calls_exec: - _SaveDeviceCaches(devices, args.output_directory) + _SaveDeviceCaches(self.devices, args.output_directory) except: - _SaveDeviceCaches(devices, args.output_directory) + _SaveDeviceCaches(self.devices, args.output_directory) raise @@ -910,17 +987,21 @@ class _DevicesCommand(_Command): class _InstallCommand(_Command): name = 'install' - description = 'Installs the APK to one or more devices.' + description = 'Installs the APK or bundle to one or more devices.' needs_apk_path = True supports_incremental = True def Run(self): - _InstallApk(self.devices, self.apk_helper, self.install_dict) + if self.is_bundle: + bundle_apks_path = _GenerateBundleApks(self.bundle_generation_info) + _InstallBundle(self.devices, bundle_apks_path) + else: + _InstallApk(self.devices, self.apk_helper, self.install_dict) class _UninstallCommand(_Command): name = 'uninstall' - description = 'Removes the APK to one or more devices.' + description = 'Removes the APK or bundle from one or more devices.' needs_package_name = True def Run(self): @@ -929,8 +1010,8 @@ class _UninstallCommand(_Command): class _LaunchCommand(_Command): name = 'launch' - description = ('Sends a launch intent for the APK after first writing the ' - 'command-line flags file.') + description = ('Sends a launch intent for the APK or bundle after first ' + 'writing the command-line flags file.') needs_package_name = True accepts_command_line_flags = True all_devices_by_default = True @@ -950,6 +1031,10 @@ class _LaunchCommand(_Command): group.add_argument('url', nargs='?', help='A URL to launch with.') def Run(self): + if self.args.url and self.is_bundle: + # TODO(digit): Support this, maybe by using 'dumpsys' as described + # in the _LaunchUrl() comment. + raise Exception('Launching with URL not supported for bundles yet!') _LaunchUrl(self.devices, self.args.package_name, argv=self.args.args, command_line_flags_file=self.args.command_line_flags_file, url=self.args.url, apk=self.apk_helper, @@ -1214,6 +1299,26 @@ class _RunCommand(_InstallCommand, _LaunchCommand, _LogcatCommand): _LogcatCommand.Run(self) +class _BuildBundleApks(_Command): + name = 'build-bundle-apks' + description = ('Build the .apks archive from an Android app bundle, and ' + 'optionally copy it to a specific destination.') + need_device_args = False + + def _RegisterExtraArgs(self, group): + group.add_argument('--output-apks', + help='Destination path for .apks archive copy.') + + def Run(self): + bundle_apks_path = _GenerateBundleApks(self.bundle_generation_info) + if self.args.output_apks: + try: + shutil.copyfile(bundle_apks_path, self.args.output_apks) + except shutil.Error as e: + logging.exception('Failed to copy .apks archive: %s', e) + + +# Shared commands for regular APKs and app bundles. _COMMANDS = [ _DevicesCommand, _InstallCommand, @@ -1233,10 +1338,17 @@ _COMMANDS = [ _RunCommand, ] +# Commands specific to app bundles. +_BUNDLE_COMMANDS = [ + _BuildBundleApks, +] -def _ParseArgs(parser, from_wrapper_script): + +def _ParseArgs(parser, from_wrapper_script, is_bundle): subparsers = parser.add_subparsers() - commands = [clazz(from_wrapper_script) for clazz in _COMMANDS] + command_list = _COMMANDS + (_BUNDLE_COMMANDS if is_bundle else []) + commands = [clazz(from_wrapper_script, is_bundle) for clazz in command_list] + for command in commands: if from_wrapper_script or not command.needs_output_directory: command.RegisterArgs(subparsers) @@ -1249,21 +1361,21 @@ def _ParseArgs(parser, from_wrapper_script): return parser.parse_args(argv) -def _RunInternal(parser, output_directory=None): +def _RunInternal(parser, output_directory=None, bundle_generation_info=None): colorama.init() parser.set_defaults(output_directory=output_directory) from_wrapper_script = bool(output_directory) - args = _ParseArgs(parser, from_wrapper_script) + args = _ParseArgs(parser, from_wrapper_script, bool(bundle_generation_info)) run_tests_helper.SetLogLevel(args.verbose_count) args.command.ProcessArgs(args) + if bundle_generation_info: + args.command.RegisterBundleGenerationInfo(bundle_generation_info) args.command.Run() # Incremental install depends on the cache being cleared when uninstalling. if args.command.name != 'uninstall': _SaveDeviceCaches(args.command.devices, output_directory) -# TODO(agrieve): Remove =None from target_cpu on or after October 2017. -# It exists only so that stale wrapper scripts continue to work. def Run(output_directory, apk_path, incremental_json, command_line_flags_file, target_cpu, proguard_mapping_path): """Entry point for generated wrapper scripts.""" @@ -1280,6 +1392,47 @@ def Run(output_directory, apk_path, incremental_json, command_line_flags_file, _RunInternal(parser, output_directory=output_directory) +def RunForBundle(output_directory, bundle_path, bundle_apks_path, + aapt2_path, keystore_path, keystore_password, + keystore_alias, package_name, command_line_flags_file, + proguard_mapping_path, target_cpu): + """Entry point for generated app bundle wrapper scripts. + + Args: + output_dir: Chromium output directory path. + bundle_path: Input bundle path. + bundle_apks_path: Output bundle .apks archive path. + aapt2_path: Aapt2 tool path. + keystore_path: Keystore file path. + keystore_password: Keystore password. + keystore_alias: Signing key name alias in keystore file. + package_name: Application's package name. + command_line_flags_file: Optional. Name of an on-device file that will be + used to store command-line flags for this bundle. + proguard_mapping_path: Input path to the Proguard mapping file, used to + deobfuscate Java stack traces. + target_cpu: Chromium target CPU name, used by the 'gdb' command. + """ + constants.SetOutputDirectory(output_directory) + devil_chromium.Initialize(output_directory=output_directory) + bundle_generation_info = BundleGenerationInfo( + bundle_path=bundle_path, + bundle_apks_path=bundle_apks_path, + aapt2_path=aapt2_path, + keystore_path=keystore_path, + keystore_password=keystore_password, + keystore_alias=keystore_alias) + + parser = argparse.ArgumentParser() + parser.set_defaults( + package_name=package_name, + command_line_flags_file=command_line_flags_file, + proguard_mapping_path=proguard_mapping_path, + target_cpu=target_cpu) + _RunInternal(parser, output_directory=output_directory, + bundle_generation_info=bundle_generation_info) + + def main(): devil_chromium.Initialize() _RunInternal(argparse.ArgumentParser(), output_directory=None) diff --git a/chromium/build/android/asan_symbolize.py b/chromium/build/android/asan_symbolize.py index 19d71ca3aff..9f2e88a60d6 100755 --- a/chromium/build/android/asan_symbolize.py +++ b/chromium/build/android/asan_symbolize.py @@ -14,6 +14,7 @@ import sys from pylib import constants from pylib.constants import host_paths +# pylint: disable=wrong-import-order # Uses symbol.py from third_party/android_platform, not python's. with host_paths.SysPath( host_paths.ANDROID_PLATFORM_DEVELOPMENT_SCRIPTS_PATH, diff --git a/chromium/build/android/devil_chromium.py b/chromium/build/android/devil_chromium.py index af2141a552d..2ae643674bd 100644 --- a/chromium/build/android/devil_chromium.py +++ b/chromium/build/android/devil_chromium.py @@ -167,4 +167,3 @@ def Initialize(output_directory=None, custom_deps=None, adb_path=None): devil_env.config.Initialize( configs=[devil_dynamic_config], config_files=[_DEVIL_CONFIG]) - diff --git a/chromium/build/android/docs/android_app_bundles.md b/chromium/build/android/docs/android_app_bundles.md new file mode 100644 index 00000000000..e9e82844940 --- /dev/null +++ b/chromium/build/android/docs/android_app_bundles.md @@ -0,0 +1,199 @@ +# Introduction + +This document describes how the Chromium build system supports Android app +bundles. + +[TOC] + +# Overview of app bundles + +An Android app bundle is an alternative application distribution format for +Android applications on the Google Play Store, that allows reducing the size +of binaries sent for installation to individual devices that run on Android L +and beyond. For more information about them, see the official Android +documentation at: + + https://developer.android.com/guide/app-bundle/ + +For the context of this document, the most important points are: + + - Unlike a regular APK (e.g. `foo.apk`), the bundle (e.g. `foo.aab`) cannot + be installed directly on a device. + + - Instead, it must be processed into a set of installable split APKs, which + are stored inside a special zip archive (e.g. `foo.apks`). + + - The splitting can be based on various criteria: e.g. language or screen + density for resources, or cpu ABI for native code. + + - The bundle also uses the notion of modules to separate several application + features. Each module has its own code, assets and resources, and can be + installed separately from the rest of the application if needed. + + - The main application itself is stored in the '`base`' module (this name + cannot be changed). + + +# Declaring app bundles with GN templates + +Here's an example that shows how to declare a simple bundle that contains +a single base module, which enables language-based splits: + +```gn + + # First declare the first bundle module. The base module is the one + # that contains the main application's code, resources and assets. + android_app_bundle_module("foo_base_module") { + # Declaration are similar to android_apk here. + ... + } + + # Second, declare the bundle itself. + android_app_bundle("foo_bundle") { + # Indicate the base module to use for this bundle + base_module_target = ":foo_base_module" + + # The name of our bundle file (without any suffix). Default would + # be 'foo_bundle' otherwise. + bundle_name = "FooBundle" + + # Signing your bundle is required to upload it to the Play Store + # but since signing is very slow, avoid doing it for non official + # builds. Signing the bundle is not required for local testing. + sign_bundle = is_official_build + + # Enable language-based splits for this bundle. Which means that + # resources and assets specific to a given language will be placed + # into their own split APK in the final .apks archive. + enable_language_splits = true + + # Proguard settings must be passed at the bundle, not module, target. + proguard_enabled = !is_java_debug + } +``` + +When generating the `foo_bundle` target with Ninja, you will end up with +the following: + + - The bundle file under `out/Release/apks/FooBundle.aab` + + - A helper script called `out/Release/bin/foo_bundle`, which can be used + to install / launch / uninstall the bundle on local devices. + + This works like an APK wrapper script (e.g. `foo_apk`). Use `--help` + to see all possible commands supported by the script. + +If you need more modules besides the base one, you will need to list all the +extra ones using the extra_modules variable which takes a list of GN scopes, +as in: + +```gn + + android_app_bundle_module("foo_base_module") { + ... + } + + android_app_bundle_module("foo_extra_module") { + ... + } + + android_app_bundle("foo_bundle") { + base_module_target = ":foo_base_module" + + extra_modules = [ + { # NOTE: Scopes require one field per line, and no comma separators. + name = "my_module" + module_target = ":foo_extra_module" + } + ] + + ... + } +``` + +Note that each extra module is identified by a unique name, which cannot +be '`base`'. + + +# Bundle signature issues + +Signing an app bundle is not necessary, unless you want to upload it to the +Play Store. Since this process is very slow (it uses `jarsigner` instead of +the much faster `apkbuilder`), you can control it with the `sign_bundle` +variable, as described in the example above. + +The `.apks` archive however always contains signed split APKs. The keystore +path/password/alias being used are the default ones, unless you use custom +values when declaring the bundle itself, as in: + +```gn + android_app_bundle("foo_bundle") { + ... + keystore_path = "//path/to/keystore" + keystore_password = "K3y$t0Re-Pa$$w0rd" + keystore_name = "my-signing-key-name" + } +``` + +These values are not stored in the bundle itself, but in the wrapper script, +which will use them to generate the `.apks` archive for you. This allows you +to properly install updates on top of existing applications on any device. + + +# Proguard and bundles + +When using an app bundle that is made of several modules, it is crucial to +ensure that proguard, if enabled: + +- Keeps the obfuscated class names used by each module consistent. +- Does not remove classes that are not used in one module, but referenced + by others. + +To achieve this, a special scheme called *synchronized proguarding* is +performed, which consists of the following steps: + +- The list of unoptimized .jar files from all modules are sent to a single + proguard command. This generates a new temporary optimized *group* .jar file. + +- Each module extracts the optimized class files from the optimized *group* + .jar file, to generate its own, module-specific, optimized .jar. + +- Each module-specific optimized .jar is then sent to dex generation. + +This synchronized proguarding step is added by the `android_app_bundle()` GN +template. In practice this means the following: + + - If `proguard_enabled` and `proguard_jar_path` must be passed to + `android_app_bundle` targets, but not to `android_app_bundle_module` ones. + + - `proguard_configs` can be still passed to individual modules, just + like regular APKs. All proguard configs will be merged during the + synchronized proguard step. + + +# Manual generation and installation of .apks archives + +Note that the `foo_bundle` script knows how to generate the .apks archive +from the bundle file, and install it to local devices for you. For example, +to install and launch a bundle, use: + +```sh + out/Release/bin/foo_bundle run +``` + +If you want to manually look or use the `.apks` archive, use the following +command to generate it: + +```sh + out/Release/bin/foo_bundle build-bundle-apks \ + --output-apks=/tmp/BundleFoo.apks +``` + +All split APKs within the archive will be properly signed. And you will be +able to look at its content (with `unzip -l`), or install it manually with: + +```sh + build/android/gyp/bundletool.py install-apks \ + --apks=/tmp/BundleFoo.apks \ + --adb=$(which adb) +``` diff --git a/chromium/build/android/gradle/generate_gradle.py b/chromium/build/android/gradle/generate_gradle.py index 302f0a679ea..dfc2f241549 100755 --- a/chromium/build/android/gradle/generate_gradle.py +++ b/chromium/build/android/gradle/generate_gradle.py @@ -169,7 +169,7 @@ class _ProjectEntry(object): self._build_config = None self._java_files = None self._all_entries = None - self.android_test_entries = None + self.android_test_entries = [] @classmethod def FromGnTarget(cls, gn_target): @@ -334,7 +334,7 @@ class _ProjectContextGenerator(object): return _DEFAULT_ANDROID_MANIFEST_PATH variables = {} - variables['compile_sdk_version'] = self.build_vars['android_sdk_version'] + variables['compile_sdk_version'] = self.build_vars['compile_sdk_version'] variables['package'] = resource_packages[0] output_file = os.path.join( @@ -373,21 +373,24 @@ class _ProjectContextGenerator(object): res_zips += entry.ResZips() return set(_RebasePath(res_zips)) - def GeneratedInputs(self, root_entry): - generated_inputs = self.AllResZips(root_entry) - generated_inputs.update(self.AllSrcjars(root_entry)) + def GeneratedInputs(self, root_entry, fast=None): + generated_inputs = set() + if not fast: + generated_inputs.update(self.AllResZips(root_entry)) + generated_inputs.update(self.AllSrcjars(root_entry)) for entry in self._GetEntries(root_entry): generated_inputs.update(entry.GeneratedJavaFiles()) generated_inputs.update(entry.PrebuiltJars()) return generated_inputs - def GeneratedZips(self, root_entry): + def GeneratedZips(self, root_entry, fast=None): entry_output_dir = self.EntryOutputDir(root_entry) tuples = [] - tuples.extend((s, os.path.join(entry_output_dir, _SRCJARS_SUBDIR)) - for s in self.AllSrcjars(root_entry)) - tuples.extend((s, os.path.join(entry_output_dir, _RES_SUBDIR)) - for s in self.AllResZips(root_entry)) + if not fast: + tuples.extend((s, os.path.join(entry_output_dir, _SRCJARS_SUBDIR)) + for s in self.AllSrcjars(root_entry)) + tuples.extend((s, os.path.join(entry_output_dir, _RES_SUBDIR)) + for s in self.AllResZips(root_entry)) return tuples def GenerateManifest(self, root_entry): @@ -550,7 +553,7 @@ def _GenerateBaseVars(generator, build_vars, source_properties): variables = {} variables['build_tools_version'] = source_properties['Pkg.Revision'] variables['compile_sdk_version'] = ( - 'android-%s' % build_vars['android_sdk_version']) + 'android-%s' % build_vars['compile_sdk_version']) target_sdk_version = build_vars['android_sdk_version'] if target_sdk_version.isalpha(): target_sdk_version = '"{}"'.format(target_sdk_version) @@ -820,6 +823,12 @@ def main(): action='append', help='GN native targets to generate for. May be ' 'repeated.') + parser.add_argument('--compile-sdk-version', + type=int, + default=0, + help='Override compileSdkVersion for android sdk docs. ' + 'Useful when sources for android_sdk_version is ' + 'not available in Android Studio.') version_group = parser.add_mutually_exclusive_group() version_group.add_argument('--beta', action='store_true', @@ -895,6 +904,10 @@ def main(): channel = 'canary' else: channel = 'stable' + if args.compile_sdk_version: + build_vars['compile_sdk_version'] = args.compile_sdk_version + else: + build_vars['compile_sdk_version'] = build_vars['android_sdk_version'] generator = _ProjectContextGenerator(_gradle_output_dir, build_vars, args.use_gradle_process_resources, jinja_processor, args.split_projects, channel) @@ -962,24 +975,24 @@ def main(): _WriteFile(os.path.join(generator.project_dir, 'local.properties'), _GenerateLocalProperties(sdk_path)) - if not args.fast: - zip_tuples = [] - generated_inputs = set() - for entry in entries: + zip_tuples = [] + generated_inputs = set() + for entry in entries: + entries_to_gen = [entry] + entries_to_gen.extend(entry.android_test_entries) + for entry_to_gen in entries_to_gen: # Build all paths references by .gradle that exist within output_dir. - generated_inputs.update(generator.GeneratedInputs(entry)) - zip_tuples.extend(generator.GeneratedZips(entry)) - if generated_inputs: - logging.warning('Building generated source files...') - targets = _RebasePath(generated_inputs, output_dir) - _RunNinja(output_dir, targets, args.j) - if zip_tuples: - _ExtractZips(generator.project_dir, zip_tuples) + generated_inputs.update( + generator.GeneratedInputs(entry_to_gen, args.fast)) + zip_tuples.extend(generator.GeneratedZips(entry_to_gen, args.fast)) + if generated_inputs: + logging.warning('Building generated source files...') + targets = _RebasePath(generated_inputs, output_dir) + _RunNinja(output_dir, targets, args.j) + if zip_tuples: + _ExtractZips(generator.project_dir, zip_tuples) logging.warning('Generated projects for Android Studio %s', channel) - if not args.fast: - logging.warning('Run with --fast flag to skip generating files (faster, ' - 'but less correct)') logging.warning('For more tips: https://chromium.googlesource.com/chromium' '/src.git/+/master/docs/android_studio.md') diff --git a/chromium/build/android/gradle/root.jinja b/chromium/build/android/gradle/root.jinja index 76ffa8f63be..ff26840f00f 100644 --- a/chromium/build/android/gradle/root.jinja +++ b/chromium/build/android/gradle/root.jinja @@ -10,7 +10,7 @@ buildscript { } dependencies { {% if channel == 'canary' %} - classpath "com.android.tools.build:gradle:3.2.0-alpha16" + classpath "com.android.tools.build:gradle:3.3.0-alpha05" {% elif channel == 'beta' %} classpath "com.android.tools.build:gradle:3.1.0-beta4" {% else %} diff --git a/chromium/build/android/gyp/aar.pydeps b/chromium/build/android/gyp/aar.pydeps new file mode 100644 index 00000000000..e08c5475e3d --- /dev/null +++ b/chromium/build/android/gyp/aar.pydeps @@ -0,0 +1,7 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/aar.pydeps build/android/gyp/aar.py +../../gn_helpers.py +aar.py +util/__init__.py +util/build_utils.py +util/md5_check.py diff --git a/chromium/build/android/gyp/aidl.py b/chromium/build/android/gyp/aidl.py index 1591f24508c..64ad29041ad 100755 --- a/chromium/build/android/gyp/aidl.py +++ b/chromium/build/android/gyp/aidl.py @@ -18,7 +18,6 @@ from util import build_utils def main(argv): option_parser = optparse.OptionParser() - build_utils.AddDepfileOption(option_parser) option_parser.add_option('--aidl-path', help='Path to the aidl binary.') option_parser.add_option('--imports', help='Files to import.') option_parser.add_option('--includes', @@ -54,9 +53,6 @@ def main(argv): pkg_name.replace('.', '/'), os.path.basename(path)) build_utils.AddToZipHermetic(srcjar, arcname, data=data) - if options.depfile: - build_utils.WriteDepfile(options.depfile, options.srcjar) - if __name__ == '__main__': sys.exit(main(sys.argv)) diff --git a/chromium/build/android/gyp/aidl.pydeps b/chromium/build/android/gyp/aidl.pydeps new file mode 100644 index 00000000000..2dbce376f1e --- /dev/null +++ b/chromium/build/android/gyp/aidl.pydeps @@ -0,0 +1,7 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/aidl.pydeps build/android/gyp/aidl.py +../../gn_helpers.py +aidl.py +util/__init__.py +util/build_utils.py +util/md5_check.py diff --git a/chromium/build/android/gyp/apkbuilder.pydeps b/chromium/build/android/gyp/apkbuilder.pydeps new file mode 100644 index 00000000000..3ae03319c90 --- /dev/null +++ b/chromium/build/android/gyp/apkbuilder.pydeps @@ -0,0 +1,8 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/apkbuilder.pydeps build/android/gyp/apkbuilder.py +../../gn_helpers.py +apkbuilder.py +finalize_apk.py +util/__init__.py +util/build_utils.py +util/md5_check.py diff --git a/chromium/build/android/gyp/app_bundle_to_apks.pydeps b/chromium/build/android/gyp/app_bundle_to_apks.pydeps new file mode 100644 index 00000000000..49c2892b2f3 --- /dev/null +++ b/chromium/build/android/gyp/app_bundle_to_apks.pydeps @@ -0,0 +1,8 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/app_bundle_to_apks.pydeps build/android/gyp/app_bundle_to_apks.py +../../gn_helpers.py +app_bundle_to_apks.py +bundletool.py +util/__init__.py +util/build_utils.py +util/md5_check.py diff --git a/chromium/build/android/gyp/bytecode_processor.pydeps b/chromium/build/android/gyp/bytecode_processor.pydeps new file mode 100644 index 00000000000..d8ff3964950 --- /dev/null +++ b/chromium/build/android/gyp/bytecode_processor.pydeps @@ -0,0 +1,7 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/bytecode_processor.pydeps build/android/gyp/bytecode_processor.py +../../gn_helpers.py +bytecode_processor.py +util/__init__.py +util/build_utils.py +util/md5_check.py diff --git a/chromium/build/android/gyp/compile_resources.py b/chromium/build/android/gyp/compile_resources.py index 412c6f7485d..cda03e60601 100755 --- a/chromium/build/android/gyp/compile_resources.py +++ b/chromium/build/android/gyp/compile_resources.py @@ -671,7 +671,8 @@ def main(args): input_paths=input_paths, input_strings=input_strings, output_paths=output_paths, - depfile_deps=options.dependencies_res_zips + options.extra_r_text_files) + depfile_deps=options.dependencies_res_zips + options.extra_r_text_files, + add_pydeps=False) if __name__ == '__main__': diff --git a/chromium/build/android/gyp/compile_resources.pydeps b/chromium/build/android/gyp/compile_resources.pydeps new file mode 100644 index 00000000000..2ffcb52a2c9 --- /dev/null +++ b/chromium/build/android/gyp/compile_resources.pydeps @@ -0,0 +1,29 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/compile_resources.pydeps build/android/gyp/compile_resources.py +../../../third_party/jinja2/__init__.py +../../../third_party/jinja2/_compat.py +../../../third_party/jinja2/bccache.py +../../../third_party/jinja2/compiler.py +../../../third_party/jinja2/defaults.py +../../../third_party/jinja2/environment.py +../../../third_party/jinja2/exceptions.py +../../../third_party/jinja2/filters.py +../../../third_party/jinja2/idtracking.py +../../../third_party/jinja2/lexer.py +../../../third_party/jinja2/loaders.py +../../../third_party/jinja2/nodes.py +../../../third_party/jinja2/optimizer.py +../../../third_party/jinja2/parser.py +../../../third_party/jinja2/runtime.py +../../../third_party/jinja2/tests.py +../../../third_party/jinja2/utils.py +../../../third_party/jinja2/visitor.py +../../../third_party/markupsafe/__init__.py +../../../third_party/markupsafe/_compat.py +../../../third_party/markupsafe/_native.py +../../gn_helpers.py +compile_resources.py +util/__init__.py +util/build_utils.py +util/md5_check.py +util/resource_utils.py diff --git a/chromium/build/android/gyp/copy_ex.py b/chromium/build/android/gyp/copy_ex.py index a9f6a9f44bb..48d1b26df18 100755 --- a/chromium/build/android/gyp/copy_ex.py +++ b/chromium/build/android/gyp/copy_ex.py @@ -35,8 +35,14 @@ def CopyFile(f, dest, deps): dest = os.path.join(dest, os.path.basename(f)) deps.append(f) - if os.path.isfile(dest) and filecmp.cmp(dest, f, shallow=False): - return + + if os.path.isfile(dest): + if filecmp.cmp(dest, f, shallow=False): + return + # The shutil.copy() below would fail if the file does not have write + # permissions. Deleting the file has similar costs to modifying the + # permissions. + os.unlink(dest) shutil.copy(f, dest) diff --git a/chromium/build/android/gyp/copy_ex.pydeps b/chromium/build/android/gyp/copy_ex.pydeps new file mode 100644 index 00000000000..e0fb31eaa9b --- /dev/null +++ b/chromium/build/android/gyp/copy_ex.pydeps @@ -0,0 +1,7 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/copy_ex.pydeps build/android/gyp/copy_ex.py +../../gn_helpers.py +copy_ex.py +util/__init__.py +util/build_utils.py +util/md5_check.py diff --git a/chromium/build/android/gyp/create_apk_operations_script.pydeps b/chromium/build/android/gyp/create_apk_operations_script.pydeps new file mode 100644 index 00000000000..9d4dcb8fe5b --- /dev/null +++ b/chromium/build/android/gyp/create_apk_operations_script.pydeps @@ -0,0 +1,3 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/create_apk_operations_script.pydeps build/android/gyp/create_apk_operations_script.py +create_apk_operations_script.py diff --git a/chromium/build/android/gyp/create_app_bundle.pydeps b/chromium/build/android/gyp/create_app_bundle.pydeps new file mode 100644 index 00000000000..fef04fab53a --- /dev/null +++ b/chromium/build/android/gyp/create_app_bundle.pydeps @@ -0,0 +1,30 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/create_app_bundle.pydeps build/android/gyp/create_app_bundle.py +../../../third_party/jinja2/__init__.py +../../../third_party/jinja2/_compat.py +../../../third_party/jinja2/bccache.py +../../../third_party/jinja2/compiler.py +../../../third_party/jinja2/defaults.py +../../../third_party/jinja2/environment.py +../../../third_party/jinja2/exceptions.py +../../../third_party/jinja2/filters.py +../../../third_party/jinja2/idtracking.py +../../../third_party/jinja2/lexer.py +../../../third_party/jinja2/loaders.py +../../../third_party/jinja2/nodes.py +../../../third_party/jinja2/optimizer.py +../../../third_party/jinja2/parser.py +../../../third_party/jinja2/runtime.py +../../../third_party/jinja2/tests.py +../../../third_party/jinja2/utils.py +../../../third_party/jinja2/visitor.py +../../../third_party/markupsafe/__init__.py +../../../third_party/markupsafe/_compat.py +../../../third_party/markupsafe/_native.py +../../gn_helpers.py +bundletool.py +create_app_bundle.py +util/__init__.py +util/build_utils.py +util/md5_check.py +util/resource_utils.py diff --git a/chromium/build/android/gyp/create_bundle_wrapper_script.py b/chromium/build/android/gyp/create_bundle_wrapper_script.py new file mode 100755 index 00000000000..04108363e3f --- /dev/null +++ b/chromium/build/android/gyp/create_bundle_wrapper_script.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# Copyright 2018 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Create a wrapper script to manage an Android App Bundle.""" + +import argparse +import os +import string +import sys + +from util import build_utils + +SCRIPT_TEMPLATE = string.Template("""\ +#!/usr/bin/env python +# +# This file was generated by build/android/gyp/create_bundle_wrapper_script.py + +import os +import sys + +def main(): + script_directory = os.path.dirname(__file__) + resolve = lambda p: p if p is None else os.path.abspath(os.path.join( + script_directory, p)) + sys.path.append(resolve(${WRAPPED_SCRIPT_DIR})) + import apk_operations + + apk_operations.RunForBundle(output_directory=resolve(${OUTPUT_DIR}), + bundle_path=resolve(${BUNDLE_PATH}), + bundle_apks_path=resolve(${BUNDLE_APKS_PATH}), + aapt2_path=resolve(${AAPT2_PATH}), + keystore_path=resolve(${KEYSTORE_PATH}), + keystore_password=${KEYSTORE_PASSWORD}, + keystore_alias=${KEY_NAME}, + package_name=${PACKAGE_NAME}, + command_line_flags_file=${FLAGS_FILE}, + proguard_mapping_path=resolve(${MAPPING_PATH}), + target_cpu=${TARGET_CPU}) + +if __name__ == '__main__': + sys.exit(main()) +""") + + +def main(args): + args = build_utils.ExpandFileArgs(args) + parser = argparse.ArgumentParser() + parser.add_argument('--script-output-path', required=True, + help='Output path for executable script.') + parser.add_argument('--bundle-path', required=True) + parser.add_argument('--bundle-apks-path', required=True) + parser.add_argument('--package-name', required=True) + parser.add_argument('--aapt2-path', required=True) + parser.add_argument('--keystore-path', required=True) + parser.add_argument('--keystore-password', required=True) + parser.add_argument('--key-name', required=True) + parser.add_argument('--command-line-flags-file') + parser.add_argument('--proguard-mapping-path') + parser.add_argument('--target-cpu') + args = parser.parse_args(args) + + def relativize(path): + """Returns the path relative to the output script directory.""" + if path is None: + return path + return os.path.relpath(path, os.path.dirname(args.script_output_path)) + + wrapped_script_dir = os.path.join(os.path.dirname(__file__), os.path.pardir) + wrapped_script_dir = relativize(wrapped_script_dir) + + with open(args.script_output_path, 'w') as script: + script_dict = { + 'WRAPPED_SCRIPT_DIR': repr(wrapped_script_dir), + 'OUTPUT_DIR': repr(relativize('.')), + 'BUNDLE_PATH': repr(relativize(args.bundle_path)), + 'BUNDLE_APKS_PATH': repr(relativize(args.bundle_apks_path)), + 'PACKAGE_NAME': repr(args.package_name), + 'AAPT2_PATH': repr(relativize(args.aapt2_path)), + 'KEYSTORE_PATH': repr(relativize(args.keystore_path)), + 'KEYSTORE_PASSWORD': repr(args.keystore_password), + 'KEY_NAME': repr(args.key_name), + 'MAPPING_PATH': repr(relativize(args.proguard_mapping_path)), + 'FLAGS_FILE': repr(args.command_line_flags_file), + 'TARGET_CPU': repr(args.target_cpu), + } + script.write(SCRIPT_TEMPLATE.substitute(script_dict)) + os.chmod(args.script_output_path, 0750) + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/chromium/build/android/gyp/create_bundle_wrapper_script.pydeps b/chromium/build/android/gyp/create_bundle_wrapper_script.pydeps new file mode 100644 index 00000000000..5c87801e253 --- /dev/null +++ b/chromium/build/android/gyp/create_bundle_wrapper_script.pydeps @@ -0,0 +1,7 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/create_bundle_wrapper_script.pydeps build/android/gyp/create_bundle_wrapper_script.py +../../gn_helpers.py +create_bundle_wrapper_script.py +util/__init__.py +util/build_utils.py +util/md5_check.py diff --git a/chromium/build/android/gyp/create_dist_jar.py b/chromium/build/android/gyp/create_dist_jar.py index 2e06478fae8..7f78935a47d 100755 --- a/chromium/build/android/gyp/create_dist_jar.py +++ b/chromium/build/android/gyp/create_dist_jar.py @@ -24,7 +24,8 @@ def main(args): build_utils.MergeZips(options.output, input_jars) if options.depfile: - build_utils.WriteDepfile(options.depfile, options.output, input_jars) + build_utils.WriteDepfile(options.depfile, options.output, input_jars, + add_pydeps=False) if __name__ == '__main__': diff --git a/chromium/build/android/gyp/create_dist_jar.pydeps b/chromium/build/android/gyp/create_dist_jar.pydeps new file mode 100644 index 00000000000..f4224d7f99d --- /dev/null +++ b/chromium/build/android/gyp/create_dist_jar.pydeps @@ -0,0 +1,7 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/create_dist_jar.pydeps build/android/gyp/create_dist_jar.py +../../gn_helpers.py +create_dist_jar.py +util/__init__.py +util/build_utils.py +util/md5_check.py diff --git a/chromium/build/android/gyp/create_java_binary_script.pydeps b/chromium/build/android/gyp/create_java_binary_script.pydeps new file mode 100644 index 00000000000..96d79bf609e --- /dev/null +++ b/chromium/build/android/gyp/create_java_binary_script.pydeps @@ -0,0 +1,7 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/create_java_binary_script.pydeps build/android/gyp/create_java_binary_script.py +../../gn_helpers.py +create_java_binary_script.py +util/__init__.py +util/build_utils.py +util/md5_check.py diff --git a/chromium/build/android/gyp/create_stack_script.pydeps b/chromium/build/android/gyp/create_stack_script.pydeps new file mode 100644 index 00000000000..7bddb156f40 --- /dev/null +++ b/chromium/build/android/gyp/create_stack_script.pydeps @@ -0,0 +1,7 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/create_stack_script.pydeps build/android/gyp/create_stack_script.py +../../gn_helpers.py +create_stack_script.py +util/__init__.py +util/build_utils.py +util/md5_check.py diff --git a/chromium/build/android/gyp/create_test_runner_script.pydeps b/chromium/build/android/gyp/create_test_runner_script.pydeps new file mode 100644 index 00000000000..4b8876bc943 --- /dev/null +++ b/chromium/build/android/gyp/create_test_runner_script.pydeps @@ -0,0 +1,7 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/create_test_runner_script.pydeps build/android/gyp/create_test_runner_script.py +../../gn_helpers.py +create_test_runner_script.py +util/__init__.py +util/build_utils.py +util/md5_check.py diff --git a/chromium/build/android/gyp/create_tool_wrapper.pydeps b/chromium/build/android/gyp/create_tool_wrapper.pydeps new file mode 100644 index 00000000000..75b8326e70b --- /dev/null +++ b/chromium/build/android/gyp/create_tool_wrapper.pydeps @@ -0,0 +1,3 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/create_tool_wrapper.pydeps build/android/gyp/create_tool_wrapper.py +create_tool_wrapper.py diff --git a/chromium/build/android/gyp/desugar.pydeps b/chromium/build/android/gyp/desugar.pydeps new file mode 100644 index 00000000000..a40f3aa7ddd --- /dev/null +++ b/chromium/build/android/gyp/desugar.pydeps @@ -0,0 +1,7 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/desugar.pydeps build/android/gyp/desugar.py +../../gn_helpers.py +desugar.py +util/__init__.py +util/build_utils.py +util/md5_check.py diff --git a/chromium/build/android/gyp/dex.pydeps b/chromium/build/android/gyp/dex.pydeps new file mode 100644 index 00000000000..0e18d02a460 --- /dev/null +++ b/chromium/build/android/gyp/dex.pydeps @@ -0,0 +1,7 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/dex.pydeps build/android/gyp/dex.py +../../gn_helpers.py +dex.py +util/__init__.py +util/build_utils.py +util/md5_check.py diff --git a/chromium/build/android/gyp/dist_aar.py b/chromium/build/android/gyp/dist_aar.py index 6dc38c488a6..ed823f18b7b 100755 --- a/chromium/build/android/gyp/dist_aar.py +++ b/chromium/build/android/gyp/dist_aar.py @@ -124,7 +124,8 @@ def main(args): if options.depfile: all_inputs = (options.jars + options.dependencies_res_zips + options.r_text_files + options.proguard_configs) - build_utils.WriteDepfile(options.depfile, options.output, all_inputs) + build_utils.WriteDepfile(options.depfile, options.output, all_inputs, + add_pydeps=False) if __name__ == '__main__': diff --git a/chromium/build/android/gyp/dist_aar.pydeps b/chromium/build/android/gyp/dist_aar.pydeps new file mode 100644 index 00000000000..da5ea8da23d --- /dev/null +++ b/chromium/build/android/gyp/dist_aar.pydeps @@ -0,0 +1,7 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/dist_aar.pydeps build/android/gyp/dist_aar.py +../../gn_helpers.py +dist_aar.py +util/__init__.py +util/build_utils.py +util/md5_check.py diff --git a/chromium/build/android/gyp/emma_instr.pydeps b/chromium/build/android/gyp/emma_instr.pydeps new file mode 100644 index 00000000000..88f752a0f9d --- /dev/null +++ b/chromium/build/android/gyp/emma_instr.pydeps @@ -0,0 +1,7 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/emma_instr.pydeps build/android/gyp/emma_instr.py +../../gn_helpers.py +emma_instr.py +util/__init__.py +util/build_utils.py +util/md5_check.py diff --git a/chromium/build/android/gyp/filter_zip.pydeps b/chromium/build/android/gyp/filter_zip.pydeps new file mode 100644 index 00000000000..67c989cf885 --- /dev/null +++ b/chromium/build/android/gyp/filter_zip.pydeps @@ -0,0 +1,7 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/filter_zip.pydeps build/android/gyp/filter_zip.py +../../gn_helpers.py +filter_zip.py +util/__init__.py +util/build_utils.py +util/md5_check.py diff --git a/chromium/build/android/gyp/gcc_preprocess.py b/chromium/build/android/gyp/gcc_preprocess.py index 8c5c404c744..8b3444c2b01 100755 --- a/chromium/build/android/gyp/gcc_preprocess.py +++ b/chromium/build/android/gyp/gcc_preprocess.py @@ -47,7 +47,7 @@ def main(args): DoGcc(options) if options.depfile: - build_utils.WriteDepfile(options.depfile, options.output) + build_utils.WriteDepfile(options.depfile, options.output, add_pydeps=False) if __name__ == '__main__': diff --git a/chromium/build/android/gyp/gcc_preprocess.pydeps b/chromium/build/android/gyp/gcc_preprocess.pydeps new file mode 100644 index 00000000000..64e776b6338 --- /dev/null +++ b/chromium/build/android/gyp/gcc_preprocess.pydeps @@ -0,0 +1,7 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/gcc_preprocess.pydeps build/android/gyp/gcc_preprocess.py +../../gn_helpers.py +gcc_preprocess.py +util/__init__.py +util/build_utils.py +util/md5_check.py diff --git a/chromium/build/android/gyp/generate_proguarded_module_jar.py b/chromium/build/android/gyp/generate_proguarded_module_jar.py new file mode 100755 index 00000000000..97b3027a51e --- /dev/null +++ b/chromium/build/android/gyp/generate_proguarded_module_jar.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python +# +# Copyright (c) 2018 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Extracts a bundle module's classes from jar created in the synchronized +proguarding step and packages them into a new jar. + +Synchronized proguarding means that, when several app modules are combined into +an app bundle, all un-optimized jars for all modules are grouped and sent to a +single proguard command, which generates a single, common, intermediate +optimized jar, and its mapping file. + +This script is used to extract, from this synchronized proguard jar, all the +optimized classes corresponding to a single module, into a new .jar file. The +latter will be compiled later into the module's dex file. + +For this, the script reads the module's un-obfuscated class names from the +module's unoptimized jars. Then, it maps those to obfuscated class names using +the proguard mapping file. Finally, it extracts the module's class files from +the proguarded jar and zips them into a new module jar. """ + +import argparse +import os +import sys +import zipfile + +from util import build_utils + +MANIFEST = """Manifest-Version: 1.0 +Created-By: generate_proguarded_module_jar.py +""" + + +# TODO(tiborg): Share with merge_jar_info_files.py. +def _FullJavaNameFromClassFilePath(path): + if not path.endswith('.class'): + return '' + path = os.path.splitext(path)[0] + parts = [] + while path: + # Use split to be platform independent. + head, tail = os.path.split(path) + path = head + parts.append(tail) + parts.reverse() # Package comes first + return '.'.join(parts) + + +def main(args): + args = build_utils.ExpandFileArgs(args) + parser = argparse.ArgumentParser() + build_utils.AddDepfileOption(parser) + parser.add_argument( + '--proguarded-jar', + required=True, + help='Path to input jar produced by synchronized proguarding') + parser.add_argument( + '--proguard-mapping', + required=True, + help='Path to input proguard mapping produced by synchronized ' + 'proguarding') + parser.add_argument( + '--module-input-jars', + required=True, + help='GN-list of input paths to un-optimized jar files for the current ' + 'module. The optimized versions of their .class files will go into ' + 'the output jar.') + parser.add_argument( + '--output-jar', + required=True, + help='Path to output jar file containing the module\'s optimized class ' + 'files') + parser.add_argument( + '--is-base-module', + action='store_true', + help='Inidcates to extract class files for a base module') + options = parser.parse_args(args) + options.module_input_jars = build_utils.ParseGnList(options.module_input_jars) + + # Read class names of the currently processed module. + classes = set() + for module_jar in options.module_input_jars: + with zipfile.ZipFile(module_jar) as zip_info: + for path in zip_info.namelist(): + fully_qualified_name = _FullJavaNameFromClassFilePath(path) + if fully_qualified_name: + classes.add(fully_qualified_name) + + # Parse the proguarding mapping to be able to map un-obfuscated to obfuscated + # names. + # Proguard mapping files have the following format: + # + # {un-obfuscated class name 1} -> {obfuscated class name 1}: + # {un-obfuscated member name 1} -> {obfuscated member name 1} + # ... + # {un-obfuscated class name 2} -> {obfuscated class name 2}: + # ... + # ... + obfuscation_map = {} + with open(options.proguard_mapping, 'r') as proguard_mapping_file: + for line in proguard_mapping_file: + # Skip indented lines since they map member names and not class names. + if line.startswith(' '): + continue + line = line.strip() + # Skip empty lines. + if not line: + continue + assert line.endswith(':') + full, obfuscated = line.strip(':').split(' -> ') + assert full + assert obfuscated + obfuscation_map[full] = obfuscated + + # Collect the obfuscated names of classes, which should go into the currently + # processed module. + obfuscated_module_classes = set( + obfuscation_map[c] for c in classes if c in obfuscation_map) + + # Collect horizontally merged classes to later make sure that those only go + # into the base module. Merging classes horizontally means that proguard took + # two classes that don't inherit from each other and merged them into one. + horiz_merged_classes = set() + obfuscated_classes = sorted(obfuscation_map.values()) + prev_obfuscated_class = None + for obfuscated_class in obfuscated_classes: + if prev_obfuscated_class and obfuscated_class == prev_obfuscated_class: + horiz_merged_classes.add(obfuscated_class) + prev_obfuscated_class = obfuscated_class + + # Move horizontally merged classes into the base module. + if options.is_base_module: + obfuscated_module_classes |= horiz_merged_classes + else: + obfuscated_module_classes -= horiz_merged_classes + + # Extract module class files from proguarded jar and store them in a module + # split jar. + with zipfile.ZipFile( + os.path.abspath(options.output_jar), 'w', + zipfile.ZIP_DEFLATED) as output_jar: + with zipfile.ZipFile(os.path.abspath(options.proguarded_jar), + 'r') as proguarded_jar: + for obfuscated_class in obfuscated_module_classes: + class_path = obfuscated_class.replace('.', '/') + '.class' + class_file_content = proguarded_jar.read(class_path) + output_jar.writestr(class_path, class_file_content) + output_jar.writestr('META-INF/MANIFEST.MF', MANIFEST) + + if options.depfile: + build_utils.WriteDepfile( + options.depfile, options.output_jar, options.module_input_jars + + [options.proguard_mapping, options.proguarded_jar], add_pydeps=False) + + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/chromium/build/android/gyp/generate_proguarded_module_jar.pydeps b/chromium/build/android/gyp/generate_proguarded_module_jar.pydeps new file mode 100644 index 00000000000..6d52b4ec543 --- /dev/null +++ b/chromium/build/android/gyp/generate_proguarded_module_jar.pydeps @@ -0,0 +1,7 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/generate_proguarded_module_jar.pydeps build/android/gyp/generate_proguarded_module_jar.py +../../gn_helpers.py +generate_proguarded_module_jar.py +util/__init__.py +util/build_utils.py +util/md5_check.py diff --git a/chromium/build/android/gyp/generate_resource_rewriter.py b/chromium/build/android/gyp/generate_resource_rewriter.py index 82ddc21dc15..ba635a293d5 100755 --- a/chromium/build/android/gyp/generate_resource_rewriter.py +++ b/chromium/build/android/gyp/generate_resource_rewriter.py @@ -107,4 +107,3 @@ def main(): if __name__ == '__main__': sys.exit(main()) - diff --git a/chromium/build/android/gyp/ijar.pydeps b/chromium/build/android/gyp/ijar.pydeps new file mode 100644 index 00000000000..ca10697c1f2 --- /dev/null +++ b/chromium/build/android/gyp/ijar.pydeps @@ -0,0 +1,7 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/ijar.pydeps build/android/gyp/ijar.py +../../gn_helpers.py +ijar.py +util/__init__.py +util/build_utils.py +util/md5_check.py diff --git a/chromium/build/android/gyp/java_cpp_enum.py b/chromium/build/android/gyp/java_cpp_enum.py index afb48430fa2..e737441005f 100755 --- a/chromium/build/android/gyp/java_cpp_enum.py +++ b/chromium/build/android/gyp/java_cpp_enum.py @@ -447,7 +447,7 @@ def DoMain(argv): build_utils.AddToZipHermetic(srcjar, output_path, data=data) if options.depfile: - build_utils.WriteDepfile(options.depfile, options.srcjar) + build_utils.WriteDepfile(options.depfile, options.srcjar, add_pydeps=False) if __name__ == '__main__': diff --git a/chromium/build/android/gyp/java_cpp_enum.pydeps b/chromium/build/android/gyp/java_cpp_enum.pydeps new file mode 100644 index 00000000000..32c8de534db --- /dev/null +++ b/chromium/build/android/gyp/java_cpp_enum.pydeps @@ -0,0 +1,7 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/java_cpp_enum.pydeps build/android/gyp/java_cpp_enum.py +../../gn_helpers.py +java_cpp_enum.py +util/__init__.py +util/build_utils.py +util/md5_check.py diff --git a/chromium/build/android/gyp/java_google_api_keys.py b/chromium/build/android/gyp/java_google_api_keys.py index f57e0b3a978..349821a8fce 100755 --- a/chromium/build/android/gyp/java_google_api_keys.py +++ b/chromium/build/android/gyp/java_google_api_keys.py @@ -121,4 +121,3 @@ def _DoMain(argv): if __name__ == '__main__': _DoMain(sys.argv[1:]) - diff --git a/chromium/build/android/gyp/javac.py b/chromium/build/android/gyp/javac.py index b5ae57a6d5a..0e1aad00922 100755 --- a/chromium/build/android/gyp/javac.py +++ b/chromium/build/android/gyp/javac.py @@ -96,6 +96,7 @@ ERRORPRONE_WARNINGS_TO_ERROR = [ 'MissingFail', 'MissingOverride', 'NarrowingCompoundAssignment', + 'OrphanedFormatString', 'ParameterName', 'ParcelableCreator', 'ReferenceEquality', @@ -248,7 +249,7 @@ def _CreateInfoFile(java_files, options, srcjar_files): info_data[fully_qualified_name] = java_file # Skip aidl srcjars since they don't indent code correctly. source = srcjar_files.get(java_file, java_file) - if source.endswith('_aidl.srcjar'): + if '_aidl.srcjar' in source: continue assert not options.chromium_code or len(class_names) == 1, ( 'Chromium java files must only have one class: {}'.format(source)) @@ -309,7 +310,10 @@ def _OnStaleMd5(changes, options, javac_cmd, java_files, classpath_inputs, extracted_files = build_utils.ExtractAll( srcjar, path=java_dir, pattern='*.java') for path in extracted_files: - srcjar_files[path] = srcjar + # We want the path inside the srcjar so the viewer can have a tree + # structure. + srcjar_files[path] = '{}/{}'.format( + srcjar, os.path.relpath(path, java_dir)) jar_srcs = build_utils.FindInDirectory(java_dir, '*.java') java_files.extend(jar_srcs) if changed_paths: @@ -596,7 +600,8 @@ def main(argv): input_strings=javac_cmd + classpath, output_paths=output_paths, force=force, - pass_changes=True) + pass_changes=True, + add_pydeps=False) if __name__ == '__main__': diff --git a/chromium/build/android/gyp/javac.pydeps b/chromium/build/android/gyp/javac.pydeps new file mode 100644 index 00000000000..a9d257b95f8 --- /dev/null +++ b/chromium/build/android/gyp/javac.pydeps @@ -0,0 +1,15 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/javac.pydeps build/android/gyp/javac.py +../../../third_party/colorama/src/colorama/__init__.py +../../../third_party/colorama/src/colorama/ansi.py +../../../third_party/colorama/src/colorama/ansitowin32.py +../../../third_party/colorama/src/colorama/initialise.py +../../../third_party/colorama/src/colorama/win32.py +../../../third_party/colorama/src/colorama/winterm.py +../../gn_helpers.py +jar.py +javac.py +util/__init__.py +util/build_utils.py +util/jar_info_utils.py +util/md5_check.py diff --git a/chromium/build/android/gyp/jinja_template.py b/chromium/build/android/gyp/jinja_template.py index 434d281099e..4d5c403dfe8 100755 --- a/chromium/build/android/gyp/jinja_template.py +++ b/chromium/build/android/gyp/jinja_template.py @@ -124,7 +124,8 @@ def main(): parser.add_argument('--variables', help='Variables to be made available in ' 'the template processing environment, as a GYP list ' '(e.g. --variables "channel=beta mstone=39")', default='') - build_utils.AddDepfileOption(parser) + parser.add_argument('--check-includes', action='store_true', + help='Enable inputs and includes checks.') options = parser.parse_args() inputs = build_utils.ParseGnList(options.inputs) @@ -146,15 +147,13 @@ def main(): _ProcessFiles(processor, inputs, options.inputs_base_dir, options.outputs_zip) - if options.depfile: - output = options.output or options.outputs_zip + if options.check_includes: all_inputs = set(processor.GetLoadedTemplates()) all_inputs.difference_update(inputs) all_inputs.difference_update(includes) if all_inputs: raise Exception('Found files not listed via --includes:\n' + '\n'.join(sorted(all_inputs))) - build_utils.WriteDepfile(options.depfile, output) if __name__ == '__main__': diff --git a/chromium/build/android/gyp/jinja_template.pydeps b/chromium/build/android/gyp/jinja_template.pydeps new file mode 100644 index 00000000000..a2a38176bfe --- /dev/null +++ b/chromium/build/android/gyp/jinja_template.pydeps @@ -0,0 +1,41 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/jinja_template.pydeps build/android/gyp/jinja_template.py +../../../third_party/catapult/devil/devil/__init__.py +../../../third_party/catapult/devil/devil/android/__init__.py +../../../third_party/catapult/devil/devil/android/constants/__init__.py +../../../third_party/catapult/devil/devil/android/constants/chrome.py +../../../third_party/catapult/devil/devil/android/sdk/__init__.py +../../../third_party/catapult/devil/devil/android/sdk/keyevent.py +../../../third_party/catapult/devil/devil/android/sdk/version_codes.py +../../../third_party/catapult/devil/devil/constants/__init__.py +../../../third_party/catapult/devil/devil/constants/exit_codes.py +../../../third_party/jinja2/__init__.py +../../../third_party/jinja2/_compat.py +../../../third_party/jinja2/bccache.py +../../../third_party/jinja2/compiler.py +../../../third_party/jinja2/defaults.py +../../../third_party/jinja2/environment.py +../../../third_party/jinja2/exceptions.py +../../../third_party/jinja2/filters.py +../../../third_party/jinja2/idtracking.py +../../../third_party/jinja2/lexer.py +../../../third_party/jinja2/loaders.py +../../../third_party/jinja2/nodes.py +../../../third_party/jinja2/optimizer.py +../../../third_party/jinja2/parser.py +../../../third_party/jinja2/runtime.py +../../../third_party/jinja2/tests.py +../../../third_party/jinja2/utils.py +../../../third_party/jinja2/visitor.py +../../../third_party/markupsafe/__init__.py +../../../third_party/markupsafe/_compat.py +../../../third_party/markupsafe/_native.py +../../gn_helpers.py +../pylib/__init__.py +../pylib/constants/__init__.py +../pylib/constants/host_paths.py +jinja_template.py +util/__init__.py +util/build_utils.py +util/md5_check.py +util/resource_utils.py diff --git a/chromium/build/android/gyp/lint.pydeps b/chromium/build/android/gyp/lint.pydeps new file mode 100644 index 00000000000..a8616e4d378 --- /dev/null +++ b/chromium/build/android/gyp/lint.pydeps @@ -0,0 +1,7 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/lint.pydeps build/android/gyp/lint.py +../../gn_helpers.py +lint.py +util/__init__.py +util/build_utils.py +util/md5_check.py diff --git a/chromium/build/android/gyp/main_dex_list.py b/chromium/build/android/gyp/main_dex_list.py index 9ccfea00ff9..24358590992 100755 --- a/chromium/build/android/gyp/main_dex_list.py +++ b/chromium/build/android/gyp/main_dex_list.py @@ -172,4 +172,3 @@ def _OnStaleMd5(proguard_cmd, main_dex_list_cmd, paths, main_dex_list_path, if __name__ == '__main__': sys.exit(main(sys.argv[1:])) - diff --git a/chromium/build/android/gyp/main_dex_list.pydeps b/chromium/build/android/gyp/main_dex_list.pydeps new file mode 100644 index 00000000000..8c482dfa523 --- /dev/null +++ b/chromium/build/android/gyp/main_dex_list.pydeps @@ -0,0 +1,8 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/main_dex_list.pydeps build/android/gyp/main_dex_list.py +../../gn_helpers.py +main_dex_list.py +util/__init__.py +util/build_utils.py +util/md5_check.py +util/proguard_util.py diff --git a/chromium/build/android/gyp/merge_jar_info_files.py b/chromium/build/android/gyp/merge_jar_info_files.py index 22a40a801bd..e6160f68469 100755 --- a/chromium/build/android/gyp/merge_jar_info_files.py +++ b/chromium/build/android/gyp/merge_jar_info_files.py @@ -61,7 +61,7 @@ def _MergeInfoFiles(output, jar_paths): for path in zip_info.namelist(): fully_qualified_name = _FullJavaNameFromClassFilePath(path) if fully_qualified_name: - info_data[fully_qualified_name] = jar_path + info_data[fully_qualified_name] = '{}/{}'.format(jar_path, path) jar_info_utils.WriteJarInfoFile(output, info_data) diff --git a/chromium/build/android/gyp/merge_jar_info_files.pydeps b/chromium/build/android/gyp/merge_jar_info_files.pydeps new file mode 100644 index 00000000000..710091c42fa --- /dev/null +++ b/chromium/build/android/gyp/merge_jar_info_files.pydeps @@ -0,0 +1,8 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/merge_jar_info_files.pydeps build/android/gyp/merge_jar_info_files.py +../../gn_helpers.py +merge_jar_info_files.py +util/__init__.py +util/build_utils.py +util/jar_info_utils.py +util/md5_check.py diff --git a/chromium/build/android/gyp/merge_manifest.pydeps b/chromium/build/android/gyp/merge_manifest.pydeps new file mode 100644 index 00000000000..37901962ce3 --- /dev/null +++ b/chromium/build/android/gyp/merge_manifest.pydeps @@ -0,0 +1,7 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/merge_manifest.pydeps build/android/gyp/merge_manifest.py +../../gn_helpers.py +merge_manifest.py +util/__init__.py +util/build_utils.py +util/md5_check.py diff --git a/chromium/build/android/gyp/prepare_resources.py b/chromium/build/android/gyp/prepare_resources.py index 6237c7db58d..ae7f53d0c30 100755 --- a/chromium/build/android/gyp/prepare_resources.py +++ b/chromium/build/android/gyp/prepare_resources.py @@ -297,7 +297,8 @@ def main(args): input_paths=input_paths, input_strings=input_strings, output_paths=output_paths, - depfile_deps=depfile_deps) + depfile_deps=depfile_deps, + add_pydeps=False) if __name__ == '__main__': diff --git a/chromium/build/android/gyp/prepare_resources.pydeps b/chromium/build/android/gyp/prepare_resources.pydeps new file mode 100644 index 00000000000..0e9ccfbe5ed --- /dev/null +++ b/chromium/build/android/gyp/prepare_resources.pydeps @@ -0,0 +1,30 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/prepare_resources.pydeps build/android/gyp/prepare_resources.py +../../../third_party/jinja2/__init__.py +../../../third_party/jinja2/_compat.py +../../../third_party/jinja2/bccache.py +../../../third_party/jinja2/compiler.py +../../../third_party/jinja2/defaults.py +../../../third_party/jinja2/environment.py +../../../third_party/jinja2/exceptions.py +../../../third_party/jinja2/filters.py +../../../third_party/jinja2/idtracking.py +../../../third_party/jinja2/lexer.py +../../../third_party/jinja2/loaders.py +../../../third_party/jinja2/nodes.py +../../../third_party/jinja2/optimizer.py +../../../third_party/jinja2/parser.py +../../../third_party/jinja2/runtime.py +../../../third_party/jinja2/tests.py +../../../third_party/jinja2/utils.py +../../../third_party/jinja2/visitor.py +../../../third_party/markupsafe/__init__.py +../../../third_party/markupsafe/_compat.py +../../../third_party/markupsafe/_native.py +../../gn_helpers.py +generate_v14_compatible_resources.py +prepare_resources.py +util/__init__.py +util/build_utils.py +util/md5_check.py +util/resource_utils.py diff --git a/chromium/build/android/gyp/proguard.py b/chromium/build/android/gyp/proguard.py index 5a61968c85c..90a461ac365 100755 --- a/chromium/build/android/gyp/proguard.py +++ b/chromium/build/android/gyp/proguard.py @@ -35,13 +35,16 @@ def _RemoveMethodMappings(orig_path, out_fd): for line in in_fd: if line[:1] != ' ': out_fd.write(line) + out_fd.flush() def _ParseOptions(args): parser = optparse.OptionParser() build_utils.AddDepfileOption(parser) parser.add_option('--proguard-path', - help='Path to the proguard executable.') + help='Path to the proguard.jar to use.') + parser.add_option('--r8-path', + help='Path to the R8.jar to use.') parser.add_option('--input-paths', help='Paths to the .jar files proguard should run on.') parser.add_option('--output-path', help='Path to the generated .jar file.') @@ -53,9 +56,6 @@ def _ParseOptions(args): 'included by --proguard-configs, but that should ' 'not actually be included.') parser.add_option('--mapping', help='Path to proguard mapping to apply.') - parser.add_option('--is-test', action='store_true', - help='If true, extra proguard options for instrumentation tests will be ' - 'added.') parser.add_option('--classpath', action='append', help='Classpath for proguard.') parser.add_option('--enable-dangerous-optimizations', action='store_true', @@ -82,6 +82,29 @@ def _ParseOptions(args): return options +def _CreateR8Command(options): + # TODO: R8 needs -applymapping equivalent. + cmd = [ + 'java', '-jar', options.r8_path, + '--no-desugaring', + '--classfile', + '--output', options.output_path, + '--pg-map-output', options.output_path + '.mapping', + ] + + classpath = [ + p for p in set(options.classpath) if p not in options.input_paths + ] + for lib in classpath: + cmd += ['--lib', lib] + + for config_file in options.proguard_configs: + cmd += ['--pg-conf', config_file] + + cmd += options.input_paths + return cmd + + def main(args): args = build_utils.ExpandFileArgs(args) options = _ParseOptions(args) @@ -92,36 +115,47 @@ def main(args): proguard.config_exclusions(options.proguard_config_exclusions) proguard.outjar(options.output_path) - classpath = list(set(options.classpath)) + # If a jar is part of input no need to include it as library jar. + classpath = [ + p for p in set(options.classpath) if p not in options.input_paths + ] proguard.libraryjars(classpath) proguard.verbose(options.verbose) if not options.enable_dangerous_optimizations: proguard.disable_optimizations(_DANGEROUS_OPTIMIZATIONS) - # Do not consider the temp file as an input since its name is random. - input_paths = proguard.GetInputs() - - with tempfile.NamedTemporaryFile() as f: - if options.mapping: - input_paths.append(options.mapping) - # Maintain only class name mappings in the .mapping file in order to work - # around what appears to be a ProGuard bug in -applymapping: - # method 'int closed()' is not being kept as 'a', but remapped to 'c' - _RemoveMethodMappings(options.mapping, f) - proguard.mapping(f.name) - - input_strings = proguard.build() - if f.name in input_strings: - input_strings[input_strings.index(f.name)] = '$M' - - build_utils.CallAndWriteDepfileIfStale( - proguard.CheckOutput, - options, - input_paths=input_paths, - input_strings=input_strings, - output_paths=proguard.GetOutputs(), - depfile_deps=proguard.GetDepfileDeps(), - add_pydeps=False) + # TODO(agrieve): Remove proguard usages. + if options.r8_path: + cmd = _CreateR8Command(options) + build_utils.CheckOutput(cmd) + build_utils.WriteDepfile(options.depfile, options.output_path, + inputs=proguard.GetDepfileDeps(), + add_pydeps=False) + else: + # Do not consider the temp file as an input since its name is random. + input_paths = proguard.GetInputs() + + with tempfile.NamedTemporaryFile() as f: + if options.mapping: + input_paths.append(options.mapping) + # Maintain only class name mappings in the .mapping file in order to + # work around what appears to be a ProGuard bug in -applymapping: + # method 'int close()' is not being kept as 'a', but remapped to 'c' + _RemoveMethodMappings(options.mapping, f) + proguard.mapping(f.name) + + input_strings = proguard.build() + if f.name in input_strings: + input_strings[input_strings.index(f.name)] = '$M' + + build_utils.CallAndWriteDepfileIfStale( + proguard.CheckOutput, + options, + input_paths=input_paths, + input_strings=input_strings, + output_paths=proguard.GetOutputs(), + depfile_deps=proguard.GetDepfileDeps(), + add_pydeps=False) if __name__ == '__main__': diff --git a/chromium/build/android/gyp/proguard.pydeps b/chromium/build/android/gyp/proguard.pydeps new file mode 100644 index 00000000000..6db3d7d0f78 --- /dev/null +++ b/chromium/build/android/gyp/proguard.pydeps @@ -0,0 +1,8 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/proguard.pydeps build/android/gyp/proguard.py +../../gn_helpers.py +proguard.py +util/__init__.py +util/build_utils.py +util/md5_check.py +util/proguard_util.py diff --git a/chromium/build/android/gyp/util/__init__.py b/chromium/build/android/gyp/util/__init__.py index 727e987e6b6..96196cffb27 100644 --- a/chromium/build/android/gyp/util/__init__.py +++ b/chromium/build/android/gyp/util/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2012 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. - diff --git a/chromium/build/android/gyp/util/proguard_util.py b/chromium/build/android/gyp/util/proguard_util.py index fd657e2aa71..8a6e785c891 100644 --- a/chromium/build/android/gyp/util/proguard_util.py +++ b/chromium/build/android/gyp/util/proguard_util.py @@ -165,7 +165,7 @@ class ProguardCmdBuilder(object): # Quite useful for auditing proguard flags. for config in sorted(self._configs): out.write('#' * 80 + '\n') - out.write(config + '\n') + out.write('# ' + config + '\n') out.write('#' * 80 + '\n') with open(config) as config_file: contents = config_file.read().rstrip() @@ -175,9 +175,9 @@ class ProguardCmdBuilder(object): out.write(contents) out.write('\n\n') out.write('#' * 80 + '\n') - out.write('Command-line\n') + out.write('# Command-line\n') out.write('#' * 80 + '\n') - out.write(' '.join(cmd) + '\n') + out.write('# ' + ' '.join(cmd) + '\n') def CheckOutput(self): cmd = self.build() diff --git a/chromium/build/android/gyp/write_build_config.py b/chromium/build/android/gyp/write_build_config.py index 734921c3334..a2387da778c 100755 --- a/chromium/build/android/gyp/write_build_config.py +++ b/chromium/build/android/gyp/write_build_config.py @@ -419,15 +419,15 @@ The list of all `deps_info['java_sources_file']` entries for all library dependencies for this APK. Note: this is a list of files, where each file contains a list of Java source files. This is used for JNI registration. -* `deps_info['proguard_all_configs"]`: +* `deps_info['proguard_all_configs']`: The collection of all 'deps_info['proguard_configs']` values from this target and all its dependencies. -* `deps_info['proguard_all_extra_jars"]`: +* `deps_info['proguard_classpath_jars']`: The collection of all 'deps_info['extra_classpath_jars']` values from all dependencies. -* `deps_info['proguard_under_test_mapping"]`: +* `deps_info['proguard_under_test_mapping']`: Applicable to apks with proguard enabled that have an apk_under_test. This is the path to the apk_under_test's output proguard .mapping file. @@ -495,6 +495,10 @@ The classpath listing the jars used for annotation processors. I.e. sent as The list of annotation processor main classes. I.e. sent as `-processor' when invoking `javac`. +## <a name="android_app_bundle">Target type `android_app_bundle`</a>: + +This type corresponds to an Android app bundle (`.aab` file). + --------------- END_MARKDOWN --------------------------------------------------- """ @@ -508,8 +512,8 @@ import xml.dom.minidom from util import build_utils # Types that should never be used as a dependency of another build config. -_ROOT_TYPES = ('android_apk', 'java_binary', - 'java_annotation_processor', 'junit_binary', 'resource_rewriter') +_ROOT_TYPES = ('android_apk', 'java_binary', 'java_annotation_processor', + 'junit_binary', 'resource_rewriter', 'android_app_bundle') # Types that should not allow code deps to pass through. _RESOURCE_TYPES = ('android_assets', 'android_resources', 'system_java_library') @@ -825,15 +829,22 @@ def main(argv): parser.add_option('--shared-libraries-runtime-deps', help='Path to file containing runtime deps for shared ' 'libraries.') + parser.add_option('--native-libs', + action='append', + help='GN-list of native libraries for primary ' + 'android-abi. Can be specified multiple times.', + default=[]) parser.add_option('--secondary-abi-shared-libraries-runtime-deps', help='Path to file containing runtime deps for secondary ' 'abi shared libraries.') + parser.add_option('--secondary-native-libs', + action='append', + help='GN-list of native libraries for secondary ' + 'android-abi. Can be specified multiple times.', + default=[]) parser.add_option('--uncompress-shared-libraries', default=False, action='store_true', help='Whether to store native libraries uncompressed') - parser.add_option('--extra-shared-libraries', - help='GN-list of paths to extra native libraries stored ' - 'in the APK.') # apk options parser.add_option('--apk-path', help='Path to the target\'s apk output.') parser.add_option('--incremental-apk-path', @@ -846,7 +857,7 @@ def main(argv): help='Path to the build config of the tested apk (for an instrumentation ' 'test apk).') parser.add_option('--proguard-enabled', action='store_true', - help='Whether proguard is enabled for this apk.') + help='Whether proguard is enabled for this apk or bundle module.') parser.add_option('--proguard-configs', help='GN-list of proguard flag files to use in final apk.') parser.add_option('--proguard-output-jar-path', @@ -883,6 +894,8 @@ def main(argv): required_options_map = { 'android_apk': ['build_config', 'dex_path', 'final_dex_path'] + \ jar_path_options, + 'android_app_bundle_module': ['build_config', 'dex_path', + 'final_dex_path'] + jar_path_options, 'android_assets': ['build_config'], 'android_resources': ['build_config', 'resources_zip'], 'dist_aar': ['build_config'], @@ -894,6 +907,7 @@ def main(argv): 'junit_binary': ['build_config'], 'resource_rewriter': ['build_config'], 'system_java_library': ['build_config'], + 'android_app_bundle': ['build_config'], } required_options = required_options_map.get(options.type) if not required_options: @@ -902,14 +916,18 @@ def main(argv): build_utils.CheckOptions(options, parser, required_options) if options.apk_proto_resources: - if options.type != 'android_apk': + if options.type != 'android_app_bundle_module': raise Exception('--apk-proto-resources can only be used with ' - '--type=android_apk') + '--type=android_app_bundle_module') + + is_apk_or_module_target = options.type in ('android_apk', + 'android_app_bundle_module') if options.uncompress_shared_libraries: - if options.type != 'android_apk': + if not is_apk_or_module_target: raise Exception('--uncompressed-shared-libraries can only be used ' - 'with --type=android_apk') + 'with --type=android_apk or ' + '--type=android_app_bundle_module') if options.jar_path and options.supports_android and not options.dex_path: raise Exception('java_library that supports Android requires a dex path.') @@ -925,7 +943,7 @@ def main(argv): is_java_target = options.type in ( 'java_binary', 'junit_binary', 'java_annotation_processor', 'java_library', 'android_apk', 'dist_aar', 'dist_jar', - 'system_java_library') + 'system_java_library', 'android_app_bundle_module') deps = _DepsFromPaths( build_utils.ParseGnList(options.deps_configs), options.type) @@ -996,7 +1014,10 @@ def main(argv): else: gradle['dependent_java_projects'].append(c['path']) - if options.type == 'android_apk': + # TODO(tiborg): Remove creation of JNI info for type group and java_library + # once we can generate the JNI registration based on APK / module targets as + # opposed to groups and libraries. + if is_apk_or_module_target or options.type in ('group', 'java_library'): config['jni'] = {} all_java_sources = [c['java_sources_file'] for c in all_library_deps if 'java_sources_file' in c] @@ -1115,7 +1136,7 @@ def main(argv): if options.type in ( 'android_resources', 'android_apk', 'junit_binary', 'resource_rewriter', - 'dist_aar'): + 'dist_aar', 'android_app_bundle_module'): config['resources'] = {} config['resources']['dependency_zips'] = [ c['resources_zip'] for c in all_resources_deps] @@ -1130,7 +1151,7 @@ def main(argv): config['resources']['extra_package_names'] = extra_package_names config['resources']['extra_r_text_files'] = extra_r_text_files - if options.type == 'android_apk': + if is_apk_or_module_target: deps_dex_files = [c['dex_path'] for c in all_library_deps] if is_java_target: @@ -1149,48 +1170,95 @@ def main(argv): # The classpath used for bytecode-rewritting. javac_full_classpath = [ c['unprocessed_jar_path'] for c in all_library_deps] - # The classpath to use to run this target (or as an input to ProGuard). - java_full_classpath = [] - if options.jar_path: - java_full_classpath.append(options.jar_path) - java_full_classpath.extend(c['jar_path'] for c in all_library_deps) # Deps to add to the compile-time classpath (but not the runtime classpath). # TODO(agrieve): Might be less confusing to fold these into bootclasspath. - extra_jars = [c['unprocessed_jar_path'] + javac_extra_jars = [c['unprocessed_jar_path'] + for c in classpath_deps.Direct('java_library')] + extra_jars = [c['jar_path'] for c in classpath_deps.Direct('java_library')] if options.extra_classpath_jars: # These are .jars to add to javac classpath but not to runtime classpath. + javac_extra_jars.extend( + build_utils.ParseGnList(options.extra_classpath_jars)) extra_jars.extend(build_utils.ParseGnList(options.extra_classpath_jars)) - extra_jars = [p for p in extra_jars if p not in javac_classpath] - javac_classpath.extend(extra_jars) - javac_interface_classpath.extend(extra_jars) - javac_full_interface_classpath.extend( - p for p in extra_jars if p not in javac_full_classpath) - javac_full_classpath.extend( - p for p in extra_jars if p not in javac_full_classpath) if extra_jars: deps_info['extra_classpath_jars'] = extra_jars + javac_extra_jars = [p for p in javac_extra_jars if p not in javac_classpath] + javac_classpath.extend(javac_extra_jars) + javac_interface_classpath.extend(javac_extra_jars) + javac_full_interface_classpath.extend( + p for p in javac_extra_jars if p not in javac_full_classpath) + javac_full_classpath.extend( + p for p in javac_extra_jars if p not in javac_full_classpath) + + if is_java_target or options.type == 'android_app_bundle': + # The classpath to use to run this target (or as an input to ProGuard). + java_full_classpath = [] + if is_java_target and options.jar_path: + java_full_classpath.append(options.jar_path) + java_full_classpath.extend(c['jar_path'] for c in all_library_deps) + if options.type == 'android_app_bundle': + for d in deps.Direct('android_app_bundle_module'): + java_full_classpath.extend( + c for c in d.get('java_runtime_classpath', []) + if c not in java_full_classpath) + + system_jars = [c['jar_path'] for c in system_library_deps] + system_interface_jars = [c['interface_jar_path'] for c in system_library_deps] + if system_library_deps: + config['android'] = {} + config['android']['sdk_interface_jars'] = system_interface_jars + config['android']['sdk_jars'] = system_jars + gradle['bootclasspath'] = system_jars + if options.proguard_configs: deps_info['proguard_configs'] = ( build_utils.ParseGnList(options.proguard_configs)) - if options.type in ('android_apk', 'dist_aar', 'dist_jar'): + if options.type in ('android_apk', 'dist_aar', + 'dist_jar', 'android_app_bundle_module', 'android_app_bundle'): all_configs = deps_info.get('proguard_configs', []) - extra_jars = [] + extra_jars = list() for c in all_library_deps: all_configs.extend( p for p in c.get('proguard_configs', []) if p not in all_configs) extra_jars.extend( p for p in c.get('extra_classpath_jars', []) if p not in extra_jars) + if options.type == 'android_app_bundle': + for c in deps.Direct('android_app_bundle_module'): + all_configs.extend( + p for p in c.get('proguard_configs', []) if p not in all_configs) deps_info['proguard_all_configs'] = all_configs - deps_info['proguard_all_extra_jars'] = extra_jars - deps_info['proguard_enabled'] = options.proguard_enabled - if options.proguard_output_jar_path: - deps_info['proguard_output_jar_path'] = options.proguard_output_jar_path + if options.type == 'android_app_bundle': + for d in deps.Direct('android_app_bundle_module'): + extra_jars.extend( + c for c in d.get('proguard_classpath_jars', []) + if c not in extra_jars) + deps_info['proguard_classpath_jars'] = extra_jars + + if options.type == 'android_app_bundle': + deps_proguard_enabled = [] + deps_proguard_disabled = [] + for d in deps.Direct('android_app_bundle_module'): + if not d['java_runtime_classpath']: + # We don't care about modules that have no Java code for proguarding. + continue + if d['proguard_enabled']: + deps_proguard_enabled.append(d['name']) + else: + deps_proguard_disabled.append(d['name']) + if deps_proguard_enabled and deps_proguard_disabled: + raise Exception('Deps %s have proguard enabled while deps %s have ' + 'proguard disabled' % (deps_proguard_enabled, + deps_proguard_disabled)) + else: + deps_info['proguard_enabled'] = bool(options.proguard_enabled) + if options.proguard_output_jar_path: + deps_info['proguard_output_jar_path'] = options.proguard_output_jar_path # The java code for an instrumentation test apk is assembled differently for # ProGuard vs. non-ProGuard. @@ -1215,7 +1283,7 @@ def main(argv): # Mutating lists, so no need to explicitly re-assign to dict. all_configs.extend(p for p in tested_apk_config['proguard_all_configs'] if p not in all_configs) - extra_jars.extend(p for p in tested_apk_config['proguard_all_extra_jars'] + extra_jars.extend(p for p in tested_apk_config['proguard_classpath_jars'] if p not in extra_jars) tested_apk_config = GetDepConfig(options.tested_apk_config) deps_info['proguard_under_test_mapping'] = ( @@ -1258,20 +1326,12 @@ def main(argv): p for p in deps_dex_files if not p in tested_apk_deps_dex_files] # Dependencies for the final dex file of an apk. - if options.type == 'android_apk': + if is_apk_or_module_target: config['final_dex'] = {} dex_config = config['final_dex'] dex_config['dependency_dex_files'] = deps_dex_files dex_config['path'] = options.final_dex_path - system_jars = [c['jar_path'] for c in system_library_deps] - system_interface_jars = [c['interface_jar_path'] for c in system_library_deps] - if system_library_deps: - config['android'] = {} - config['android']['sdk_interface_jars'] = system_interface_jars - config['android']['sdk_jars'] = system_jars - gradle['bootclasspath'] = system_jars - if is_java_target: config['javac']['bootclasspath'] = system_jars config['javac']['classpath'] = javac_classpath @@ -1285,9 +1345,9 @@ def main(argv): deps_info['javac_full_classpath'] = javac_full_classpath deps_info['javac_full_interface_classpath'] = javac_full_interface_classpath - if options.type in ( - 'android_apk', 'dist_jar', 'java_binary', 'junit_binary'): - deps_info['java_runtime_classpath'] = java_full_classpath + if options.type in ('android_apk', 'dist_jar', 'java_binary', 'junit_binary', + 'android_app_bundle_module', 'android_app_bundle'): + deps_info['java_runtime_classpath'] = java_full_classpath if options.type in ('android_apk', 'dist_jar'): all_interface_jars = [] @@ -1299,7 +1359,7 @@ def main(argv): 'all_interface_jars': all_interface_jars, } - if options.type == 'android_apk': + if is_apk_or_module_target: manifest = AndroidManifest(options.android_manifest) deps_info['package_name'] = manifest.GetPackageName() if not options.tested_apk_config and manifest.GetInstrumentationElements(): @@ -1323,9 +1383,12 @@ def main(argv): secondary_abi_runtime_deps_files) secondary_abi_java_libraries_list = _CreateJavaLibrariesList( secondary_abi_library_paths) + for gn_list in options.secondary_native_libs: + secondary_abi_library_paths.extend(build_utils.ParseGnList(gn_list)) - extra_shared_libraries = build_utils.ParseGnList( - options.extra_shared_libraries) + extra_shared_libraries = [] + for gn_list in options.native_libs: + extra_shared_libraries.extend(build_utils.ParseGnList(gn_list)) all_inputs.extend(runtime_deps_files) config['native'] = { diff --git a/chromium/build/android/gyp/write_build_config.pydeps b/chromium/build/android/gyp/write_build_config.pydeps new file mode 100644 index 00000000000..e317c47cae4 --- /dev/null +++ b/chromium/build/android/gyp/write_build_config.pydeps @@ -0,0 +1,7 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/write_build_config.pydeps build/android/gyp/write_build_config.py +../../gn_helpers.py +util/__init__.py +util/build_utils.py +util/md5_check.py +write_build_config.py diff --git a/chromium/build/android/gyp/write_ordered_libraries.py b/chromium/build/android/gyp/write_ordered_libraries.py index e3d87c16bb0..92c82bb4c8c 100755 --- a/chromium/build/android/gyp/write_ordered_libraries.py +++ b/chromium/build/android/gyp/write_ordered_libraries.py @@ -109,10 +109,9 @@ def main(): only_if_changed=True) if options.depfile: - build_utils.WriteDepfile(options.depfile, options.output, libraries) + build_utils.WriteDepfile(options.depfile, options.output, libraries, + add_pydeps=False) if __name__ == '__main__': sys.exit(main()) - - diff --git a/chromium/build/android/gyp/write_ordered_libraries.pydeps b/chromium/build/android/gyp/write_ordered_libraries.pydeps new file mode 100644 index 00000000000..c2ed1fee36e --- /dev/null +++ b/chromium/build/android/gyp/write_ordered_libraries.pydeps @@ -0,0 +1,7 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/write_ordered_libraries.pydeps build/android/gyp/write_ordered_libraries.py +../../gn_helpers.py +util/__init__.py +util/build_utils.py +util/md5_check.py +write_ordered_libraries.py diff --git a/chromium/build/android/incremental_install/__init__.py b/chromium/build/android/incremental_install/__init__.py index 1aaf0e179ff..50b23dff631 100644 --- a/chromium/build/android/incremental_install/__init__.py +++ b/chromium/build/android/incremental_install/__init__.py @@ -1,4 +1,3 @@ # Copyright 2015 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. - diff --git a/chromium/build/android/incremental_install/generate_android_manifest.pydeps b/chromium/build/android/incremental_install/generate_android_manifest.pydeps new file mode 100644 index 00000000000..de92fafa9bb --- /dev/null +++ b/chromium/build/android/incremental_install/generate_android_manifest.pydeps @@ -0,0 +1,7 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/incremental_install --output build/android/incremental_install/generate_android_manifest.pydeps build/android/incremental_install/generate_android_manifest.py +../../gn_helpers.py +../gyp/util/__init__.py +../gyp/util/build_utils.py +../gyp/util/md5_check.py +generate_android_manifest.py diff --git a/chromium/build/android/incremental_install/write_installer_json.py b/chromium/build/android/incremental_install/write_installer_json.py index b7e4e773adb..75bd6d1aab7 100755 --- a/chromium/build/android/incremental_install/write_installer_json.py +++ b/chromium/build/android/incremental_install/write_installer_json.py @@ -19,7 +19,6 @@ from util import build_utils def _ParseArgs(args): args = build_utils.ExpandFileArgs(args) parser = argparse.ArgumentParser() - build_utils.AddDepfileOption(parser) parser.add_argument('--output-path', help='Output path for .json file.', required=True) @@ -76,9 +75,6 @@ def main(args): with build_utils.AtomicOutput(options.output_path) as f: json.dump(data, f, indent=2, sort_keys=True) - if options.depfile: - build_utils.WriteDepfile(options.depfile, options.output_path) - if __name__ == '__main__': main(sys.argv[1:]) diff --git a/chromium/build/android/incremental_install/write_installer_json.pydeps b/chromium/build/android/incremental_install/write_installer_json.pydeps new file mode 100644 index 00000000000..851e6c5bd1b --- /dev/null +++ b/chromium/build/android/incremental_install/write_installer_json.pydeps @@ -0,0 +1,7 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/incremental_install --output build/android/incremental_install/write_installer_json.pydeps build/android/incremental_install/write_installer_json.py +../../gn_helpers.py +../gyp/util/__init__.py +../gyp/util/build_utils.py +../gyp/util/md5_check.py +write_installer_json.py diff --git a/chromium/build/android/lighttpd_server.py b/chromium/build/android/lighttpd_server.py index 89a828ab54a..c77d740d66a 100755 --- a/chromium/build/android/lighttpd_server.py +++ b/chromium/build/android/lighttpd_server.py @@ -104,7 +104,7 @@ class LighttpdServer(object): break self.process.close() - if self.fixed_port or not 'in use' in server_error: + if self.fixed_port or 'in use' not in server_error: print 'Client error:', client_error print 'Server error:', server_error return False diff --git a/chromium/build/android/lint/suppressions.xml b/chromium/build/android/lint/suppressions.xml index 6e5562a5a70..c4b132f5ea3 100644 --- a/chromium/build/android/lint/suppressions.xml +++ b/chromium/build/android/lint/suppressions.xml @@ -333,10 +333,6 @@ Still reading? <!-- 2 resource sets used by clank widgets for each channel --> <ignore regexp="The resource `R.string.bookmark_widget_title.*` appears to be unused"/> <ignore regexp="The resource `R.string.search_widget_title.*` appears to be unused"/> - <!-- 3 Download location strings checked in before M68 deadline --> - <!-- TODO(xingliu): Remove these. see crbug.com/840464. --> - <ignore regexp="The resource `R.string.download_location_not_enough_space_sd_card_full` appears to be unused"/> - <ignore regexp="The resource `R.string.download_location_notification_retry*` appears to be unused"/> <!-- Endnote: Please specify number of suppressions when adding more --> </issue> <issue id="UseCompoundDrawables"> diff --git a/chromium/build/android/method_count.py b/chromium/build/android/method_count.py index 68823e15482..490887adbca 100755 --- a/chromium/build/android/method_count.py +++ b/chromium/build/android/method_count.py @@ -114,4 +114,3 @@ def main(): if __name__ == '__main__': sys.exit(main()) - diff --git a/chromium/build/android/pylib/android/__init__.py b/chromium/build/android/pylib/android/__init__.py index 7a90b532c39..a67c3501b20 100644 --- a/chromium/build/android/pylib/android/__init__.py +++ b/chromium/build/android/pylib/android/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2016 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. - diff --git a/chromium/build/android/pylib/base/__init__.py b/chromium/build/android/pylib/base/__init__.py index 727e987e6b6..96196cffb27 100644 --- a/chromium/build/android/pylib/base/__init__.py +++ b/chromium/build/android/pylib/base/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2012 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. - diff --git a/chromium/build/android/pylib/base/base_test_result.py b/chromium/build/android/pylib/base/base_test_result.py index af9f68f8e53..a5535322965 100644 --- a/chromium/build/android/pylib/base/base_test_result.py +++ b/chromium/build/android/pylib/base/base_test_result.py @@ -260,4 +260,3 @@ class TestRunResults(object): def DidRunPass(self): """Return whether the test run was successful.""" return not self.GetNotPass() - self.GetSkip() - diff --git a/chromium/build/android/pylib/base/test_exception.py b/chromium/build/android/pylib/base/test_exception.py index f00f0d0ee85..c98d2cb73eb 100644 --- a/chromium/build/android/pylib/base/test_exception.py +++ b/chromium/build/android/pylib/base/test_exception.py @@ -6,4 +6,3 @@ class TestException(Exception): """Base class for exceptions thrown by the test runner.""" pass - diff --git a/chromium/build/android/pylib/base/test_instance.py b/chromium/build/android/pylib/base/test_instance.py index cdf678f2d28..f0ff83019bc 100644 --- a/chromium/build/android/pylib/base/test_instance.py +++ b/chromium/build/android/pylib/base/test_instance.py @@ -32,4 +32,3 @@ class TestInstance(object): def __exit__(self, _exc_type, _exc_val, _exc_tb): self.TearDown() - diff --git a/chromium/build/android/pylib/base/test_run.py b/chromium/build/android/pylib/base/test_run.py index 9b16f890f69..59e595d926b 100644 --- a/chromium/build/android/pylib/base/test_run.py +++ b/chromium/build/android/pylib/base/test_run.py @@ -41,4 +41,3 @@ class TestRun(object): def __exit__(self, exc_type, exc_val, exc_tb): self.TearDown() - diff --git a/chromium/build/android/pylib/base/test_run_factory.py b/chromium/build/android/pylib/base/test_run_factory.py index 1a2872819d9..1f63a059c98 100644 --- a/chromium/build/android/pylib/base/test_run_factory.py +++ b/chromium/build/android/pylib/base/test_run_factory.py @@ -54,4 +54,3 @@ def CreateTestRun(args, env, test_instance, error_func): error_func('Unable to create test run for %s tests in %s environment' % (str(test_instance), str(env))) - diff --git a/chromium/build/android/pylib/base/test_server.py b/chromium/build/android/pylib/base/test_server.py index 085a51e9bae..763e1212c36 100644 --- a/chromium/build/android/pylib/base/test_server.py +++ b/chromium/build/android/pylib/base/test_server.py @@ -16,4 +16,3 @@ class TestServer(object): def TearDown(self): raise NotImplementedError - diff --git a/chromium/build/android/pylib/constants/__init__.py b/chromium/build/android/pylib/constants/__init__.py index 2101b9c1ca1..cf6ca113390 100644 --- a/chromium/build/android/pylib/constants/__init__.py +++ b/chromium/build/android/pylib/constants/__init__.py @@ -104,8 +104,6 @@ ANDROID_SDK_TOOLS = os.path.join(ANDROID_SDK_ROOT, ANDROID_NDK_ROOT = os.path.join(DIR_SOURCE_ROOT, 'third_party', 'android_ndk') -PROGUARD_ROOT = os.path.join(DIR_SOURCE_ROOT, 'third_party', 'proguard') - BAD_DEVICES_JSON = os.path.join(DIR_SOURCE_ROOT, os.environ.get('CHROMIUM_OUT_DIR', 'out'), 'bad_devices.json') diff --git a/chromium/build/android/pylib/gtest/__init__.py b/chromium/build/android/pylib/gtest/__init__.py index 727e987e6b6..96196cffb27 100644 --- a/chromium/build/android/pylib/gtest/__init__.py +++ b/chromium/build/android/pylib/gtest/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2012 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. - diff --git a/chromium/build/android/pylib/gtest/gtest_test_instance.py b/chromium/build/android/pylib/gtest/gtest_test_instance.py index d93edc65070..cd8ddbc3921 100644 --- a/chromium/build/android/pylib/gtest/gtest_test_instance.py +++ b/chromium/build/android/pylib/gtest/gtest_test_instance.py @@ -533,7 +533,9 @@ class GtestTestInstance(test_instance.TestInstance): disabled_filter_items = [] if disabled_prefixes is None: - disabled_prefixes = ['FAILS_', 'PRE_', 'MANUAL_'] + disabled_prefixes = ['FAILS_', 'PRE_'] + if '--run-manual' not in self._flags: + disabled_prefixes += ['MANUAL_'] if not self._run_disabled: disabled_prefixes += ['DISABLED_', 'FLAKY_'] @@ -555,4 +557,3 @@ class GtestTestInstance(test_instance.TestInstance): def TearDown(self): """Do nothing.""" pass - diff --git a/chromium/build/android/pylib/gtest/gtest_test_instance_test.py b/chromium/build/android/pylib/gtest/gtest_test_instance_test.py index 3466924a0d9..a34ab84c7dd 100755 --- a/chromium/build/android/pylib/gtest/gtest_test_instance_test.py +++ b/chromium/build/android/pylib/gtest/gtest_test_instance_test.py @@ -260,4 +260,3 @@ class GtestTestInstanceTests(unittest.TestCase): if __name__ == '__main__': unittest.main(verbosity=2) - diff --git a/chromium/build/android/pylib/instrumentation/__init__.py b/chromium/build/android/pylib/instrumentation/__init__.py index 727e987e6b6..96196cffb27 100644 --- a/chromium/build/android/pylib/instrumentation/__init__.py +++ b/chromium/build/android/pylib/instrumentation/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2012 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. - diff --git a/chromium/build/android/pylib/instrumentation/instrumentation_test_instance.py b/chromium/build/android/pylib/instrumentation/instrumentation_test_instance.py index 452b259849c..809fce60105 100644 --- a/chromium/build/android/pylib/instrumentation/instrumentation_test_instance.py +++ b/chromium/build/android/pylib/instrumentation/instrumentation_test_instance.py @@ -568,15 +568,15 @@ class InstrumentationTestInstance(test_instance.TestInstance): self._test_package = self._test_apk.GetPackageName() all_instrumentations = self._test_apk.GetAllInstrumentations() all_junit3_runner_classes = [ - x for x in all_instrumentations if ('0xffffffff' not in x.get( - 'chromium-junit4', ''))] - all_junit4_test_runner_classes = [ x for x in all_instrumentations if ('0xffffffff' in x.get( - 'chromium-junit4', ''))] + 'chromium-junit3', ''))] + all_junit4_runner_classes = [ + x for x in all_instrumentations if ('0xffffffff' not in x.get( + 'chromium-junit3', ''))] if len(all_junit3_runner_classes) > 1: logging.warning('This test apk has more than one JUnit3 instrumentation') - if len(all_junit4_test_runner_classes) > 1: + if len(all_junit4_runner_classes) > 1: logging.warning('This test apk has more than one JUnit4 instrumentation') self._junit3_runner_class = ( @@ -584,8 +584,8 @@ class InstrumentationTestInstance(test_instance.TestInstance): if all_junit3_runner_classes else self.test_apk.GetInstrumentationName()) self._junit4_runner_class = ( - all_junit4_test_runner_classes[0]['android:name'] - if all_junit4_test_runner_classes else None) + all_junit4_runner_classes[0]['android:name'] + if all_junit4_runner_classes else None) if self._junit4_runner_class: if self._test_apk_incremental_install_json: diff --git a/chromium/build/android/pylib/junit/__init__.py b/chromium/build/android/pylib/junit/__init__.py index 5cac026153c..4d6aabb953d 100644 --- a/chromium/build/android/pylib/junit/__init__.py +++ b/chromium/build/android/pylib/junit/__init__.py @@ -1,4 +1,3 @@ # Copyright 2014 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. - diff --git a/chromium/build/android/pylib/linker/__init__.py b/chromium/build/android/pylib/linker/__init__.py index af994371233..9228df89b0e 100644 --- a/chromium/build/android/pylib/linker/__init__.py +++ b/chromium/build/android/pylib/linker/__init__.py @@ -1,4 +1,3 @@ # Copyright 2013 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. - diff --git a/chromium/build/android/pylib/linker/linker_test_instance.py b/chromium/build/android/pylib/linker/linker_test_instance.py index 1de3e17f1df..6ace7a3633c 100644 --- a/chromium/build/android/pylib/linker/linker_test_instance.py +++ b/chromium/build/android/pylib/linker/linker_test_instance.py @@ -48,4 +48,3 @@ class LinkerTestInstance(test_instance.TestInstance): def TestType(self): return 'linker' - diff --git a/chromium/build/android/pylib/local/device/local_device_gtest_run.py b/chromium/build/android/pylib/local/device/local_device_gtest_run.py index 21e9fe85121..47348834c99 100644 --- a/chromium/build/android/pylib/local/device/local_device_gtest_run.py +++ b/chromium/build/android/pylib/local/device/local_device_gtest_run.py @@ -152,6 +152,7 @@ class _ApkDelegate(object): extras[gtest_test_instance.EXTRA_SHARD_NANO_TIMEOUT] = int( kwargs['timeout'] * _SECONDS_TO_NANOS) + # pylint: disable=redefined-variable-type command_line_file = _NullContextManager() if flags: if len(flags) > _MAX_INLINE_FLAGS_LENGTH: @@ -169,6 +170,7 @@ class _ApkDelegate(object): extras[_EXTRA_TEST_LIST] = test_list_file.name else: extras[_EXTRA_TEST] = test[0] + # pylint: enable=redefined-variable-type stdout_file = device_temp_file.DeviceTempFile( device.adb, dir=device.GetExternalStoragePath(), suffix='.gtest_out') @@ -283,11 +285,13 @@ class LocalDeviceGtestRun(local_device_test_run.LocalDeviceTestRun): assert isinstance(test_instance, gtest_test_instance.GtestTestInstance) super(LocalDeviceGtestRun, self).__init__(env, test_instance) + # pylint: disable=redefined-variable-type if self._test_instance.apk: self._delegate = _ApkDelegate(self._test_instance, env.tool) elif self._test_instance.exe_dist_dir: self._delegate = _ExeDelegate(self, self._test_instance.exe_dist_dir, self._env.tool) + # pylint: enable=redefined-variable-type self._crashes = set() self._servers = collections.defaultdict(list) diff --git a/chromium/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/chromium/build/android/pylib/local/device/local_device_instrumentation_test_run.py index 40b857d5fbf..de7ed933b5d 100644 --- a/chromium/build/android/pylib/local/device/local_device_instrumentation_test_run.py +++ b/chromium/build/android/pylib/local/device/local_device_instrumentation_test_run.py @@ -153,7 +153,11 @@ class LocalDeviceInstrumentationTestRun( self._replace_package_contextmanager = system_app.ReplaceSystemApp( dev, self._test_instance.replace_system_package.package, self._test_instance.replace_system_package.replacement_apk) + # Pylint is not smart enough to realize that this field has + # an __enter__ method, and will complain loudly. + # pylint: disable=no-member self._replace_package_contextmanager.__enter__() + # pylint: enable=no-member steps.append(replace_package) @@ -308,12 +312,15 @@ class LocalDeviceInstrumentationTestRun( valgrind_tools.SetChromeTimeoutScale(dev, None) if self._replace_package_contextmanager: + # See pylint-related commend above with __enter__() + # pylint: disable=no-member self._replace_package_contextmanager.__exit__(*sys.exc_info()) + # pylint: enable=no-member self._env.parallel_devices.pMap(individual_device_tear_down) def _CreateFlagChangerIfNeeded(self, device): - if not str(device) in self._flag_changers: + if str(device) not in self._flag_changers: self._flag_changers[str(device)] = flag_changer.FlagChanger( device, "test-cmdline-file") diff --git a/chromium/build/android/pylib/local/device/local_device_test_run.py b/chromium/build/android/pylib/local/device/local_device_test_run.py index 90e6b674cb0..00d715b3ade 100644 --- a/chromium/build/android/pylib/local/device/local_device_test_run.py +++ b/chromium/build/android/pylib/local/device/local_device_test_run.py @@ -220,7 +220,7 @@ class LocalDeviceTestRun(test_run.TestRun): if hash(self._GetUniqueTestName(t)) % total_shards == shard_index] def GetTool(self, device): - if not str(device) in self._tools: + if str(device) not in self._tools: self._tools[str(device)] = valgrind_tools.CreateTool( self._env.tool, device) return self._tools[str(device)] diff --git a/chromium/build/android/pylib/local/local_test_server_spawner.py b/chromium/build/android/pylib/local/local_test_server_spawner.py index 8e416fac5d0..6cd282e3a45 100644 --- a/chromium/build/android/pylib/local/local_test_server_spawner.py +++ b/chromium/build/android/pylib/local/local_test_server_spawner.py @@ -98,4 +98,3 @@ class LocalTestServerSpawner(test_server.TestServer): self.Reset() self._spawning_server.Stop() forwarder.Forwarder.UnmapDevicePort(self.port, self._device) - diff --git a/chromium/build/android/pylib/results/flakiness_dashboard/json_results_generator.py b/chromium/build/android/pylib/results/flakiness_dashboard/json_results_generator.py index 9693977e6bf..5e5f83f2a27 100644 --- a/chromium/build/android/pylib/results/flakiness_dashboard/json_results_generator.py +++ b/chromium/build/android/pylib/results/flakiness_dashboard/json_results_generator.py @@ -380,6 +380,7 @@ class JSONResultsGeneratorBase(object): urllib2.quote(self._test_type), urllib2.quote(self._master_name))) + # pylint: disable=redefined-variable-type try: # FIXME: We should talk to the network via a Host object. results_file = urllib2.urlopen(results_file_url) @@ -391,6 +392,7 @@ class JSONResultsGeneratorBase(object): error = http_error except urllib2.URLError, url_error: error = url_error + # pylint: enable=redefined-variable-type if old_results: # Strip the prefix and suffix so we can get the actual JSON object. diff --git a/chromium/build/android/pylib/results/json_results.py b/chromium/build/android/pylib/results/json_results.py index 1243691b7f8..3f87b46d8c5 100644 --- a/chromium/build/android/pylib/results/json_results.py +++ b/chromium/build/android/pylib/results/json_results.py @@ -175,4 +175,3 @@ def ParseResultsFromJson(json_results): duration=tr['elapsed_time_ms']) for tr in test_runs]) return results_list - diff --git a/chromium/build/android/pylib/results/json_results_test.py b/chromium/build/android/pylib/results/json_results_test.py index 276171353f4..e8b983b56ed 100755 --- a/chromium/build/android/pylib/results/json_results_test.py +++ b/chromium/build/android/pylib/results/json_results_test.py @@ -178,4 +178,3 @@ class JsonResultsTest(unittest.TestCase): if __name__ == '__main__': unittest.main(verbosity=2) - diff --git a/chromium/build/android/pylib/results/presentation/test_results_presentation.py b/chromium/build/android/pylib/results/presentation/test_results_presentation.py index eaae68f85b7..82d6c884700 100755 --- a/chromium/build/android/pylib/results/presentation/test_results_presentation.py +++ b/chromium/build/android/pylib/results/presentation/test_results_presentation.py @@ -268,6 +268,7 @@ def create_suite_table(results_dict): def feedback_url(result_details_link): + # pylint: disable=redefined-variable-type url_args = [ ('labels', 'Pri-2,Type-Bug,Restrict-View-Google'), ('summary', 'Result Details Feedback:'), @@ -276,6 +277,7 @@ def feedback_url(result_details_link): if result_details_link: url_args.append(('comment', 'Please check out: %s' % result_details_link)) url_args = urllib.urlencode(url_args) + # pylint: enable=redefined-variable-type return 'https://bugs.chromium.org/p/chromium/issues/entry?%s' % url_args @@ -370,6 +372,7 @@ def ui_screenshot_set(json_path): # This will be reported as an error by result_details, no need to duplicate. return None ui_screenshots = [] + # pylint: disable=too-many-nested-blocks for testsuite_run in json_object['per_iteration_data']: for _, test_runs in testsuite_run.iteritems(): for test_run in test_runs: @@ -388,6 +391,7 @@ def ui_screenshot_set(json_path): test_screenshots = json.loads( screenshot_string) ui_screenshots.extend(test_screenshots) + # pylint: enable=too-many-nested-blocks if ui_screenshots: return json.dumps(ui_screenshots) diff --git a/chromium/build/android/pylib/symbols/deobfuscator.py b/chromium/build/android/pylib/symbols/deobfuscator.py index 9d4f5fdfb4b..ac4ff7e4b4b 100644 --- a/chromium/build/android/pylib/symbols/deobfuscator.py +++ b/chromium/build/android/pylib/symbols/deobfuscator.py @@ -25,6 +25,8 @@ class Deobfuscator(object): cmd = [script_path, mapping_path] # Allow only one thread to call TransformLines() at a time. self._lock = threading.Lock() + # Ensure that only one thread attempts to kill self._proc in Close(). + self._close_lock = threading.Lock() self._closed_called = False # Assign to None so that attribute exists if Popen() throws. self._proc = None @@ -114,8 +116,11 @@ class Deobfuscator(object): return lines def Close(self): - self._closed_called = True - if not self.IsClosed(): + with self._close_lock: + needs_closing = not self.IsClosed() + self._closed_called = True + + if needs_closing: self._proc.stdin.close() self._proc.kill() self._proc.wait() diff --git a/chromium/build/android/pylib/symbols/symbol_utils.py b/chromium/build/android/pylib/symbols/symbol_utils.py new file mode 100644 index 00000000000..e4e3faac803 --- /dev/null +++ b/chromium/build/android/pylib/symbols/symbol_utils.py @@ -0,0 +1,812 @@ +# Copyright 2018 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import bisect +import collections +import logging +import os +import re + +from pylib.constants import host_paths +from pylib.symbols import elf_symbolizer + + +def _AndroidAbiToCpuArch(android_abi): + """Return the Chromium CPU architecture name for a given Android ABI.""" + _ARCH_MAP = { + 'armeabi': 'arm', + 'armeabi-v7a': 'arm', + 'arm64-v8a': 'arm64', + 'x86_64': 'x64', + } + return _ARCH_MAP.get(android_abi, android_abi) + + +def _HexAddressRegexpFor(android_abi): + """Return a regexp matching hexadecimal addresses for a given Android ABI.""" + if android_abi in ['x86_64', 'arm64-v8a', 'mips64']: + width = 16 + else: + width = 8 + return '[0-9a-f]{%d}' % width + + +class HostLibraryFinder(object): + """Translate device library path to matching host unstripped library path. + + Usage is the following: + 1) Create instance. + 2) Call AddSearchDir() once or more times to add host directory path to + look for unstripped native libraries. + 3) Call Find(device_libpath) repeatedly to translate a device-specific + library path into the corresponding host path to the unstripped + version. + """ + def __init__(self): + """Initialize instance.""" + self._search_dirs = [] + self._lib_map = {} # Map of library name to host file paths. + + def AddSearchDir(self, lib_dir): + """Add a directory to the search path for host native shared libraries. + + Args: + lib_dir: host path containing native libraries. + """ + if not os.path.exists(lib_dir): + logging.warning('Ignoring missing host library directory: %s', lib_dir) + return + if not os.path.isdir(lib_dir): + logging.warning('Ignoring invalid host library directory: %s', lib_dir) + return + self._search_dirs.append(lib_dir) + self._lib_map = {} # Reset the map. + + def Find(self, device_libpath): + """Find the host file path matching a specific device library path. + + Args: + device_libpath: device-specific file path to library or executable. + Returns: + host file path to the unstripped version of the library, or None. + """ + host_lib_path = None + lib_name = os.path.basename(device_libpath) + host_lib_path = self._lib_map.get(lib_name) + if not host_lib_path: + for search_dir in self._search_dirs: + lib_path = os.path.join(search_dir, lib_name) + if os.path.exists(lib_path): + host_lib_path = lib_path + break + + if not host_lib_path: + logging.debug('Could not find host library for: %s', lib_name) + self._lib_map[lib_name] = host_lib_path + + return host_lib_path + + + +class SymbolResolver(object): + """A base class for objets that can symbolize library (path, offset) + pairs into symbol information strings. Usage is the following: + + 1) Create new instance (by calling the constructor of a derived + class, since this is only the base one). + + 2) Call SetAndroidAbi() before any call to FindSymbolInfo() in order + to set the Android CPU ABI used for symbolization. + + 3) Before the first call to FindSymbolInfo(), one can call + AddLibraryOffset(), or AddLibraryOffsets() to record a set of offsets + that you will want to symbolize later through FindSymbolInfo(). Doing + so allows some SymbolResolver derived classes to work faster (e.g. the + one that invokes the 'addr2line' program, since the latter works faster + if the offsets provided as inputs are sorted in increasing order). + + 3) Call FindSymbolInfo(path, offset) to return the corresponding + symbol information string, or None if this doesn't correspond + to anything the instance can handle. + + Note that whether the path is specific to the device or to the + host depends on the derived class implementation. + """ + def __init__(self): + self._android_abi = None + self._lib_offsets_map = collections.defaultdict(set) + + def SetAndroidAbi(self, android_abi): + """Set the Android ABI value for this instance. + + Calling this function before FindSymbolInfo() is required by some + derived class implementations. + + Args: + android_abi: Native Android CPU ABI name (e.g. 'armeabi-v7a'). + Raises: + Exception if the ABI was already set with a different value. + """ + if self._android_abi and self._android_abi != android_abi: + raise Exception('Cannot reset Android ABI to new value %s, already set ' + 'to %s' % (android_abi, self._android_abi)) + + self._android_abi = android_abi + + def AddLibraryOffset(self, lib_path, offset): + """Associate a single offset to a given device library. + + This must be called before FindSymbolInfo(), otherwise its input arguments + will be ignored. + + Args: + lib_path: A library path. + offset: An integer offset within the corresponding library that will be + symbolized by future calls to FindSymbolInfo. + """ + self._lib_offsets_map[lib_path].add(offset) + + def AddLibraryOffsets(self, lib_path, lib_offsets): + """Associate a set of wanted offsets to a given device library. + + This must be called before FindSymbolInfo(), otherwise its input arguments + will be ignored. + + Args: + lib_path: A library path. + lib_offsets: An iterable of integer offsets within the corresponding + library that will be symbolized by future calls to FindSymbolInfo. + """ + self._lib_offsets_map[lib_path].update(lib_offsets) + + # pylint: disable=unused-argument,no-self-use + def FindSymbolInfo(self, lib_path, lib_offset): + """Symbolize a device library path and offset. + + Args: + lib_path: Library path (device or host specific, depending on the + derived class implementation). + lib_offset: Integer offset within the library. + Returns: + Corresponding symbol information string, or None. + """ + # The base implementation cannot symbolize anything. + return None + # pylint: enable=unused-argument,no-self-use + + +class ElfSymbolResolver(SymbolResolver): + """A SymbolResolver that can symbolize host path + offset values using + an elf_symbolizer.ELFSymbolizer instance. + """ + def __init__(self, addr2line_path_for_tests=None): + super(ElfSymbolResolver, self).__init__() + self._addr2line_path = addr2line_path_for_tests + + # Used to cache one ELFSymbolizer instance per library path. + self._elf_symbolizer_cache = {} + + # Used to cache FindSymbolInfo() results. Maps host library paths + # to (offset -> symbol info string) dictionaries. + self._symbol_info_cache = collections.defaultdict(dict) + self._allow_symbolizer = True + + def _CreateSymbolizerFor(self, host_path): + """Create the ELFSymbolizer instance associated with a given lib path.""" + addr2line_path = self._addr2line_path + if not addr2line_path: + if not self._android_abi: + raise Exception( + 'Android CPU ABI must be set before calling FindSymbolInfo!') + + cpu_arch = _AndroidAbiToCpuArch(self._android_abi) + self._addr2line_path = host_paths.ToolPath('addr2line', cpu_arch) + + return elf_symbolizer.ELFSymbolizer( + elf_file_path=host_path, addr2line_path=self._addr2line_path, + callback=ElfSymbolResolver._Callback, inlines=True) + + def DisallowSymbolizerForTesting(self): + """Disallow FindSymbolInfo() from using a symbolizer. + + This is used during unit-testing to ensure that the offsets that were + recorded via AddLibraryOffset()/AddLibraryOffsets() are properly + symbolized, but not anything else. + """ + self._allow_symbolizer = False + + def FindSymbolInfo(self, host_path, offset): + """Override SymbolResolver.FindSymbolInfo. + + Args: + host_path: Host-specific path to the native shared library. + offset: Integer offset within the native library. + Returns: + A symbol info string, or None. + """ + offset_map = self._symbol_info_cache[host_path] + symbol_info = offset_map.get(offset) + if symbol_info: + return symbol_info + + # Create symbolizer on demand. + symbolizer = self._elf_symbolizer_cache.get(host_path) + if not symbolizer: + symbolizer = self._CreateSymbolizerFor(host_path) + self._elf_symbolizer_cache[host_path] = symbolizer + + # If there are pre-recorded offsets for this path, symbolize them now. + offsets = self._lib_offsets_map.get(host_path) + if offsets: + offset_map = {} + for pre_offset in offsets: + symbolizer.SymbolizeAsync( + pre_offset, callback_arg=(offset_map, pre_offset)) + symbolizer.WaitForIdle() + self._symbol_info_cache[host_path] = offset_map + + symbol_info = offset_map.get(offset) + if symbol_info: + return symbol_info + + if not self._allow_symbolizer: + return None + + # Symbolize single offset. Slower if addresses are not provided in + # increasing order to addr2line. + symbolizer.SymbolizeAsync(offset, + callback_arg=(offset_map, offset)) + symbolizer.WaitForIdle() + return offset_map.get(offset) + + @staticmethod + def _Callback(sym_info, callback_arg): + offset_map, offset = callback_arg + offset_map[offset] = str(sym_info) + + +class DeviceSymbolResolver(SymbolResolver): + """A SymbolResolver instance that accepts device-specific path. + + Usage is the following: + 1) Create new instance, passing a parent SymbolResolver instance that + accepts host-specific paths, and a HostLibraryFinder instance. + + 2) Optional: call AddApkOffsets() to add offsets from within an APK + that contains uncompressed native shared libraries. + + 3) Use it as any SymbolResolver instance. + """ + def __init__(self, host_resolver, host_lib_finder): + """Initialize instance. + + Args: + host_resolver: A parent SymbolResolver instance that will be used + to resolve symbols from host library paths. + host_lib_finder: A HostLibraryFinder instance used to locate + unstripped libraries on the host. + """ + super(DeviceSymbolResolver, self).__init__() + self._host_lib_finder = host_lib_finder + self._bad_device_lib_paths = set() + self._host_resolver = host_resolver + + def SetAndroidAbi(self, android_abi): + super(DeviceSymbolResolver, self).SetAndroidAbi(android_abi) + self._host_resolver.SetAndroidAbi(android_abi) + + def AddLibraryOffsets(self, device_lib_path, lib_offsets): + """Associate a set of wanted offsets to a given device library. + + This must be called before FindSymbolInfo(), otherwise its input arguments + will be ignored. + + Args: + device_lib_path: A device-specific library path. + lib_offsets: An iterable of integer offsets within the corresponding + library that will be symbolized by future calls to FindSymbolInfo. + want to symbolize. + """ + if device_lib_path in self._bad_device_lib_paths: + return + + host_lib_path = self._host_lib_finder.Find(device_lib_path) + if not host_lib_path: + # NOTE: self._bad_device_lib_paths is only used to only print this + # warning once per bad library. + logging.warning('Could not find host library matching device path: %s', + device_lib_path) + self._bad_device_lib_paths.add(device_lib_path) + return + + self._host_resolver.AddLibraryOffsets(host_lib_path, lib_offsets) + + def AddApkOffsets(self, device_apk_path, apk_offsets, apk_translator): + """Associate a set of wanted offsets to a given device APK path. + + This converts the APK-relative offsets into offsets relative to the + uncompressed libraries it contains, then calls AddLibraryOffsets() + for each one of the libraries. + + Must be called before FindSymbolInfo() as well, otherwise input arguments + will be ignored. + + Args: + device_apk_path: Device-specific APK path. + apk_offsets: Iterable of offsets within the APK file. + apk_translator: An ApkLibraryPathTranslator instance used to extract + library paths from the APK. + """ + libraries_map = collections.defaultdict(set) + for offset in apk_offsets: + lib_path, lib_offset = apk_translator.TranslatePath(device_apk_path, + offset) + libraries_map[lib_path].add(lib_offset) + + for lib_path, lib_offsets in libraries_map.iteritems(): + self.AddLibraryOffsets(lib_path, lib_offsets) + + def FindSymbolInfo(self, device_path, offset): + """Overrides SymbolResolver.FindSymbolInfo. + + Args: + device_path: Device-specific library path (e.g. + '/data/app/com.example.app-1/lib/x86/libfoo.so') + offset: Offset in device library path. + Returns: + Corresponding symbol information string, or None. + """ + host_path = self._host_lib_finder.Find(device_path) + if not host_path: + return None + + return self._host_resolver.FindSymbolInfo(host_path, offset) + + +class MemoryMap(object): + """Models the memory map of a given process. Usage is: + + 1) Create new instance, passing the Android ABI. + + 2) Call TranslateLine() whenever you want to detect and translate any + memory map input line. + + 3) Otherwise, it is possible to parse the whole memory map input with + ParseLines(), then call FindSectionForAddress() repeatedly in order + to translate a memory address into the corresponding mapping and + file information tuple (e.g. to symbolize stack entries). + """ + + # A named tuple describing interesting memory map line items. + # Fields: + # addr_start: Mapping start address in memory. + # file_offset: Corresponding file offset. + # file_size: Corresponding mapping size in bytes. + # file_path: Input file path. + # match: Corresponding regular expression match object. + LineTuple = collections.namedtuple('MemoryMapLineTuple', + 'addr_start,file_offset,file_size,' + 'file_path, match') + + # A name tuple describing a memory map section. + # Fields: + # address: Memory address. + # size: Size in bytes in memory + # offset: Starting file offset. + # path: Input file path. + SectionTuple = collections.namedtuple('MemoryMapSection', + 'address,size,offset,path') + + def __init__(self, android_abi): + """Initializes instance. + + Args: + android_abi: Android CPU ABI name (e.g. 'armeabi-v7a') + """ + hex_addr = _HexAddressRegexpFor(android_abi) + + # pylint: disable=line-too-long + # A regular expression used to match memory map entries which look like: + # b278c000-b2790fff r-- 4fda000 5000 /data/app/com.google.android.apps.chrome-2/base.apk + # pylint: enable=line-too-long + self._re_map_section = re.compile( + r'\s*(?P<addr_start>' + hex_addr + r')-(?P<addr_end>' + hex_addr + ')' + + r'\s+' + + r'(?P<perm>...)\s+' + + r'(?P<file_offset>[0-9a-f]+)\s+' + + r'(?P<file_size>[0-9a-f]+)\s*' + + r'(?P<file_path>[^ \t]+)?') + + self._addr_map = [] # Sorted list of (address, size, path, offset) tuples. + self._sorted_addresses = [] # Sorted list of address fields in _addr_map. + self._in_section = False + + def TranslateLine(self, line, apk_path_translator): + """Try to translate a memory map input line, if detected. + + This only takes care of converting mapped APK file path and offsets + into a corresponding uncompressed native library file path + new offsets, + e.g. '..... <offset> <size> /data/.../base.apk' gets + translated into '.... <new-offset> <size> /data/.../base.apk!lib/libfoo.so' + + This function should always work, even if ParseLines() was not called + previously. + + Args: + line: Input memory map / tombstone line. + apk_translator: An ApkLibraryPathTranslator instance, used to map + APK offsets into uncompressed native libraries + new offsets. + Returns: + Translated memory map line, if relevant, or unchanged input line + otherwise. + """ + t = self._ParseLine(line.rstrip()) + if not t: + return line + + new_path, new_offset = apk_path_translator.TranslatePath( + t.file_path, t.file_offset) + + if new_path == t.file_path: + return line + + pos = t.match.start('file_path') + return '%s%s (offset 0x%x)%s' % (line[0:pos], new_path, new_offset, + line[t.match.end('file_path'):]) + + def ParseLines(self, input_lines, in_section=False): + """Parse a list of input lines and extract the APK memory map out of it. + + Args: + input_lines: list, or iterable, of input lines. + in_section: Optional. If true, considers that the input lines are + already part of the memory map. Otherwise, wait until the start of + the section appears in the input before trying to record data. + Returns: + True iff APK-related memory map entries were found. False otherwise. + """ + addr_list = [] # list of (address, size, file_path, file_offset) tuples. + self._in_section = in_section + for line in input_lines: + t = self._ParseLine(line.rstrip()) + if not t: + continue + + addr_list.append(t) + + self._addr_map = sorted(addr_list, + lambda x, y: cmp(x.addr_start, y.addr_start)) + self._sorted_addresses = [e.addr_start for e in self._addr_map] + return bool(self._addr_map) + + def _ParseLine(self, line): + """Used internally to recognized memory map input lines. + + Args: + line: Input logcat or tomstone line. + Returns: + A LineTuple instance on success, or None on failure. + """ + if not self._in_section: + self._in_section = line.startswith('memory map:') + return None + + m = self._re_map_section.match(line) + if not m: + self._in_section = False # End of memory map section + return None + + # Only accept .apk and .so files that are not from the system partitions. + file_path = m.group('file_path') + if not file_path: + return None + + if file_path.startswith('/system') or file_path.startswith('/vendor'): + return None + + if not (file_path.endswith('.apk') or file_path.endswith('.so')): + return None + + addr_start = int(m.group('addr_start'), 16) + file_offset = int(m.group('file_offset'), 16) + file_size = int(m.group('file_size'), 16) + + return self.LineTuple(addr_start, file_offset, file_size, file_path, m) + + def Dump(self): + """Print memory map for debugging.""" + print 'MEMORY MAP [' + for t in self._addr_map: + print '[%08x-%08x %08x %08x %s]' % ( + t.addr_start, t.addr_start + t.file_size, t.file_size, t.file_offset, + t.file_path) + print '] MEMORY MAP' + + def FindSectionForAddress(self, addr): + """Find the map section corresponding to a specific memory address. + + Call this method only after using ParseLines() was called to extract + relevant information from the memory map. + + Args: + addr: Memory address + Returns: + A SectionTuple instance on success, or None on failure. + """ + pos = bisect.bisect_right(self._sorted_addresses, addr) + if pos > 0: + # All values in [0,pos) are <= addr, just ensure that the last + # one contains the address as well. + entry = self._addr_map[pos - 1] + if entry.addr_start + entry.file_size > addr: + return self.SectionTuple(entry.addr_start, entry.file_size, + entry.file_offset, entry.file_path) + return None + + +class BacktraceTranslator(object): + """Translates backtrace-related lines in a tombstone or crash report. + + Usage is the following: + 1) Create new instance with appropriate arguments. + 2) If the tombstone / logcat input is available, one can call + FindLibraryOffsets() in order to detect which library offsets + will need to be symbolized during a future parse. Doing so helps + speed up the ELF symbolizer. + 3) For each tombstone/logcat input line, call TranslateLine() to + try to detect and symbolize backtrace lines. + """ + + # A named tuple for relevant input backtrace lines. + # Fields: + # rel_pc: Instruction pointer, relative to offset in library start. + # location: Library or APK file path. + # offset: Load base of executable code in library or apk file path. + # match: The corresponding regular expression match object. + # Note: + # The actual instruction pointer always matches the position at + # |offset + rel_pc| in |location|. + LineTuple = collections.namedtuple('BacktraceLineTuple', + 'rel_pc,location,offset,match') + + def __init__(self, android_abi, apk_translator): + """Initialize instance. + + Args: + android_abi: Android CPU ABI name (e.g. 'armeabi-v7a'). + apk_translator: ApkLibraryPathTranslator instance used to convert + mapped APK file offsets into uncompressed library file paths with + new offsets. + """ + hex_addr = _HexAddressRegexpFor(android_abi) + + # A regular expression used to match backtrace lines. + self._re_backtrace = re.compile( + r'.*#(?P<frame>[0-9]{2})\s+' + + r'(..)\s+' + + r'(?P<rel_pc>' + hex_addr + r')\s+' + + r'(?P<location>[^ \t]+)' + + r'(\s+\(offset 0x(?P<offset>[0-9a-f]+)\))?') + + # In certain cases, offset will be provided as <location>+0x<offset> + # instead of <location> (offset 0x<offset>). This is a regexp to detect + # this. + self._re_location_offset = re.compile( + r'.*\+0x(?P<offset>[0-9a-f]+)$') + + self._apk_translator = apk_translator + self._in_section = False + + def _ParseLine(self, line): + """Used internally to detect and decompose backtrace input lines. + + Args: + line: input tombstone line. + Returns: + A LineTuple instance on success, None on failure. + """ + if not self._in_section: + self._in_section = line.startswith('backtrace:') + return None + + line = line.rstrip() + m = self._re_backtrace.match(line) + if not m: + self._in_section = False + return None + + location = m.group('location') + offset = m.group('offset') + if not offset: + m2 = self._re_location_offset.match(location) + if m2: + offset = m2.group('offset') + location = location[0:m2.start('offset') - 3] + + if not offset: + return None + + offset = int(offset, 16) + rel_pc = int(m.group('rel_pc'), 16) + + # Two cases to consider here: + # + # * If this is a library file directly mapped in memory, then |rel_pc| + # if the direct offset within the library, and doesn't need any kind + # of adjustement. + # + # * If this is a library mapped directly from an .apk file, then + # |rel_pc| is the offset in the APK, and |offset| happens to be the + # load base of the corresponding library. + # + if location.endswith('.so'): + # For a native library directly mapped from the file system, + return self.LineTuple(rel_pc, location, offset, m) + + if location.endswith('.apk'): + # For a native library inside an memory-mapped APK file, + new_location, new_offset = self._apk_translator.TranslatePath( + location, offset) + + return self.LineTuple(rel_pc, new_location, new_offset, m) + + # Ignore anything else (e.g. .oat or .odex files). + return None + + def FindLibraryOffsets(self, input_lines, in_section=False): + """Parse a tombstone's backtrace section and find all library offsets in it. + + Args: + input_lines: List or iterables of intput tombstone lines. + in_section: Optional. If True, considers that the stack section has + already started. + Returns: + A dictionary mapping device library paths to sets of offsets within + then. + """ + self._in_section = in_section + result = collections.defaultdict(set) + for line in input_lines: + t = self._ParseLine(line) + if not t: + continue + + result[t.location].add(t.offset + t.rel_pc) + return result + + def TranslateLine(self, line, symbol_resolver): + """Symbolize backtrace line if recognized. + + Args: + line: input backtrace line. + symbol_resolver: symbol resolver instance to use. This method will + call its FindSymbolInfo(device_lib_path, lib_offset) method to + convert offsets into symbol informations strings. + Returns: + Translated line (unchanged if not recognized as a back trace). + """ + t = self._ParseLine(line) + if not t: + return line + + symbol_info = symbol_resolver.FindSymbolInfo(t.location, + t.offset + t.rel_pc) + if not symbol_info: + symbol_info = 'offset 0x%x' % t.offset + + pos = t.match.start('location') + pos2 = t.match.end('offset') + 1 + if pos2 <= 0: + pos2 = t.match.end('location') + return '%s%s (%s)%s' % (line[:pos], t.location, symbol_info, line[pos2:]) + + +class StackTranslator(object): + """Translates stack-related lines in a tombstone or crash report.""" + + # A named tuple describing relevant stack input lines. + # Fields: + # address: Address as it appears in the stack. + # lib_path: Library path where |address| is mapped. + # lib_offset: Library load base offset. for |lib_path|. + # match: Corresponding regular expression match object. + LineTuple = collections.namedtuple('StackLineTuple', + 'address, lib_path, lib_offset, match') + + def __init__(self, android_abi, memory_map, apk_translator): + """Initialize instance.""" + hex_addr = _HexAddressRegexpFor(android_abi) + + # pylint: disable=line-too-long + # A regular expression used to recognize stack entries like: + # + # #05 bf89a180 bf89a1e4 [stack] + # bf89a1c8 a0c01c51 /data/app/com.google.android.apps.chrome-2/base.apk + # bf89a080 00000000 + # ........ ........ + # pylint: enable=line-too-long + self._re_stack_line = re.compile( + r'\s+(?P<frame_number>#[0-9]+)?\s*' + + r'(?P<stack_addr>' + hex_addr + r')\s+' + + r'(?P<stack_value>' + hex_addr + r')' + + r'(\s+(?P<location>[^ \t]+))?') + + self._re_stack_abbrev = re.compile(r'\s+[.]+\s+[.]+') + + self._memory_map = memory_map + self._apk_translator = apk_translator + self._in_section = False + + def _ParseLine(self, line): + """Check a given input line for a relevant _re_stack_line match. + + Args: + line: input tombstone line. + Returns: + A LineTuple instance on success, None on failure. + """ + line = line.rstrip() + if not self._in_section: + self._in_section = line.startswith('stack:') + return None + + m = self._re_stack_line.match(line) + if not m: + if not self._re_stack_abbrev.match(line): + self._in_section = False + return None + + location = m.group('location') + if not location: + return None + + if not location.endswith('.apk') and not location.endswith('.so'): + return None + + addr = int(m.group('stack_value'), 16) + t = self._memory_map.FindSectionForAddress(addr) + if t is None: + return None + + lib_path = t.path + lib_offset = t.offset + (addr - t.address) + + if lib_path.endswith('.apk'): + lib_path, lib_offset = self._apk_translator.TranslatePath( + lib_path, lib_offset) + + return self.LineTuple(addr, lib_path, lib_offset, m) + + def FindLibraryOffsets(self, input_lines, in_section=False): + """Parse a tombstone's stack section and find all library offsets in it. + + Args: + input_lines: List or iterables of intput tombstone lines. + in_section: Optional. If True, considers that the stack section has + already started. + Returns: + A dictionary mapping device library paths to sets of offsets within + then. + """ + result = collections.defaultdict(set) + self._in_section = in_section + for line in input_lines: + t = self._ParseLine(line) + if t: + result[t.lib_path].add(t.lib_offset) + return result + + def TranslateLine(self, line, symbol_resolver=None): + """Try to translate a line of the stack dump.""" + t = self._ParseLine(line) + if not t: + return line + + symbol_info = symbol_resolver.FindSymbolInfo(t.lib_path, t.lib_offset) + if not symbol_info: + return line + + pos = t.match.start('location') + pos2 = t.match.end('location') + return '%s%s (%s)%s' % (line[:pos], t.lib_path, symbol_info, line[pos2:]) diff --git a/chromium/build/android/pylib/symbols/symbol_utils_unittest.py b/chromium/build/android/pylib/symbols/symbol_utils_unittest.py new file mode 100644 index 00000000000..d350c17489b --- /dev/null +++ b/chromium/build/android/pylib/symbols/symbol_utils_unittest.py @@ -0,0 +1,944 @@ +# Copyright 2018 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import collections +import contextlib +import logging +import os +import re +import shutil +import tempfile +import unittest + +from pylib.symbols import apk_native_libs_unittest +from pylib.symbols import mock_addr2line +from pylib.symbols import symbol_utils + +_MOCK_ELF_DATA = apk_native_libs_unittest.MOCK_ELF_DATA + +_MOCK_A2L_PATH = os.path.join(os.path.dirname(mock_addr2line.__file__), + 'mock_addr2line') + + +# pylint: disable=line-too-long + +# list of (start_offset, end_offset, size, libpath) tuples corresponding +# to the content of base.apk. This was taken from an x86 ChromeModern.apk +# component build. +_TEST_APK_LIBS = [ + (0x01331000, 0x013696bc, 0x000386bc, 'libaccessibility.cr.so'), + (0x0136a000, 0x013779c4, 0x0000d9c4, 'libanimation.cr.so'), + (0x01378000, 0x0137f7e8, 0x000077e8, 'libapdu.cr.so'), + (0x01380000, 0x0155ccc8, 0x001dccc8, 'libbase.cr.so'), + (0x0155d000, 0x015ab98c, 0x0004e98c, 'libbase_i18n.cr.so'), + (0x015ac000, 0x015dff4c, 0x00033f4c, 'libbindings.cr.so'), + (0x015e0000, 0x015f5a54, 0x00015a54, 'libbindings_base.cr.so'), + (0x015f6000, 0x0160d770, 0x00017770, 'libblink_android_mojo_bindings_shared.cr.so'), + (0x0160e000, 0x01731960, 0x00123960, 'libblink_common.cr.so'), + (0x01732000, 0x0174ce54, 0x0001ae54, 'libblink_controller.cr.so'), + (0x0174d000, 0x0318c528, 0x01a3f528, 'libblink_core.cr.so'), + (0x0318d000, 0x03191700, 0x00004700, 'libblink_core_mojo_bindings_shared.cr.so'), + (0x03192000, 0x03cd7918, 0x00b45918, 'libblink_modules.cr.so'), + (0x03cd8000, 0x03d137d0, 0x0003b7d0, 'libblink_mojo_bindings_shared.cr.so'), + (0x03d14000, 0x03d2670c, 0x0001270c, 'libblink_offscreen_canvas_mojo_bindings_shared.cr.so'), + (0x03d27000, 0x046c7054, 0x009a0054, 'libblink_platform.cr.so'), + (0x046c8000, 0x0473fbfc, 0x00077bfc, 'libbluetooth.cr.so'), + (0x04740000, 0x04878f40, 0x00138f40, 'libboringssl.cr.so'), + (0x04879000, 0x0498466c, 0x0010b66c, 'libc++_shared.so'), + (0x04985000, 0x0498d93c, 0x0000893c, 'libcaptive_portal.cr.so'), + (0x0498e000, 0x049947cc, 0x000067cc, 'libcapture_base.cr.so'), + (0x04995000, 0x04b39f18, 0x001a4f18, 'libcapture_lib.cr.so'), + (0x04b3a000, 0x04b488ec, 0x0000e8ec, 'libcbor.cr.so'), + (0x04b49000, 0x04e9ea5c, 0x00355a5c, 'libcc.cr.so'), + (0x04e9f000, 0x04ed6404, 0x00037404, 'libcc_animation.cr.so'), + (0x04ed7000, 0x04ef5ab4, 0x0001eab4, 'libcc_base.cr.so'), + (0x04ef6000, 0x04fd9364, 0x000e3364, 'libcc_blink.cr.so'), + (0x04fda000, 0x04fe2758, 0x00008758, 'libcc_debug.cr.so'), + (0x04fe3000, 0x0500ae0c, 0x00027e0c, 'libcc_ipc.cr.so'), + (0x0500b000, 0x05078f38, 0x0006df38, 'libcc_paint.cr.so'), + (0x05079000, 0x0507e734, 0x00005734, 'libcdm_manager.cr.so'), + (0x0507f000, 0x06f4d744, 0x01ece744, 'libchrome.cr.so'), + (0x06f54000, 0x06feb830, 0x00097830, 'libchromium_sqlite3.cr.so'), + (0x06fec000, 0x0706f554, 0x00083554, 'libclient.cr.so'), + (0x07070000, 0x0708da60, 0x0001da60, 'libcloud_policy_proto_generated_compile.cr.so'), + (0x0708e000, 0x07121f28, 0x00093f28, 'libcodec.cr.so'), + (0x07122000, 0x07134ab8, 0x00012ab8, 'libcolor_space.cr.so'), + (0x07135000, 0x07138614, 0x00003614, 'libcommon.cr.so'), + (0x07139000, 0x0717c938, 0x00043938, 'libcompositor.cr.so'), + (0x0717d000, 0x0923d78c, 0x020c078c, 'libcontent.cr.so'), + (0x0923e000, 0x092ae87c, 0x0007087c, 'libcontent_common_mojo_bindings_shared.cr.so'), + (0x092af000, 0x092be718, 0x0000f718, 'libcontent_public_common_mojo_bindings_shared.cr.so'), + (0x092bf000, 0x092d9a20, 0x0001aa20, 'libcrash_key.cr.so'), + (0x092da000, 0x092eda58, 0x00013a58, 'libcrcrypto.cr.so'), + (0x092ee000, 0x092f16e0, 0x000036e0, 'libdevice_base.cr.so'), + (0x092f2000, 0x092fe8d8, 0x0000c8d8, 'libdevice_event_log.cr.so'), + (0x092ff000, 0x093026a4, 0x000036a4, 'libdevice_features.cr.so'), + (0x09303000, 0x093f1220, 0x000ee220, 'libdevice_gamepad.cr.so'), + (0x093f2000, 0x09437f54, 0x00045f54, 'libdevice_vr_mojo_bindings.cr.so'), + (0x09438000, 0x0954c168, 0x00114168, 'libdevice_vr_mojo_bindings_blink.cr.so'), + (0x0954d000, 0x0955d720, 0x00010720, 'libdevice_vr_mojo_bindings_shared.cr.so'), + (0x0955e000, 0x0956b9c0, 0x0000d9c0, 'libdevices.cr.so'), + (0x0956c000, 0x0957cae8, 0x00010ae8, 'libdiscardable_memory_client.cr.so'), + (0x0957d000, 0x09588854, 0x0000b854, 'libdiscardable_memory_common.cr.so'), + (0x09589000, 0x0959cbb4, 0x00013bb4, 'libdiscardable_memory_service.cr.so'), + (0x0959d000, 0x095b6b90, 0x00019b90, 'libdisplay.cr.so'), + (0x095b7000, 0x095be930, 0x00007930, 'libdisplay_types.cr.so'), + (0x095bf000, 0x095c46c4, 0x000056c4, 'libdisplay_util.cr.so'), + (0x095c5000, 0x095f54a4, 0x000304a4, 'libdomain_reliability.cr.so'), + (0x095f6000, 0x0966fe08, 0x00079e08, 'libembedder.cr.so'), + (0x09670000, 0x096735f8, 0x000035f8, 'libembedder_switches.cr.so'), + (0x09674000, 0x096a3460, 0x0002f460, 'libevents.cr.so'), + (0x096a4000, 0x096b6d40, 0x00012d40, 'libevents_base.cr.so'), + (0x096b7000, 0x0981a778, 0x00163778, 'libffmpeg.cr.so'), + (0x0981b000, 0x09945c94, 0x0012ac94, 'libfido.cr.so'), + (0x09946000, 0x09a330dc, 0x000ed0dc, 'libfingerprint.cr.so'), + (0x09a34000, 0x09b53170, 0x0011f170, 'libfreetype_harfbuzz.cr.so'), + (0x09b54000, 0x09bc5c5c, 0x00071c5c, 'libgcm.cr.so'), + (0x09bc6000, 0x09cc8584, 0x00102584, 'libgeolocation.cr.so'), + (0x09cc9000, 0x09cdc8d4, 0x000138d4, 'libgeometry.cr.so'), + (0x09cdd000, 0x09cec8b4, 0x0000f8b4, 'libgeometry_skia.cr.so'), + (0x09ced000, 0x09d10e14, 0x00023e14, 'libgesture_detection.cr.so'), + (0x09d11000, 0x09d7595c, 0x0006495c, 'libgfx.cr.so'), + (0x09d76000, 0x09d7d7cc, 0x000077cc, 'libgfx_ipc.cr.so'), + (0x09d7e000, 0x09d82708, 0x00004708, 'libgfx_ipc_buffer_types.cr.so'), + (0x09d83000, 0x09d89748, 0x00006748, 'libgfx_ipc_color.cr.so'), + (0x09d8a000, 0x09d8f6f4, 0x000056f4, 'libgfx_ipc_geometry.cr.so'), + (0x09d90000, 0x09d94754, 0x00004754, 'libgfx_ipc_skia.cr.so'), + (0x09d95000, 0x09d9869c, 0x0000369c, 'libgfx_switches.cr.so'), + (0x09d99000, 0x09dba0ac, 0x000210ac, 'libgin.cr.so'), + (0x09dbb000, 0x09e0a8cc, 0x0004f8cc, 'libgl_in_process_context.cr.so'), + (0x09e0b000, 0x09e17a18, 0x0000ca18, 'libgl_init.cr.so'), + (0x09e18000, 0x09ee34e4, 0x000cb4e4, 'libgl_wrapper.cr.so'), + (0x09ee4000, 0x0a1a2e00, 0x002bee00, 'libgles2.cr.so'), + (0x0a1a3000, 0x0a24556c, 0x000a256c, 'libgles2_implementation.cr.so'), + (0x0a246000, 0x0a267038, 0x00021038, 'libgles2_utils.cr.so'), + (0x0a268000, 0x0a3288e4, 0x000c08e4, 'libgpu.cr.so'), + (0x0a329000, 0x0a3627ec, 0x000397ec, 'libgpu_ipc_service.cr.so'), + (0x0a363000, 0x0a388a18, 0x00025a18, 'libgpu_util.cr.so'), + (0x0a389000, 0x0a506d8c, 0x0017dd8c, 'libhost.cr.so'), + (0x0a507000, 0x0a6f0ec0, 0x001e9ec0, 'libicui18n.cr.so'), + (0x0a6f1000, 0x0a83b4c8, 0x0014a4c8, 'libicuuc.cr.so'), + (0x0a83c000, 0x0a8416e4, 0x000056e4, 'libinterfaces_shared.cr.so'), + (0x0a842000, 0x0a87e2a0, 0x0003c2a0, 'libipc.cr.so'), + (0x0a87f000, 0x0a88c98c, 0x0000d98c, 'libipc_mojom.cr.so'), + (0x0a88d000, 0x0a8926e4, 0x000056e4, 'libipc_mojom_shared.cr.so'), + (0x0a893000, 0x0a8a1e18, 0x0000ee18, 'libkeyed_service_content.cr.so'), + (0x0a8a2000, 0x0a8b4a30, 0x00012a30, 'libkeyed_service_core.cr.so'), + (0x0a8b5000, 0x0a930a80, 0x0007ba80, 'libleveldatabase.cr.so'), + (0x0a931000, 0x0a9b3908, 0x00082908, 'libmanager.cr.so'), + (0x0a9b4000, 0x0aea9bb4, 0x004f5bb4, 'libmedia.cr.so'), + (0x0aeaa000, 0x0b08cb88, 0x001e2b88, 'libmedia_blink.cr.so'), + (0x0b08d000, 0x0b0a4728, 0x00017728, 'libmedia_devices_mojo_bindings_shared.cr.so'), + (0x0b0a5000, 0x0b1943ec, 0x000ef3ec, 'libmedia_gpu.cr.so'), + (0x0b195000, 0x0b2d07d4, 0x0013b7d4, 'libmedia_mojo_services.cr.so'), + (0x0b2d1000, 0x0b2d4760, 0x00003760, 'libmessage_center.cr.so'), + (0x0b2d5000, 0x0b2e0938, 0x0000b938, 'libmessage_support.cr.so'), + (0x0b2e1000, 0x0b2f3ad0, 0x00012ad0, 'libmetrics_cpp.cr.so'), + (0x0b2f4000, 0x0b313bb8, 0x0001fbb8, 'libmidi.cr.so'), + (0x0b314000, 0x0b31b848, 0x00007848, 'libmojo_base_lib.cr.so'), + (0x0b31c000, 0x0b3329f8, 0x000169f8, 'libmojo_base_mojom.cr.so'), + (0x0b333000, 0x0b34b98c, 0x0001898c, 'libmojo_base_mojom_blink.cr.so'), + (0x0b34c000, 0x0b354700, 0x00008700, 'libmojo_base_mojom_shared.cr.so'), + (0x0b355000, 0x0b3608b0, 0x0000b8b0, 'libmojo_base_shared_typemap_traits.cr.so'), + (0x0b361000, 0x0b3ad454, 0x0004c454, 'libmojo_edk.cr.so'), + (0x0b3ae000, 0x0b3c4a20, 0x00016a20, 'libmojo_edk_ports.cr.so'), + (0x0b3c5000, 0x0b3d38a0, 0x0000e8a0, 'libmojo_mojom_bindings.cr.so'), + (0x0b3d4000, 0x0b3da6e8, 0x000066e8, 'libmojo_mojom_bindings_shared.cr.so'), + (0x0b3db000, 0x0b3e27f0, 0x000077f0, 'libmojo_public_system.cr.so'), + (0x0b3e3000, 0x0b3fa9fc, 0x000179fc, 'libmojo_public_system_cpp.cr.so'), + (0x0b3fb000, 0x0b407728, 0x0000c728, 'libmojom_core_shared.cr.so'), + (0x0b408000, 0x0b421744, 0x00019744, 'libmojom_platform_shared.cr.so'), + (0x0b422000, 0x0b43451c, 0x0001251c, 'libnative_theme.cr.so'), + (0x0b435000, 0x0baaa1bc, 0x006751bc, 'libnet.cr.so'), + (0x0baab000, 0x0bac3c08, 0x00018c08, 'libnet_with_v8.cr.so'), + (0x0bac4000, 0x0bb74670, 0x000b0670, 'libnetwork_cpp.cr.so'), + (0x0bb75000, 0x0bbaee8c, 0x00039e8c, 'libnetwork_cpp_base.cr.so'), + (0x0bbaf000, 0x0bd21844, 0x00172844, 'libnetwork_service.cr.so'), + (0x0bd22000, 0x0bd256e4, 0x000036e4, 'libnetwork_session_configurator.cr.so'), + (0x0bd26000, 0x0bd33734, 0x0000d734, 'libonc.cr.so'), + (0x0bd34000, 0x0bd9ce18, 0x00068e18, 'libperfetto.cr.so'), + (0x0bd9d000, 0x0bda4854, 0x00007854, 'libplatform.cr.so'), + (0x0bda5000, 0x0bec5ce4, 0x00120ce4, 'libpolicy_component.cr.so'), + (0x0bec6000, 0x0bf5ab58, 0x00094b58, 'libpolicy_proto.cr.so'), + (0x0bf5b000, 0x0bf86fbc, 0x0002bfbc, 'libprefs.cr.so'), + (0x0bf87000, 0x0bfa5d74, 0x0001ed74, 'libprinting.cr.so'), + (0x0bfa6000, 0x0bfe0e80, 0x0003ae80, 'libprotobuf_lite.cr.so'), + (0x0bfe1000, 0x0bff0a18, 0x0000fa18, 'libproxy_config.cr.so'), + (0x0bff1000, 0x0c0f6654, 0x00105654, 'libpublic.cr.so'), + (0x0c0f7000, 0x0c0fa6a4, 0x000036a4, 'librange.cr.so'), + (0x0c0fb000, 0x0c118058, 0x0001d058, 'libraster.cr.so'), + (0x0c119000, 0x0c133d00, 0x0001ad00, 'libresource_coordinator_cpp.cr.so'), + (0x0c134000, 0x0c1396a0, 0x000056a0, 'libresource_coordinator_cpp_base.cr.so'), + (0x0c13a000, 0x0c1973b8, 0x0005d3b8, 'libresource_coordinator_public_mojom.cr.so'), + (0x0c198000, 0x0c2033e8, 0x0006b3e8, 'libresource_coordinator_public_mojom_blink.cr.so'), + (0x0c204000, 0x0c219744, 0x00015744, 'libresource_coordinator_public_mojom_shared.cr.so'), + (0x0c21a000, 0x0c21e700, 0x00004700, 'libsandbox.cr.so'), + (0x0c21f000, 0x0c22f96c, 0x0001096c, 'libsandbox_services.cr.so'), + (0x0c230000, 0x0c249d58, 0x00019d58, 'libseccomp_bpf.cr.so'), + (0x0c24a000, 0x0c24e714, 0x00004714, 'libseccomp_starter_android.cr.so'), + (0x0c24f000, 0x0c4ae9f0, 0x0025f9f0, 'libservice.cr.so'), + (0x0c4af000, 0x0c4c3ae4, 0x00014ae4, 'libservice_manager_cpp.cr.so'), + (0x0c4c4000, 0x0c4cb708, 0x00007708, 'libservice_manager_cpp_types.cr.so'), + (0x0c4cc000, 0x0c4fbe30, 0x0002fe30, 'libservice_manager_mojom.cr.so'), + (0x0c4fc000, 0x0c532e78, 0x00036e78, 'libservice_manager_mojom_blink.cr.so'), + (0x0c533000, 0x0c53669c, 0x0000369c, 'libservice_manager_mojom_constants.cr.so'), + (0x0c537000, 0x0c53e85c, 0x0000785c, 'libservice_manager_mojom_constants_blink.cr.so'), + (0x0c53f000, 0x0c542668, 0x00003668, 'libservice_manager_mojom_constants_shared.cr.so'), + (0x0c543000, 0x0c54d700, 0x0000a700, 'libservice_manager_mojom_shared.cr.so'), + (0x0c54e000, 0x0c8fc6ec, 0x003ae6ec, 'libsessions.cr.so'), + (0x0c8fd000, 0x0c90a924, 0x0000d924, 'libshared_memory_support.cr.so'), + (0x0c90b000, 0x0c9148ec, 0x000098ec, 'libshell_dialogs.cr.so'), + (0x0c915000, 0x0cf8de70, 0x00678e70, 'libskia.cr.so'), + (0x0cf8e000, 0x0cf978bc, 0x000098bc, 'libsnapshot.cr.so'), + (0x0cf98000, 0x0cfb7d9c, 0x0001fd9c, 'libsql.cr.so'), + (0x0cfb8000, 0x0cfbe744, 0x00006744, 'libstartup_tracing.cr.so'), + (0x0cfbf000, 0x0d19b4e4, 0x001dc4e4, 'libstorage_browser.cr.so'), + (0x0d19c000, 0x0d2a773c, 0x0010b73c, 'libstorage_common.cr.so'), + (0x0d2a8000, 0x0d2ac6fc, 0x000046fc, 'libsurface.cr.so'), + (0x0d2ad000, 0x0d2baa98, 0x0000da98, 'libtracing.cr.so'), + (0x0d2bb000, 0x0d2f36b0, 0x000386b0, 'libtracing_cpp.cr.so'), + (0x0d2f4000, 0x0d326e70, 0x00032e70, 'libtracing_mojom.cr.so'), + (0x0d327000, 0x0d33270c, 0x0000b70c, 'libtracing_mojom_shared.cr.so'), + (0x0d333000, 0x0d46d804, 0x0013a804, 'libui_android.cr.so'), + (0x0d46e000, 0x0d4cb3f8, 0x0005d3f8, 'libui_base.cr.so'), + (0x0d4cc000, 0x0d4dbc40, 0x0000fc40, 'libui_base_ime.cr.so'), + (0x0d4dc000, 0x0d4e58d4, 0x000098d4, 'libui_data_pack.cr.so'), + (0x0d4e6000, 0x0d51d1e0, 0x000371e0, 'libui_devtools.cr.so'), + (0x0d51e000, 0x0d52b984, 0x0000d984, 'libui_message_center_cpp.cr.so'), + (0x0d52c000, 0x0d539a48, 0x0000da48, 'libui_touch_selection.cr.so'), + (0x0d53a000, 0x0d55bc60, 0x00021c60, 'liburl.cr.so'), + (0x0d55c000, 0x0d55f6b4, 0x000036b4, 'liburl_ipc.cr.so'), + (0x0d560000, 0x0d5af110, 0x0004f110, 'liburl_matcher.cr.so'), + (0x0d5b0000, 0x0d5e2fac, 0x00032fac, 'libuser_manager.cr.so'), + (0x0d5e3000, 0x0d5e66e4, 0x000036e4, 'libuser_prefs.cr.so'), + (0x0d5e7000, 0x0e3e1cc8, 0x00dfacc8, 'libv8.cr.so'), + (0x0e3e2000, 0x0e400ae0, 0x0001eae0, 'libv8_libbase.cr.so'), + (0x0e401000, 0x0e4d91d4, 0x000d81d4, 'libviz_common.cr.so'), + (0x0e4da000, 0x0e4df7e4, 0x000057e4, 'libviz_resource_format.cr.so'), + (0x0e4e0000, 0x0e5b7120, 0x000d7120, 'libweb_dialogs.cr.so'), + (0x0e5b8000, 0x0e5c7a18, 0x0000fa18, 'libwebdata_common.cr.so'), + (0x0e5c8000, 0x0e61bfe4, 0x00053fe4, 'libwtf.cr.so'), +] + + +# A small memory map fragment extracted from a tombstone for a process that +# had loaded the APK corresponding to _TEST_APK_LIBS above. +_TEST_MEMORY_MAP = r'''memory map: +12c00000-12ccafff rw- 0 cb000 /dev/ashmem/dalvik-main space (deleted) +12ccb000-130cafff rw- cb000 400000 /dev/ashmem/dalvik-main space (deleted) +130cb000-32bfffff --- 4cb000 1fb35000 /dev/ashmem/dalvik-main space (deleted) +32c00000-32c00fff rw- 0 1000 /dev/ashmem/dalvik-main space 1 (deleted) +32c01000-52bfffff --- 1000 1ffff000 /dev/ashmem/dalvik-main space 1 (deleted) +6f3b8000-6fd90fff rw- 0 9d9000 /data/dalvik-cache/x86/system@framework@boot.art +6fd91000-71c42fff r-- 0 1eb2000 /data/dalvik-cache/x86/system@framework@boot.oat +71c43000-7393efff r-x 1eb2000 1cfc000 /data/dalvik-cache/x86/system@framework@boot.oat (load base 0x71c43000) +7393f000-7393ffff rw- 3bae000 1000 /data/dalvik-cache/x86/system@framework@boot.oat +73940000-73a1bfff rw- 0 dc000 /dev/ashmem/dalvik-zygote space (deleted) +73a1c000-73a1cfff rw- 0 1000 /dev/ashmem/dalvik-non moving space (deleted) +73a1d000-73a2dfff rw- 1000 11000 /dev/ashmem/dalvik-non moving space (deleted) +73a2e000-77540fff --- 12000 3b13000 /dev/ashmem/dalvik-non moving space (deleted) +77541000-7793ffff rw- 3b25000 3ff000 /dev/ashmem/dalvik-non moving space (deleted) +923aa000-92538fff r-- 8a9000 18f000 /data/app/com.example.app-2/base.apk +92539000-9255bfff r-- 0 23000 /data/data/com.example.app/app_data/paks/es.pak@162db1c6689 +9255c000-92593fff r-- 213000 38000 /data/app/com.example.app-2/base.apk +92594000-925c0fff r-- 87d000 2d000 /data/app/com.example.app-2/base.apk +925c1000-927d3fff r-- a37000 213000 /data/app/com.example.app-2/base.apk +927d4000-92e07fff r-- 24a000 634000 /data/app/com.example.app-2/base.apk +92e08000-92e37fff r-- a931000 30000 /data/app/com.example.app-2/base.apk +92e38000-92e86fff r-x a961000 4f000 /data/app/com.example.app-2/base.apk +92e87000-92e8afff rw- a9b0000 4000 /data/app/com.example.app-2/base.apk +92e8b000-92e8bfff rw- 0 1000 +92e8c000-92e9dfff r-- d5b0000 12000 /data/app/com.example.app-2/base.apk +92e9e000-92ebcfff r-x d5c2000 1f000 /data/app/com.example.app-2/base.apk +92ebd000-92ebefff rw- d5e1000 2000 /data/app/com.example.app-2/base.apk +92ebf000-92ebffff rw- 0 1000 +''' + +# list of (address, size, path, offset) tuples that must appear in +# _TEST_MEMORY_MAP. Not all sections need to be listed. +_TEST_MEMORY_MAP_SECTIONS = [ + (0x923aa000, 0x18f000, '/data/app/com.example.app-2/base.apk', 0x8a9000), + (0x9255c000, 0x038000, '/data/app/com.example.app-2/base.apk', 0x213000), + (0x92594000, 0x02d000, '/data/app/com.example.app-2/base.apk', 0x87d000), + (0x925c1000, 0x213000, '/data/app/com.example.app-2/base.apk', 0xa37000), +] + +_EXPECTED_TEST_MEMORY_MAP = r'''memory map: +12c00000-12ccafff rw- 0 cb000 /dev/ashmem/dalvik-main space (deleted) +12ccb000-130cafff rw- cb000 400000 /dev/ashmem/dalvik-main space (deleted) +130cb000-32bfffff --- 4cb000 1fb35000 /dev/ashmem/dalvik-main space (deleted) +32c00000-32c00fff rw- 0 1000 /dev/ashmem/dalvik-main space 1 (deleted) +32c01000-52bfffff --- 1000 1ffff000 /dev/ashmem/dalvik-main space 1 (deleted) +6f3b8000-6fd90fff rw- 0 9d9000 /data/dalvik-cache/x86/system@framework@boot.art +6fd91000-71c42fff r-- 0 1eb2000 /data/dalvik-cache/x86/system@framework@boot.oat +71c43000-7393efff r-x 1eb2000 1cfc000 /data/dalvik-cache/x86/system@framework@boot.oat (load base 0x71c43000) +7393f000-7393ffff rw- 3bae000 1000 /data/dalvik-cache/x86/system@framework@boot.oat +73940000-73a1bfff rw- 0 dc000 /dev/ashmem/dalvik-zygote space (deleted) +73a1c000-73a1cfff rw- 0 1000 /dev/ashmem/dalvik-non moving space (deleted) +73a1d000-73a2dfff rw- 1000 11000 /dev/ashmem/dalvik-non moving space (deleted) +73a2e000-77540fff --- 12000 3b13000 /dev/ashmem/dalvik-non moving space (deleted) +77541000-7793ffff rw- 3b25000 3ff000 /dev/ashmem/dalvik-non moving space (deleted) +923aa000-92538fff r-- 8a9000 18f000 /data/app/com.example.app-2/base.apk +92539000-9255bfff r-- 0 23000 /data/data/com.example.app/app_data/paks/es.pak@162db1c6689 +9255c000-92593fff r-- 213000 38000 /data/app/com.example.app-2/base.apk +92594000-925c0fff r-- 87d000 2d000 /data/app/com.example.app-2/base.apk +925c1000-927d3fff r-- a37000 213000 /data/app/com.example.app-2/base.apk +927d4000-92e07fff r-- 24a000 634000 /data/app/com.example.app-2/base.apk +92e08000-92e37fff r-- a931000 30000 /data/app/com.example.app-2/base.apk!lib/libmanager.cr.so (offset 0x0) +92e38000-92e86fff r-x a961000 4f000 /data/app/com.example.app-2/base.apk!lib/libmanager.cr.so (offset 0x30000) +92e87000-92e8afff rw- a9b0000 4000 /data/app/com.example.app-2/base.apk!lib/libmanager.cr.so (offset 0x7f000) +92e8b000-92e8bfff rw- 0 1000 +92e8c000-92e9dfff r-- d5b0000 12000 /data/app/com.example.app-2/base.apk!lib/libuser_manager.cr.so (offset 0x0) +92e9e000-92ebcfff r-x d5c2000 1f000 /data/app/com.example.app-2/base.apk!lib/libuser_manager.cr.so (offset 0x12000) +92ebd000-92ebefff rw- d5e1000 2000 /data/app/com.example.app-2/base.apk!lib/libuser_manager.cr.so (offset 0x31000) +92ebf000-92ebffff rw- 0 1000 +''' + +# Example stack section, taken from the same tombstone that _TEST_MEMORY_MAP +# was extracted from. +_TEST_STACK = r'''stack: + bf89a070 b7439468 /system/lib/libc.so + bf89a074 bf89a1e4 [stack] + bf89a078 932d4000 /data/app/com.example.app-2/base.apk + bf89a07c b73bfbc9 /system/lib/libc.so (pthread_mutex_lock+65) + bf89a080 00000000 + bf89a084 4000671c /dev/ashmem/dalvik-main space 1 (deleted) + bf89a088 932d1d86 /data/app/com.example.app-2/base.apk + bf89a08c b743671c /system/lib/libc.so + bf89a090 b77f8c00 /system/bin/linker + bf89a094 b743cc90 + bf89a098 932d1d4a /data/app/com.example.app-2/base.apk + bf89a09c b73bf271 /system/lib/libc.so (__pthread_internal_find(long)+65) + bf89a0a0 b743cc90 + bf89a0a4 bf89a0b0 [stack] + bf89a0a8 bf89a0b8 [stack] + bf89a0ac 00000008 + ........ ........ + #00 bf89a0b0 00000006 + bf89a0b4 00000002 + bf89a0b8 b743671c /system/lib/libc.so + bf89a0bc b73bf5d9 /system/lib/libc.so (pthread_kill+71) + #01 bf89a0c0 00006937 + bf89a0c4 00006937 + bf89a0c8 00000006 + bf89a0cc b77fd3a9 /system/bin/app_process32 (sigprocmask+141) + bf89a0d0 00000002 + bf89a0d4 bf89a0ec [stack] + bf89a0d8 00000000 + bf89a0dc b743671c /system/lib/libc.so + bf89a0e0 bf89a12c [stack] + bf89a0e4 bf89a1e4 [stack] + bf89a0e8 932d1d4a /data/app/com.example.app-2/base.apk + bf89a0ec b7365206 /system/lib/libc.so (raise+37) + #02 bf89a0f0 b77f8c00 /system/bin/linker + bf89a0f4 00000006 + bf89a0f8 b7439468 /system/lib/libc.so + bf89a0fc b743671c /system/lib/libc.so + bf89a100 bf89a12c [stack] + bf89a104 b743671c /system/lib/libc.so + bf89a108 bf89a12c [stack] + bf89a10c b735e9e5 /system/lib/libc.so (abort+81) + #03 bf89a110 00000006 + bf89a114 bf89a12c [stack] + bf89a118 00000000 + bf89a11c b55a3d3b /system/lib/libprotobuf-cpp-lite.so (google::protobuf::internal::DefaultLogHandler(google::protobuf::LogLevel, char const*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)+99) + bf89a120 b7439468 /system/lib/libc.so + bf89a124 b55ba38d /system/lib/libprotobuf-cpp-lite.so + bf89a128 b55ba408 /system/lib/libprotobuf-cpp-lite.so + bf89a12c ffffffdf + bf89a130 0000003d + bf89a134 adfedf00 [anon:libc_malloc] + bf89a138 bf89a158 [stack] + #04 bf89a13c a0cee7f0 /data/app/com.example.app-2/base.apk + bf89a140 b55c1cb0 /system/lib/libprotobuf-cpp-lite.so + bf89a144 bf89a1e4 [stack] +''' + +# Expected value of _TEST_STACK after translation of addresses in the APK +# into offsets into libraries. +_EXPECTED_STACK = r'''stack: + bf89a070 b7439468 /system/lib/libc.so + bf89a074 bf89a1e4 [stack] + bf89a078 932d4000 /data/app/com.example.app-2/base.apk + bf89a07c b73bfbc9 /system/lib/libc.so (pthread_mutex_lock+65) + bf89a080 00000000 + bf89a084 4000671c /dev/ashmem/dalvik-main space 1 (deleted) + bf89a088 932d1d86 /data/app/com.example.app-2/base.apk + bf89a08c b743671c /system/lib/libc.so + bf89a090 b77f8c00 /system/bin/linker + bf89a094 b743cc90 + bf89a098 932d1d4a /data/app/com.example.app-2/base.apk + bf89a09c b73bf271 /system/lib/libc.so (__pthread_internal_find(long)+65) + bf89a0a0 b743cc90 + bf89a0a4 bf89a0b0 [stack] + bf89a0a8 bf89a0b8 [stack] + bf89a0ac 00000008 + ........ ........ + #00 bf89a0b0 00000006 + bf89a0b4 00000002 + bf89a0b8 b743671c /system/lib/libc.so + bf89a0bc b73bf5d9 /system/lib/libc.so (pthread_kill+71) + #01 bf89a0c0 00006937 + bf89a0c4 00006937 + bf89a0c8 00000006 + bf89a0cc b77fd3a9 /system/bin/app_process32 (sigprocmask+141) + bf89a0d0 00000002 + bf89a0d4 bf89a0ec [stack] + bf89a0d8 00000000 + bf89a0dc b743671c /system/lib/libc.so + bf89a0e0 bf89a12c [stack] + bf89a0e4 bf89a1e4 [stack] + bf89a0e8 932d1d4a /data/app/com.example.app-2/base.apk + bf89a0ec b7365206 /system/lib/libc.so (raise+37) + #02 bf89a0f0 b77f8c00 /system/bin/linker + bf89a0f4 00000006 + bf89a0f8 b7439468 /system/lib/libc.so + bf89a0fc b743671c /system/lib/libc.so + bf89a100 bf89a12c [stack] + bf89a104 b743671c /system/lib/libc.so + bf89a108 bf89a12c [stack] + bf89a10c b735e9e5 /system/lib/libc.so (abort+81) + #03 bf89a110 00000006 + bf89a114 bf89a12c [stack] + bf89a118 00000000 + bf89a11c b55a3d3b /system/lib/libprotobuf-cpp-lite.so (google::protobuf::internal::DefaultLogHandler(google::protobuf::LogLevel, char const*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)+99) + bf89a120 b7439468 /system/lib/libc.so + bf89a124 b55ba38d /system/lib/libprotobuf-cpp-lite.so + bf89a128 b55ba408 /system/lib/libprotobuf-cpp-lite.so + bf89a12c ffffffdf + bf89a130 0000003d + bf89a134 adfedf00 [anon:libc_malloc] + bf89a138 bf89a158 [stack] + #04 bf89a13c a0cee7f0 /data/app/com.example.app-2/base.apk + bf89a140 b55c1cb0 /system/lib/libprotobuf-cpp-lite.so + bf89a144 bf89a1e4 [stack] +''' + +_TEST_BACKTRACE = r'''backtrace: + #00 pc 00084126 /system/lib/libc.so (tgkill+22) + #01 pc 000815d8 /system/lib/libc.so (pthread_kill+70) + #02 pc 00027205 /system/lib/libc.so (raise+36) + #03 pc 000209e4 /system/lib/libc.so (abort+80) + #04 pc 0000cf73 /system/lib/libprotobuf-cpp-lite.so (google::protobuf::internal::LogMessage::Finish()+117) + #05 pc 0000cf8e /system/lib/libprotobuf-cpp-lite.so (google::protobuf::internal::LogFinisher::operator=(google::protobuf::internal::LogMessage&)+26) + #06 pc 0000d27f /system/lib/libprotobuf-cpp-lite.so (google::protobuf::internal::VerifyVersion(int, int, char const*)+574) + #07 pc 007cd236 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000) + #08 pc 000111a9 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0xbfc2000) + #09 pc 00013228 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0xbfc2000) + #10 pc 000131de /data/app/com.google.android.apps.chrome-2/base.apk (offset 0xbfc2000) + #11 pc 007cd2d8 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000) + #12 pc 007cd956 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000) + #13 pc 007c2d4a /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000) + #14 pc 009fc9f1 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000) + #15 pc 009fc8ea /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000) + #16 pc 00561c63 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000) + #17 pc 0106fbdb /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000) + #18 pc 004d7371 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000) + #19 pc 004d8159 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000) + #20 pc 004d7b96 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000) + #21 pc 004da4b6 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000) + #22 pc 005ab66c /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x7daa000) + #23 pc 005afca2 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x7daa000) + #24 pc 0000cae8 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x598d000) + #25 pc 00ce864f /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x7daa000) + #26 pc 00ce8dfa /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x7daa000) + #27 pc 00ce74c6 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x7daa000) + #28 pc 00004616 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x961e000) + #29 pc 00ce8215 /data/app/com.google.android.apps.chrome-2/base.apk (offset 0x7daa000) + #30 pc 0013d8c7 /system/lib/libart.so (art_quick_generic_jni_trampoline+71) + #31 pc 00137c52 /system/lib/libart.so (art_quick_invoke_static_stub+418) + #32 pc 00143651 /system/lib/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+353) + #33 pc 005e06ae /system/lib/libart.so (artInterpreterToCompiledCodeBridge+190) + #34 pc 00328b5d /system/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+445) + #35 pc 0032cfc0 /system/lib/libart.so (bool art::interpreter::DoInvoke<(art::InvokeType)0, false, false>(art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+160) + #36 pc 000fc703 /system/lib/libart.so (art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)+29891) + #37 pc 00300af7 /system/lib/libart.so (artInterpreterToInterpreterBridge+188) + #38 pc 00328b5d /system/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+445) + #39 pc 0032cfc0 /system/lib/libart.so (bool art::interpreter::DoInvoke<(art::InvokeType)0, false, false>(art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+160) + #40 pc 000fc703 /system/lib/libart.so (art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)+29891) + #41 pc 00300af7 /system/lib/libart.so (artInterpreterToInterpreterBridge+188) + #42 pc 00328b5d /system/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+445) + #43 pc 0032ebf9 /system/lib/libart.so (bool art::interpreter::DoInvoke<(art::InvokeType)2, false, false>(art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+297) + #44 pc 000fc955 /system/lib/libart.so (art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)+30485) + #45 pc 00300af7 /system/lib/libart.so (artInterpreterToInterpreterBridge+188) + #46 pc 00328b5d /system/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+445) + #47 pc 0033090c /system/lib/libart.so (bool art::interpreter::DoInvoke<(art::InvokeType)4, false, false>(art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+636) + #48 pc 000fc67f /system/lib/libart.so (art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)+29759) + #49 pc 00300700 /system/lib/libart.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame*)+128) + #50 pc 00667c73 /system/lib/libart.so (artQuickToInterpreterBridge+808) + #51 pc 0013d98d /system/lib/libart.so (art_quick_to_interpreter_bridge+77) + #52 pc 7264bc5b /data/dalvik-cache/x86/system@framework@boot.oat (offset 0x1eb2000) +''' + +_EXPECTED_BACKTRACE = r'''backtrace: + #00 pc 00084126 /system/lib/libc.so (tgkill+22) + #01 pc 000815d8 /system/lib/libc.so (pthread_kill+70) + #02 pc 00027205 /system/lib/libc.so (raise+36) + #03 pc 000209e4 /system/lib/libc.so (abort+80) + #04 pc 0000cf73 /system/lib/libprotobuf-cpp-lite.so (google::protobuf::internal::LogMessage::Finish()+117) + #05 pc 0000cf8e /system/lib/libprotobuf-cpp-lite.so (google::protobuf::internal::LogFinisher::operator=(google::protobuf::internal::LogMessage&)+26) + #06 pc 0000d27f /system/lib/libprotobuf-cpp-lite.so (google::protobuf::internal::VerifyVersion(int, int, char const*)+574) + #07 pc 007cd236 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000) + #08 pc 000111a9 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libprotobuf_lite.cr.so (offset 0x1c000) + #09 pc 00013228 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libprotobuf_lite.cr.so (offset 0x1c000) + #10 pc 000131de /data/app/com.google.android.apps.chrome-2/base.apk!lib/libprotobuf_lite.cr.so (offset 0x1c000) + #11 pc 007cd2d8 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000) + #12 pc 007cd956 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000) + #13 pc 007c2d4a /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000) + #14 pc 009fc9f1 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000) + #15 pc 009fc8ea /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000) + #16 pc 00561c63 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000) + #17 pc 0106fbdb /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000) + #18 pc 004d7371 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000) + #19 pc 004d8159 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000) + #20 pc 004d7b96 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000) + #21 pc 004da4b6 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000) + #22 pc 005ab66c /data/app/com.google.android.apps.chrome-2/base.apk!lib/libcontent.cr.so (offset 0xc2d000) + #23 pc 005afca2 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libcontent.cr.so (offset 0xc2d000) + #24 pc 0000cae8 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so (offset 0x90e000) + #25 pc 00ce864f /data/app/com.google.android.apps.chrome-2/base.apk!lib/libcontent.cr.so (offset 0xc2d000) + #26 pc 00ce8dfa /data/app/com.google.android.apps.chrome-2/base.apk!lib/libcontent.cr.so (offset 0xc2d000) + #27 pc 00ce74c6 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libcontent.cr.so (offset 0xc2d000) + #28 pc 00004616 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libembedder.cr.so (offset 0x28000) + #29 pc 00ce8215 /data/app/com.google.android.apps.chrome-2/base.apk!lib/libcontent.cr.so (offset 0xc2d000) + #30 pc 0013d8c7 /system/lib/libart.so (art_quick_generic_jni_trampoline+71) + #31 pc 00137c52 /system/lib/libart.so (art_quick_invoke_static_stub+418) + #32 pc 00143651 /system/lib/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+353) + #33 pc 005e06ae /system/lib/libart.so (artInterpreterToCompiledCodeBridge+190) + #34 pc 00328b5d /system/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+445) + #35 pc 0032cfc0 /system/lib/libart.so (bool art::interpreter::DoInvoke<(art::InvokeType)0, false, false>(art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+160) + #36 pc 000fc703 /system/lib/libart.so (art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)+29891) + #37 pc 00300af7 /system/lib/libart.so (artInterpreterToInterpreterBridge+188) + #38 pc 00328b5d /system/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+445) + #39 pc 0032cfc0 /system/lib/libart.so (bool art::interpreter::DoInvoke<(art::InvokeType)0, false, false>(art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+160) + #40 pc 000fc703 /system/lib/libart.so (art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)+29891) + #41 pc 00300af7 /system/lib/libart.so (artInterpreterToInterpreterBridge+188) + #42 pc 00328b5d /system/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+445) + #43 pc 0032ebf9 /system/lib/libart.so (bool art::interpreter::DoInvoke<(art::InvokeType)2, false, false>(art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+297) + #44 pc 000fc955 /system/lib/libart.so (art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)+30485) + #45 pc 00300af7 /system/lib/libart.so (artInterpreterToInterpreterBridge+188) + #46 pc 00328b5d /system/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+445) + #47 pc 0033090c /system/lib/libart.so (bool art::interpreter::DoInvoke<(art::InvokeType)4, false, false>(art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+636) + #48 pc 000fc67f /system/lib/libart.so (art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)+29759) + #49 pc 00300700 /system/lib/libart.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame*)+128) + #50 pc 00667c73 /system/lib/libart.so (artQuickToInterpreterBridge+808) + #51 pc 0013d98d /system/lib/libart.so (art_quick_to_interpreter_bridge+77) + #52 pc 7264bc5b /data/dalvik-cache/x86/system@framework@boot.oat (offset 0x1eb2000) +''' + +_EXPECTED_BACKTRACE_OFFSETS_MAP = { + '/data/app/com.google.android.apps.chrome-2/base.apk!lib/libprotobuf_lite.cr.so': + set([ + 0x1c000 + 0x111a9, + 0x1c000 + 0x13228, + 0x1c000 + 0x131de, + ]), + + '/data/app/com.google.android.apps.chrome-2/base.apk!lib/libchrome.cr.so': + set([ + 0x90e000 + 0x7cd236, + 0x90e000 + 0x7cd2d8, + 0x90e000 + 0x7cd956, + 0x90e000 + 0x7c2d4a, + 0x90e000 + 0x9fc9f1, + 0x90e000 + 0x9fc8ea, + 0x90e000 + 0x561c63, + 0x90e000 + 0x106fbdb, + 0x90e000 + 0x4d7371, + 0x90e000 + 0x4d8159, + 0x90e000 + 0x4d7b96, + 0x90e000 + 0x4da4b6, + 0x90e000 + 0xcae8, + ]), + '/data/app/com.google.android.apps.chrome-2/base.apk!lib/libcontent.cr.so': + set([ + 0xc2d000 + 0x5ab66c, + 0xc2d000 + 0x5afca2, + 0xc2d000 + 0xce864f, + 0xc2d000 + 0xce8dfa, + 0xc2d000 + 0xce74c6, + 0xc2d000 + 0xce8215, + ]), + '/data/app/com.google.android.apps.chrome-2/base.apk!lib/libembedder.cr.so': + set([ + 0x28000 + 0x4616, + ]) +} + +# pylint: enable=line-too-long + +_ONE_MB = 1024 * 1024 +_TEST_SYMBOL_DATA = { + # Regular symbols + 0: 'mock_sym_for_addr_0 [mock_src/libmock1.so.c:0]', + 0x1000: 'mock_sym_for_addr_4096 [mock_src/libmock1.so.c:4096]', + + # Symbols without source file path. + _ONE_MB: 'mock_sym_for_addr_1048576 [??:0]', + _ONE_MB + 0x8234: 'mock_sym_for_addr_1081908 [??:0]', + + # Unknown symbol. + 2 * _ONE_MB: '?? [??:0]', + + # Inlined symbol. + 3 * _ONE_MB: + 'mock_sym_for_addr_3145728_inner [mock_src/libmock1.so.c:3145728]', +} + +@contextlib.contextmanager +def _TempDir(): + dirname = tempfile.mkdtemp() + try: + yield dirname + finally: + shutil.rmtree(dirname) + + +def _TouchFile(path): + # Create parent directories. + try: + os.makedirs(os.path.dirname(path)) + except OSError: + pass + with open(path, 'a'): + os.utime(path, None) + +class MockApkTranslator(object): + """A mock ApkLibraryPathTranslator object used for testing.""" + + # Regex that matches the content of APK native library map files generated + # with apk_lib_dump.py. + _RE_MAP_FILE = re.compile( + r'0x(?P<file_start>[0-9a-f]+)\s+' + + r'0x(?P<file_end>[0-9a-f]+)\s+' + + r'0x(?P<file_size>[0-9a-f]+)\s+' + + r'0x(?P<lib_path>[0-9a-f]+)\s+') + + def __init__(self, test_apk_libs=None): + """Initialize instance. + + Args: + test_apk_libs: Optional list of (file_start, file_end, size, lib_path) + tuples, like _TEST_APK_LIBS for example. This will be used to + implement TranslatePath(). + """ + self._apk_libs = [] + if test_apk_libs: + self._AddLibEntries(test_apk_libs) + + def _AddLibEntries(self, entries): + self._apk_libs = sorted(self._apk_libs + entries, + lambda x, y: cmp(x[0], y[0])) + + def ReadMapFile(self, file_path): + """Read an .apk.native-libs file that was produced with apk_lib_dump.py. + + Args: + file_path: input path to .apk.native-libs file. Its format is + essentially: 0x<start> 0x<end> 0x<size> <library-path> + """ + new_libs = [] + with open(file_path) as f: + for line in f.readlines(): + m = MockApkTranslator._RE_MAP_FILE.match(line) + if m: + file_start = int(m.group('file_start'), 16) + file_end = int(m.group('file_end'), 16) + file_size = int(m.group('file_size'), 16) + lib_path = m.group('lib_path') + # Sanity check + if file_start + file_size != file_end: + logging.warning('%s: Inconsistent (start, end, size) values ' + '(0x%x, 0x%x, 0x%x)', + file_path, file_start, file_end, file_size) + else: + new_libs.append((file_start, file_end, file_size, lib_path)) + + self._AddLibEntries(new_libs) + + def TranslatePath(self, lib_path, lib_offset): + """Translate an APK file path + offset into a library path + offset.""" + min_pos = 0 + max_pos = len(self._apk_libs) + while min_pos < max_pos: + mid_pos = (min_pos + max_pos) / 2 + mid_entry = self._apk_libs[mid_pos] + mid_offset = mid_entry[0] + mid_size = mid_entry[2] + if lib_offset < mid_offset: + max_pos = mid_pos + elif lib_offset >= mid_offset + mid_size: + min_pos = mid_pos + 1 + else: + # Found it + new_path = '%s!lib/%s' % (lib_path, mid_entry[3]) + new_offset = lib_offset - mid_offset + return (new_path, new_offset) + + return lib_path, lib_offset + + +class HostLibraryFinderTest(unittest.TestCase): + + def testEmpty(self): + finder = symbol_utils.HostLibraryFinder() + self.assertIsNone(finder.Find('/data/data/com.example.app-1/lib/libfoo.so')) + self.assertIsNone( + finder.Find('/data/data/com.example.app-1/base.apk!lib/libfoo.so')) + + + def testSimpleDirectory(self): + finder = symbol_utils.HostLibraryFinder() + with _TempDir() as tmp_dir: + host_libfoo_path = os.path.join(tmp_dir, 'libfoo.so') + host_libbar_path = os.path.join(tmp_dir, 'libbar.so') + _TouchFile(host_libfoo_path) + _TouchFile(host_libbar_path) + + finder.AddSearchDir(tmp_dir) + + # Regular library path (extracted at installation by the PackageManager). + # Note that the extraction path has changed between Android releases, + # i.e. it can be /data/app/, /data/data/ or /data/app-lib/ depending + # on the system. + self.assertEqual( + host_libfoo_path, + finder.Find('/data/app-lib/com.example.app-1/lib/libfoo.so')) + + # Verify that the path doesn't really matter + self.assertEqual( + host_libfoo_path, + finder.Find('/whatever/what.apk!lib/libfoo.so')) + + self.assertEqual( + host_libbar_path, + finder.Find('/data/data/com.example.app-1/lib/libbar.so')) + + self.assertIsNone( + finder.Find('/data/data/com.example.app-1/lib/libunknown.so')) + + + def testMultipleDirectories(self): + with _TempDir() as tmp_dir: + # Create the following files: + # <tmp_dir>/aaa/ + # libfoo.so + # <tmp_dir>/bbb/ + # libbar.so + # libfoo.so (this one should never be seen because 'aaa' + # shall be first in the search path list). + # + aaa_dir = os.path.join(tmp_dir, 'aaa') + bbb_dir = os.path.join(tmp_dir, 'bbb') + os.makedirs(aaa_dir) + os.makedirs(bbb_dir) + + host_libfoo_path = os.path.join(aaa_dir, 'libfoo.so') + host_libbar_path = os.path.join(bbb_dir, 'libbar.so') + host_libfoo2_path = os.path.join(bbb_dir, 'libfoo.so') + + _TouchFile(host_libfoo_path) + _TouchFile(host_libbar_path) + _TouchFile(host_libfoo2_path) + + finder = symbol_utils.HostLibraryFinder() + finder.AddSearchDir(aaa_dir) + finder.AddSearchDir(bbb_dir) + + self.assertEqual( + host_libfoo_path, + finder.Find('/data/data/com.example.app-1/lib/libfoo.so')) + + self.assertEqual( + host_libfoo_path, + finder.Find('/data/whatever/base.apk!lib/libfoo.so')) + + self.assertEqual( + host_libbar_path, + finder.Find('/data/data/com.example.app-1/lib/libbar.so')) + + self.assertIsNone( + finder.Find('/data/data/com.example.app-1/lib/libunknown.so')) + + +class ElfSymbolResolverTest(unittest.TestCase): + + def testCreation(self): + resolver = symbol_utils.ElfSymbolResolver( + addr2line_path_for_tests=_MOCK_A2L_PATH) + self.assertTrue(resolver) + + def testWithSimpleOffsets(self): + resolver = symbol_utils.ElfSymbolResolver( + addr2line_path_for_tests=_MOCK_A2L_PATH) + resolver.SetAndroidAbi('ignored-abi') + + for addr, expected_sym in _TEST_SYMBOL_DATA.iteritems(): + self.assertEqual(resolver.FindSymbolInfo('/some/path/libmock1.so', addr), + expected_sym) + + def testWithPreResolvedSymbols(self): + resolver = symbol_utils.ElfSymbolResolver( + addr2line_path_for_tests=_MOCK_A2L_PATH) + resolver.SetAndroidAbi('ignored-abi') + resolver.AddLibraryOffsets('/some/path/libmock1.so', + _TEST_SYMBOL_DATA.keys()) + + resolver.DisallowSymbolizerForTesting() + + for addr, expected_sym in _TEST_SYMBOL_DATA.iteritems(): + sym_info = resolver.FindSymbolInfo('/some/path/libmock1.so', addr) + self.assertIsNotNone(sym_info, 'None symbol info for addr %x' % addr) + self.assertEqual( + sym_info, expected_sym, + 'Invalid symbol info for addr %x [%s] expected [%s]' % ( + addr, sym_info, expected_sym)) + + +class MemoryMapTest(unittest.TestCase): + + def testCreation(self): + mem_map = symbol_utils.MemoryMap('test-abi32') + self.assertIsNone(mem_map.FindSectionForAddress(0)) + + def testParseLines(self): + mem_map = symbol_utils.MemoryMap('test-abi32') + mem_map.ParseLines(_TEST_MEMORY_MAP.splitlines()) + for exp_addr, exp_size, exp_path, exp_offset in _TEST_MEMORY_MAP_SECTIONS: + text = '(addr:%x, size:%x, path:%s, offset=%x)' % ( + exp_addr, exp_size, exp_path, exp_offset) + + t = mem_map.FindSectionForAddress(exp_addr) + self.assertTrue(t, 'Could not find %s' % text) + self.assertEqual(t.address, exp_addr) + self.assertEqual(t.size, exp_size) + self.assertEqual(t.offset, exp_offset) + self.assertEqual(t.path, exp_path) + + def testTranslateLine(self): + android_abi = 'test-abi' + apk_translator = MockApkTranslator(_TEST_APK_LIBS) + mem_map = symbol_utils.MemoryMap(android_abi) + for line, expected_line in zip(_TEST_MEMORY_MAP.splitlines(), + _EXPECTED_TEST_MEMORY_MAP.splitlines()): + self.assertEqual(mem_map.TranslateLine(line, apk_translator), + expected_line) + +class StackTranslatorTest(unittest.TestCase): + + def testSimpleStack(self): + android_abi = 'test-abi32' + mem_map = symbol_utils.MemoryMap(android_abi) + mem_map.ParseLines(_TEST_MEMORY_MAP) + apk_translator = MockApkTranslator(_TEST_APK_LIBS) + stack_translator = symbol_utils.StackTranslator(android_abi, mem_map, + apk_translator) + input_stack = _TEST_STACK.splitlines() + expected_stack = _EXPECTED_STACK.splitlines() + self.assertEqual(len(input_stack), len(expected_stack)) + for stack_line, expected_line in zip(input_stack, expected_stack): + new_line = stack_translator.TranslateLine(stack_line) + self.assertEqual(new_line, expected_line) + + +class MockSymbolResolver(symbol_utils.SymbolResolver): + + # A regex matching a symbol definition as it appears in a test symbol file. + # Format is: <hex-offset> <whitespace> <symbol-string> + _RE_SYMBOL_DEFINITION = re.compile( + r'(?P<offset>[0-9a-f]+)\s+(?P<symbol>.*)') + + def __init__(self): + super(MockSymbolResolver, self).__init__() + self._map = collections.defaultdict(dict) + + def AddTestLibrarySymbols(self, lib_name, offsets_map): + """Add a new test entry for a given library name. + + Args: + lib_name: Library name (e.g. 'libfoo.so') + offsets_map: A mapping from offsets to symbol info strings. + """ + self._map[lib_name] = offsets_map + + def ReadTestFile(self, file_path, lib_name): + """Read a single test symbol file, matching a given library. + + Args: + file_path: Input file path. + lib_name: Library name these symbols correspond to (e.g. 'libfoo.so') + """ + with open(file_path) as f: + for line in f.readlines(): + line = line.rstrip() + m = MockSymbolResolver._RE_SYMBOL_DEFINITION.match(line) + if m: + offset = int(m.group('offset')) + symbol = m.group('symbol') + self._map[lib_name][offset] = symbol + + def ReadTestFilesInDir(self, dir_path, file_suffix): + """Read all symbol test files in a given directory. + + Args: + dir_path: Directory path. + file_suffix: File suffix used to detect test symbol files. + """ + for filename in os.listdir(dir_path): + if filename.endswith(file_suffix): + lib_name = filename[:-len(file_suffix)] + self.ReadTestFile(os.path.join(dir_path, filename), lib_name) + + def FindSymbolInfo(self, device_path, device_offset): + """Implement SymbolResolver.FindSymbolInfo.""" + lib_name = os.path.basename(device_path) + offsets = self._map.get(lib_name) + if not offsets: + return None + + return offsets.get(device_offset) + + +class BacktraceTranslatorTest(unittest.TestCase): + + def testEmpty(self): + android_abi = 'test-abi' + apk_translator = MockApkTranslator() + backtrace_translator = symbol_utils.BacktraceTranslator(android_abi, + apk_translator) + self.assertTrue(backtrace_translator) + + def testFindLibraryOffsets(self): + android_abi = 'test-abi' + apk_translator = MockApkTranslator(_TEST_APK_LIBS) + backtrace_translator = symbol_utils.BacktraceTranslator(android_abi, + apk_translator) + input_backtrace = _EXPECTED_BACKTRACE.splitlines() + expected_lib_offsets_map = _EXPECTED_BACKTRACE_OFFSETS_MAP + offset_map = backtrace_translator.FindLibraryOffsets(input_backtrace) + for lib_path, offsets in offset_map.iteritems(): + self.assertTrue(lib_path in expected_lib_offsets_map, + '%s is not in expected library-offsets map!' % lib_path) + sorted_offsets = sorted(offsets) + sorted_expected_offsets = sorted(expected_lib_offsets_map[lib_path]) + self.assertEqual(sorted_offsets, sorted_expected_offsets, + '%s has invalid offsets %s expected %s' % ( + lib_path, sorted_offsets, sorted_expected_offsets)) + + def testTranslateLine(self): + android_abi = 'test-abi' + apk_translator = MockApkTranslator(_TEST_APK_LIBS) + backtrace_translator = symbol_utils.BacktraceTranslator(android_abi, + apk_translator) + input_backtrace = _TEST_BACKTRACE.splitlines() + expected_backtrace = _EXPECTED_BACKTRACE.splitlines() + self.assertEqual(len(input_backtrace), len(expected_backtrace)) + for trace_line, expected_line in zip(input_backtrace, expected_backtrace): + line = backtrace_translator.TranslateLine(trace_line, + MockSymbolResolver()) + self.assertEqual(line, expected_line) + + +if __name__ == '__main__': + unittest.main() diff --git a/chromium/build/android/pylib/utils/app_bundle_utils.py b/chromium/build/android/pylib/utils/app_bundle_utils.py new file mode 100644 index 00000000000..946e62661ba --- /dev/null +++ b/chromium/build/android/pylib/utils/app_bundle_utils.py @@ -0,0 +1,66 @@ +# Copyright 2018 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import logging +import os +import sys + +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'gyp')) + +from util import build_utils +from util import md5_check +import bundletool + +def GenerateBundleApks(bundle_path, bundle_apks_path, aapt2_path, + keystore_path, keystore_password, keystore_alias): + """Generate an .apks archive from a an app bundle if needed. + + Args: + bundle_path: Input bundle file path. + bundle_apks_path: Output bundle .apks archive path. Name must end with + '.apks' or this operation will fail. + aapt2_path: Path to aapt2 build tool. + keystore_path: Path to keystore. + keystore_password: Keystore password, as a string. + keystore_alias: Keystore signing key alias. + """ + # NOTE: BUNDLETOOL_JAR_PATH is added to input_strings, rather than + # input_paths, to speed up MD5 computations by about 400ms (the .jar file + # contains thousands of class files which are checked independently, + # resulting in an .md5.stamp of more than 60000 lines!). + input_paths = [ + bundle_path, + aapt2_path, + keystore_path + ] + input_strings = [ + keystore_password, + keystore_alias, + bundletool.BUNDLETOOL_JAR_PATH, + # NOTE: BUNDLETOOL_VERSION is already part of BUNDLETOOL_JAR_PATH, but + # it's simpler to assume that this may not be the case in the future. + bundletool.BUNDLETOOL_VERSION + ] + output_paths = [bundle_apks_path] + + def rebuild(): + logging.info('Building %s', os.path.basename(bundle_apks_path)) + with build_utils.AtomicOutput(bundle_apks_path) as tmp_apks: + cmd_args = [ + 'java', '-jar', bundletool.BUNDLETOOL_JAR_PATH, 'build-apks', + '--aapt2=%s' % aapt2_path, + '--output=%s' % tmp_apks.name, + '--bundle=%s' % bundle_path, + '--ks=%s' % keystore_path, + '--ks-pass=pass:%s' % keystore_password, + '--ks-key-alias=%s' % keystore_alias, + '--overwrite', + ] + build_utils.CheckOutput(cmd_args) + + md5_check.CallAndRecordIfStale( + rebuild, + input_paths=input_paths, + input_strings=input_strings, + output_paths=output_paths) diff --git a/chromium/build/android/pylib/utils/device_dependencies.py b/chromium/build/android/pylib/utils/device_dependencies.py index 6481d43881f..bccc1c37a67 100644 --- a/chromium/build/android/pylib/utils/device_dependencies.py +++ b/chromium/build/android/pylib/utils/device_dependencies.py @@ -16,6 +16,7 @@ _BLACKLIST = [ re.compile(r'.*\.py'), # Some test_support targets include python deps. re.compile(r'.*\.stamp'), # Stamp files should never be included. re.compile(r'.*\.apk'), # Should be installed separately. + re.compile(r'.*lib.java/.*'), # Never need java intermediates. # Chrome external extensions config file. re.compile(r'.*external_extensions\.json'), diff --git a/chromium/build/android/pylib/utils/google_storage_helper.py b/chromium/build/android/pylib/utils/google_storage_helper.py index 3101d71439a..d184810517a 100644 --- a/chromium/build/android/pylib/utils/google_storage_helper.py +++ b/chromium/build/android/pylib/utils/google_storage_helper.py @@ -79,10 +79,7 @@ def exists(name, bucket): cmd = [_GSUTIL_PATH, '-q', 'stat', gs_path] return_code = cmd_helper.RunCmd(cmd) - if return_code == 0: - return True - else: - return False + return return_code == 0 # TODO(jbudorick): Delete this function. Only one user of it. diff --git a/chromium/build/android/pylib/utils/logdog_helper.py b/chromium/build/android/pylib/utils/logdog_helper.py index 58fe7814bea..68a7ba57ab7 100644 --- a/chromium/build/android/pylib/utils/logdog_helper.py +++ b/chromium/build/android/pylib/utils/logdog_helper.py @@ -92,4 +92,3 @@ def get_viewer_url(name): def get_logdog_client(): logging.info('Getting logdog client.') return bootstrap.ButlerBootstrap.probe().stream_client() - diff --git a/chromium/build/android/pylib/utils/maven_downloader.py b/chromium/build/android/pylib/utils/maven_downloader.py index c5b0badf29c..c60b0140acc 100755 --- a/chromium/build/android/pylib/utils/maven_downloader.py +++ b/chromium/build/android/pylib/utils/maven_downloader.py @@ -36,7 +36,8 @@ class MavenDownloader(object): _REMOTE_REPO = 'https://maven.google.com' # Default Maven repository. - _DEFAULT_REPO_PATH = os.path.join(os.getenv('HOME'), '.m2', 'repository') + _DEFAULT_REPO_PATH = os.path.join( + os.path.expanduser('~'), '.m2', 'repository') def __init__(self, debug=False): self._repo_path = MavenDownloader._DEFAULT_REPO_PATH diff --git a/chromium/build/android/pylib/utils/proguard.py b/chromium/build/android/pylib/utils/proguard.py index c243ce9c9f3..2d439a52c32 100644 --- a/chromium/build/android/pylib/utils/proguard.py +++ b/chromium/build/android/pylib/utils/proguard.py @@ -31,15 +31,13 @@ _PROGUARD_ELEMENT_RES = [ _PROGUARD_INDENT_WIDTH = 2 _PROGUARD_ANNOTATION_VALUE_RE = re.compile(r'^(\s*?)- \S+? \[(.*)\]$') -_PROGUARD_PATH_SDK = os.path.join( - constants.PROGUARD_ROOT, 'lib', 'proguard.jar') -_PROGUARD_PATH_BUILT = ( - os.path.join(os.environ['ANDROID_BUILD_TOP'], 'external', 'proguard', - 'lib', 'proguard.jar') - if 'ANDROID_BUILD_TOP' in os.environ else None) -_PROGUARD_PATH = ( - _PROGUARD_PATH_SDK if os.path.exists(_PROGUARD_PATH_SDK) - else _PROGUARD_PATH_BUILT) + +def _GetProguardPath(): + # Use the one in lib.java rather than source tree because it is the one that + # is added to swarming .isolate files. + return os.path.join( + constants.GetOutDirectory(), 'lib.java', 'third_party', 'proguard', + 'proguard603.jar') def Dump(jar_path): @@ -96,7 +94,7 @@ def Dump(jar_path): with tempfile.NamedTemporaryFile() as proguard_output: cmd_helper.GetCmdStatusAndOutput([ 'java', - '-jar', _PROGUARD_PATH, + '-jar', _GetProguardPath(), '-injars', jar_path, '-dontshrink', '-dontoptimize', '-dontobfuscate', '-dontpreverify', '-dump', proguard_output.name]) diff --git a/chromium/build/android/pylib/valgrind_tools.py b/chromium/build/android/pylib/valgrind_tools.py index 3dc2488b269..4c27b083b7a 100644 --- a/chromium/build/android/pylib/valgrind_tools.py +++ b/chromium/build/android/pylib/valgrind_tools.py @@ -127,4 +127,3 @@ def PushFilesForTool(tool_name, device): print 'Unknown tool %s, available tools: %s' % ( tool_name, ', '.join(sorted(TOOL_REGISTRY.keys()))) sys.exit(1) - diff --git a/chromium/build/android/pylintrc b/chromium/build/android/pylintrc index 8005a5d276c..2a721bf2709 100644 --- a/chromium/build/android/pylintrc +++ b/chromium/build/android/pylintrc @@ -4,7 +4,7 @@ max-line-length=80 [MESSAGES CONTROL] -disable=abstract-class-not-used,bad-continuation,bad-indentation,duplicate-code,fixme,invalid-name,locally-disabled,locally-enabled,missing-docstring,star-args,too-few-public-methods,too-many-arguments,too-many-branches,too-many-instance-attributes,too-many-lines,too-many-locals,too-many-public-methods,too-many-statements, +disable=abstract-class-not-used,bad-continuation,bad-indentation,duplicate-code,fixme,invalid-name,locally-disabled,locally-enabled,missing-docstring,star-args,too-few-public-methods,too-many-arguments,too-many-branches,too-many-instance-attributes,too-many-lines,too-many-locals,too-many-public-methods,too-many-statements,wrong-import-position [REPORTS] diff --git a/chromium/build/android/resource_sizes.py b/chromium/build/android/resource_sizes.py index 6287d9842e4..d87a8e25a62 100755 --- a/chromium/build/android/resource_sizes.py +++ b/chromium/build/android/resource_sizes.py @@ -99,9 +99,9 @@ _BASE_CHART = { } _DUMP_STATIC_INITIALIZERS_PATH = os.path.join( host_paths.DIR_SOURCE_ROOT, 'tools', 'linux', 'dump-static-initializers.py') -# Pragma exists when enable_resource_whitelist_generation=true. -_RC_HEADER_RE = re.compile( - r'^#define (?P<name>\w+) (?:_Pragma\(.*?\) )?(?P<id>\d+)$') +# Macro definitions look like (something, 123) when +# enable_resource_whitelist_generation=true. +_RC_HEADER_RE = re.compile(r'^#define (?P<name>\w+).* (?P<id>\d+)\)?$') _RE_NON_LANGUAGE_PAK = re.compile(r'^assets/.*(resources|percent)\.pak$') _RE_COMPRESSED_LANGUAGE_PAK = re.compile( r'\.lpak$|^assets/(?!stored-locales/).*(?!resources|percent)\.pak$') diff --git a/chromium/build/android/stacktrace/java_deobfuscate_test.py b/chromium/build/android/stacktrace/java_deobfuscate_test.py index 58ab1e8f48a..6ee40ce5b5d 100755 --- a/chromium/build/android/stacktrace/java_deobfuscate_test.py +++ b/chromium/build/android/stacktrace/java_deobfuscate_test.py @@ -6,10 +6,14 @@ """Tests for java_deobfuscate.""" import argparse +import os import subprocess import sys import tempfile +import unittest +# Set by command-line argument. +_JAVA_DEOBFUSCATE_PATH = None LINE_PREFIXES = [ '', @@ -59,44 +63,75 @@ this.was.Deobfuscated: Error message """.splitlines(True) -def _RunTest(bin_path, map_file, prefix): - cmd = [bin_path, map_file] - payload = TEST_DATA - expected_output = EXPECTED_OUTPUT +class JavaDeobfuscateTest(unittest.TestCase): - payload = [prefix + x for x in payload] - expected_output = [prefix + x for x in expected_output] + def __init__(self, *args, **kwargs): + super(JavaDeobfuscateTest, self).__init__(*args, **kwargs) + self._map_file = None - proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) - actual_output = proc.communicate(''.join(payload))[0].splitlines(True) - any_unexpected_failures = False - for actual, expected in zip(actual_output, expected_output): - if actual == expected: - sys.stdout.write('Good: ' + actual) - elif actual.replace('bar', 'someMethod') == expected: - # TODO(agrieve): Fix ReTrace's ability to deobfuscated methods. - sys.stdout.write('Fine: ' + actual) - else: - sys.stdout.write('BAD: ' + actual) - any_unexpected_failures = True - return not any_unexpected_failures + def setUp(self): + self._map_file = tempfile.NamedTemporaryFile() + self._map_file.write(TEST_MAP) + self._map_file.flush() + def tearDown(self): + if self._map_file: + self._map_file.close() -def main(): - parser = argparse.ArgumentParser() - parser.add_argument('path_to_java_deobfuscate') - args = parser.parse_args() + def _testImpl(self, input_lines=None, expected_output_lines=None, + prefix=''): + self.assertTrue(bool(input_lines) == bool(expected_output_lines)) + + if not input_lines: + input_lines = [prefix + x for x in TEST_DATA] + if not expected_output_lines: + expected_output_lines = [prefix + x for x in EXPECTED_OUTPUT] + + cmd = [_JAVA_DEOBFUSCATE_PATH, self._map_file.name] + proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) + proc_output, _ = proc.communicate(''.join(input_lines)) + actual_output_lines = proc_output.splitlines(True) + for actual, expected in zip(actual_output_lines, expected_output_lines): + self.assertTrue( + actual == expected or actual.replace('bar', 'someMethod') == expected, + msg=''.join([ + 'Deobfuscation failed.\n', + ' actual: %s' % actual, + ' expected: %s' % expected])) + + def testNoPrefix(self): + self._testImpl(prefix='') - with tempfile.NamedTemporaryFile() as map_file: - map_file.write(TEST_MAP) - map_file.flush() - passed = True - for prefix in LINE_PREFIXES: - if not _RunTest(args.path_to_java_deobfuscate, map_file.name, prefix): - passed = False - print 'Result:', 'PASS' if passed else 'FAIL' - sys.exit(int(not passed)) + def testThreadtimePrefix(self): + self._testImpl(prefix='09-08 14:38:35.535 18029 18084 E qcom_sensors_hal: ') + + def testStandardPrefix(self): + self._testImpl(prefix='W/GCM (15158): ') + + def testStandardPrefixWithPadding(self): + self._testImpl(prefix='W/GCM ( 158): ') + + @unittest.skip('causes java_deobfuscate to hang, see crbug.com/876539') + def testIndefiniteHang(self): + # Test for crbug.com/876539. + self._testImpl( + input_lines=[ + 'VFY: unable to resolve virtual method 2: LFOO;' + + '.onDescendantInvalidated ' + + '(Landroid/view/View;Landroid/view/View;)V', + ], + expected_output_lines=[ + 'VFY: unable to resolve virtual method 2: Lthis.was.Deobfuscated;' + + '.onDescendantInvalidated ' + + '(Landroid/view/View;Landroid/view/View;)V', + ]) if __name__ == '__main__': - main() + parser = argparse.ArgumentParser() + parser.add_argument('--java-deobfuscate-path', type=os.path.realpath, + required=True) + known_args, unittest_args = parser.parse_known_args() + _JAVA_DEOBFUSCATE_PATH = known_args.java_deobfuscate_path + unittest_args = [sys.argv[0]] + unittest_args + unittest.main(argv=unittest_args) diff --git a/chromium/build/android/test_runner.py b/chromium/build/android/test_runner.py index a83d9ab6950..8512accd1b9 100755 --- a/chromium/build/android/test_runner.py +++ b/chromium/build/android/test_runner.py @@ -25,6 +25,7 @@ import unittest # See http://crbug.com/724524 and https://bugs.python.org/issue7980. import _strptime # pylint: disable=unused-import +# pylint: disable=ungrouped-imports from pylib.constants import host_paths if host_paths.DEVIL_PATH not in sys.path: @@ -49,7 +50,6 @@ from pylib.utils import logging_utils from py_utils import contextlib_ext - _DEVIL_STATIC_CONFIG_FILE = os.path.abspath(os.path.join( host_paths.DIR_SOURCE_ROOT, 'build', 'android', 'devil_config.json')) @@ -212,10 +212,12 @@ def AddCommonOptions(parser): def ProcessCommonOptions(args): """Processes and handles all common options.""" run_tests_helper.SetLogLevel(args.verbose_count, add_handler=False) + # pylint: disable=redefined-variable-type if args.verbose_count > 0: handler = logging_utils.ColorStreamHandler() else: handler = logging.StreamHandler(sys.stdout) + # pylint: enable=redefined-variable-type handler.setFormatter(run_tests_helper.CustomFormatter()) logging.getLogger().addHandler(handler) @@ -429,6 +431,7 @@ def AddInstrumentationTestOptions(parser): split_arg = arg.split(',') if len(split_arg) != 2: raise argparse.ArgumentError( + arg, 'Expected two comma-separated strings for --replace-system-package, ' 'received %d' % len(split_arg)) PackageReplacement = collections.namedtuple('PackageReplacement', |