Index: tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_timeline.py |
diff --git a/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_timeline.py b/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_timeline.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9289f26674ce9c0d33be1ecba1c6d4570a0b14e3 |
--- /dev/null |
+++ b/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_timeline.py |
@@ -0,0 +1,109 @@ |
+# Copyright 2013 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. |
+ |
+from telemetry.core.backends.chrome_inspector import timeline_recorder |
+ |
+ |
+class TabBackendException(Exception): |
+ """An exception which indicates an error response from devtools inspector.""" |
+ pass |
+ |
+ |
+class InspectorTimeline(timeline_recorder.TimelineRecorder): |
+ """Implementation of dev tools timeline.""" |
+ |
+ class Recorder(object): |
+ """Utility class to Start and Stop recording timeline. |
+ |
+ Example usage: |
+ |
+ with inspector_timeline.InspectorTimeline.Recorder(tab): |
+ # Something to run while the timeline is recording. |
+ |
+ This is an alternative to directly calling the Start and Stop methods below. |
+ """ |
+ def __init__(self, tab): |
+ self._tab = tab |
+ |
+ def __enter__(self): |
+ self._tab.StartTimelineRecording() |
+ |
+ def __exit__(self, *args): |
+ self._tab.StopTimelineRecording() |
+ |
+ def __init__(self, inspector_websocket): |
+ super(InspectorTimeline, self).__init__() |
+ self._inspector_websocket = inspector_websocket |
+ self._is_recording = False |
+ self._raw_events = None |
+ |
+ @property |
+ def is_timeline_recording_running(self): |
+ return self._is_recording |
+ |
+ def Start(self): |
+ """Starts recording.""" |
+ assert not self._is_recording, 'Start should only be called once.' |
+ self._raw_events = None |
+ self._is_recording = True |
+ self._inspector_websocket.RegisterDomain( |
+ 'Timeline', self._OnNotification, self._OnClose) |
+ # The 'bufferEvents' parameter below means that events should not be sent |
+ # individually as messages, but instead all at once when a Timeline.stop |
+ # request is sent. |
+ request = { |
+ 'method': 'Timeline.start', |
+ 'params': {'bufferEvents': True}, |
+ } |
+ self._SendSyncRequest(request) |
+ |
+ def Stop(self): |
+ """Stops recording and returns timeline event data.""" |
+ if not self._is_recording: |
+ return None |
+ request = {'method': 'Timeline.stop'} |
+ result = self._SendSyncRequest(request) |
+ self._inspector_websocket.UnregisterDomain('Timeline') |
+ self._is_recording = False |
+ |
+ # TODO: Backward compatibility. Needs to be removed when |
+ # M38 becomes stable. |
+ if 'events' in result: |
+ raw_events = result['events'] |
+ else: # In M38 events will arrive via Timeline.stopped event. |
+ raw_events = self._raw_events |
+ self._raw_events = None |
+ return raw_events |
+ |
+ def _SendSyncRequest(self, request, timeout=60): |
+ """Sends a devtools remote debugging protocol request. |
+ |
+ The types of request that are valid is determined by protocol.json: |
+ https://src.chromium.org/viewvc/blink/trunk/Source/devtools/protocol.json |
+ |
+ Args: |
+ request: Request dict, may contain the keys 'method' and 'params'. |
+ timeout: Number of seconds to wait for a response. |
+ |
+ Returns: |
+ The result given in the response message. |
+ |
+ Raises: |
+ TabBackendException: The response indicates an error occurred. |
+ """ |
+ response = self._inspector_websocket.SyncRequest(request, timeout) |
+ if 'error' in response: |
+ raise TabBackendException(response['error']['message']) |
+ return response['result'] |
+ |
+ def _OnNotification(self, msg): |
+ """Handler called when a message is received.""" |
+ # Since 'Timeline.start' was invoked with the 'bufferEvents' parameter, |
+ # the events will arrive in Timeline.stopped event. |
+ if msg['method'] == 'Timeline.stopped' and 'events' in msg['params']: |
+ self._raw_events = msg['params']['events'] |
+ |
+ def _OnClose(self): |
+ """Handler called when a domain is unregistered.""" |
+ pass |