Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / tools / telemetry / telemetry / core / timeline / model.py
1 # Copyright (c) 2013 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 import weakref
12
13 import telemetry.core.timeline.process as tracing_process
14 from telemetry.core import web_contents
15 from telemetry.core import browser
16
17 # Register importers for data
18 from telemetry.core.timeline import bounds
19 from telemetry.core.timeline import empty_trace_importer
20 from telemetry.core.timeline import inspector_importer
21 from telemetry.core.timeline import trace_event_importer
22
23 _IMPORTERS = [
24     empty_trace_importer.EmptyTraceImporter,
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
42 class TimelineModel(object):
43   def __init__(self, event_data=None, shift_world_to_zero=True):
44     self._bounds = bounds.Bounds()
45     self._thread_time_bounds = {}
46     self._processes = {}
47     self._browser_process = None
48     self._frozen = False
49     self.import_errors = []
50     self.metadata = []
51     self.flow_events = []
52     # Use a WeakKeyDictionary, because an ordinary dictionary could keep
53     # references to Tab objects around until it gets garbage collected.
54     # This would prevent telemetry from navigating to another page.
55     self._core_object_to_timeline_container_map = weakref.WeakKeyDictionary()
56
57     if event_data is not None:
58       self.ImportTraces([event_data], shift_world_to_zero=shift_world_to_zero)
59
60   @property
61   def bounds(self):
62     return self._bounds
63
64   @property
65   def thread_time_bounds(self):
66     return self._thread_time_bounds
67
68   @property
69   def processes(self):
70     return self._processes
71
72   @property
73   def browser_process(self):
74     return self._browser_process
75
76   @browser_process.setter
77   def browser_process(self, browser_process):
78     self._browser_process = browser_process
79
80   def ImportTraces(self, traces, shift_world_to_zero=True):
81     if self._frozen:
82       raise Exception("Cannot add events once recording is done")
83
84     importers = []
85     for event_data in traces:
86       importers.append(self._CreateImporter(event_data))
87
88     importers.sort(cmp=lambda x, y: x.import_priority - y.import_priority)
89
90     for importer in importers:
91       # TODO: catch exceptions here and add it to error list
92       importer.ImportEvents()
93     self.FinalizeImport(shift_world_to_zero, importers)
94
95   def FinalizeImport(self, shift_world_to_zero=False, importers=None):
96     if importers == None:
97       importers = []
98     self.UpdateBounds()
99     if not self.bounds.is_empty:
100       for process in self._processes.itervalues():
101         process.AutoCloseOpenSlices(self.bounds.max,
102                                     self.thread_time_bounds)
103
104     for importer in importers:
105       importer.FinalizeImport()
106
107     for process in self.processes.itervalues():
108       process.FinalizeImport()
109
110     if shift_world_to_zero:
111       self.ShiftWorldToZero()
112     self.UpdateBounds()
113
114     # Because of FinalizeImport, it would probably be a good idea
115     # to prevent the timeline from from being modified.
116     self._frozen = True
117
118   def ShiftWorldToZero(self):
119     self.UpdateBounds()
120     if self._bounds.is_empty:
121       return
122     shift_amount = self._bounds.min
123     for event in self.IterAllEvents():
124       event.start -= shift_amount
125
126   def UpdateBounds(self):
127     self._bounds.Reset()
128     for event in self.IterAllEvents():
129       self._bounds.AddValue(event.start)
130       self._bounds.AddValue(event.end)
131
132     self._thread_time_bounds = {}
133     for thread in self.GetAllThreads():
134       self._thread_time_bounds[thread] = bounds.Bounds()
135       for event in thread.IterEventsInThisContainer():
136         if event.thread_start != None:
137           self._thread_time_bounds[thread].AddValue(event.thread_start)
138         if event.thread_end != None:
139           self._thread_time_bounds[thread].AddValue(event.thread_end)
140
141   def GetAllContainers(self):
142     containers = []
143     def Iter(container):
144       containers.append(container)
145       for container in container.IterChildContainers():
146         Iter(container)
147     for process in self._processes.itervalues():
148       Iter(process)
149     return containers
150
151   def IterAllEvents(self):
152     for container in self.GetAllContainers():
153       for event in container.IterEventsInThisContainer():
154         yield event
155
156   def GetAllProcesses(self):
157     return self._processes.values()
158
159   def GetAllThreads(self):
160     threads = []
161     for process in self._processes.values():
162       threads.extend(process.threads.values())
163     return threads
164
165   def GetAllEvents(self):
166     return list(self.IterAllEvents())
167
168   def GetAllEventsOfName(self, name, only_root_events=False):
169     events = [e for e in self.IterAllEvents() if e.name == name]
170     if only_root_events:
171       return filter(lambda ev: ev.parent_slice == None, events)
172     else:
173       return events
174
175   def GetEventOfName(self, name, only_root_events=False,
176                      fail_if_more_than_one=False):
177     events = self.GetAllEventsOfName(name, only_root_events)
178     if len(events) == 0:
179       raise Exception('No event of name "%s" found.' % name)
180     if fail_if_more_than_one and len(events) > 1:
181       raise Exception('More than one event of name "%s" found.' % name)
182     return events[0]
183
184   def GetOrCreateProcess(self, pid):
185     if pid not in self._processes:
186       assert not self._frozen
187       self._processes[pid] = tracing_process.Process(self, pid)
188     return self._processes[pid]
189
190   def FindTimelineMarkers(self, timeline_marker_names):
191     """Find the timeline events with the given names.
192
193     If the number and order of events found does not match the names,
194     raise an error.
195     """
196     # Make sure names are in a list and remove all None names
197     if not isinstance(timeline_marker_names, list):
198       timeline_marker_names = [timeline_marker_names]
199     names = [x for x in timeline_marker_names if x is not None]
200
201     # Gather all events that match the names and sort them.
202     events = []
203     name_set = set()
204     for name in names:
205       name_set.add(name)
206     for name in name_set:
207       events.extend(self.GetAllEventsOfName(name, True))
208     events.sort(key=attrgetter('start'))
209
210     # Check if the number and order of events matches the provided names,
211     # and that the events don't overlap.
212     if len(events) != len(names):
213       raise MarkerMismatchError()
214     for (i, event) in enumerate(events):
215       if event.name != names[i]:
216         raise MarkerMismatchError()
217     for i in xrange(0, len(events)):
218       for j in xrange(i+1, len(events)):
219         if (events[j].start < events[i].start + events[i].duration):
220           raise MarkerOverlapError()
221
222     return events
223
224   def GetRendererProcessFromTab(self, tab):
225     return self._core_object_to_timeline_container_map[tab]
226
227   def AddCoreObjectToContainerMapping(self, core_object, container):
228     """ Add a mapping from a core object to a timeline container.
229
230     Used for example to map a Tab to its renderer process in the timeline model.
231     """
232     assert(isinstance(core_object, web_contents.WebContents) or
233            isinstance(core_object, browser.Browser))
234     self._core_object_to_timeline_container_map[core_object] = container
235
236   def _CreateImporter(self, event_data):
237     for importer_class in _IMPORTERS:
238       if importer_class.CanImport(event_data):
239         return importer_class(self, event_data)
240     raise ValueError("Could not find an importer for the provided event data")