Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2013 The Chromium Authors. All rights reserved. | 1 # Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import json | 5 import json |
| 6 import logging | 6 import logging |
| 7 import socket | 7 import socket |
| 8 import time | 8 import time |
| 9 import traceback | 9 import traceback |
| 10 | 10 |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 84 | 84 |
| 85 class TracingBackend(object): | 85 class TracingBackend(object): |
| 86 | 86 |
| 87 _TRACING_DOMAIN = 'Tracing' | 87 _TRACING_DOMAIN = 'Tracing' |
| 88 | 88 |
| 89 def __init__(self, inspector_socket, is_tracing_running=False, | 89 def __init__(self, inspector_socket, is_tracing_running=False, |
| 90 support_modern_devtools_tracing_start_api=False): | 90 support_modern_devtools_tracing_start_api=False): |
| 91 self._inspector_websocket = inspector_socket | 91 self._inspector_websocket = inspector_socket |
| 92 self._inspector_websocket.RegisterDomain( | 92 self._inspector_websocket.RegisterDomain( |
| 93 self._TRACING_DOMAIN, self._NotificationHandler) | 93 self._TRACING_DOMAIN, self._NotificationHandler) |
| 94 self._trace_events = [] | |
| 95 self._is_tracing_running = is_tracing_running | 94 self._is_tracing_running = is_tracing_running |
| 96 self._can_collect_data = False | 95 self._can_collect_data = False |
| 97 self._has_received_all_tracing_data = False | 96 self._has_received_all_tracing_data = False |
| 97 self._had_data_collected = False | |
| 98 self._support_modern_devtools_tracing_start_api = ( | 98 self._support_modern_devtools_tracing_start_api = ( |
| 99 support_modern_devtools_tracing_start_api) | 99 support_modern_devtools_tracing_start_api) |
| 100 self._trace_data_builder = None | |
| 100 | 101 |
| 101 @property | 102 @property |
| 102 def is_tracing_running(self): | 103 def is_tracing_running(self): |
| 103 return self._is_tracing_running | 104 return self._is_tracing_running |
| 104 | 105 |
| 105 def StartTracing(self, chrome_trace_config, timeout=10): | 106 def StartTracing(self, chrome_trace_config, timeout=10): |
| 106 """When first called, starts tracing, and returns True. | 107 """When first called, starts tracing, and returns True. |
| 107 | 108 |
| 108 If called during tracing, tracing is unchanged, and it returns False. | 109 If called during tracing, tracing is unchanged, and it returns False. |
| 109 """ | 110 """ |
| 110 if self.is_tracing_running: | 111 if self.is_tracing_running: |
| 111 return False | 112 return False |
| 112 assert not self._can_collect_data, 'Data not collected from last trace.' | 113 assert not self._can_collect_data, 'Data not collected from last trace.' |
| 113 # Reset collected tracing data from previous tracing calls. | 114 # Reset collected tracing data from previous tracing calls. |
| 114 self._trace_events = [] | 115 self._had_data_collected = False |
| 115 | 116 |
| 116 if not self.IsTracingSupported(): | 117 if not self.IsTracingSupported(): |
| 117 raise TracingUnsupportedException( | 118 raise TracingUnsupportedException( |
| 118 'Chrome tracing not supported for this app.') | 119 'Chrome tracing not supported for this app.') |
| 119 | 120 |
| 120 params = {'transferMode': 'ReturnAsStream'} | 121 params = {'transferMode': 'ReturnAsStream'} |
| 121 if self._support_modern_devtools_tracing_start_api: | 122 if self._support_modern_devtools_tracing_start_api: |
| 122 params['traceConfig'] = ( | 123 params['traceConfig'] = ( |
| 123 chrome_trace_config.GetChromeTraceConfigForDevTools()) | 124 chrome_trace_config.GetChromeTraceConfigForDevTools()) |
| 124 else: | 125 else: |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 153 if 'error' in rc: | 154 if 'error' in rc: |
| 154 raise ClockSyncResponseException(rc['error']['message']) | 155 raise ClockSyncResponseException(rc['error']['message']) |
| 155 | 156 |
| 156 def StopTracing(self): | 157 def StopTracing(self): |
| 157 """Stops tracing and pushes results to the supplied TraceDataBuilder. | 158 """Stops tracing and pushes results to the supplied TraceDataBuilder. |
| 158 | 159 |
| 159 If this is called after tracing has been stopped, trace data from the last | 160 If this is called after tracing has been stopped, trace data from the last |
| 160 tracing run is pushed. | 161 tracing run is pushed. |
| 161 """ | 162 """ |
| 162 if not self.is_tracing_running: | 163 if not self.is_tracing_running: |
| 163 if not self._trace_events: | 164 raise TracingHasNotRunException() |
| 164 raise TracingHasNotRunException() | |
| 165 else: | |
| 166 req = {'method': 'Tracing.end'} | |
| 167 self._inspector_websocket.SendAndIgnoreResponse(req) | |
| 168 | 165 |
| 166 req = {'method': 'Tracing.end'} | |
| 167 self._inspector_websocket.SendAndIgnoreResponse(req) | |
| 169 self._is_tracing_running = False | 168 self._is_tracing_running = False |
| 170 self._can_collect_data = True | 169 self._can_collect_data = True |
| 171 | 170 |
| 172 def DumpMemory(self, timeout=30): | 171 def DumpMemory(self, timeout=30): |
| 173 """Dumps memory. | 172 """Dumps memory. |
| 174 | 173 |
| 175 Returns: | 174 Returns: |
| 176 GUID of the generated dump if successful, None otherwise. | 175 GUID of the generated dump if successful, None otherwise. |
| 177 | 176 |
| 178 Raises: | 177 Raises: |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 205 raise TracingUnexpectedResponseException( | 204 raise TracingUnexpectedResponseException( |
| 206 'Inspector returned unexpected response for ' | 205 'Inspector returned unexpected response for ' |
| 207 'Tracing.requestMemoryDump:\n' + json.dumps(response, indent=2)) | 206 'Tracing.requestMemoryDump:\n' + json.dumps(response, indent=2)) |
| 208 | 207 |
| 209 result = response['result'] | 208 result = response['result'] |
| 210 return result['dumpGuid'] if result['success'] else None | 209 return result['dumpGuid'] if result['success'] else None |
| 211 | 210 |
| 212 def CollectTraceData(self, trace_data_builder, timeout=30): | 211 def CollectTraceData(self, trace_data_builder, timeout=30): |
| 213 if not self._can_collect_data: | 212 if not self._can_collect_data: |
| 214 raise Exception('Cannot collect before tracing is finished.') | 213 raise Exception('Cannot collect before tracing is finished.') |
| 215 self._CollectTracingData(timeout) | 214 self._CollectTracingData(trace_data_builder, timeout) |
| 216 self._can_collect_data = False | 215 self._can_collect_data = False |
| 217 trace_data_builder.AddEventsTo( | |
| 218 trace_data_module.CHROME_TRACE_PART, self._trace_events) | |
| 219 | 216 |
| 220 def _CollectTracingData(self, timeout): | 217 def _CollectTracingData(self, trace_data_builder, timeout): |
| 221 """Collects tracing data. Assumes that Tracing.end has already been sent. | 218 """Collects tracing data. Assumes that Tracing.end has already been sent. |
| 222 | 219 |
| 223 Args: | 220 Args: |
| 221 trace_data_builder: An instance of TraceDataBuilder to put results into. | |
| 224 timeout: The timeout in seconds. | 222 timeout: The timeout in seconds. |
| 225 | 223 |
| 226 Raises: | 224 Raises: |
| 227 TracingTimeoutException: If more than |timeout| seconds has passed | 225 TracingTimeoutException: If more than |timeout| seconds has passed |
| 228 since the last time any data is received. | 226 since the last time any data is received. |
| 229 TracingUnrecoverableException: If there is a websocket error. | 227 TracingUnrecoverableException: If there is a websocket error. |
| 230 """ | 228 """ |
| 231 self._has_received_all_tracing_data = False | 229 self._has_received_all_tracing_data = False |
| 232 start_time = time.time() | 230 start_time = time.time() |
| 233 while True: | 231 self._trace_data_builder = trace_data_builder |
| 234 try: | 232 try: |
| 235 self._inspector_websocket.DispatchNotifications(timeout) | 233 while True: |
| 236 start_time = time.time() | 234 try: |
| 237 except websocket.WebSocketTimeoutException: | 235 self._inspector_websocket.DispatchNotifications(timeout) |
| 238 pass | 236 start_time = time.time() |
| 239 except (socket.error, websocket.WebSocketException): | 237 except websocket.WebSocketTimeoutException: |
| 240 raise TracingUnrecoverableException( | 238 pass |
| 241 'Exception raised while collecting tracing data:\n' + | 239 except (socket.error, websocket.WebSocketException): |
| 242 traceback.format_exc()) | 240 raise TracingUnrecoverableException( |
| 241 'Exception raised while collecting tracing data:\n' + | |
| 242 traceback.format_exc()) | |
| 243 | 243 |
| 244 if self._has_received_all_tracing_data: | 244 if self._has_received_all_tracing_data: |
| 245 break | 245 break |
| 246 | 246 |
| 247 elapsed_time = time.time() - start_time | 247 elapsed_time = time.time() - start_time |
| 248 if elapsed_time > timeout: | 248 if elapsed_time > timeout: |
| 249 raise TracingTimeoutException( | 249 raise TracingTimeoutException( |
| 250 'Only received partial trace data due to timeout after %s seconds. ' | 250 'Only received partial trace data due to timeout after %s ' |
| 251 'If the trace data is big, you may want to increase the timeout ' | 251 'seconds. If the trace data is big, you may want to increase ' |
| 252 'amount.' % elapsed_time) | 252 'the timeout amount.' % elapsed_time) |
| 253 finally: | |
| 254 self._trace_data_builder = None | |
| 253 | 255 |
| 254 def _NotificationHandler(self, res): | 256 def _NotificationHandler(self, res): |
| 255 if 'Tracing.dataCollected' == res.get('method'): | 257 if 'Tracing.dataCollected' == res.get('method'): |
| 256 value = res.get('params', {}).get('value') | 258 value = res.get('params', {}).get('value') |
| 257 self._trace_events.extend(value) | 259 self._trace_data_builder.AddEventsTo( |
| 260 trace_data_module.CHROME_TRACE_PART, value) | |
| 261 self._had_data_collected = True | |
| 258 elif 'Tracing.tracingComplete' == res.get('method'): | 262 elif 'Tracing.tracingComplete' == res.get('method'): |
| 259 stream_handle = res.get('params', {}).get('stream') | 263 stream_handle = res.get('params', {}).get('stream') |
| 260 if not stream_handle: | 264 if not stream_handle: |
| 261 self._has_received_all_tracing_data = True | 265 self._has_received_all_tracing_data = True |
| 262 return | 266 return |
| 263 | 267 if self._had_data_collected: |
|
Zhen Wang
2016/07/29 14:45:42
So this will never happen? If so, I would suggest
| |
| 264 if self._trace_events: | |
| 265 raise TracingUnexpectedResponseException( | 268 raise TracingUnexpectedResponseException( |
| 266 'Got both dataCollected events and a stream from server') | 269 'Got both dataCollected events and a stream from server') |
| 267 reader = _DevToolsStreamReader(self._inspector_websocket, stream_handle) | 270 reader = _DevToolsStreamReader(self._inspector_websocket, stream_handle) |
| 268 reader.Read(self._ReceivedAllTraceDataFromStream) | 271 reader.Read(self._ReceivedAllTraceDataFromStream) |
| 269 | 272 |
| 270 def _ReceivedAllTraceDataFromStream(self, data): | 273 def _ReceivedAllTraceDataFromStream(self, data): |
| 271 self._trace_events = json.loads(data) | 274 trace = json.loads(data) |
| 275 if type(trace) == dict: | |
| 276 for part in trace_data_module.ALL_TRACE_PARTS: | |
| 277 field_name = part.raw_field_name | |
| 278 if field_name in trace: | |
| 279 self._trace_data_builder.AddEventsTo(part, trace[field_name]) | |
| 280 | |
| 281 if 'metadata' in trace: | |
| 282 self._trace_data_builder.SetMetadataFor( | |
| 283 trace_data_module.CHROME_TRACE_PART, trace['metadata']) | |
| 284 | |
| 285 elif type(trace) == list: | |
| 286 self._trace_data_builder.AddEventsTo( | |
| 287 trace_data_module.CHROME_TRACE_PART, trace) | |
| 288 else: | |
| 289 raise TracingUnexpectedResponseException('Unexpected trace type') | |
| 272 self._has_received_all_tracing_data = True | 290 self._has_received_all_tracing_data = True |
| 273 | 291 |
| 274 def Close(self): | 292 def Close(self): |
| 275 self._inspector_websocket.UnregisterDomain(self._TRACING_DOMAIN) | 293 self._inspector_websocket.UnregisterDomain(self._TRACING_DOMAIN) |
| 276 self._inspector_websocket = None | 294 self._inspector_websocket = None |
| 277 | 295 |
| 278 @decorators.Cache | 296 @decorators.Cache |
| 279 def IsTracingSupported(self): | 297 def IsTracingSupported(self): |
| 280 req = {'method': 'Tracing.hasCompleted'} | 298 req = {'method': 'Tracing.hasCompleted'} |
| 281 res = self._inspector_websocket.SyncRequest(req, timeout=10) | 299 res = self._inspector_websocket.SyncRequest(req, timeout=10) |
| 282 return not res.get('response') | 300 return not res.get('response') |
| OLD | NEW |