Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(211)

Unified Diff: tools/telemetry/telemetry/value/trace.py

Issue 441873007: Move timeline and importers to use telemetry.value.TraceValue (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: tools/telemetry/telemetry/value/trace.py
diff --git a/tools/telemetry/telemetry/value/trace.py b/tools/telemetry/telemetry/value/trace.py
new file mode 100644
index 0000000000000000000000000000000000000000..3d17aeefe54a7786ca9a18e230f6964d4e92be28
--- /dev/null
+++ b/tools/telemetry/telemetry/value/trace.py
@@ -0,0 +1,194 @@
+# Copyright 2014 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.
+
+import json
+import numbers
+
+def _ValidateRawData(raw):
+ if isinstance(raw, basestring):
+ return
+ if isinstance(raw, numbers.Number):
+ return
+
nednguyen 2014/08/05 14:34:34 Can None be raw data? For example: {1:None, 2:'foo
+ if isinstance(raw, list):
+ for r in raw:
+ _ValidateRawData(r)
+ return
+
+ if isinstance(raw, dict):
+ for k,v in raw.iteritems():
+ _ValidateRawData(k)
+ _ValidateRawData(v)
+ return
+
+ raise Exception('%s is not allowed in TraceValue', raw)
nednguyen 2014/08/05 14:34:34 Throwing exception at the leaf level may make it h
+
+
+# TraceValues have a variety of events in them. These are called "parts" and
+# aare accessed by name, using the following fixed field names.
nednguyen 2014/08/05 14:34:34 nit: are
+class TraceValuePart(object):
chrishenry 2014/08/05 23:44:15 Just TracePart? TraceValuePart is a bit ambiguous.
+ def __init__(self, rawFieldName):
+ self.rawFieldName = rawFieldName
chrishenry 2014/08/05 23:44:15 nit: raw_field_name
+
+ def __repr__(self):
+ return 'TraceValuePart("%s")' % self.rawFieldName
+
+CHROME_TRACE_PART = TraceValuePart('traceEvents')
+INSPECTOR_TRACE_PART = TraceValuePart('inspectorTimelineEvents')
+TAB_ID_PART = TraceValuePart('tabIds')
+
+ALL_TRACE_VALUE_PARTS = set([CHROME_TRACE_PART,
+ INSPECTOR_TRACE_PART,
+ TAB_ID_PART])
+
+class RawTraceValueHelpersMixin(object):
nednguyen 2014/08/05 14:34:34 Don't you need __init__(self) to define self._raw_
chrishenry 2014/08/05 23:44:16 Does TraceValue and Builder really need to mixin t
+ @property
+ def active_parts(self):
+ return set([p for p in ALL_TRACE_VALUE_PARTS
+ if p.rawFieldName in self._raw_data])
+
+ def HasEventsFor(self, part):
+ assert isinstance(part, TraceValuePart)
+ if part.rawFieldName not in self._raw_data:
+ return False
+ return len(self._raw_data[part.rawFieldName]) > 0
+
+ def GetEventsFor(self, part):
+ assert isinstance(part, TraceValuePart)
+ return self._raw_data[part.rawFieldName]
+
+ @property
+ def metadata_records(self):
+ part_field_names = set([p.rawFieldName for p in ALL_TRACE_VALUE_PARTS])
slamm 2014/08/12 23:11:59 You can build the set with a generator expression
+ for k,v in self._raw_data.iteritems():
chrishenry 2014/08/05 23:44:15 nit: k, v (space after ,)
+ if k in part_field_names:
+ continue
+ yield {
+ 'name' : k,
chrishenry 2014/08/05 23:44:15 nit: no space before :
+ 'value' : self._raw_data[v]
nednguyen 2014/08/05 14:34:34 'value': v ?
+ }
+
+
+# TODO(nduca): Make this subclass value. Moving it was a huge enough patch
+# that the true subclassing will be done in a followup.
+class TraceValue(RawTraceValueHelpersMixin):
+ def __init__(self, raw_data=None):
+ """Creates a TraceValue from the given data.
+
+ NOTE: raw data must NOT include any non-primitive objects!
+ By design, TraceValue must contain only data that is BOTH serializable
+ to a file, AND restorable once again from that file into a TraceValue
+ without assistance from other classes.
+
+ raw_data can be any of the three standard trace_event formats:
nednguyen 2014/08/05 14:34:34 So we will need something else for video and image
+ 1. Trace container format: a json-parseable dict.
+ 2. A json-parseable array: assumed to be chrome trace data.
+ 3. A json-parseable array missing the final ]: assumed to be
+ chrome trace data.
+ """
+ self._raw_data = {}
+ self._events_are_safely_mutable = False
+ if raw_data == None:
+ return
+
+ _ValidateRawData(raw_data)
+
+ if self._TryInitFromRaw(raw_data):
+ return
+
+ if not isinstance(raw_data, basestring):
chrishenry 2014/08/05 23:44:16 I think it's clearer to refactor TryInitFromRaw an
+ raise ValueError('Unrecognized data format')
+
+ if len(raw_data) == 0:
+ self._raw_data == {}
slamm 2014/08/12 23:11:59 self._raw_data = {} ? Same in _TryInitFromRaw.
+ return
+
+ if raw_data[0] == '[' and raw_data[-1] != ']':
slamm 2014/08/12 23:11:59 Better? if raw_data.startswith('[') and not raw_d
+ if raw_data[-1] == ',':
+ raw_data = raw_data[:-1] + ']'
+ else:
+ raw_data = raw_data + ']'
+
+ raw_data = json.loads(raw_data)
+ if self._TryInitFromRaw(raw_data):
+ self._events_are_safely_mutable = True
+ return
+
+ raise Exception('Unrecognized data format')
+
+ def _TryInitFromRaw(self, raw_data):
+ if isinstance(raw_data, dict):
+ self._raw_data = raw_data
+ return True
+
+ if isinstance(raw_data, list):
+ if len(raw_data) == 0:
+ self._raw_data == {}
+ return True
+
+ self._raw_data = {'traceEvents': raw_data}
nednguyen 2014/08/05 14:34:34 How about self._raw_data = { CHROME_TRACE_PART.raw
+ return True
+
+ return False
+
+ def _SetFromBuilder(self, d):
+ self._raw_data = d
+ self._event_data_is_mutable = True
chrishenry 2014/08/05 23:44:16 Unused? Or do you mean _events_are_safely_mutable?
+
+ @property
+ def events_are_safely_mutable(self):
+ """Returns true if the events in this value are completely sealed.
+
+ Some importers want to take complex fields out of the TraceValue and add
+ them to the model, changing them subtly as they do so. If the TraceValue was
chrishenry 2014/08/05 23:44:16 Do these importers modify the raw data in this Tra
+ constructed with data that is shared with something outside the trace value,
+ for instance a test harness, then this mutation is unexpected. But, if the
+ values are sealed, then mutating the events is a lot faster.
+
+ We know if events are sealed if the value came from a string, or if the
+ value came from
chrishenry 2014/08/05 23:44:16 incomplete sentence?
+ """
+ return self._events_are_safely_mutable
+
+ def Serialize(self, f, gzip_result=False):
+ """Serializes the trace result to a file-like object.
+
+ Always writes in the trace container format."""
+ assert not gzip_result, 'Not implemented'
+ json.dump(self._raw_data, f)
+
+
+class TraceValueBuilder(RawTraceValueHelpersMixin):
+ def __init__(self):
+ """TraceValueBuilder helps build up of a trace from multiple trace agents.
+
+ TraceValue is supposed to be immutable, but it is useful during recording
+ to have a mutable version. That is TraceValueBuilder.
+ """
+ self._raw_data = {}
+
+ def AsValue(self):
+ if self._raw_data == None:
+ raise Exception('Can only AsValue() once')
+
+ res = TraceValue()
chrishenry 2014/08/05 23:44:16 nit: s/res/value/
+ res._SetFromBuilder(self._raw_data)
+ self._raw_data = None
+ return res
+
+ def AddEventsTo(self, part, events):
+ # Note: this wont work when you call this from multiple browsers.
+ # Each browser's trace_event_impl zeros its timestamps when it writes them
+ # out and doesn't write a timebase that can be used to re-sync them.
chrishenry 2014/08/05 23:44:16 Turn this into pydoc?
+ assert isinstance(part, TraceValuePart)
+ assert isinstance(events, list)
+ if self._raw_data == None:
+ raise Exception('Already called AsValue() on this builder.')
+ existing_events = None
+ existing_events = self._raw_data.get(part.rawFieldName, None)
+ if existing_events == None:
+ existing_events = []
+ self._raw_data[part.rawFieldName] = existing_events
+ existing_events.extend(events)
+

Powered by Google App Engine
This is Rietveld 408576698