| #!/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, 'flutter', '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()) |