221 lines
9.0 KiB
Python
221 lines
9.0 KiB
Python
# 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 re
|
|
|
|
import py_utils
|
|
|
|
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')
|
|
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('toplevel.flow')
|
|
# toplevel.flow was moved out of disabled-by-default, leaving here for
|
|
# compatibility with older versions of Chrome.
|
|
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')
|
|
categories.append('disabled-by-default-sequence_manager')
|
|
categories.append('sequence_manager')
|
|
if config.chrome_categories:
|
|
categories += config.chrome_categories.split(',')
|
|
return categories
|