 Chromium Code Reviews
 Chromium Code Reviews Issue 19772005:
  [telemetry] Timeline model cleanups  (Closed) 
  Base URL: svn://svn.chromium.org/chrome/trunk/src
    
  
    Issue 19772005:
  [telemetry] Timeline model cleanups  (Closed) 
  Base URL: svn://svn.chromium.org/chrome/trunk/src| OLD | NEW | 
|---|---|
| 1 # Copyright 2013 The Chromium Authors. All rights reserved. | 1 # Copyright 2013 The Chromium Authors. All rights reserved. | 
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be | 
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. | 
| 4 | 4 | 
| 5 import telemetry.core.timeline.event as timeline_event | 5 import telemetry.core.timeline.event_container as event_container | 
| 6 import telemetry.core.timeline.tracing.sample as tracing_sample | 6 import telemetry.core.timeline.sample as tracing_sample | 
| 7 import telemetry.core.timeline.tracing.slice as tracing_slice | 7 import telemetry.core.timeline.slice as tracing_slice | 
| 8 | 8 | 
| 9 class Thread(timeline_event.TimelineEvent): | 9 class Thread(event_container.TimelineEventContainer): | 
| 10 ''' A Thread stores all the trace events collected for a particular | 10 ''' A Thread stores all the trace events collected for a particular | 
| 11 thread. We organize the synchronous slices on a thread by "subrows," where | 11 thread. We organize the synchronous slices on a thread by "subrows," where | 
| 12 subrow 0 has all the root slices, subrow 1 those nested 1 deep, and so on. | 12 subrow 0 has all the root slices, subrow 1 those nested 1 deep, and so on. | 
| 13 The asynchronous slices are stored in an AsyncSliceGroup object. | 13 The asynchronous slices are stored in an AsyncSliceGroup object. | 
| 14 ''' | 14 ''' | 
| 15 def __init__(self, process, tid): | 15 def __init__(self, process, tid): | 
| 16 super(Thread, self).__init__('thread %s' % tid, 0, 0, parent=process) | 16 super(Thread, self).__init__('thread %s' % tid, parent=process) | 
| 17 self.tid = tid | 17 self.tid = tid | 
| 18 self._open_slices = [] | |
| 19 self._async_slices = [] | 18 self._async_slices = [] | 
| 20 self._samples = [] | 19 self._samples = [] | 
| 21 self._slices = [] | 20 self._toplevel_slices = [] | 
| 21 | |
| 22 # State only valid during import. | |
| 23 self._open_slices = [] | |
| 24 self._newly_added_slices = [] | |
| 22 | 25 | 
| 23 @property | 26 @property | 
| 24 def slices(self): | 27 def toplevel_slices(self): | 
| 25 return self._slices | 28 return self._toplevel_slices | 
| 29 | |
| 30 @property | |
| 31 def all_slices(self): | |
| 32 return list(self.IterAllSlices()) | |
| 26 | 33 | 
| 27 @property | 34 @property | 
| 28 def samples(self): | 35 def samples(self): | 
| 29 return self._samples | 36 return self._samples | 
| 30 | 37 | 
| 31 @property | 38 @property | 
| 39 def async_slices(self): | |
| 40 return self._async_slices | |
| 41 | |
| 42 @property | |
| 32 def open_slice_count(self): | 43 def open_slice_count(self): | 
| 33 return len(self._open_slices) | 44 return len(self._open_slices) | 
| 34 | 45 | 
| 35 @property | 46 def IterChildContainers(self): | 
| 36 def async_slices(self): | 47 return iter([]) | 
| 37 return self._async_slices | 48 | 
| 49 def IterAllSlices(self): | |
| 50 for s in self._toplevel_slices: | |
| 51 yield s | |
| 52 for sub_slice in s.IterEventsInThisContainerRecrusively(): | |
| 53 yield sub_slice | |
| 54 def IterAllAsyncSlices(self): | |
| 55 for async_slice in self._async_slices: | |
| 56 yield async_slice | |
| 57 for sub_slice in async_slice.IterEventsInThisContainerRecrusively(): | |
| 58 yield sub_slice | |
| 59 | |
| 60 def IterEventsInThisContainer(self): | |
| 61 for s in self._open_slices: | |
| 
Tim Song
2013/07/18 23:13:55
There is a convenience function itertools.chain th
 | |
| 62 yield s | |
| 63 for s in self._newly_added_slices: | |
| 64 yield s | |
| 65 for s in self.IterAllAsyncSlices(): | |
| 66 yield s | |
| 67 for s in self.IterAllSlices(): | |
| 68 yield s | |
| 69 for sample in self._samples: | |
| 70 yield sample | |
| 38 | 71 | 
| 39 def AddSample(self, category, name, timestamp, args=None): | 72 def AddSample(self, category, name, timestamp, args=None): | 
| 40 if len(self._samples) and timestamp < self._samples[-1].start: | 73 if len(self._samples) and timestamp < self._samples[-1].start: | 
| 41 raise ValueError( | 74 raise ValueError( | 
| 42 'Samples must be added in increasing timestamp order') | 75 'Samples must be added in increasing timestamp order') | 
| 43 sample = tracing_sample.Sample( | 76 sample = tracing_sample.Sample(self, | 
| 44 category, name, timestamp, args=args, parent=self) | 77 category, name, timestamp, args=args) | 
| 45 self._samples.append(sample) | 78 self._samples.append(sample) | 
| 46 self.children.append(sample) | |
| 47 | 79 | 
| 48 def AddAsyncSlice(self, async_slice): | 80 def AddAsyncSlice(self, async_slice): | 
| 49 self._async_slices.append(async_slice) | 81 self._async_slices.append(async_slice) | 
| 50 async_slice.parent = self | |
| 51 self.children.append(async_slice) | |
| 52 | 82 | 
| 53 def BeginSlice(self, category, name, timestamp, args=None): | 83 def BeginSlice(self, category, name, timestamp, args=None): | 
| 54 """Opens a new slice for the thread. | 84 """Opens a new slice for the thread. | 
| 55 Calls to beginSlice and endSlice must be made with | 85 Calls to beginSlice and endSlice must be made with | 
| 56 non-monotonically-decreasing timestamps. | 86 non-monotonically-decreasing timestamps. | 
| 57 | 87 | 
| 58 * category: Category to which the slice belongs. | 88 * category: Category to which the slice belongs. | 
| 59 * name: Name of the slice to add. | 89 * name: Name of the slice to add. | 
| 60 * timestamp: The timetsamp of the slice, in milliseconds. | 90 * timestamp: The timetsamp of the slice, in milliseconds. | 
| 61 * args: Arguments associated with | 91 * args: Arguments associated with | 
| 62 | 92 | 
| 63 Returns newly opened slice | 93 Returns newly opened slice | 
| 64 """ | 94 """ | 
| 65 if len(self._open_slices) > 0 and timestamp < self._open_slices[-1].start: | 95 if len(self._open_slices) > 0 and timestamp < self._open_slices[-1].start: | 
| 66 raise ValueError( | 96 raise ValueError( | 
| 67 'Slices must be added in increasing timestamp order') | 97 'Slices must be added in increasing timestamp order') | 
| 68 self._open_slices.append( | 98 self._open_slices.append( | 
| 69 tracing_slice.Slice(category, name, timestamp, args=args, parent=self)) | 99 tracing_slice.Slice(self, category, name, timestamp, args=args)) | 
| 70 | 100 | 
| 71 def EndSlice(self, end_timestamp): | 101 def EndSlice(self, end_timestamp): | 
| 72 """ Ends the last begun slice in this group and pushes it onto the slice | 102 """ Ends the last begun slice in this group and pushes it onto the slice | 
| 73 array. | 103 array. | 
| 74 | 104 | 
| 75 * end_timestamp: Timestamp when the slice ended in milliseconds | 105 * end_timestamp: Timestamp when the slice ended in milliseconds | 
| 76 | 106 | 
| 77 returns completed slice. | 107 returns completed slice. | 
| 78 """ | 108 """ | 
| 79 if not len(self._open_slices): | 109 if not len(self._open_slices): | 
| 80 raise ValueError( | 110 raise ValueError( | 
| 81 'EndSlice called without an open slice') | 111 'EndSlice called without an open slice') | 
| 82 curr_slice = self._open_slices.pop() | 112 curr_slice = self._open_slices.pop() | 
| 83 if end_timestamp < curr_slice.start: | 113 if end_timestamp < curr_slice.start: | 
| 84 raise ValueError( | 114 raise ValueError( | 
| 85 'Slice %s end time is before its start.' % curr_slice.name) | 115 'Slice %s end time is before its start.' % curr_slice.name) | 
| 86 curr_slice.duration = end_timestamp - curr_slice.start | 116 curr_slice.duration = end_timestamp - curr_slice.start | 
| 87 return self.PushSlice(curr_slice) | 117 return self.PushSlice(curr_slice) | 
| 88 | 118 | 
| 89 def PushSlice(self, new_slice): | 119 def PushSlice(self, new_slice): | 
| 90 self._slices.append(new_slice) | 120 self._newly_added_slices.append(new_slice) | 
| 91 self.children.append(new_slice) | |
| 92 return new_slice | 121 return new_slice | 
| 93 | 122 | 
| 94 def AutoCloseOpenSlices(self, max_timestamp=None): | 123 def AutoCloseOpenSlices(self, max_timestamp): | 
| 95 if max_timestamp is None: | |
| 96 self.UpdateBounds() | |
| 97 max_timestamp = self.end | |
| 98 while len(self._open_slices) > 0: | 124 while len(self._open_slices) > 0: | 
| 99 curr_slice = self.EndSlice(max_timestamp) | 125 curr_slice = self.EndSlice(max_timestamp) | 
| 100 curr_slice.did_not_finish = True | 126 curr_slice.did_not_finish = True | 
| 101 | 127 | 
| 102 def IsTimestampValidForBeginOrEnd(self, timestamp): | 128 def IsTimestampValidForBeginOrEnd(self, timestamp): | 
| 103 if not len(self._open_slices): | 129 if not len(self._open_slices): | 
| 104 return True | 130 return True | 
| 105 return timestamp >= self._open_slices[-1].start | 131 return timestamp >= self._open_slices[-1].start | 
| 106 | 132 | 
| 107 def UpdateBounds(self): | |
| 108 super(Thread, self).UpdateBounds() | |
| 109 | |
| 110 # Take open slices into account for the start and duration of thread event | |
| 111 if len(self._open_slices) > 0: | |
| 112 if not len(self.slices) or self.start > self._open_slices[0].start: | |
| 113 self.start = self._open_slices[0].start | |
| 114 if not len(self.slices) or self.end < self._open_slices[-1].start: | |
| 115 self.duration = self._open_slices[-1].start - self.start | |
| 116 | |
| 117 def FinalizeImport(self): | 133 def FinalizeImport(self): | 
| 118 self._BuildSliceSubRows() | 134 self._BuildSliceSubRows() | 
| 119 | 135 | 
| 120 def _BuildSliceSubRows(self): | 136 def _BuildSliceSubRows(self): | 
| 121 '''This function works by walking through slices by start time. | 137 '''This function works by walking through slices by start time. | 
| 122 | 138 | 
| 123 The basic idea here is to insert each slice as deep into the subrow | 139 The basic idea here is to insert each slice as deep into the subrow | 
| 124 list as it can go such that every subslice is fully contained by its | 140 list as it can go such that every subslice is fully contained by its | 
| 125 parent slice. | 141 parent slice. | 
| 126 | 142 | 
| (...skipping 21 matching lines...) Expand all Loading... | |
| 148 0: [ a ] [f] | 164 0: [ a ] [f] | 
| 149 1: [ b ][e] | 165 1: [ b ][e] | 
| 150 ''' | 166 ''' | 
| 151 def CompareSlices(s1, s2): | 167 def CompareSlices(s1, s2): | 
| 152 if s1.start == s2.start: | 168 if s1.start == s2.start: | 
| 153 # Break ties by having the slice with the greatest | 169 # Break ties by having the slice with the greatest | 
| 154 # end timestamp come first. | 170 # end timestamp come first. | 
| 155 return cmp(s2.end, s1.end) | 171 return cmp(s2.end, s1.end) | 
| 156 return cmp(s1.start, s2.start) | 172 return cmp(s1.start, s2.start) | 
| 157 | 173 | 
| 158 if not len(self._slices): | 174 assert len(self._toplevel_slices) == 0 | 
| 175 if not len(self._newly_added_slices): | |
| 159 return | 176 return | 
| 160 | 177 | 
| 161 sorted_slices = sorted(self._slices, cmp=CompareSlices) | 178 sorted_slices = sorted(self._newly_added_slices, cmp=CompareSlices) | 
| 162 root_slice = sorted_slices[0] | 179 root_slice = sorted_slices[0] | 
| 180 self._toplevel_slices.append(root_slice) | |
| 163 for s in sorted_slices[1:]: | 181 for s in sorted_slices[1:]: | 
| 164 if not self._AddSliceIfBounds(root_slice, s): | 182 if not self._AddSliceIfBounds(root_slice, s): | 
| 165 root_slice = s | 183 root_slice = s | 
| 184 self._toplevel_slices.append(root_slice) | |
| 185 self._newly_added_slices = [] | |
| 166 | 186 | 
| 167 def _AddSliceIfBounds(self, root, child): | 187 def _AddSliceIfBounds(self, root, child): | 
| 168 ''' Adds a child slice to a root slice its proper row. | 188 ''' Adds a child slice to a root slice its proper row. | 
| 169 Return False if the child slice is not in the bounds | 189 Return False if the child slice is not in the bounds | 
| 170 of the root slice. | 190 of the root slice. | 
| 171 | 191 | 
| 172 Because we know that the start time of child is >= the start time | 192 Because we know that the start time of child is >= the start time | 
| 173 of all other slices seen so far, we can just check the last slice | 193 of all other slices seen so far, we can just check the last slice | 
| 174 of each row for bounding. | 194 of each row for bounding. | 
| 175 ''' | 195 ''' | 
| 176 if child.start >= root.start and child.end <= root.end: | 196 if child.start >= root.start and child.end <= root.end: | 
| 177 if len(root.sub_slices) > 0: | 197 if len(root.sub_slices) > 0: | 
| 178 if self._AddSliceIfBounds(root.sub_slices[-1], child): | 198 if self._AddSliceIfBounds(root.sub_slices[-1], child): | 
| 179 return True | 199 return True | 
| 200 child.parent_slice = root | |
| 180 root.AddSubSlice(child) | 201 root.AddSubSlice(child) | 
| 181 return True | 202 return True | 
| 182 return False | 203 return False | 
| OLD | NEW |