| Index: tools/telemetry/telemetry/core/backends/chrome/tracing_backend.py
|
| diff --git a/tools/telemetry/telemetry/core/backends/chrome/tracing_backend.py b/tools/telemetry/telemetry/core/backends/chrome/tracing_backend.py
|
| index 4d0c68a8a3edb617314407f60d0a8624407e4a14..f0802802dadb102c44c40ec0f650eb07b01106a7 100644
|
| --- a/tools/telemetry/telemetry/core/backends/chrome/tracing_backend.py
|
| +++ b/tools/telemetry/telemetry/core/backends/chrome/tracing_backend.py
|
| @@ -8,7 +8,6 @@ import logging
|
| import socket
|
| import threading
|
|
|
| -
|
| from telemetry.core.backends.chrome import trace_result
|
| from telemetry.core.backends.chrome import websocket
|
| from telemetry.core.backends.chrome import websocket_browser_connection
|
| @@ -65,16 +64,97 @@ class RawTraceResultImpl(object):
|
| def AsTimelineModel(self):
|
| return model.TimelineModel(self._tracing_data)
|
|
|
| +class CategoryFilter(object):
|
| + def __init__(self, filter_string):
|
| + self.excluded = set()
|
| + self.included = set()
|
| + self.disabled = set()
|
| + self.contains_wildcards = False
|
| +
|
| + if not filter_string:
|
| + return
|
| +
|
| + if '*' in filter_string or '?' in filter_string:
|
| + self.contains_wildcards = True
|
| +
|
| + filter_set = set(filter_string.split(','))
|
| + for category in filter_set:
|
| + if category == '':
|
| + continue
|
| + if category[0] == '-':
|
| + category = category[1:]
|
| + self.excluded.add(category)
|
| + elif category.startswith('disabled-by-default-'):
|
| + self.disabled.add(category)
|
| + else:
|
| + self.included.add(category)
|
| +
|
| + def IsSubset(self, other):
|
| + """ Determine if filter A (self) is a subset of filter B (other).
|
| + Returns True if A is a subset of B, False if A is not a subset of B,
|
| + and None if we can't tell for sure.
|
| + """
|
| + # We don't handle filters with wildcards in this test.
|
| + if self.contains_wildcards or other.contains_wildcards:
|
| + return None
|
| +
|
| + # Disabled categories get into a trace if and only if they are contained in
|
| + # the 'disabled' set. Return False if A's disabled set is a superset of B's
|
| + # disabled set.
|
| + if len(self.disabled):
|
| + if self.disabled > other.disabled:
|
| + return False
|
| +
|
| + if len(self.included) and len(other.included):
|
| + # A and B have explicit include lists. If A includes something that B
|
| + # doesn't, return False.
|
| + if not self.included <= other.included:
|
| + return False
|
| + elif len(self.included):
|
| + # Only A has an explicit include list. If A includes something that B
|
| + # excludes, return False.
|
| + if len(self.included.intersection(other.excluded)):
|
| + return False
|
| + elif len(other.included):
|
| + # Only B has an explicit include list. We don't know which categories are
|
| + # contained in the default list, so return None.
|
| + return None
|
| + else:
|
| + # None of the filter have explicit include list. If B excludes categories
|
| + # that A doesn't exclude, return False.
|
| + if not other.excluded <= self.excluded:
|
| + return False
|
| +
|
| + return True
|
| +
|
| class TracingBackend(object):
|
| def __init__(self, devtools_port):
|
| self._conn = websocket_browser_connection.WebSocketBrowserConnection(
|
| devtools_port)
|
| self._thread = None
|
| + self._category_filter = None
|
| + self._nesting = 0
|
| self._tracing_data = []
|
|
|
| - def BeginTracing(self, custom_categories=None, timeout=10):
|
| + def _IsTracing(self):
|
| + return self._thread != None
|
| +
|
| + def StartTracing(self, custom_categories=None, timeout=10):
|
| + """ Starts tracing on the first nested call and returns True. Returns False
|
| + and does nothing on subsequent nested calls.
|
| + """
|
| + self._nesting += 1
|
| + if self._IsTracing():
|
| + new_category_filter = CategoryFilter(custom_categories)
|
| + is_subset = new_category_filter.IsSubset(self._category_filter)
|
| + assert(is_subset != False)
|
| + if is_subset == None:
|
| + logging.warning('Cannot determine if category filter of nested ' +
|
| + 'StartTracing call is subset of current filter.')
|
| + return False
|
| self._CheckNotificationSupported()
|
| req = {'method': 'Tracing.start'}
|
| + self._category_filter = CategoryFilter(custom_categories)
|
| if custom_categories:
|
| req['params'] = {'categories': custom_categories}
|
| self._conn.SendRequest(req, timeout)
|
| @@ -82,22 +162,39 @@ class TracingBackend(object):
|
| # data, until Tracing.end is called.
|
| self._thread = threading.Thread(target=self._TracingReader)
|
| self._thread.start()
|
| + return True
|
| +
|
| + def StopTracing(self):
|
| + """ Stops tracing on the innermost (!) nested call, because we cannot get
|
| + results otherwise. Resets _tracing_data on the outermost nested call.
|
| + Returns the result of the trace, as TraceResult object.
|
| + """
|
| + self._nesting -= 1
|
| + assert self._nesting >= 0
|
| + if self._IsTracing():
|
| + req = {'method': 'Tracing.end'}
|
| + self._conn.SendRequest(req)
|
| + self._thread.join()
|
| + self._thread = None
|
| + if self._nesting == 0:
|
| + self._category_filter = None
|
| + return self._GetTraceResultAndReset()
|
| + else:
|
| + return self._GetTraceResult()
|
|
|
| - def EndTracing(self):
|
| - req = {'method': 'Tracing.end'}
|
| - self._conn.SendRequest(req)
|
| - self._thread.join()
|
| - self._thread = None
|
| -
|
| - def GetTraceResultAndReset(self):
|
| - assert not self._thread
|
| + def _GetTraceResult(self):
|
| + assert not self._IsTracing()
|
| if self._tracing_data and type(self._tracing_data[0]) in [str, unicode]:
|
| result_impl = TraceResultImpl(self._tracing_data)
|
| else:
|
| result_impl = RawTraceResultImpl(self._tracing_data)
|
| - self._tracing_data = []
|
| return trace_result.TraceResult(result_impl)
|
|
|
| + def _GetTraceResultAndReset(self):
|
| + result = self._GetTraceResult()
|
| + self._tracing_data = []
|
| + return result
|
| +
|
| def _TracingReader(self):
|
| while self._conn.socket:
|
| try:
|
|
|