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
5import optparse
6import os
7import py_utils
8import re
9
10from devil.android import flag_changer
11from devil.android.perf import cache_control
12from devil.android.sdk import intent
13
14from systrace import trace_result
15from systrace import tracing_agents
16
17
18class ChromeStartupTracingAgent(tracing_agents.TracingAgent):
19  def __init__(self, device, package_info, cold, url):
20    tracing_agents.TracingAgent.__init__(self)
21    self._device = device
22    self._package_info = package_info
23    self._cold = cold
24    self._logcat_monitor = self._device.GetLogcatMonitor()
25    self._url = url
26    self._trace_file = None
27    self._trace_finish_re = re.compile(r' Completed startup tracing to (.*)')
28    self._flag_changer = flag_changer.FlagChanger(
29      self._device, self._package_info.cmdline_file)
30
31  def __repr__(self):
32    return 'Browser Startup Trace'
33
34  def _SetupTracing(self):
35    # TODO(lizeb): Figure out how to clean up the command-line file when
36    # _TearDownTracing() is not executed in StopTracing().
37    self._flag_changer.AddFlags(['--trace-startup'])
38    self._device.ForceStop(self._package_info.package)
39    if self._cold:
40      self._device.EnableRoot()
41      cache_control.CacheControl(self._device).DropRamCaches()
42    launch_intent = None
43    if self._url == '':
44      launch_intent = intent.Intent(
45          action='android.intent.action.MAIN',
46          package=self._package_info.package,
47          activity=self._package_info.activity)
48    else:
49      launch_intent = intent.Intent(
50          package=self._package_info.package,
51          activity=self._package_info.activity,
52          data=self._url,
53          extras={'create_new_tab': True})
54    self._device.StartActivity(launch_intent, blocking=True)
55
56  def _TearDownTracing(self):
57    self._flag_changer.Restore()
58
59  @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
60  def StartAgentTracing(self, config, timeout=None):
61    self._SetupTracing()
62    self._logcat_monitor.Start()
63    return True
64
65  @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
66  def StopAgentTracing(self, timeout=None):
67    try:
68      self._trace_file = self._logcat_monitor.WaitFor(
69          self._trace_finish_re).group(1)
70    finally:
71      self._TearDownTracing()
72    return True
73
74  @py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT)
75  def GetResults(self, timeout=None):
76    with open(self._PullTrace(), 'r') as f:
77      trace_data = f.read()
78    return trace_result.TraceResult('traceEvents', trace_data)
79
80  def _PullTrace(self):
81    trace_file = self._trace_file.replace('/storage/emulated/0/', '/sdcard/')
82    host_file = os.path.join(os.path.curdir, os.path.basename(trace_file))
83    self._device.PullFile(trace_file, host_file)
84    return host_file
85
86  def SupportsExplicitClockSync(self):
87    return False
88
89  def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback):
90    # pylint: disable=unused-argument
91    assert self.SupportsExplicitClockSync(), ('Clock sync marker cannot be '
92        'recorded since explicit clock sync is not supported.')
93
94
95class ChromeStartupConfig(tracing_agents.TracingConfig):
96  def __init__(self, device, package_info, cold, url, chrome_categories):
97    tracing_agents.TracingConfig.__init__(self)
98    self.device = device
99    self.package_info = package_info
100    self.cold = cold
101    self.url = url
102    self.chrome_categories = chrome_categories
103
104
105def try_create_agent(config):
106  return ChromeStartupTracingAgent(config.device, config.package_info,
107                                   config.cold, config.url)
108
109def add_options(parser):
110  options = optparse.OptionGroup(parser, 'Chrome startup tracing')
111  options.add_option('--url', help='URL to visit on startup. Default: '
112                     'https://www.google.com. An empty URL launches Chrome '
113                     'with a MAIN action instead of VIEW.',
114                     default='https://www.google.com', metavar='URL')
115  options.add_option('--cold', help='Flush the OS page cache before starting '
116                     'the browser. Note that this require a device with root '
117                     'access.', default=False, action='store_true')
118  return options
119
120def get_config(options):
121  return ChromeStartupConfig(options.device, options.package_info,
122                             options.cold, options.url,
123                             options.chrome_categories)
124