| Index: chromecast/tools/trace.py
|
| diff --git a/chromecast/tools/trace.py b/chromecast/tools/trace.py
|
| new file mode 100755
|
| index 0000000000000000000000000000000000000000..bf93611e0bae34344435424b64f8d85bd4165305
|
| --- /dev/null
|
| +++ b/chromecast/tools/trace.py
|
| @@ -0,0 +1,185 @@
|
| +#!/usr/bin/env python
|
| +#
|
| +# 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.
|
| +#
|
| +# This script was originally written by Alok Priyadarshi (alokp@)
|
| +# with some minor local modifications.
|
| +
|
| +import contextlib
|
| +import json
|
| +import logging
|
| +import math
|
| +import optparse
|
| +import os
|
| +import sys
|
| +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):
|
| + def __init__(self, devtools_port):
|
| + self._socket = None
|
| + self._next_request_id = 0
|
| + self._tracing_client = None
|
| + self._tracing_data = []
|
| +
|
| + def Connect(self, device_ip, devtools_port, timeout=10):
|
| + 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 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):
|
| + 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):
|
| + 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):
|
| + req['id'] = self._next_request_id
|
| + self._next_request_id += 1
|
| + data = json.dumps(req)
|
| + self._socket.send(data)
|
| +
|
| + def _ReceiveResponse(self):
|
| + while self._socket:
|
| + data = self._socket.recv()
|
| + res = json.loads(data)
|
| + return res
|
| +
|
| + def _HandleResponse(self, res):
|
| + 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
|
| +
|
| +
|
| +@contextlib.contextmanager
|
| +def Connect(device_ip, devtools_port):
|
| + backend = TracingBackend(devtools_port)
|
| + try:
|
| + backend.Connect(device_ip, devtools_port)
|
| + yield backend
|
| + finally:
|
| + backend.Disconnect()
|
| +
|
| +
|
| +def DumpTrace(trace, options):
|
| + filepath = os.path.expanduser(options.output) if options.output \
|
| + else os.path.join(os.getcwd(), 'trace.json')
|
| +
|
| + dirname = os.path.dirname(filepath)
|
| + if dirname:
|
| + if not os.path.exists(dirname):
|
| + os.makedirs(dirname)
|
| + else:
|
| + filepath = os.path.join(os.getcwd(), filepath)
|
| +
|
| + with open(filepath, "w") as f:
|
| + json.dump(trace, f)
|
| + return filepath
|
| +
|
| +
|
| +def _CreateOptionParser():
|
| + parser = optparse.OptionParser(description='Record about://tracing profiles '
|
| + 'from any running instance of Chrome.')
|
| + parser.add_option(
|
| + '-v', '--verbose', help='Verbose logging.', action='store_true')
|
| + parser.add_option(
|
| + '-p', '--port', help='Remote debugging port.', type="int", default=9222)
|
| + parser.add_option(
|
| + '-d', '--device', help='Device ip address.', type='string',
|
| + default='127.0.0.1')
|
| +
|
| + tracing_opts = optparse.OptionGroup(parser, 'Tracing options')
|
| + tracing_opts.add_option(
|
| + '-c', '--category-filter',
|
| + help='Apply filter to control what category groups should be traced.',
|
| + type='string')
|
| + tracing_opts.add_option(
|
| + '--record-continuously',
|
| + help='Keep recording until stopped. The trace buffer is of fixed size '
|
| + 'and used as a ring buffer. If this option is omitted then '
|
| + 'recording stops when the trace buffer is full.',
|
| + action='store_true')
|
| + parser.add_option_group(tracing_opts)
|
| +
|
| + output_options = optparse.OptionGroup(parser, 'Output options')
|
| + output_options.add_option(
|
| + '-o', '--output',
|
| + help='Save trace output to file.')
|
| + parser.add_option_group(output_options)
|
| +
|
| + return parser
|
| +
|
| +
|
| +def _ProcessOptions(options):
|
| + websocket.enableTrace(options.verbose)
|
| +
|
| +
|
| +def main():
|
| + parser = _CreateOptionParser()
|
| + options, _args = parser.parse_args()
|
| + _ProcessOptions(options)
|
| +
|
| + with Connect(options.device, options.port) as tracing_backend:
|
| + tracing_backend.StartTracing(TracingClient(),
|
| + options.category_filter,
|
| + options.record_continuously)
|
| + raw_input('Capturing trace. Press Enter to stop...')
|
| + trace = tracing_backend.StopTracing()
|
| +
|
| + filepath = DumpTrace(trace, options)
|
| + print('Done')
|
| + print('Trace written to file://%s' % filepath)
|
| +
|
| +
|
| +if __name__ == '__main__':
|
| + sys.exit(main())
|
|
|