| #!/usr/bin/env python3 | |
| # | |
| # Copyright 2013 The Flutter 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 argparse | |
| import os | |
| import subprocess | |
| import sys | |
| BUILDROOT_DIR = os.path.abspath(os.path.join(os.path.realpath(__file__), '..', '..', '..')) | |
| PERFETTO_SESSION_KEY = 'session1' | |
| PERFETTO_TRACE_FILE = '/data/misc/perfetto-traces/trace' | |
| PERFETTO_CONFIG = """ | |
| write_into_file: true | |
| file_write_period_ms: 1000000000 | |
| flush_period_ms: 1000 | |
| buffers: { | |
| size_kb: 129024 | |
| } | |
| data_sources: { | |
| config { | |
| name: "linux.ftrace" | |
| ftrace_config { | |
| ftrace_events: "ftrace/print" | |
| atrace_apps: "%s" | |
| } | |
| } | |
| } | |
| """ | |
| def install_apk(apk_path, package_name, adb_path='adb'): | |
| print('Installing APK') | |
| subprocess.check_output([adb_path, 'shell', 'am', 'force-stop', package_name]) | |
| # Allowed to fail if APK was never installed. | |
| subprocess.call([adb_path, 'uninstall', package_name], stdout=subprocess.DEVNULL) | |
| subprocess.check_output([adb_path, 'install', apk_path]) | |
| def start_perfetto(package_name, adb_path='adb'): | |
| print('Starting trace') | |
| cmd = [ | |
| adb_path, 'shell', 'echo', "'" + PERFETTO_CONFIG % package_name + "'", '|', 'perfetto', '-c', | |
| '-', '--txt', '-o', PERFETTO_TRACE_FILE, '--detach', PERFETTO_SESSION_KEY | |
| ] | |
| subprocess.check_output(cmd, stderr=subprocess.STDOUT) | |
| def launch_package(package_name, activity_name, adb_path='adb'): | |
| print('Scanning logcat') | |
| subprocess.check_output([adb_path, 'logcat', '-c'], stderr=subprocess.STDOUT) | |
| logcat = subprocess.Popen([adb_path, 'logcat'], | |
| stdout=subprocess.PIPE, | |
| stderr=subprocess.STDOUT, | |
| universal_newlines=True) | |
| print('Launching %s (%s)' % (package_name, activity_name)) | |
| subprocess.check_output([ | |
| adb_path, 'shell', 'am ', 'start', '-n', | |
| '%s/%s' % (package_name, activity_name) | |
| ], | |
| stderr=subprocess.STDOUT) | |
| for line in logcat.stdout: | |
| print('>>>>>>>> ' + line.strip()) | |
| if ('Observatory listening' in line) or ('Dart VM service is listening' in line): | |
| logcat.kill() | |
| break | |
| def collect_and_validate_trace(adb_path='adb'): | |
| print('Fetching trace') | |
| subprocess.check_output([ | |
| adb_path, 'shell', 'perfetto', '--attach', PERFETTO_SESSION_KEY, '--stop' | |
| ], | |
| stderr=subprocess.STDOUT) | |
| subprocess.check_output([adb_path, 'pull', PERFETTO_TRACE_FILE, 'trace.pb'], | |
| stderr=subprocess.STDOUT) | |
| print('Validating trace') | |
| traceconv = os.path.join( | |
| BUILDROOT_DIR, 'third_party', 'android_tools', 'trace_to_text', 'trace_to_text' | |
| ) | |
| traceconv_output = subprocess.check_output([traceconv, 'systrace', 'trace.pb'], | |
| stderr=subprocess.STDOUT, | |
| universal_newlines=True) | |
| print('Trace output:') | |
| print(traceconv_output) | |
| if 'ShellSetupUISubsystem' in traceconv_output: | |
| return 0 | |
| print('Trace did not contain ShellSetupUISubsystem, failing.') | |
| return 1 | |
| def main(): | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument( | |
| '--apk-path', dest='apk_path', action='store', help='Provide the path to the APK to install' | |
| ) | |
| parser.add_argument( | |
| '--package-name', | |
| dest='package_name', | |
| action='store', | |
| help='The package name of the APK, e.g. dev.flutter.scenarios' | |
| ) | |
| parser.add_argument( | |
| '--activity-name', | |
| dest='activity_name', | |
| action='store', | |
| help='The activity to launch as it appears in AndroidManifest.xml, ' | |
| 'e.g. .PlatformViewsActivity' | |
| ) | |
| parser.add_argument( | |
| '--adb-path', | |
| dest='adb_path', | |
| action='store', | |
| default='adb', | |
| help='Provide the path of adb used for android tests. ' | |
| 'By default it looks on $PATH.' | |
| ) | |
| args = parser.parse_args() | |
| android_api_level = subprocess.check_output([ | |
| args.adb_path, 'shell', 'getprop', 'ro.build.version.sdk' | |
| ], | |
| text=True).strip() | |
| if int(android_api_level) < 29: | |
| print('Android API %s detected. This script requires API 29 or above.' % android_api_level) | |
| return 0 | |
| install_apk(args.apk_path, args.package_name, args.adb_path) | |
| start_perfetto(args.package_name, args.adb_path) | |
| launch_package(args.package_name, args.activity_name, args.adb_path) | |
| return collect_and_validate_trace(args.adb_path) | |
| if __name__ == '__main__': | |
| sys.exit(main()) |