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