diff options
Diffstat (limited to 'systrace/catapult/systrace/profile_chrome/chrome_tracing_agent.py')
-rw-r--r-- | systrace/catapult/systrace/profile_chrome/chrome_tracing_agent.py | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/systrace/catapult/systrace/profile_chrome/chrome_tracing_agent.py b/systrace/catapult/systrace/profile_chrome/chrome_tracing_agent.py new file mode 100644 index 0000000..656a559 --- /dev/null +++ b/systrace/catapult/systrace/profile_chrome/chrome_tracing_agent.py @@ -0,0 +1,215 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import json +import optparse +import os +import py_utils +import re + +from devil.android import device_errors +from devil.android.sdk import intent +from systrace import trace_result +from systrace import tracing_agents + + +_DEFAULT_CHROME_CATEGORIES = '_DEFAULT_CHROME_CATEGORIES' +_HEAP_PROFILE_MMAP_PROPERTY = 'heapprof.mmap' + + +class ChromeTracingAgent(tracing_agents.TracingAgent): + def __init__(self, device, package_info, ring_buffer, trace_memory=False): + tracing_agents.TracingAgent.__init__(self) + self._device = device + self._package_info = package_info + self._ring_buffer = ring_buffer + self._logcat_monitor = self._device.GetLogcatMonitor() + self._trace_file = None + self._trace_memory = trace_memory + self._is_tracing = False + self._trace_start_re = \ + re.compile(r'Logging performance trace to file') + self._trace_finish_re = \ + re.compile(r'Profiler finished[.] Results are in (.*)[.]') + self._categories = None + + def __repr__(self): + return 'chrome trace' + + @staticmethod + def GetCategories(device, package_info): + with device.GetLogcatMonitor() as logmon: + device.BroadcastIntent(intent.Intent( + action='%s.GPU_PROFILER_LIST_CATEGORIES' % package_info.package)) + try: + json_category_list = logmon.WaitFor( + re.compile(r'{"traceCategoriesList(.*)'), timeout=5).group(0) + except device_errors.CommandTimeoutError: + raise RuntimeError('Performance trace category list marker not found. ' + 'Is the correct version of the browser running?') + + record_categories = set() + disabled_by_default_categories = set() + json_data = json.loads(json_category_list)['traceCategoriesList'] + for item in json_data: + for category in item.split(','): + if category.startswith('disabled-by-default'): + disabled_by_default_categories.add(category) + else: + record_categories.add(category) + + return list(record_categories), list(disabled_by_default_categories) + + @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT) + def StartAgentTracing(self, config, timeout=None): + self._categories = _ComputeChromeCategories(config) + self._logcat_monitor.Start() + start_extras = {'categories': ','.join(self._categories)} + if self._ring_buffer: + start_extras['continuous'] = None + self._device.BroadcastIntent(intent.Intent( + action='%s.GPU_PROFILER_START' % self._package_info.package, + extras=start_extras)) + + if self._trace_memory: + self._device.EnableRoot() + self._device.SetProp(_HEAP_PROFILE_MMAP_PROPERTY, 1) + + # Chrome logs two different messages related to tracing: + # + # 1. "Logging performance trace to file" + # 2. "Profiler finished. Results are in [...]" + # + # The first one is printed when tracing starts and the second one indicates + # that the trace file is ready to be pulled. + try: + self._logcat_monitor.WaitFor(self._trace_start_re, timeout=5) + self._is_tracing = True + except device_errors.CommandTimeoutError: + raise RuntimeError( + 'Trace start marker not found. Possible causes: 1) Is the correct ' + 'version of the browser running? 2) Is the browser already launched?') + return True + + @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT) + def StopAgentTracing(self, timeout=None): + if self._is_tracing: + self._device.BroadcastIntent(intent.Intent( + action='%s.GPU_PROFILER_STOP' % self._package_info.package)) + self._trace_file = self._logcat_monitor.WaitFor( + self._trace_finish_re, timeout=120).group(1) + self._is_tracing = False + if self._trace_memory: + self._device.SetProp(_HEAP_PROFILE_MMAP_PROPERTY, 0) + return True + + @py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT) + def GetResults(self, timeout=None): + with open(self._PullTrace(), 'r') as f: + trace_data = f.read() + return trace_result.TraceResult('traceEvents', trace_data) + + def _PullTrace(self): + trace_file = self._trace_file.replace('/storage/emulated/0/', '/sdcard/') + host_file = os.path.join(os.path.curdir, os.path.basename(trace_file)) + try: + self._device.PullFile(trace_file, host_file) + except device_errors.AdbCommandFailedError: + raise RuntimeError( + 'Cannot pull the trace file. Have you granted Storage permission to ' + 'the browser? (Android Settings -> Apps -> [the browser app] -> ' + 'Permissions -> Storage)') + return host_file + + def SupportsExplicitClockSync(self): + return False + + def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback): + # pylint: disable=unused-argument + assert self.SupportsExplicitClockSync(), ('Clock sync marker cannot be ' + 'recorded since explicit clock sync is not supported.') + + +class ChromeConfig(tracing_agents.TracingConfig): + def __init__(self, chrome_categories, trace_cc, trace_frame_viewer, + trace_ubercompositor, trace_gpu, trace_flow, trace_memory, + trace_scheduler, ring_buffer, device, package_info): + tracing_agents.TracingConfig.__init__(self) + self.chrome_categories = chrome_categories + self.trace_cc = trace_cc + self.trace_frame_viewer = trace_frame_viewer + self.trace_ubercompositor = trace_ubercompositor + self.trace_gpu = trace_gpu + self.trace_flow = trace_flow + self.trace_memory = trace_memory + self.trace_scheduler = trace_scheduler + self.ring_buffer = ring_buffer + self.device = device + self.package_info = package_info + + +def try_create_agent(config): + if config.chrome_categories: + return ChromeTracingAgent(config.device, config.package_info, + config.ring_buffer, config.trace_memory) + return None + +def add_options(parser): + chrome_opts = optparse.OptionGroup(parser, 'Chrome tracing options') + chrome_opts.add_option('-c', '--categories', help='Select Chrome tracing ' + 'categories with comma-delimited wildcards, ' + 'e.g., "*", "cat1*,-cat1a". Omit this option to trace ' + 'Chrome\'s default categories. Chrome tracing can be ' + 'disabled with "--categories=\'\'". Use "list" to ' + 'see the available categories.', + metavar='CHROME_CATEGORIES', dest='chrome_categories', + default=_DEFAULT_CHROME_CATEGORIES) + chrome_opts.add_option('--trace-cc', + help='Deprecated, use --trace-frame-viewer.', + action='store_true') + chrome_opts.add_option('--trace-frame-viewer', + help='Enable enough trace categories for ' + 'compositor frame viewing.', action='store_true') + chrome_opts.add_option('--trace-ubercompositor', + help='Enable enough trace categories for ' + 'ubercompositor frame data.', action='store_true') + chrome_opts.add_option('--trace-gpu', help='Enable extra trace categories ' + 'for GPU data.', action='store_true') + chrome_opts.add_option('--trace-flow', help='Enable extra trace categories ' + 'for IPC message flows.', action='store_true') + chrome_opts.add_option('--trace-memory', help='Enable extra trace categories ' + 'for memory profile. (tcmalloc required)', + action='store_true') + chrome_opts.add_option('--trace-scheduler', help='Enable extra trace ' + 'categories for scheduler state', + action='store_true') + return chrome_opts + +def get_config(options): + return ChromeConfig(options.chrome_categories, options.trace_cc, + options.trace_frame_viewer, options.trace_ubercompositor, + options.trace_gpu, options.trace_flow, + options.trace_memory, options.trace_scheduler, + options.ring_buffer, options.device, + options.package_info) + +def _ComputeChromeCategories(config): + categories = [] + if config.trace_frame_viewer: + categories.append('disabled-by-default-cc.debug') + if config.trace_ubercompositor: + categories.append('disabled-by-default-cc.debug*') + if config.trace_gpu: + categories.append('disabled-by-default-gpu.debug*') + if config.trace_flow: + categories.append('disabled-by-default-toplevel.flow') + if config.trace_memory: + categories.append('disabled-by-default-memory') + if config.trace_scheduler: + categories.append('disabled-by-default-blink.scheduler') + categories.append('disabled-by-default-cc.debug.scheduler') + categories.append('disabled-by-default-renderer.scheduler') + if config.chrome_categories: + categories += config.chrome_categories.split(',') + return categories |