3 # Copyright 2015 The Chromium Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
7 # This script was originally written by Alok Priyadarshi (alokp@)
8 # with some minor local modifications.
20 class TracingClient(object):
21 def BufferUsage(self
, buffer_usage
):
22 percent
= int(math
.floor(buffer_usage
* 100))
23 logging
.debug('Buffer Usage: %i', percent
)
26 class TracingBackend(object):
27 def __init__(self
, devtools_port
):
29 self
._next
_request
_id
= 0
30 self
._tracing
_client
= None
31 self
._tracing
_data
= []
33 def Connect(self
, device_ip
, devtools_port
, timeout
=10):
34 assert not self
._socket
35 url
= 'ws://%s:%i/devtools/browser' % (device_ip
, devtools_port
)
36 print('Connect to %s ...' % url
)
37 self
._socket
= websocket
.create_connection(url
, timeout
=timeout
)
38 self
._next
_request
_id
= 0
45 def StartTracing(self
,
47 custom_categories
=None,
48 record_continuously
=False,
49 buffer_usage_reporting_interval
=0,
51 self
._tracing
_client
= tracing_client
52 self
._socket
.settimeout(timeout
)
54 'method': 'Tracing.start',
56 'categories': custom_categories
,
57 'bufferUsageReportingInterval': buffer_usage_reporting_interval
,
58 'options': 'record-continuously' if record_continuously
else
62 self
._SendRequest
(req
)
64 def StopTracing(self
, timeout
=30):
65 self
._socket
.settimeout(timeout
)
66 req
= {'method': 'Tracing.end'}
67 self
._SendRequest
(req
)
69 res
= self
._ReceiveResponse
()
70 if 'method' in res
and self
._HandleResponse
(res
):
71 self
._tracing
_client
= None
72 result
= self
._tracing
_data
73 self
._tracing
_data
= []
76 def _SendRequest(self
, req
):
77 req
['id'] = self
._next
_request
_id
78 self
._next
_request
_id
+= 1
79 data
= json
.dumps(req
)
80 self
._socket
.send(data
)
82 def _ReceiveResponse(self
):
84 data
= self
._socket
.recv()
85 res
= json
.loads(data
)
88 def _HandleResponse(self
, res
):
89 method
= res
.get('method')
90 value
= res
.get('params', {}).get('value')
91 if 'Tracing.dataCollected' == method
:
92 if type(value
) in [str, unicode]:
93 self
._tracing
_data
.append(value
)
94 elif type(value
) is list:
95 self
._tracing
_data
.extend(value
)
97 logging
.warning('Unexpected type in tracing data')
98 elif 'Tracing.bufferUsage' == method
and self
._tracing
_client
:
99 self
._tracing
_client
.BufferUsage(value
)
100 elif 'Tracing.tracingComplete' == method
:
104 @contextlib.contextmanager
105 def Connect(device_ip
, devtools_port
):
106 backend
= TracingBackend(devtools_port
)
108 backend
.Connect(device_ip
, devtools_port
)
114 def DumpTrace(trace
, options
):
115 filepath
= os
.path
.expanduser(options
.output
) if options
.output \
116 else os
.path
.join(os
.getcwd(), 'trace.json')
118 dirname
= os
.path
.dirname(filepath
)
120 if not os
.path
.exists(dirname
):
123 filepath
= os
.path
.join(os
.getcwd(), filepath
)
125 with
open(filepath
, "w") as f
:
130 def _CreateOptionParser():
131 parser
= optparse
.OptionParser(description
='Record about://tracing profiles '
132 'from any running instance of Chrome.')
134 '-v', '--verbose', help='Verbose logging.', action
='store_true')
136 '-p', '--port', help='Remote debugging port.', type="int", default
=9222)
138 '-d', '--device', help='Device ip address.', type='string',
141 tracing_opts
= optparse
.OptionGroup(parser
, 'Tracing options')
142 tracing_opts
.add_option(
143 '-c', '--category-filter',
144 help='Apply filter to control what category groups should be traced.',
146 tracing_opts
.add_option(
147 '--record-continuously',
148 help='Keep recording until stopped. The trace buffer is of fixed size '
149 'and used as a ring buffer. If this option is omitted then '
150 'recording stops when the trace buffer is full.',
152 parser
.add_option_group(tracing_opts
)
154 output_options
= optparse
.OptionGroup(parser
, 'Output options')
155 output_options
.add_option(
157 help='Save trace output to file.')
158 parser
.add_option_group(output_options
)
163 def _ProcessOptions(options
):
164 websocket
.enableTrace(options
.verbose
)
168 parser
= _CreateOptionParser()
169 options
, _args
= parser
.parse_args()
170 _ProcessOptions(options
)
172 with
Connect(options
.device
, options
.port
) as tracing_backend
:
173 tracing_backend
.StartTracing(TracingClient(),
174 options
.category_filter
,
175 options
.record_continuously
)
176 raw_input('Capturing trace. Press Enter to stop...')
177 trace
= tracing_backend
.StopTracing()
179 filepath
= DumpTrace(trace
, options
)
181 print('Trace written to file://%s' % filepath
)
184 if __name__
== '__main__':