blob: 9eb7030dea427c9086e3a3f68e56175ff9a3109f [file] [log] [blame]
# Copyright 2016 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.
"""Utilities for capturing traces for chromecast devices."""
import json
import logging
import math
import websocket
class TracingClient(object):
def BufferUsage(self, buffer_usage):
percent = int(math.floor(buffer_usage * 100))
logging.debug('Buffer Usage: %i', percent)
class TracingBackend(object):
"""Class for starting a tracing session with cast_shell."""
def __init__(self):
self._socket = None
self._next_request_id = 0
self._tracing_client = None
self._tracing_data = []
def Connect(self, device_ip, devtools_port=9222, timeout=10):
"""Connect to cast_shell on given device and port.
Args:
device_ip: IP of device to connect to.
devtools_port: Remote dev tool port to connect to. Defaults to 9222.
timeout: Amount of time to wait for connection in seconds. Default 10s.
"""
assert not self._socket
url = 'ws://%s:%i/devtools/browser' % (device_ip, devtools_port)
print('Connect to %s ...' % url)
self._socket = websocket.create_connection(url, timeout=timeout)
self._next_request_id = 0
def Disconnect(self):
"""If connected to device, disconnect from device."""
if self._socket:
self._socket.close()
self._socket = None
def StartTracing(self,
tracing_client=None,
custom_categories=None,
record_continuously=False,
buffer_usage_reporting_interval=0,
timeout=10):
"""Begin a tracing session on device.
Args:
tracing_client: client for this tracing session.
custom_categories: Categories to filter for. None records all categories.
record_continuously: Keep tracing until stopped. If false, will exit when
buffer is full.
buffer_usage_reporting_interval: How often to report buffer usage.
timeout: Time to wait to start tracing in seconds. Default 10s.
"""
self._tracing_client = tracing_client
self._socket.settimeout(timeout)
req = {
'method': 'Tracing.start',
'params': {
'categories': custom_categories,
'bufferUsageReportingInterval': buffer_usage_reporting_interval,
'options': 'record-continuously' if record_continuously else
'record-until-full'
}
}
self._SendRequest(req)
def StopTracing(self, timeout=30):
"""End a tracing session on device.
Args:
timeout: Time to wait to stop tracing in seconds. Default 30s.
Returns:
Trace file for the stopped session.
"""
self._socket.settimeout(timeout)
req = {'method': 'Tracing.end'}
self._SendRequest(req)
while self._socket:
res = self._ReceiveResponse()
if 'method' in res and self._HandleResponse(res):
self._tracing_client = None
result = self._tracing_data
self._tracing_data = []
return result
def _SendRequest(self, req):
"""Sends request to remote devtools.
Args:
req: Request to send.
"""
req['id'] = self._next_request_id
self._next_request_id += 1
data = json.dumps(req)
self._socket.send(data)
def _ReceiveResponse(self):
"""Get response from remote devtools.
Returns:
Response received.
"""
while self._socket:
data = self._socket.recv()
res = json.loads(data)
return res
def _HandleResponse(self, res):
"""Handle response from remote devtools.
Args:
res: Recieved tresponse that should be handled.
"""
method = res.get('method')
value = res.get('params', {}).get('value')
if 'Tracing.dataCollected' == method:
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.bufferUsage' == method and self._tracing_client:
self._tracing_client.BufferUsage(value)
elif 'Tracing.tracingComplete' == method:
return True