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 |