• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2014 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 json
6import optparse
7import os
8import re
9
10import py_utils
11
12from devil.android import device_errors
13from devil.android.sdk import intent
14from systrace import trace_result
15from systrace import tracing_agents
16
17
18DEFAULT_CHROME_CATEGORIES = '_DEFAULT_CHROME_CATEGORIES'
19_HEAP_PROFILE_MMAP_PROPERTY = 'heapprof.mmap'
20
21
22class ChromeTracingAgent(tracing_agents.TracingAgent):
23  def __init__(self, device, package_info, ring_buffer, trace_memory=False):
24    tracing_agents.TracingAgent.__init__(self)
25    self._device = device
26    self._package_info = package_info
27    self._ring_buffer = ring_buffer
28    self._logcat_monitor = self._device.GetLogcatMonitor()
29    self._trace_file = None
30    self._trace_memory = trace_memory
31    self._is_tracing = False
32    self._trace_start_re = \
33       re.compile(r'Logging performance trace to file')
34    self._trace_finish_re = \
35       re.compile(r'Profiler finished[.] Results are in (.*)[.]')
36    self._categories = None
37
38  def __repr__(self):
39    return 'chrome trace'
40
41  @staticmethod
42  def GetCategories(device, package_info):
43    with device.GetLogcatMonitor() as logmon:
44      device.BroadcastIntent(intent.Intent(
45          action='%s.GPU_PROFILER_LIST_CATEGORIES' % package_info.package))
46      try:
47        json_category_list = logmon.WaitFor(
48            re.compile(r'{"traceCategoriesList(.*)'), timeout=5).group(0)
49      except device_errors.CommandTimeoutError:
50        raise RuntimeError('Performance trace category list marker not found. '
51                           'Is the correct version of the browser running?')
52
53    record_categories = set()
54    disabled_by_default_categories = set()
55    json_data = json.loads(json_category_list)['traceCategoriesList']
56    for item in json_data:
57      for category in item.split(','):
58        if category.startswith('disabled-by-default'):
59          disabled_by_default_categories.add(category)
60        else:
61          record_categories.add(category)
62
63    return list(record_categories), list(disabled_by_default_categories)
64
65  @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
66  def StartAgentTracing(self, config, timeout=None):
67    self._categories = _ComputeChromeCategories(config)
68    self._logcat_monitor.Start()
69    start_extras = {'categories': ','.join(self._categories)}
70    if self._ring_buffer:
71      start_extras['continuous'] = None
72    self._device.BroadcastIntent(intent.Intent(
73        action='%s.GPU_PROFILER_START' % self._package_info.package,
74        extras=start_extras))
75
76    if self._trace_memory:
77      self._device.EnableRoot()
78      self._device.SetProp(_HEAP_PROFILE_MMAP_PROPERTY, 1)
79
80    # Chrome logs two different messages related to tracing:
81    #
82    # 1. "Logging performance trace to file"
83    # 2. "Profiler finished. Results are in [...]"
84    #
85    # The first one is printed when tracing starts and the second one indicates
86    # that the trace file is ready to be pulled.
87    try:
88      self._logcat_monitor.WaitFor(self._trace_start_re, timeout=5)
89      self._is_tracing = True
90    except device_errors.CommandTimeoutError:
91      raise RuntimeError(
92          'Trace start marker not found. Possible causes: 1) Is the correct '
93          'version of the browser running? 2) Is the browser already launched?')
94    return True
95
96  @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
97  def StopAgentTracing(self, timeout=None):
98    if self._is_tracing:
99      self._device.BroadcastIntent(intent.Intent(
100          action='%s.GPU_PROFILER_STOP' % self._package_info.package))
101      self._trace_file = self._logcat_monitor.WaitFor(
102          self._trace_finish_re, timeout=120).group(1)
103      self._is_tracing = False
104    if self._trace_memory:
105      self._device.SetProp(_HEAP_PROFILE_MMAP_PROPERTY, 0)
106    return True
107
108  @py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT)
109  def GetResults(self, timeout=None):
110    with open(self._PullTrace(), 'r') as f:
111      trace_data = f.read()
112    return trace_result.TraceResult('traceEvents', trace_data)
113
114  def _PullTrace(self):
115    trace_file = self._trace_file.replace('/storage/emulated/0/', '/sdcard/')
116    host_file = os.path.join(os.path.curdir, os.path.basename(trace_file))
117    try:
118      self._device.PullFile(trace_file, host_file)
119    except device_errors.AdbCommandFailedError:
120      raise RuntimeError(
121          'Cannot pull the trace file. Have you granted Storage permission to '
122          'the browser? (Android Settings -> Apps -> [the browser app] -> '
123          'Permissions -> Storage)')
124    return host_file
125
126  def SupportsExplicitClockSync(self):
127    return False
128
129  def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback):
130    # pylint: disable=unused-argument
131    assert self.SupportsExplicitClockSync(), ('Clock sync marker cannot be '
132        'recorded since explicit clock sync is not supported.')
133
134
135class ChromeConfig(tracing_agents.TracingConfig):
136  def __init__(self, chrome_categories, trace_cc, trace_frame_viewer,
137               trace_ubercompositor, trace_gpu, trace_flow, trace_memory,
138               trace_scheduler, ring_buffer, device, package_info):
139    tracing_agents.TracingConfig.__init__(self)
140    self.chrome_categories = chrome_categories
141    self.trace_cc = trace_cc
142    self.trace_frame_viewer = trace_frame_viewer
143    self.trace_ubercompositor = trace_ubercompositor
144    self.trace_gpu = trace_gpu
145    self.trace_flow = trace_flow
146    self.trace_memory = trace_memory
147    self.trace_scheduler = trace_scheduler
148    self.ring_buffer = ring_buffer
149    self.device = device
150    self.package_info = package_info
151
152
153def try_create_agent(config):
154  if config.chrome_categories:
155    return ChromeTracingAgent(config.device, config.package_info,
156                              config.ring_buffer, config.trace_memory)
157  return None
158
159def add_options(parser):
160  chrome_opts = optparse.OptionGroup(parser, 'Chrome tracing options')
161  chrome_opts.add_option('-c', '--categories', help='Select Chrome tracing '
162                         'categories with comma-delimited wildcards, '
163                         'e.g., "*", "cat1*,-cat1a". Omit this option to trace '
164                         'Chrome\'s default categories. Chrome tracing can be '
165                         'disabled with "--categories=\'\'". Use "list" to '
166                         'see the available categories.',
167                         metavar='CHROME_CATEGORIES', dest='chrome_categories')
168  chrome_opts.add_option('--trace-cc',
169                         help='Deprecated, use --trace-frame-viewer.',
170                         action='store_true')
171  chrome_opts.add_option('--trace-frame-viewer',
172                         help='Enable enough trace categories for '
173                         'compositor frame viewing.', action='store_true')
174  chrome_opts.add_option('--trace-ubercompositor',
175                         help='Enable enough trace categories for '
176                         'ubercompositor frame data.', action='store_true')
177  chrome_opts.add_option('--trace-gpu', help='Enable extra trace categories '
178                         'for GPU data.', action='store_true')
179  chrome_opts.add_option('--trace-flow', help='Enable extra trace categories '
180                         'for IPC message flows.', action='store_true')
181  chrome_opts.add_option('--trace-memory', help='Enable extra trace categories '
182                         'for memory profile. (tcmalloc required)',
183                         action='store_true')
184  chrome_opts.add_option('--trace-scheduler', help='Enable extra trace '
185                         'categories for scheduler state',
186                         action='store_true')
187  return chrome_opts
188
189def get_config(options):
190  return ChromeConfig(options.chrome_categories, options.trace_cc,
191                      options.trace_frame_viewer, options.trace_ubercompositor,
192                      options.trace_gpu, options.trace_flow,
193                      options.trace_memory, options.trace_scheduler,
194                      options.ring_buffer, options.device,
195                      options.package_info)
196
197def _ComputeChromeCategories(config):
198  categories = []
199  if config.trace_frame_viewer:
200    categories.append('disabled-by-default-cc.debug')
201  if config.trace_ubercompositor:
202    categories.append('disabled-by-default-cc.debug*')
203  if config.trace_gpu:
204    categories.append('disabled-by-default-gpu.debug*')
205  if config.trace_flow:
206    categories.append('toplevel.flow')
207    # toplevel.flow was moved out of disabled-by-default, leaving here for
208    # compatibility with older versions of Chrome.
209    categories.append('disabled-by-default-toplevel.flow')
210  if config.trace_memory:
211    categories.append('disabled-by-default-memory')
212  if config.trace_scheduler:
213    categories.append('disabled-by-default-blink.scheduler')
214    categories.append('disabled-by-default-cc.debug.scheduler')
215    categories.append('disabled-by-default-renderer.scheduler')
216    categories.append('disabled-by-default-sequence_manager')
217    categories.append('sequence_manager')
218  if config.chrome_categories:
219    categories += config.chrome_categories.split(',')
220  return categories
221