OLD | NEW |
---|---|
(Empty) | |
1 # Copyright 2014 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 json | |
6 import numbers | |
7 | |
8 def _ValidateRawData(raw): | |
9 if isinstance(raw, basestring): | |
10 return | |
11 if isinstance(raw, numbers.Number): | |
12 return | |
13 | |
nednguyen
2014/08/05 14:34:34
Can None be raw data?
For example: {1:None, 2:'foo
| |
14 if isinstance(raw, list): | |
15 for r in raw: | |
16 _ValidateRawData(r) | |
17 return | |
18 | |
19 if isinstance(raw, dict): | |
20 for k,v in raw.iteritems(): | |
21 _ValidateRawData(k) | |
22 _ValidateRawData(v) | |
23 return | |
24 | |
25 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
| |
26 | |
27 | |
28 # TraceValues have a variety of events in them. These are called "parts" and | |
29 # aare accessed by name, using the following fixed field names. | |
nednguyen
2014/08/05 14:34:34
nit: are
| |
30 class TraceValuePart(object): | |
chrishenry
2014/08/05 23:44:15
Just TracePart? TraceValuePart is a bit ambiguous.
| |
31 def __init__(self, rawFieldName): | |
32 self.rawFieldName = rawFieldName | |
chrishenry
2014/08/05 23:44:15
nit: raw_field_name
| |
33 | |
34 def __repr__(self): | |
35 return 'TraceValuePart("%s")' % self.rawFieldName | |
36 | |
37 CHROME_TRACE_PART = TraceValuePart('traceEvents') | |
38 INSPECTOR_TRACE_PART = TraceValuePart('inspectorTimelineEvents') | |
39 TAB_ID_PART = TraceValuePart('tabIds') | |
40 | |
41 ALL_TRACE_VALUE_PARTS = set([CHROME_TRACE_PART, | |
42 INSPECTOR_TRACE_PART, | |
43 TAB_ID_PART]) | |
44 | |
45 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
| |
46 @property | |
47 def active_parts(self): | |
48 return set([p for p in ALL_TRACE_VALUE_PARTS | |
49 if p.rawFieldName in self._raw_data]) | |
50 | |
51 def HasEventsFor(self, part): | |
52 assert isinstance(part, TraceValuePart) | |
53 if part.rawFieldName not in self._raw_data: | |
54 return False | |
55 return len(self._raw_data[part.rawFieldName]) > 0 | |
56 | |
57 def GetEventsFor(self, part): | |
58 assert isinstance(part, TraceValuePart) | |
59 return self._raw_data[part.rawFieldName] | |
60 | |
61 @property | |
62 def metadata_records(self): | |
63 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
| |
64 for k,v in self._raw_data.iteritems(): | |
chrishenry
2014/08/05 23:44:15
nit: k, v (space after ,)
| |
65 if k in part_field_names: | |
66 continue | |
67 yield { | |
68 'name' : k, | |
chrishenry
2014/08/05 23:44:15
nit: no space before :
| |
69 'value' : self._raw_data[v] | |
nednguyen
2014/08/05 14:34:34
'value': v ?
| |
70 } | |
71 | |
72 | |
73 # TODO(nduca): Make this subclass value. Moving it was a huge enough patch | |
74 # that the true subclassing will be done in a followup. | |
75 class TraceValue(RawTraceValueHelpersMixin): | |
76 def __init__(self, raw_data=None): | |
77 """Creates a TraceValue from the given data. | |
78 | |
79 NOTE: raw data must NOT include any non-primitive objects! | |
80 By design, TraceValue must contain only data that is BOTH serializable | |
81 to a file, AND restorable once again from that file into a TraceValue | |
82 without assistance from other classes. | |
83 | |
84 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
| |
85 1. Trace container format: a json-parseable dict. | |
86 2. A json-parseable array: assumed to be chrome trace data. | |
87 3. A json-parseable array missing the final ]: assumed to be | |
88 chrome trace data. | |
89 """ | |
90 self._raw_data = {} | |
91 self._events_are_safely_mutable = False | |
92 if raw_data == None: | |
93 return | |
94 | |
95 _ValidateRawData(raw_data) | |
96 | |
97 if self._TryInitFromRaw(raw_data): | |
98 return | |
99 | |
100 if not isinstance(raw_data, basestring): | |
chrishenry
2014/08/05 23:44:16
I think it's clearer to refactor TryInitFromRaw an
| |
101 raise ValueError('Unrecognized data format') | |
102 | |
103 if len(raw_data) == 0: | |
104 self._raw_data == {} | |
slamm
2014/08/12 23:11:59
self._raw_data = {} ?
Same in _TryInitFromRaw.
| |
105 return | |
106 | |
107 if raw_data[0] == '[' and raw_data[-1] != ']': | |
slamm
2014/08/12 23:11:59
Better?
if raw_data.startswith('[') and not raw_d
| |
108 if raw_data[-1] == ',': | |
109 raw_data = raw_data[:-1] + ']' | |
110 else: | |
111 raw_data = raw_data + ']' | |
112 | |
113 raw_data = json.loads(raw_data) | |
114 if self._TryInitFromRaw(raw_data): | |
115 self._events_are_safely_mutable = True | |
116 return | |
117 | |
118 raise Exception('Unrecognized data format') | |
119 | |
120 def _TryInitFromRaw(self, raw_data): | |
121 if isinstance(raw_data, dict): | |
122 self._raw_data = raw_data | |
123 return True | |
124 | |
125 if isinstance(raw_data, list): | |
126 if len(raw_data) == 0: | |
127 self._raw_data == {} | |
128 return True | |
129 | |
130 self._raw_data = {'traceEvents': raw_data} | |
nednguyen
2014/08/05 14:34:34
How about self._raw_data = { CHROME_TRACE_PART.raw
| |
131 return True | |
132 | |
133 return False | |
134 | |
135 def _SetFromBuilder(self, d): | |
136 self._raw_data = d | |
137 self._event_data_is_mutable = True | |
chrishenry
2014/08/05 23:44:16
Unused? Or do you mean _events_are_safely_mutable?
| |
138 | |
139 @property | |
140 def events_are_safely_mutable(self): | |
141 """Returns true if the events in this value are completely sealed. | |
142 | |
143 Some importers want to take complex fields out of the TraceValue and add | |
144 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
| |
145 constructed with data that is shared with something outside the trace value, | |
146 for instance a test harness, then this mutation is unexpected. But, if the | |
147 values are sealed, then mutating the events is a lot faster. | |
148 | |
149 We know if events are sealed if the value came from a string, or if the | |
150 value came from | |
chrishenry
2014/08/05 23:44:16
incomplete sentence?
| |
151 """ | |
152 return self._events_are_safely_mutable | |
153 | |
154 def Serialize(self, f, gzip_result=False): | |
155 """Serializes the trace result to a file-like object. | |
156 | |
157 Always writes in the trace container format.""" | |
158 assert not gzip_result, 'Not implemented' | |
159 json.dump(self._raw_data, f) | |
160 | |
161 | |
162 class TraceValueBuilder(RawTraceValueHelpersMixin): | |
163 def __init__(self): | |
164 """TraceValueBuilder helps build up of a trace from multiple trace agents. | |
165 | |
166 TraceValue is supposed to be immutable, but it is useful during recording | |
167 to have a mutable version. That is TraceValueBuilder. | |
168 """ | |
169 self._raw_data = {} | |
170 | |
171 def AsValue(self): | |
172 if self._raw_data == None: | |
173 raise Exception('Can only AsValue() once') | |
174 | |
175 res = TraceValue() | |
chrishenry
2014/08/05 23:44:16
nit: s/res/value/
| |
176 res._SetFromBuilder(self._raw_data) | |
177 self._raw_data = None | |
178 return res | |
179 | |
180 def AddEventsTo(self, part, events): | |
181 # Note: this wont work when you call this from multiple browsers. | |
182 # Each browser's trace_event_impl zeros its timestamps when it writes them | |
183 # 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?
| |
184 assert isinstance(part, TraceValuePart) | |
185 assert isinstance(events, list) | |
186 if self._raw_data == None: | |
187 raise Exception('Already called AsValue() on this builder.') | |
188 existing_events = None | |
189 existing_events = self._raw_data.get(part.rawFieldName, None) | |
190 if existing_events == None: | |
191 existing_events = [] | |
192 self._raw_data[part.rawFieldName] = existing_events | |
193 existing_events.extend(events) | |
194 | |
OLD | NEW |