1 # Copyright 2015 The Chromium Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 5 import logging 6 import optparse 7 import os 8 import py_utils 9 import re 10 11 from devil.android import flag_changer 12 from devil.android.constants import webapk 13 from devil.android.perf import cache_control 14 from devil.android.sdk import intent 15 16 from systrace import trace_result 17 from systrace import tracing_agents 18 19 20 class ChromeStartupTracingAgent(tracing_agents.TracingAgent): 21 def __init__(self, device, package_info, webapk_package, cold, url, 22 trace_time=None): 23 tracing_agents.TracingAgent.__init__(self) 24 self._device = device 25 self._package_info = package_info 26 self._webapk_package = webapk_package 27 self._cold = cold 28 self._logcat_monitor = self._device.GetLogcatMonitor() 29 self._url = url 30 self._trace_time = trace_time 31 self._trace_file = None 32 self._trace_finish_re = re.compile(r' Completed startup tracing to (.*)') 33 self._flag_changer = flag_changer.FlagChanger( 34 self._device, self._package_info.cmdline_file) 35 36 def __repr__(self): 37 return 'Browser Startup Trace' 38 39 def _SetupTracing(self): 40 # TODO(lizeb): Figure out how to clean up the command-line file when 41 # _TearDownTracing() is not executed in StopTracing(). 42 flags = ['--trace-startup'] 43 if self._trace_time is not None: 44 flags.append('--trace-startup-duration={}'.format(self._trace_time)) 45 self._flag_changer.AddFlags(flags) 46 self._device.ForceStop(self._package_info.package) 47 if self._webapk_package: 48 self._device.ForceStop(self._webapk_package) 49 logging.warning('Forces to stop the WebAPK and the browser provided by ' 50 '--browser: %s. Please make sure that this browser ' 51 'matches the host browser of the WebAPK %s. ', 52 self._package_info.package, 53 self._webapk_package) 54 if self._cold: 55 self._device.EnableRoot() 56 cache_control.CacheControl(self._device).DropRamCaches() 57 launch_intent = None 58 if self._webapk_package: 59 launch_intent = intent.Intent( 60 package=self._webapk_package, 61 activity=webapk.WEBAPK_MAIN_ACTIVITY, 62 data=self._url) 63 elif self._url == '': 64 launch_intent = intent.Intent( 65 action='android.intent.action.MAIN', 66 package=self._package_info.package, 67 activity=self._package_info.activity) 68 else: 69 launch_intent = intent.Intent( 70 package=self._package_info.package, 71 activity=self._package_info.activity, 72 data=self._url, 73 extras={'create_new_tab': True}) 74 self._logcat_monitor.Start() 75 self._device.StartActivity(launch_intent, blocking=True) 76 77 def _TearDownTracing(self): 78 self._flag_changer.Restore() 79 80 @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT) 81 def StartAgentTracing(self, config, timeout=None): 82 self._SetupTracing() 83 return True 84 85 @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT) 86 def StopAgentTracing(self, timeout=None): 87 try: 88 self._trace_file = self._logcat_monitor.WaitFor( 89 self._trace_finish_re).group(1) 90 finally: 91 self._TearDownTracing() 92 return True 93 94 @py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT) 95 def GetResults(self, timeout=None): 96 with open(self._PullTrace(), 'r') as f: 97 trace_data = f.read() 98 return trace_result.TraceResult('traceEvents', trace_data) 99 100 def _PullTrace(self): 101 trace_file = self._trace_file.replace('/storage/emulated/0/', '/sdcard/') 102 host_file = os.path.join(os.path.curdir, os.path.basename(trace_file)) 103 self._device.PullFile(trace_file, host_file) 104 return host_file 105 106 def SupportsExplicitClockSync(self): 107 return False 108 109 def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback): 110 # pylint: disable=unused-argument 111 assert self.SupportsExplicitClockSync(), ('Clock sync marker cannot be ' 112 'recorded since explicit clock sync is not supported.') 113 114 115 class ChromeStartupConfig(tracing_agents.TracingConfig): 116 def __init__(self, device, package_info, webapk_package, cold, url, 117 chrome_categories, trace_time): 118 tracing_agents.TracingConfig.__init__(self) 119 self.device = device 120 self.package_info = package_info 121 self.webapk_package = webapk_package 122 self.cold = cold 123 self.url = url 124 self.chrome_categories = chrome_categories 125 self.trace_time = trace_time 126 127 128 def try_create_agent(config): 129 return ChromeStartupTracingAgent(config.device, config.package_info, 130 config.webapk_package, 131 config.cold, config.url, config.trace_time) 132 133 def add_options(parser): 134 options = optparse.OptionGroup(parser, 'Chrome startup tracing') 135 options.add_option('--url', help='URL to visit on startup. Default: ' 136 'https://www.google.com. An empty URL launches Chrome ' 137 'with a MAIN action instead of VIEW.', 138 default='https://www.google.com', metavar='URL') 139 options.add_option('--cold', help='Flush the OS page cache before starting ' 140 'the browser. Note that this require a device with root ' 141 'access.', default=False, action='store_true') 142 options.add_option('--webapk-package', help='Specify the package name ' 143 'of the WebAPK to launch the given URL. An empty URL ' 144 'laucnhes the host browser of the WebAPK with an new ' 145 'tab.', default=None) 146 147 return options 148 149 def get_config(options): 150 return ChromeStartupConfig(options.device, options.package_info, 151 options.webapk_package, options.cold, 152 options.url, options.chrome_categories, 153 options.trace_time) 154