| OLD | NEW |
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 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 re | 5 import re |
| 6 | 6 |
| 7 from telemetry import decorators | 7 from telemetry import decorators |
| 8 import telemetry.core.timeline.bounds as timeline_bounds | 8 import telemetry.core.timeline.bounds as timeline_bounds |
| 9 | 9 |
| 10 | 10 |
| 11 IS_SMOOTH = 'is_smooth' | 11 IS_SMOOTH = 'is_smooth' |
| 12 IS_LOADING_RESOURCES = 'is_loading_resources' | 12 IS_RESPONSIVE = 'is_responsive' |
| 13 | 13 |
| 14 FLAGS = [ | 14 FLAGS = [ |
| 15 IS_SMOOTH, | 15 IS_SMOOTH, |
| 16 IS_LOADING_RESOURCES | 16 IS_RESPONSIVE |
| 17 ] | 17 ] |
| 18 | 18 |
| 19 | 19 |
| 20 class ThreadTimeRangeOverlappedException(Exception): | 20 class ThreadTimeRangeOverlappedException(Exception): |
| 21 """Exception that can be thrown when computing overlapped thread time range | 21 """Exception that can be thrown when computing overlapped thread time range |
| 22 with other events. | 22 with other events. |
| 23 """ | 23 """ |
| 24 | 24 |
| 25 class NoThreadTimeDataException(ThreadTimeRangeOverlappedException): |
| 26 """Exception that can be thrown if there is not sufficient thread time data |
| 27 to compute the overlapped thread time range.""" |
| 25 | 28 |
| 26 def IsTimelineInteractionRecord(event_name): | 29 def IsTimelineInteractionRecord(event_name): |
| 27 return event_name.startswith('Interaction.') | 30 return event_name.startswith('Interaction.') |
| 28 | 31 |
| 29 | 32 |
| 30 class TimelineInteractionRecord(object): | 33 class TimelineInteractionRecord(object): |
| 31 """Represents an interaction that took place during a timeline recording. | 34 """Represents an interaction that took place during a timeline recording. |
| 32 | 35 |
| 33 As a page runs, typically a number of different (simulated) user interactions | 36 As a page runs, typically a number of different (simulated) user interactions |
| 34 take place. For instance, a user might click a button in a mail app causing a | 37 take place. For instance, a user might click a button in a mail app causing a |
| (...skipping 10 matching lines...) Expand all Loading... |
| 45 Determining these things is hard to do, simply by observing the state given to | 48 Determining these things is hard to do, simply by observing the state given to |
| 46 a page from javascript. There are hints, for instance if network requests are | 49 a page from javascript. There are hints, for instance if network requests are |
| 47 sent, or if a CSS animation is pending. But this is by no means a complete | 50 sent, or if a CSS animation is pending. But this is by no means a complete |
| 48 story. | 51 story. |
| 49 | 52 |
| 50 Instead, we expect pages to mark up the timeline what they are doing, with | 53 Instead, we expect pages to mark up the timeline what they are doing, with |
| 51 logical names, and flags indicating the semantics of that interaction. This | 54 logical names, and flags indicating the semantics of that interaction. This |
| 52 is currently done by pushing markers into the console.time/timeEnd API: this | 55 is currently done by pushing markers into the console.time/timeEnd API: this |
| 53 for instance can be issued in JS: | 56 for instance can be issued in JS: |
| 54 | 57 |
| 55 var str = 'Interaction.SendEmail/is_smooth,is_loading_resources'; | 58 var str = 'Interaction.SendEmail/is_smooth,is_responsive'; |
| 56 console.time(str); | 59 console.time(str); |
| 57 setTimeout(function() { | 60 setTimeout(function() { |
| 58 console.timeEnd(str); | 61 console.timeEnd(str); |
| 59 }, 1000); | 62 }, 1000); |
| 60 | 63 |
| 61 When run with perf.measurements.timeline_based_measurement running, this will | 64 When run with perf.measurements.timeline_based_measurement running, this will |
| 62 then cause a TimelineInteractionRecord to be created for this range and both | 65 then cause a TimelineInteractionRecord to be created for this range and both |
| 63 smoothness and network metrics to be reported for the marked up 1000ms | 66 smoothness and network metrics to be reported for the marked up 1000ms |
| 64 time-range. | 67 time-range. |
| 65 | 68 |
| 66 """ | 69 """ |
| 67 | 70 |
| 68 def __init__(self, logical_name, start, end, async_event=None): | 71 def __init__(self, logical_name, start, end, async_event=None): |
| 69 assert logical_name | 72 assert logical_name |
| 70 self.logical_name = logical_name | 73 self.logical_name = logical_name |
| 71 self.start = start | 74 self.start = start |
| 72 self.end = end | 75 self.end = end |
| 73 self.is_smooth = False | 76 self.is_smooth = False |
| 74 self.is_loading_resources = False | 77 self.is_responsive = False |
| 75 self._async_event = async_event | 78 self._async_event = async_event |
| 76 | 79 |
| 77 # TODO(nednguyen): After crbug.com/367175 is marked fixed, we should be able | 80 # TODO(nednguyen): After crbug.com/367175 is marked fixed, we should be able |
| 78 # to get rid of perf.measurements.smooth_gesture_util and make this the only | 81 # to get rid of perf.measurements.smooth_gesture_util and make this the only |
| 79 # constructor method for TimelineInteractionRecord. | 82 # constructor method for TimelineInteractionRecord. |
| 80 @staticmethod | 83 @staticmethod |
| 81 def FromAsyncEvent(async_event): | 84 def FromAsyncEvent(async_event): |
| 82 """Construct an timeline_interaction_record from an async event. | 85 """Construct an timeline_interaction_record from an async event. |
| 83 Args: | 86 Args: |
| 84 async_event: An instance of | 87 async_event: An instance of |
| 85 telemetry.core.timeline.async_slices.AsyncSlice | 88 telemetry.core.timeline.async_slices.AsyncSlice |
| 86 """ | 89 """ |
| 90 assert async_event.start_thread == async_event.end_thread, ( |
| 91 'Start thread of this record\'s async event is not the same as its ' |
| 92 'end thread') |
| 87 m = re.match('Interaction\.(.+)\/(.+)', async_event.name) | 93 m = re.match('Interaction\.(.+)\/(.+)', async_event.name) |
| 88 if m: | 94 if m: |
| 89 logical_name = m.group(1) | 95 logical_name = m.group(1) |
| 90 if m.group(1) != '': | 96 if m.group(1) != '': |
| 91 flags = m.group(2).split(',') | 97 flags = m.group(2).split(',') |
| 92 else: | 98 else: |
| 93 flags = [] | 99 flags = [] |
| 94 else: | 100 else: |
| 95 m = re.match('Interaction\.(.+)', async_event.name) | 101 m = re.match('Interaction\.(.+)', async_event.name) |
| 96 assert m | 102 assert m |
| 97 logical_name = m.group(1) | 103 logical_name = m.group(1) |
| 98 flags = [] | 104 flags = [] |
| 99 | 105 |
| 100 record = TimelineInteractionRecord(logical_name, async_event.start, | 106 record = TimelineInteractionRecord(logical_name, async_event.start, |
| 101 async_event.end, async_event) | 107 async_event.end, async_event) |
| 102 for f in flags: | 108 for f in flags: |
| 103 if not f in FLAGS: | 109 if not f in FLAGS: |
| 104 raise Exception( | 110 raise Exception( |
| 105 'Unrecognized flag in timeline Interaction record: %s' % f) | 111 'Unrecognized flag in timeline Interaction record: %s' % f) |
| 106 record.is_smooth = IS_SMOOTH in flags | 112 record.is_smooth = IS_SMOOTH in flags |
| 107 record.is_loading_resources = IS_LOADING_RESOURCES in flags | 113 record.is_responsive = IS_RESPONSIVE in flags |
| 108 return record | 114 return record |
| 109 | 115 |
| 110 def GetResultNameFor(self, result_name): | 116 def GetResultNameFor(self, result_name): |
| 111 return '%s-%s' % (self.logical_name, result_name) | 117 return '%s-%s' % (self.logical_name, result_name) |
| 112 | 118 |
| 113 @decorators.Cache | 119 @decorators.Cache |
| 114 def GetBounds(self): | 120 def GetBounds(self): |
| 115 bounds = timeline_bounds.Bounds() | 121 bounds = timeline_bounds.Bounds() |
| 116 bounds.AddValue(self.start) | 122 bounds.AddValue(self.start) |
| 117 bounds.AddValue(self.end) | 123 bounds.AddValue(self.end) |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 162 thread, and thus can't be used to compute the overlapped thread duration. | 168 thread, and thus can't be used to compute the overlapped thread duration. |
| 163 Hence, we use a heuristic to compute the overlap (see | 169 Hence, we use a heuristic to compute the overlap (see |
| 164 _GetOverlappedThreadTimeForSliceInDifferentThread for more details) | 170 _GetOverlappedThreadTimeForSliceInDifferentThread for more details) |
| 165 | 171 |
| 166 Args: | 172 Args: |
| 167 timeline_slice: An instance of telemetry.core.timeline.slice.Slice | 173 timeline_slice: An instance of telemetry.core.timeline.slice.Slice |
| 168 """ | 174 """ |
| 169 if not self._async_event: | 175 if not self._async_event: |
| 170 raise ThreadTimeRangeOverlappedException( | 176 raise ThreadTimeRangeOverlappedException( |
| 171 'This record was not constructed from async event') | 177 'This record was not constructed from async event') |
| 172 if self._async_event.start_thread != self._async_event.end_thread: | |
| 173 raise ThreadTimeRangeOverlappedException( | |
| 174 'Start thread of this record\'s async event is not the same as its ' | |
| 175 'end thread') | |
| 176 if not self._async_event.has_thread_timestamps: | 178 if not self._async_event.has_thread_timestamps: |
| 177 raise ThreadTimeRangeOverlappedException( | 179 raise NoThreadTimeDataException( |
| 178 'This record\'s async_event does not contain thread time data. ' | 180 'This record\'s async_event does not contain thread time data. ' |
| 179 'Event data: %s' % repr(self._async_event)) | 181 'Event data: %s' % repr(self._async_event)) |
| 180 if not timeline_slice.has_thread_timestamps: | 182 if not timeline_slice.has_thread_timestamps: |
| 181 raise ThreadTimeRangeOverlappedException( | 183 raise NoThreadTimeDataException( |
| 182 'slice does not contain thread time data') | 184 'slice does not contain thread time data') |
| 183 | 185 |
| 184 if timeline_slice.parent_thread == self._async_event.start_thread: | 186 if timeline_slice.parent_thread == self._async_event.start_thread: |
| 185 return self._GetOverlappedThreadTimeForSliceInSameThread( | 187 return self._GetOverlappedThreadTimeForSliceInSameThread( |
| 186 timeline_slice) | 188 timeline_slice) |
| 187 else: | 189 else: |
| 188 return self._GetOverlappedThreadTimeForSliceInDifferentThread( | 190 return self._GetOverlappedThreadTimeForSliceInDifferentThread( |
| 189 timeline_slice) | 191 timeline_slice) |
| 190 | 192 |
| 191 def _GetOverlappedThreadTimeForSliceInSameThread(self, timeline_slice): | 193 def _GetOverlappedThreadTimeForSliceInSameThread(self, timeline_slice): |
| (...skipping 11 matching lines...) Expand all Loading... |
| 203 timeline_slice.start, timeline_slice.end, | 205 timeline_slice.start, timeline_slice.end, |
| 204 self.start, self.end) | 206 self.start, self.end) |
| 205 if timeline_slice.duration == 0 or self._async_event.duration == 0: | 207 if timeline_slice.duration == 0 or self._async_event.duration == 0: |
| 206 return 0 | 208 return 0 |
| 207 timeline_slice_scheduled_ratio = ( | 209 timeline_slice_scheduled_ratio = ( |
| 208 timeline_slice.thread_duration / float(timeline_slice.duration)) | 210 timeline_slice.thread_duration / float(timeline_slice.duration)) |
| 209 record_scheduled_ratio = ( | 211 record_scheduled_ratio = ( |
| 210 self._async_event.thread_duration / float(self._async_event.duration)) | 212 self._async_event.thread_duration / float(self._async_event.duration)) |
| 211 return (overlapped_walltime_duration * timeline_slice_scheduled_ratio * | 213 return (overlapped_walltime_duration * timeline_slice_scheduled_ratio * |
| 212 record_scheduled_ratio) | 214 record_scheduled_ratio) |
| OLD | NEW |