Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / tools / telemetry / telemetry / timeline / model.py
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 '''A container for timeline-based events and traces and can handle importing
5 raw event data from different sources. This model closely resembles that in the
6 trace_viewer project:
7 https://code.google.com/p/trace-viewer/
8 '''
9
10 from operator import attrgetter
11
12 import telemetry.timeline.process as process_module
13 from telemetry.timeline import async_slice as async_slice_module
14 from telemetry.timeline import slice as slice_module
15 from telemetry.timeline import bounds
16 from telemetry.timeline import empty_timeline_data_importer
17 from telemetry.timeline import event_container
18 from telemetry.timeline import inspector_importer
19 from telemetry.timeline import trace_event_importer
20
21 # Register importers for data
22
23 _IMPORTERS = [
24     empty_timeline_data_importer.EmptyTimelineDataImporter,
25     inspector_importer.InspectorTimelineImporter,
26     trace_event_importer.TraceEventTimelineImporter
27 ]
28
29
30 class MarkerMismatchError(Exception):
31   def __init__(self):
32     super(MarkerMismatchError, self).__init__(
33         'Number or order of timeline markers does not match provided labels')
34
35
36 class MarkerOverlapError(Exception):
37   def __init__(self):
38     super(MarkerOverlapError, self).__init__(
39         'Overlapping timeline markers found')
40
41 def IsSliceOrAsyncSlice(t):
42   if t == async_slice_module.AsyncSlice:
43     return True
44   return t == slice_module.Slice
45
46
47 class TimelineModel(event_container.TimelineEventContainer):
48   def __init__(self, timeline_data=None, shift_world_to_zero=True):
49     """ Initializes a TimelineModel. timeline_data can be a single TimelineData
50     object, a list of TimelineData objects, or None. If timeline_data is not
51     None, all events from it will be imported into the model. The events will
52     be shifted such that the first event starts at time 0, if
53     shift_world_to_zero is True.
54     """
55     super(TimelineModel, self).__init__(name='TimelineModel', parent=None)
56     self._bounds = bounds.Bounds()
57     self._thread_time_bounds = {}
58     self._processes = {}
59     self._browser_process = None
60     self._frozen = False
61     self._tab_ids_to_renderer_threads_map = {}
62     self.import_errors = []
63     self.metadata = []
64     self.flow_events = []
65     if timeline_data is not None:
66       self.ImportTraces(timeline_data, shift_world_to_zero=shift_world_to_zero)
67
68   def IterChildContainers(self):
69     for process in self._processes.itervalues():
70       yield process
71
72   def GetAllProcesses(self):
73     return self._processes.values()
74
75   def GetAllThreads(self):
76     threads = []
77     for process in self._processes.values():
78       threads.extend(process.threads.values())
79     return threads
80
81   @property
82   def bounds(self):
83     return self._bounds
84
85   @property
86   def processes(self):
87     return self._processes
88
89   @property
90   #pylint: disable=E0202
91   def browser_process(self):
92     return self._browser_process
93
94   @browser_process.setter
95   #pylint: disable=E0202
96   def browser_process(self, browser_process):
97     self._browser_process = browser_process
98
99   def AddMappingFromTabIdToRendererThread(self, tab_id, renderer_thread):
100     if self._frozen:
101       raise Exception('Cannot add mapping from tab id to renderer thread once '
102                       'trace is imported')
103     self._tab_ids_to_renderer_threads_map[tab_id] = renderer_thread
104
105   def ImportTraces(self, timeline_data, shift_world_to_zero=True):
106     if self._frozen:
107       raise Exception("Cannot add events once trace is imported")
108
109     importers = []
110     if isinstance(timeline_data, list):
111       for item in timeline_data:
112         importers.append(self._CreateImporter(item))
113     else:
114       importers.append(self._CreateImporter(timeline_data))
115
116     importers.sort(cmp=lambda x, y: x.import_priority - y.import_priority)
117
118     for importer in importers:
119       # TODO: catch exceptions here and add it to error list
120       importer.ImportEvents()
121     self.FinalizeImport(shift_world_to_zero, importers)
122
123   def FinalizeImport(self, shift_world_to_zero=False, importers=None):
124     if importers == None:
125       importers = []
126     self.UpdateBounds()
127     if not self.bounds.is_empty:
128       for process in self._processes.itervalues():
129         process.AutoCloseOpenSlices(self.bounds.max,
130                                     self._thread_time_bounds)
131
132     for importer in importers:
133       importer.FinalizeImport()
134
135     for process in self.processes.itervalues():
136       process.FinalizeImport()
137
138     if shift_world_to_zero:
139       self.ShiftWorldToZero()
140     self.UpdateBounds()
141
142     # Because of FinalizeImport, it would probably be a good idea
143     # to prevent the timeline from from being modified.
144     self._frozen = True
145
146   def ShiftWorldToZero(self):
147     self.UpdateBounds()
148     if self._bounds.is_empty:
149       return
150     shift_amount = self._bounds.min
151     for event in self.IterAllEvents():
152       event.start -= shift_amount
153
154   def UpdateBounds(self):
155     self._bounds.Reset()
156     for event in self.IterAllEvents():
157       self._bounds.AddValue(event.start)
158       self._bounds.AddValue(event.end)
159
160     self._thread_time_bounds = {}
161     for thread in self.GetAllThreads():
162       self._thread_time_bounds[thread] = bounds.Bounds()
163       for event in thread.IterEventsInThisContainer(
164           event_type_predicate=lambda t: True,
165           event_predicate=lambda e: True):
166         if event.thread_start != None:
167           self._thread_time_bounds[thread].AddValue(event.thread_start)
168         if event.thread_end != None:
169           self._thread_time_bounds[thread].AddValue(event.thread_end)
170
171   def GetOrCreateProcess(self, pid):
172     if pid not in self._processes:
173       assert not self._frozen
174       self._processes[pid] = process_module.Process(self, pid)
175     return self._processes[pid]
176
177   def FindTimelineMarkers(self, timeline_marker_names):
178     """Find the timeline events with the given names.
179
180     If the number and order of events found does not match the names,
181     raise an error.
182     """
183     # Make sure names are in a list and remove all None names
184     if not isinstance(timeline_marker_names, list):
185       timeline_marker_names = [timeline_marker_names]
186     names = [x for x in timeline_marker_names if x is not None]
187
188     # Gather all events that match the names and sort them.
189     events = []
190     name_set = set()
191     for name in names:
192       name_set.add(name)
193
194     def IsEventNeeded(event):
195       if event.parent_slice != None:
196         return
197       return event.name in name_set
198
199     events = list(self.IterAllEvents(
200       recursive=True,
201       event_type_predicate=IsSliceOrAsyncSlice,
202       event_predicate=IsEventNeeded))
203     events.sort(key=attrgetter('start'))
204
205     # Check if the number and order of events matches the provided names,
206     # and that the events don't overlap.
207     if len(events) != len(names):
208       raise MarkerMismatchError()
209     for (i, event) in enumerate(events):
210       if event.name != names[i]:
211         raise MarkerMismatchError()
212     for i in xrange(0, len(events)):
213       for j in xrange(i+1, len(events)):
214         if (events[j].start < events[i].start + events[i].duration):
215           raise MarkerOverlapError()
216
217     return events
218
219   def GetRendererProcessFromTabId(self, tab_id):
220     renderer_thread = self.GetRendererThreadFromTabId(tab_id)
221     if renderer_thread:
222       return renderer_thread.parent
223     return None
224
225   def GetRendererThreadFromTabId(self, tab_id):
226     return self._tab_ids_to_renderer_threads_map.get(tab_id, None)
227
228   def _CreateImporter(self, event_data):
229     for importer_class in _IMPORTERS:
230       if importer_class.CanImport(event_data):
231         return importer_class(self, event_data)
232     raise ValueError("Could not find an importer for the provided event data")