| # 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 |