1f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)# Copyright 2014 The Chromium Authors. All rights reserved.
2eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch# Use of this source code is governed by a BSD-style license that can be
3eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch# found in the LICENSE file.
4116680a4aac90f2aa7413d9095a592090648e557Ben Murdochimport telemetry.timeline.async_slice as async_slice_module
56e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)import telemetry.timeline.event_container as event_container
6116680a4aac90f2aa7413d9095a592090648e557Ben Murdochimport telemetry.timeline.flow_event as flow_event_module
76e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)import telemetry.timeline.sample as sample_module
8116680a4aac90f2aa7413d9095a592090648e557Ben Murdochimport telemetry.timeline.slice as slice_module
9eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
11a3f7b4e666c476898878fa745f637129375cd889Ben Murdochclass Thread(event_container.TimelineEventContainer):
12eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  ''' A Thread stores all the trace events collected for a particular
13eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  thread. We organize the synchronous slices on a thread by "subrows," where
14eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  subrow 0 has all the root slices, subrow 1 those nested 1 deep, and so on.
15eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  The asynchronous slices are stored in an AsyncSliceGroup object.
16eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  '''
17eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  def __init__(self, process, tid):
18a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    super(Thread, self).__init__('thread %s' % tid, parent=process)
19eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    self.tid = tid
20eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    self._async_slices = []
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self._flow_events = []
22eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    self._samples = []
23a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    self._toplevel_slices = []
24116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    self._all_slices = []
25a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch
26a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    # State only valid during import.
27a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    self._open_slices = []
28a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    self._newly_added_slices = []
29eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
30eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  @property
31a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  def toplevel_slices(self):
32a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    return self._toplevel_slices
33a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch
34a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  @property
35a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  def all_slices(self):
36116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return self._all_slices
37eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
38eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  @property
39eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  def samples(self):
40eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return self._samples
41eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
42eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  @property
43a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  def async_slices(self):
44a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    return self._async_slices
45a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch
46a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  @property
47eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  def open_slice_count(self):
48eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return len(self._open_slices)
49eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
50a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  def IterChildContainers(self):
51116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return
52116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    yield # pylint: disable=W0101
53116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
54116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  def IterEventsInThisContainer(self, event_type_predicate, event_predicate):
55116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if event_type_predicate(slice_module.Slice):
56116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      for s in self._newly_added_slices:
57116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        if event_predicate(s):
58116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          yield s
59116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      for s in self._all_slices:
60116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        if event_predicate(s):
61116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          yield s
62116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
63116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if event_type_predicate(async_slice_module.AsyncSlice):
64116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      for async_slice in self._async_slices:
65116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        if event_predicate(async_slice):
66116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          yield async_slice
67116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        for sub_slice in async_slice.IterEventsInThisContainerRecrusively():
68116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          if event_predicate(sub_slice):
69116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch            yield sub_slice
70116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
71116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if event_type_predicate(flow_event_module.FlowEvent):
72116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      for flow_event in self._flow_events:
73116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        if event_predicate(flow_event):
74116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          yield flow_event
75116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
76116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if event_type_predicate(sample_module.Sample):
77116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      for sample in self._samples:
78116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        if event_predicate(sample):
79116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          yield sample
80eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
81eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  def AddSample(self, category, name, timestamp, args=None):
82eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if len(self._samples) and timestamp < self._samples[-1].start:
83eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      raise ValueError(
84eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          'Samples must be added in increasing timestamp order')
85116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    sample = sample_module.Sample(self,
86a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch        category, name, timestamp, args=args)
87eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    self._samples.append(sample)
88eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
89eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  def AddAsyncSlice(self, async_slice):
90eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    self._async_slices.append(async_slice)
91eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def AddFlowEvent(self, flow_event):
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self._flow_events.append(flow_event)
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
95a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  def BeginSlice(self, category, name, timestamp, thread_timestamp=None,
96a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                 args=None):
97eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    """Opens a new slice for the thread.
98eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    Calls to beginSlice and endSlice must be made with
99eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    non-monotonically-decreasing timestamps.
100eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
101eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    * category: Category to which the slice belongs.
102eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    * name: Name of the slice to add.
103eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    * timestamp: The timetsamp of the slice, in milliseconds.
104a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    * thread_timestamp: Thread specific clock (scheduled) timestamp of the
105a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                        slice, in milliseconds.
106eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    * args: Arguments associated with
107eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
108eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    Returns newly opened slice
109eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    """
110eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if len(self._open_slices) > 0 and timestamp < self._open_slices[-1].start:
111eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      raise ValueError(
112eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          'Slices must be added in increasing timestamp order')
113116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    new_slice = slice_module.Slice(self, category, name, timestamp,
114a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                    thread_timestamp=thread_timestamp,
115a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                    args=args)
1161e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self._open_slices.append(new_slice)
1171e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    new_slice.did_not_finish = True
1181e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self.PushSlice(new_slice)
1191e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    return new_slice
120eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
121a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  def EndSlice(self, end_timestamp, end_thread_timestamp=None):
122eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    """ Ends the last begun slice in this group and pushes it onto the slice
123eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    array.
124eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
125eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    * end_timestamp: Timestamp when the slice ended in milliseconds
126a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    * end_thread_timestamp: Timestamp when the scheduled time of the slice ended
127a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                            in milliseconds
128eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
129eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    returns completed slice.
130eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    """
131eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if not len(self._open_slices):
132eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      raise ValueError(
133eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          'EndSlice called without an open slice')
134eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    curr_slice = self._open_slices.pop()
135eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if end_timestamp < curr_slice.start:
136eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      raise ValueError(
137eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          'Slice %s end time is before its start.' % curr_slice.name)
138eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    curr_slice.duration = end_timestamp - curr_slice.start
139a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if end_thread_timestamp != None:
140a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      if curr_slice.thread_start == None:
141a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        raise ValueError(
142a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            'EndSlice with thread_timestamp called on open slice without ' +
143a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            'thread_timestamp')
144a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      curr_slice.thread_duration = (end_thread_timestamp -
145a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                    curr_slice.thread_start)
1461e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    curr_slice.did_not_finish = False
1471e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    return curr_slice
1481e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
149a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  def PushCompleteSlice(self, category, name, timestamp, duration,
150a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                        thread_timestamp, thread_duration, args=None):
151116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    new_slice = slice_module.Slice(self, category, name, timestamp,
152116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                   thread_timestamp=thread_timestamp,
153116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                   args=args)
1541e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    if duration == None:
1551e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      new_slice.did_not_finish = True
1561e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    else:
1571e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      new_slice.duration = duration
158a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      new_slice.thread_duration = thread_duration
1591e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self.PushSlice(new_slice)
1601e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    return new_slice
161eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
162eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  def PushSlice(self, new_slice):
163a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    self._newly_added_slices.append(new_slice)
164eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return new_slice
165eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
166a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  def AutoCloseOpenSlices(self, max_timestamp, max_thread_timestamp):
1671e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    for s in self._newly_added_slices:
1681e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      if s.did_not_finish:
1691e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        s.duration = max_timestamp - s.start
1701e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        assert s.duration >= 0
171a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        if s.thread_start != None:
172a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)          s.thread_duration = max_thread_timestamp - s.thread_start
173a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)          assert s.thread_duration >= 0
1741e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self._open_slices = []
175eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
176eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  def IsTimestampValidForBeginOrEnd(self, timestamp):
177eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if not len(self._open_slices):
178eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      return True
179eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return timestamp >= self._open_slices[-1].start
180eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
181eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  def FinalizeImport(self):
182eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    self._BuildSliceSubRows()
183eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
184eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  def _BuildSliceSubRows(self):
185eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    '''This function works by walking through slices by start time.
186eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
187eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch     The basic idea here is to insert each slice as deep into the subrow
188eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch     list as it can go such that every subslice is fully contained by its
189eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch     parent slice.
190eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
191eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch     Visually, if we start with this:
192eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      0:  [    a       ]
193eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      1:    [  b  ]
194eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      2:    [c][d]
195eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
196eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch     To place this slice:
197eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                   [e]
198eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch     We first check row 2's last item, [d]. [e] wont fit into [d] (they dont
199eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch     even intersect). So we go to row 1. That gives us [b], and [d] wont fit
200eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch     into that either. So, we go to row 0 and its last slice, [a]. That can
201eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch     completely contain [e], so that means we should add [e] as a subslice
202eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch     of [a]. That puts it on row 1, yielding:
203eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      0:  [    a       ]
204eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      1:    [  b  ][e]
205eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      2:    [c][d]
206eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
207eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch     If we then get this slice:
208eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                          [f]
209eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch     We do the same deepest-to-shallowest walk of the subrows trying to fit
210eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch     it. This time, it doesn't fit in any open slice. So, we simply append
211eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch     it to row 0 (a root slice):
212eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      0:  [    a       ]  [f]
213eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      1:    [  b  ][e]
214eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    '''
215eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    def CompareSlices(s1, s2):
216eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      if s1.start == s2.start:
217eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        # Break ties by having the slice with the greatest
218eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        # end timestamp come first.
219eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        return cmp(s2.end, s1.end)
220eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      return cmp(s1.start, s2.start)
221eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
222a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    assert len(self._toplevel_slices) == 0
223116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    assert len(self._all_slices) == 0
224a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    if not len(self._newly_added_slices):
225eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      return
226eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
227116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    self._all_slices.extend(self._newly_added_slices)
228116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
229a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    sorted_slices = sorted(self._newly_added_slices, cmp=CompareSlices)
230eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    root_slice = sorted_slices[0]
231a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    self._toplevel_slices.append(root_slice)
232eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    for s in sorted_slices[1:]:
233eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      if not self._AddSliceIfBounds(root_slice, s):
234eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        root_slice = s
235a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch        self._toplevel_slices.append(root_slice)
236a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    self._newly_added_slices = []
237eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
238116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
239eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  def _AddSliceIfBounds(self, root, child):
240eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    ''' Adds a child slice to a root slice its proper row.
241eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    Return False if the child slice is not in the bounds
242eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    of the root slice.
243eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
244eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    Because we know that the start time of child is >= the start time
245eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    of all other slices seen so far, we can just check the last slice
246eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    of each row for bounding.
247eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    '''
2485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # The source trace data is in microseconds but we store it as milliseconds
2495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # in floating-point. Since we can't represent micros as millis perfectly,
2505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # two end=start+duration combos that should be the same will be slightly
2515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # different. Round back to micros to ensure equality below.
2525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    child_end_micros = round(child.end * 1000)
2535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    root_end_micros =  round(root.end * 1000)
2545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if child.start >= root.start and child_end_micros <= root_end_micros:
255eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      if len(root.sub_slices) > 0:
256eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        if self._AddSliceIfBounds(root.sub_slices[-1], child):
257eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          return True
258a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch      child.parent_slice = root
259eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      root.AddSubSlice(child)
260eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      return True
261eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return False
262