| #!/usr/bin/env vpython3 |
| # Copyright 2024 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| """Runs benchmarks and generates an orderfile, similar to generate_profile.py. |
| |
| Example: |
| Build trichrome_chrome_64_32_bundle and install it on device. |
| |
| Run this script with: |
| $ tools/cygprofile/generate_orderfile.py -C out/orderfile-arm64 \ |
| --android-browser android-trichrome-chrome-64-32-bundle \ |
| --target-arch arm64 |
| |
| The orderfiles should be located in out/orderfile-arm64/orderfiles. |
| """ |
| |
| import argparse |
| import logging |
| import pathlib |
| import sys |
| |
| import android_profile_tool |
| import orderfile_shared |
| |
| _SRC_PATH = pathlib.Path(__file__).resolve().parents[2] |
| sys.path.append(str(_SRC_PATH / 'third_party/catapult/devil')) |
| from devil.android import device_utils |
| |
| |
| def _GetOrderfilesDir(options) -> pathlib.Path: |
| if options.isolated_script_test_output: |
| orderfiles_dir = options.isolated_script_test_output.parent / 'orderfiles' |
| else: |
| orderfiles_dir = options.out_dir / 'orderfiles' |
| orderfiles_dir.mkdir(exist_ok=True) |
| return orderfiles_dir |
| |
| |
| def _GetOrderfileFilename(options): |
| """Gets the path to the architecture-specific orderfile.""" |
| arch = options.arch |
| orderfiles_dir = _GetOrderfilesDir(options) |
| return str(orderfiles_dir / f'orderfile.{arch}.out') |
| |
| |
| def _GetUnpatchedOrderfileFilename(options): |
| """Gets the path to the architecture-specific unpatched orderfile.""" |
| arch = options.arch |
| orderfiles_dir = _GetOrderfilesDir(options) |
| return str(orderfiles_dir / f'unpatched_orderfile.{arch}') |
| |
| |
| def CreateArgumentParser(): |
| """Creates and returns the argument parser.""" |
| parser = argparse.ArgumentParser() |
| orderfile_shared.AddCommonArguments(parser) |
| |
| # Essential arguments for profiling and processing: |
| parser.add_argument('--android-browser', |
| required=True, |
| help='Browser string to pass to run_benchmark.') |
| parser.add_argument('-C', |
| '--out-dir', |
| type=pathlib.Path, |
| required=True, |
| help='Path to the output directory (e.g. out/Release).') |
| # The following two are bot-specific args. |
| parser.add_argument('--isolated-script-test-output', |
| type=pathlib.Path, |
| help='Output.json file that the script can write to.') |
| parser.add_argument('--isolated-script-test-perf-output', |
| help='Deprecated and ignored, but bots pass it.') |
| |
| return parser |
| |
| |
| def GenerateOrderfile(options, device): |
| """Generates an orderfile for a given device.""" |
| host_profile_root = options.out_dir / 'profile_data' |
| profiler = android_profile_tool.AndroidProfileTool( |
| str(host_profile_root), |
| device, |
| debug=options.streamline_for_debugging, |
| verbosity=options.verbosity) |
| |
| lib_chrome_so = orderfile_shared.GetLibchromeSoPath(options.out_dir, |
| options.arch, |
| options.profile_webview) |
| try: |
| if options.profile_webview: |
| if options.arch == 'arm64': |
| webview_target = 'system_webview_64_32_apk' |
| apk_name = 'SystemWebView6432.apk' |
| else: |
| webview_target = 'system_webview_apk' |
| apk_name = 'SystemWebView.apk' |
| webview_installer_path = str(options.out_dir / 'bin' / webview_target) |
| apk_or_browser = str(options.out_dir / 'apks' / apk_name) |
| else: |
| apk_or_browser = options.android_browser |
| webview_installer_path = None |
| files = orderfile_shared.CollectProfiles(profiler, options.profile_webview, |
| options.arch, |
| apk_or_browser, |
| str(options.out_dir), |
| webview_installer_path) |
| ordered_symbols, _ = orderfile_shared.ProcessProfiles(files, lib_chrome_so) |
| with open(_GetUnpatchedOrderfileFilename(options), 'w') as orderfile: |
| orderfile.write('\n'.join(ordered_symbols)) |
| finally: |
| if not options.save_profile_data: |
| profiler.Cleanup() |
| logging.getLogger().setLevel(logging.INFO) |
| |
| orderfile_shared.AddDummyFunctions(_GetUnpatchedOrderfileFilename(options), |
| _GetOrderfileFilename(options)) |
| |
| |
| def main(): |
| parser = CreateArgumentParser() |
| options = parser.parse_args() |
| if options.verbosity >= 2: |
| level = logging.DEBUG |
| elif options.verbosity == 1: |
| level = logging.INFO |
| else: |
| level = logging.WARNING |
| logging.basicConfig(level=level, |
| format='%(levelname).1s %(relativeCreated)6d %(message)s') |
| |
| logging.info('Generate Profile Data') |
| |
| # Ensure that the output directory is an absolute path. |
| options.out_dir = options.out_dir.resolve(strict=True) |
| logging.info('Using options.out_dir=%s', options.out_dir) |
| |
| devices = device_utils.DeviceUtils.HealthyDevices() |
| assert devices, 'Expected at least one connected device' |
| device = devices[0] |
| |
| logging.getLogger().setLevel(logging.DEBUG) |
| GenerateOrderfile(options, device) |
| |
| |
| if __name__ == '__main__': |
| main() |