OLD | NEW |
| (Empty) |
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 import cStringIO | |
6 import json | |
7 import logging | |
8 import socket | |
9 import threading | |
10 | |
11 | |
12 from telemetry import trace_event_importer | |
13 from telemetry import trace_result | |
14 from telemetry import util | |
15 from telemetry import websocket | |
16 | |
17 | |
18 class TracingUnsupportedException(Exception): | |
19 pass | |
20 | |
21 class TraceResultImpl(object): | |
22 def __init__(self, tracing_data): | |
23 self._tracing_data = tracing_data | |
24 | |
25 def Serialize(self, f): | |
26 f.write('{"traceEvents": [') | |
27 d = self._tracing_data | |
28 # Note: we're not using ','.join here because the strings that are in the | |
29 # tracing data are typically many megabytes in size. In the fast case, f is | |
30 # just a file, so by skipping the in memory step we keep our memory | |
31 # footprint low and avoid additional processing. | |
32 if len(d) == 0: | |
33 pass | |
34 elif len(d) == 1: | |
35 f.write(d[0]) | |
36 else: | |
37 f.write(d[0]) | |
38 for i in range(1, len(d)): | |
39 f.write(',') | |
40 f.write(d[i]) | |
41 f.write(']}') | |
42 | |
43 def AsTimelineModel(self): | |
44 f = cStringIO.StringIO() | |
45 self.Serialize(f) | |
46 return trace_event_importer.Import( | |
47 f.getvalue()) | |
48 | |
49 class TracingBackend(object): | |
50 def __init__(self, devtools_port): | |
51 debugger_url = 'ws://localhost:%i/devtools/browser' % devtools_port | |
52 self._socket = websocket.create_connection(debugger_url) | |
53 self._next_request_id = 0 | |
54 self._cur_socket_timeout = 0 | |
55 self._thread = None | |
56 self._tracing_data = [] | |
57 | |
58 def BeginTracing(self): | |
59 self._CheckNotificationSupported() | |
60 req = {'method': 'Tracing.start'} | |
61 self._SyncRequest(req) | |
62 # Tracing.start will send asynchronous notifications containing trace | |
63 # data, until Tracing.end is called. | |
64 self._thread = threading.Thread(target=self._TracingReader) | |
65 self._thread.start() | |
66 | |
67 def EndTracing(self): | |
68 req = {'method': 'Tracing.end'} | |
69 self._SyncRequest(req) | |
70 self._thread.join() | |
71 self._thread = None | |
72 | |
73 def GetTraceResultAndReset(self): | |
74 assert not self._thread | |
75 ret = trace_result.TraceResult( | |
76 TraceResultImpl(self._tracing_data)) | |
77 self._tracing_data = [] | |
78 return ret | |
79 | |
80 def Close(self): | |
81 if self._socket: | |
82 self._socket.close() | |
83 self._socket = None | |
84 | |
85 def _TracingReader(self): | |
86 while self._socket: | |
87 try: | |
88 data = self._socket.recv() | |
89 if not data: | |
90 break | |
91 res = json.loads(data) | |
92 logging.debug('got [%s]', data) | |
93 if 'Tracing.dataCollected' == res.get('method'): | |
94 value = res.get('params', {}).get('value') | |
95 self._tracing_data.append(value) | |
96 elif 'Tracing.tracingComplete' == res.get('method'): | |
97 break | |
98 except (socket.error, websocket.WebSocketException): | |
99 logging.warning('Timeout waiting for tracing response, unusual.') | |
100 | |
101 def _SyncRequest(self, req, timeout=10): | |
102 self._SetTimeout(timeout) | |
103 req['id'] = self._next_request_id | |
104 self._next_request_id += 1 | |
105 data = json.dumps(req) | |
106 logging.debug('will send [%s]', data) | |
107 self._socket.send(data) | |
108 | |
109 def _SetTimeout(self, timeout): | |
110 if self._cur_socket_timeout != timeout: | |
111 self._socket.settimeout(timeout) | |
112 self._cur_socket_timeout = timeout | |
113 | |
114 def _CheckNotificationSupported(self): | |
115 """Ensures we're running against a compatible version of chrome.""" | |
116 req = {'method': 'Tracing.hasCompleted'} | |
117 self._SyncRequest(req) | |
118 while True: | |
119 try: | |
120 data = self._socket.recv() | |
121 except (socket.error, websocket.WebSocketException): | |
122 raise util.TimeoutException( | |
123 'Timed out waiting for reply. This is unusual.') | |
124 logging.debug('got [%s]', data) | |
125 res = json.loads(data) | |
126 if res['id'] != req['id']: | |
127 logging.debug('Dropped reply: %s', json.dumps(res)) | |
128 continue | |
129 if res.get('response'): | |
130 raise TracingUnsupportedException( | |
131 'Tracing not supported for this browser') | |
132 elif 'error' in res: | |
133 return | |
OLD | NEW |