blob: 16e6e48a7d0a8bcf5aadf55de693b36ea114387f [file] [log] [blame]
# Copyright 2015 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 logging
import sys
import traceback
from py_trace_event import trace_time
from telemetry.core import exceptions
from telemetry.internal.platform import tracing_agent
from telemetry.internal.platform.tracing_agent import (
chrome_tracing_devtools_manager)
class ChromeTracingStartedError(exceptions.Error):
pass
class ChromeTracingStoppedError(exceptions.Error):
pass
class ChromeClockSyncError(exceptions.Error):
pass
class ChromeTracingAgent(tracing_agent.TracingAgent):
def __init__(self, platform_backend, config):
super(ChromeTracingAgent, self).__init__(platform_backend, config)
self._trace_config = None
self._trace_config_file = None
self._previously_responsive_devtools = []
@property
def trace_config(self):
# Trace config is also used to check if Chrome tracing is running or not.
return self._trace_config
@property
def trace_config_file(self):
return self._trace_config_file
@classmethod
def IsSupported(cls, platform_backend):
raise NotImplementedError
def _GetTransferMode(self):
return None
def _StartStartupTracing(self, config):
raise NotImplementedError
def _StartDevToolsTracing(self, config, timeout):
devtools_clients = (
chrome_tracing_devtools_manager
.GetActiveDevToolsClients(self._platform_backend))
if not devtools_clients:
return False
for client in devtools_clients:
if client.is_tracing_running:
raise ChromeTracingStartedError(
'Tracing is already running on devtools at port %s on platform'
'backend %s.' % (client.remote_port, self._platform_backend))
client.StartChromeTracing(config, transfer_mode=self._GetTransferMode(),
timeout=timeout)
return True
def StartAgentTracing(self, config, timeout):
if not config.enable_chrome_trace:
return False
if self._trace_config:
raise ChromeTracingStartedError(
'Tracing is already running on platform backend %s.'
% self._platform_backend)
if (config.enable_android_graphics_memtrack and
self._platform_backend.GetOSName() == 'android'):
self._platform_backend.SetGraphicsMemoryTrackingEnabled(True)
# Chrome tracing Agent needs to start tracing for chrome browsers that are
# not yet started, and for the ones that already are. For the former, we
# first setup the trace_config_file, which allows browsers that starts after
# this point to use it for enabling tracing upon browser startup. For the
# latter, we invoke start tracing command through devtools for browsers that
# are already started and tracked by chrome_tracing_devtools_manager.
started_startup_tracing = self._StartStartupTracing(config)
started_devtools_tracing = self._StartDevToolsTracing(config, timeout)
if started_startup_tracing or started_devtools_tracing:
self._trace_config = config
return True
return False
def SupportsExplicitClockSync(self):
return True
def RecordClockSyncMarker(self, sync_id,
record_controller_clock_sync_marker_callback):
devtools_clients = (chrome_tracing_devtools_manager
.GetActiveDevToolsClients(self._platform_backend))
if not devtools_clients:
logging.info('No devtools clients for issuing clock sync.')
return False
has_clock_synced = False
for client in devtools_clients:
try:
timestamp = trace_time.Now()
client.RecordChromeClockSyncMarker(sync_id)
# We only need one successful clock sync.
has_clock_synced = True
break
except Exception: # pylint: disable=broad-except
logging.exception('Failed to record clock sync marker with sync_id=%r '
'via DevTools client %r:', sync_id, client)
if not has_clock_synced:
raise ChromeClockSyncError(
'Failed to issue clock sync to devtools client')
record_controller_clock_sync_marker_callback(sync_id, timestamp)
return True
def StopAgentTracing(self):
if not self._trace_config:
raise ChromeTracingStoppedError(
'Tracing is not running on platform backend %s.'
% self._platform_backend)
self._RemoveTraceConfigFile()
# We get all DevTools clients including the stale ones, so that we get an
# exception if there is a stale client. This is because we will potentially
# lose data if there is a stale client.
# TODO(crbug.com/1029812): Check if this actually works. It looks like the
# call to GetActiveDevToolsClients in RecordClockSyncMarker would have
# wiped out the stale clients anyway.
devtools_clients = (chrome_tracing_devtools_manager
.GetDevToolsClients(self._platform_backend))
raised_exception_messages = []
assert len(self._previously_responsive_devtools) == 0
for client in devtools_clients:
try:
client.StopChromeTracing()
self._previously_responsive_devtools.append(client)
except Exception: # pylint: disable=broad-except
raised_exception_messages.append(
"""Error when trying to stop Chrome tracing
on devtools at port %s:\n%s"""
% (client.remote_port,
''.join(traceback.format_exception(*sys.exc_info()))))
if (self._trace_config.enable_android_graphics_memtrack and
self._platform_backend.GetOSName() == 'android'):
self._platform_backend.SetGraphicsMemoryTrackingEnabled(False)
self._trace_config = None
if raised_exception_messages:
raise ChromeTracingStoppedError(
'Exceptions raised when trying to stop Chrome devtool tracing:\n' +
'\n'.join(raised_exception_messages))
def _RemoveTraceConfigFile(self):
raise NotImplementedError
def CollectAgentTraceData(self, trace_data_builder, timeout=None):
raised_exception_messages = []
for client in self._previously_responsive_devtools:
try:
client.CollectChromeTracingData(trace_data_builder)
except Exception: # pylint: disable=broad-except
raised_exception_messages.append(
'Error when collecting Chrome tracing on devtools at port %s:\n%s' %
(client.remote_port,
''.join(traceback.format_exception(*sys.exc_info()))))
self._previously_responsive_devtools = []
if raised_exception_messages:
raise ChromeTracingStoppedError(
'Exceptions raised when trying to collect Chrome devtool tracing:\n' +
'\n'.join(raised_exception_messages))
def SupportsFlushingAgentTracing(self):
return True
def FlushAgentTracing(self, config, timeout, trace_data_builder):
if not self._trace_config:
raise ChromeTracingStoppedError(
'Tracing is not running on platform backend %s.'
% self._platform_backend)
for backend in self._IterFirstTabBackends():
backend.EvaluateJavaScript("console.time('flush-tracing');")
self.StopAgentTracing()
self.CollectAgentTraceData(trace_data_builder)
self.StartAgentTracing(config, timeout)
for backend in self._IterFirstTabBackends():
backend.EvaluateJavaScript("console.timeEnd('flush-tracing');")
def _IterFirstTabBackends(self):
for client in chrome_tracing_devtools_manager.GetDevToolsClients(
self._platform_backend):
backend = client.FirstTabBackend()
if backend is not None:
yield backend