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