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: |