Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / tools / telemetry / telemetry / core / backends / chrome / tracing_backend.py
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
3 # found in the LICENSE file.
4
5 import logging
6
7 from telemetry.core.backends.chrome import inspector_websocket
8 from telemetry.core.platform import tracing_category_filter
9 from telemetry.core.platform import tracing_options
10
11
12 class TracingUnsupportedException(Exception):
13   pass
14
15
16 class TracingTimeoutException(Exception):
17   pass
18
19
20 class TracingBackend(object):
21   def __init__(self, devtools_port, chrome_browser_backend):
22     self._inspector_websocket = inspector_websocket.InspectorWebsocket(
23         self._NotificationHandler,
24         self._ErrorHandler)
25
26     self._inspector_websocket.Connect(
27         'ws://127.0.0.1:%i/devtools/browser' % devtools_port)
28     self._category_filter = None
29     self._nesting = 0
30     self._tracing_data = []
31     self._is_tracing_running = False
32     self._chrome_browser_backend = chrome_browser_backend
33
34   @property
35   def is_tracing_running(self):
36     return self._is_tracing_running
37
38   def StartTracing(self, trace_options, custom_categories=None, timeout=10):
39     """ Starts tracing on the first nested call and returns True. Returns False
40         and does nothing on subsequent nested calls.
41     """
42     self._nesting += 1
43     if self.is_tracing_running:
44       new_category_filter = tracing_category_filter.TracingCategoryFilter(
45           filter_string=custom_categories)
46       is_subset = new_category_filter.IsSubset(self._category_filter)
47       assert(is_subset != False)
48       if is_subset == None:
49         logging.warning('Cannot determine if category filter of nested ' +
50                         'StartTracing call is subset of current filter.')
51       return False
52     self._CheckNotificationSupported()
53     #TODO(nednguyen): remove this when the stable branch pass 2118.
54     if (trace_options.record_mode == tracing_options.RECORD_AS_MUCH_AS_POSSIBLE
55         and self._chrome_browser_backend.chrome_branch_number
56         and self._chrome_browser_backend.chrome_branch_number < 2118):
57       logging.warning(
58           'Cannot use %s tracing mode on chrome browser with branch version %i,'
59           ' (<2118) fallback to use %s tracing mode' % (
60               trace_options.record_mode,
61               self._chrome_browser_backend.chrome_branch_number,
62               tracing_options.RECORD_UNTIL_FULL))
63       trace_options.record_mode = tracing_options.RECORD_UNTIL_FULL
64     req = {'method': 'Tracing.start'}
65     req['params'] = {}
66     m = {tracing_options.RECORD_UNTIL_FULL: 'record-until-full',
67          tracing_options.RECORD_AS_MUCH_AS_POSSIBLE:
68          'record-as-much-as-possible'}
69     req['params']['options'] = m[trace_options.record_mode]
70     self._category_filter = tracing_category_filter.TracingCategoryFilter(
71         filter_string=custom_categories)
72     if custom_categories:
73       req['params']['categories'] = custom_categories
74     self._inspector_websocket.SyncRequest(req, timeout)
75     self._is_tracing_running = True
76     return True
77
78   def StopTracing(self, timeout=30):
79     """ Stops tracing on the innermost (!) nested call, because we cannot get
80         results otherwise. Resets _tracing_data on the outermost nested call.
81         Returns the result of the trace, as TracingTimelineData object.
82     """
83     self._nesting -= 1
84     assert self._nesting >= 0
85     if self.is_tracing_running:
86       req = {'method': 'Tracing.end'}
87       self._inspector_websocket.SendAndIgnoreResponse(req)
88       # After Tracing.end, chrome browser will send asynchronous notifications
89       # containing trace data. This is until Tracing.tracingComplete is sent,
90       # which means there is no trace buffers pending flush.
91       try:
92         self._inspector_websocket.DispatchNotificationsUntilDone(timeout)
93       except \
94           inspector_websocket.DispatchNotificationsUntilDoneTimeoutException \
95           as err:
96         raise TracingTimeoutException(
97             'Trace data was not fully received due to timeout after %s '
98             'seconds. If the trace data is big, you may want to increase the '
99             'time out amount.' % err.elapsed_time)
100       self._is_tracing_running = False
101     if self._nesting == 0:
102       self._category_filter = None
103       return self._GetTraceResultAndReset()
104     else:
105       return self._GetTraceResult()
106
107   def _GetTraceResult(self):
108     assert not self.is_tracing_running
109     return self._tracing_data
110
111   def _GetTraceResultAndReset(self):
112     result = self._GetTraceResult()
113
114     self._tracing_data = []
115     return result
116
117   def _ErrorHandler(self, elapsed):
118     logging.error('Unrecoverable error after %ds reading tracing response.',
119                   elapsed)
120     raise
121
122   def _NotificationHandler(self, res):
123     if 'Tracing.dataCollected' == res.get('method'):
124       value = res.get('params', {}).get('value')
125       if type(value) in [str, unicode]:
126         self._tracing_data.append(value)
127       elif type(value) is list:
128         self._tracing_data.extend(value)
129       else:
130         logging.warning('Unexpected type in tracing data')
131     elif 'Tracing.tracingComplete' == res.get('method'):
132       return True
133
134   def Close(self):
135     self._inspector_websocket.Disconnect()
136
137   def _CheckNotificationSupported(self):
138     """Ensures we're running against a compatible version of chrome."""
139     req = {'method': 'Tracing.hasCompleted'}
140     res = self._inspector_websocket.SyncRequest(req)
141     if res.get('response'):
142       raise TracingUnsupportedException(
143           'Tracing not supported for this browser')
144     elif 'error' in res:
145       return