blob: ad63ef309b139e45bba2af867e9f0e95c0169290 [file] [log] [blame]
# Copyright 2013 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
from telemetry.core.backends.chrome import inspector_websocket
from telemetry.core.platform import tracing_options
class TracingUnsupportedException(Exception):
pass
class TracingTimeoutException(Exception):
pass
class TracingHasNotRunException(Exception):
pass
class TracingBackend(object):
def __init__(self, devtools_port, chrome_browser_backend):
self._inspector_websocket = inspector_websocket.InspectorWebsocket(
self._NotificationHandler,
self._ErrorHandler)
self._inspector_websocket.Connect(
'ws://127.0.0.1:%i/devtools/browser' % devtools_port)
self._tracing_data = []
self._is_tracing_running = False
self._chrome_browser_backend = chrome_browser_backend
@property
def is_tracing_running(self):
return self._is_tracing_running
def StartTracing(self, trace_options, custom_categories=None, timeout=10):
""" Starts tracing on the first call and returns True. Returns False
and does nothing on subsequent nested calls.
"""
if self.is_tracing_running:
return False
# Reset collected tracing data from previous tracing calls.
self._tracing_data = []
self._CheckNotificationSupported()
#TODO(nednguyen): remove this when the stable branch pass 2118.
if (trace_options.record_mode == tracing_options.RECORD_AS_MUCH_AS_POSSIBLE
and self._chrome_browser_backend.chrome_branch_number
and self._chrome_browser_backend.chrome_branch_number < 2118):
logging.warning(
'Cannot use %s tracing mode on chrome browser with branch version %i,'
' (<2118) fallback to use %s tracing mode' % (
trace_options.record_mode,
self._chrome_browser_backend.chrome_branch_number,
tracing_options.RECORD_UNTIL_FULL))
trace_options.record_mode = tracing_options.RECORD_UNTIL_FULL
req = {'method': 'Tracing.start'}
req['params'] = {}
m = {tracing_options.RECORD_UNTIL_FULL: 'record-until-full',
tracing_options.RECORD_AS_MUCH_AS_POSSIBLE:
'record-as-much-as-possible'}
req['params']['options'] = m[trace_options.record_mode]
if custom_categories:
req['params']['categories'] = custom_categories
self._inspector_websocket.SyncRequest(req, timeout)
self._is_tracing_running = True
return True
def StopTracing(self, timeout=30):
""" Stops tracing and returns the raw json trace result. It this is called
after tracing has been stopped, trace data from the last tracing run is
returned.
"""
if not self.is_tracing_running:
if not self._tracing_data:
raise TracingHasNotRunException()
else:
return self._tracing_data
req = {'method': 'Tracing.end'}
self._inspector_websocket.SendAndIgnoreResponse(req)
# After Tracing.end, chrome browser will send asynchronous notifications
# containing trace data. This is until Tracing.tracingComplete is sent,
# which means there is no trace buffers pending flush.
try:
self._inspector_websocket.DispatchNotificationsUntilDone(timeout)
except \
inspector_websocket.DispatchNotificationsUntilDoneTimeoutException \
as err:
raise TracingTimeoutException(
'Trace data was not fully received due to timeout after %s '
'seconds. If the trace data is big, you may want to increase the '
'time out amount.' % err.elapsed_time)
self._is_tracing_running = False
return self._tracing_data
def _ErrorHandler(self, elapsed):
logging.error('Unrecoverable error after %ds reading tracing response.',
elapsed)
raise
def _NotificationHandler(self, res):
if 'Tracing.dataCollected' == res.get('method'):
value = res.get('params', {}).get('value')
if type(value) in [str, unicode]:
self._tracing_data.append(value)
elif type(value) is list:
self._tracing_data.extend(value)
else:
logging.warning('Unexpected type in tracing data')
elif 'Tracing.tracingComplete' == res.get('method'):
return True
def Close(self):
self._inspector_websocket.Disconnect()
def _CheckNotificationSupported(self):
"""Ensures we're running against a compatible version of chrome."""
req = {'method': 'Tracing.hasCompleted'}
res = self._inspector_websocket.SyncRequest(req)
if res.get('response'):
raise TracingUnsupportedException(
'Tracing not supported for this browser')
elif 'error' in res:
return