1a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)# Copyright 2014 The Chromium Authors. All rights reserved.
2a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
3a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)# found in the LICENSE file.
4a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
5a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import re
6a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
7f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)import telemetry.timeline.bounds as timeline_bounds
86e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)from telemetry import decorators
9a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
10116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch# Enables the fast metric for this interaction
11116680a4aac90f2aa7413d9095a592090648e557Ben MurdochIS_FAST = 'is_fast'
12116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch# Enables the responsiveness metric for this interaction
1346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)IS_RESPONSIVE = 'is_responsive'
14116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch# Enables the smoothness metric for this interaction
15116680a4aac90f2aa7413d9095a592090648e557Ben MurdochIS_SMOOTH = 'is_smooth'
16116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch# Allows multiple duplicate interactions of the same type
17116680a4aac90f2aa7413d9095a592090648e557Ben MurdochREPEATABLE = 'repeatable'
18a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
19116680a4aac90f2aa7413d9095a592090648e557Ben MurdochMETRICS = [
20116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    IS_FAST,
21116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    IS_RESPONSIVE,
22116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    IS_SMOOTH
23a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch]
24116680a4aac90f2aa7413d9095a592090648e557Ben MurdochFLAGS = METRICS + [REPEATABLE]
25a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
26cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
27cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)class ThreadTimeRangeOverlappedException(Exception):
28cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  """Exception that can be thrown when computing overlapped thread time range
29cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  with other events.
30cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  """
31cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
3246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)class NoThreadTimeDataException(ThreadTimeRangeOverlappedException):
3346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  """Exception that can be thrown if there is not sufficient thread time data
3446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  to compute the overlapped thread time range."""
35cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
36a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)def IsTimelineInteractionRecord(event_name):
37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return event_name.startswith('Interaction.')
38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
39116680a4aac90f2aa7413d9095a592090648e557Ben Murdochdef _AssertFlagsAreValid(flags):
40116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  assert isinstance(flags, list)
41116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  for f in flags:
42116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if f not in FLAGS:
43116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      raise AssertionError(
44116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          'Unrecognized flag for a timeline interaction record: %s' % f)
45116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
46116680a4aac90f2aa7413d9095a592090648e557Ben Murdochdef GetJavaScriptMarker(label, flags):
47116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  """Computes the marker string of an interaction record.
48116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
49116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  This marker string can be used with JavaScript API console.time()
50116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  and console.timeEnd() to mark the beginning and end of the
51116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  interaction record..
52116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
53116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  Args:
54116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    label: The label used to identify the interaction record.
55116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    flags: the flags for the interaction record see FLAGS above.
56116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
57116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  Returns:
58116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    The interaction record marker string (e.g., Interaction.Label/flag1,flag2).
59116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
60116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  Raises:
61116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    AssertionError: If one or more of the flags is unrecognized.
62116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  """
63116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  _AssertFlagsAreValid(flags)
64116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return 'Interaction.%s/%s' % (label, ','.join(flags))
65a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
66a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)class TimelineInteractionRecord(object):
67a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  """Represents an interaction that took place during a timeline recording.
68a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
69a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  As a page runs, typically a number of different (simulated) user interactions
70a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  take place. For instance, a user might click a button in a mail app causing a
71a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  popup to animate in. Then they might press another button that sends data to a
72a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  server and simultaneously closes the popup without an animation. These are two
73a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  interactions.
74a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
75a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  From the point of view of the page, each interaction might have a different
76116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  label: ClickComposeButton and SendEmail, for instance. From the point
77116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  of view of the benchmarking harness, the labels aren't so interesting as what
78a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  the performance expectations are for that interaction: was it loading
79a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  resources from the network? was there an animation?
80a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
81a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  Determining these things is hard to do, simply by observing the state given to
82a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  a page from javascript. There are hints, for instance if network requests are
83a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  sent, or if a CSS animation is pending. But this is by no means a complete
84a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  story.
85a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
86a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  Instead, we expect pages to mark up the timeline what they are doing, with
87116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  label and flags indicating the semantics of that interaction. This
88a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  is currently done by pushing markers into the console.time/timeEnd API: this
89a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  for instance can be issued in JS:
90a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
91116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch     var str = 'Interaction.SendEmail/is_smooth,is_responsive,is_fast';
92a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     console.time(str);
93a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     setTimeout(function() {
94a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)       console.timeEnd(str);
95a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     }, 1000);
96a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
97a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  When run with perf.measurements.timeline_based_measurement running, this will
98116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  then cause a TimelineInteractionRecord to be created for this range with
99116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  smoothness, responsive, and fast metrics reported for the marked up 1000ms
100a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  time-range.
101a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
102116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  The valid interaction flags are:
103116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch     * is_fast: Enables the fast metric
104116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch     * is_responsive: Enables the responsiveness metric
105116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch     * is_smooth: Enables the smoothness metric
106116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch     * repeatable: Allows other interactions to use the same label
107a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  """
108cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
109116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  def __init__(self, label, start, end, async_event=None, flags=None):
110116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    assert label
111116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    self._label = label
112116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    self._start = start
113116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    self._end = end
114cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    self._async_event = async_event
115116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    self._flags = flags if flags is not None else []
116116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    _AssertFlagsAreValid(self._flags)
117116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
118116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  @property
119116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  def label(self):
120116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return self._label
121116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
122116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  @property
123116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  def start(self):
124116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return self._start
125116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
126116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  @property
127116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  def end(self):
128116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return self._end
129116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
130116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  @property
131116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  def is_fast(self):
132116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return IS_FAST in self._flags
133116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
134116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  @property
135116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  def is_responsive(self):
136116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return IS_RESPONSIVE in self._flags
137116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
138116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  @property
139116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  def is_smooth(self):
140116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return IS_SMOOTH in self._flags
141116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
142116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  @property
143116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  def repeatable(self):
144116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return REPEATABLE in self._flags
145a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
146cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  # TODO(nednguyen): After crbug.com/367175 is marked fixed, we should be able
147cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  # to get rid of perf.measurements.smooth_gesture_util and make this the only
148cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  # constructor method for TimelineInteractionRecord.
149116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  @classmethod
150116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  def FromAsyncEvent(cls, async_event):
151cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    """Construct an timeline_interaction_record from an async event.
152cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    Args:
153cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      async_event: An instance of
154f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        telemetry.timeline.async_slices.AsyncSlice
155cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    """
15646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    assert async_event.start_thread == async_event.end_thread, (
15746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        'Start thread of this record\'s async event is not the same as its '
15846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        'end thread')
159116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    m = re.match('Interaction\.(?P<label>.+?)(/(?P<flags>[^/]+))?$',
160116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                 async_event.name)
161116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    assert m, "Async event is not an interaction record."
162116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    label = m.group('label')
163116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    flags = m.group('flags').split(',') if m.group('flags') is not None else []
164116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return cls(label, async_event.start, async_event.end, async_event, flags)
165a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
166a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  @decorators.Cache
167a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  def GetBounds(self):
168a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    bounds = timeline_bounds.Bounds()
169a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    bounds.AddValue(self.start)
170a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    bounds.AddValue(self.end)
171a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    return bounds
172a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
173116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  def HasMetric(self, metric_type):
174116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if metric_type not in METRICS:
175116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      raise AssertionError('Unrecognized metric type for a timeline '
176116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                           'interaction record: %s' % metric_type)
177116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return metric_type in self._flags
178cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
179cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  def GetOverlappedThreadTimeForSlice(self, timeline_slice):
180cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    """Get the thread duration of timeline_slice that overlaps with this record.
181cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
182cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    There are two cases :
183cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
184cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    Case 1: timeline_slice runs in the same thread as the record.
185cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
186cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                  |    [       timeline_slice         ]
187cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      THREAD 1    |                  |                              |
188cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                  |            record starts                    record ends
189cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
190cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                      (relative order in thread time)
191cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
192cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      As the thread timestamps in timeline_slice and record are consistent, we
193cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      simply use them to compute the overlap.
194cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
195cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    Case 2: timeline_slice runs in a different thread from the record's.
196cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
197cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                  |
198cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      THREAD 2    |    [       timeline_slice         ]
199cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                  |
200cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
201cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                  |
202cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      THREAD 1    |               |                               |
203cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                  |          record starts                      record ends
204cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
205cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                      (relative order in wall-time)
206cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
207cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      Unlike case 1, thread timestamps of a thread are measured by its
208cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      thread-specific clock, which is inconsistent with that of the other
209cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      thread, and thus can't be used to compute the overlapped thread duration.
210cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      Hence, we use a heuristic to compute the overlap (see
211cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      _GetOverlappedThreadTimeForSliceInDifferentThread for more details)
212cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
213cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    Args:
214f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      timeline_slice: An instance of telemetry.timeline.slice.Slice
215cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    """
216cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if not self._async_event:
217cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      raise ThreadTimeRangeOverlappedException(
218cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)          'This record was not constructed from async event')
219cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if not self._async_event.has_thread_timestamps:
22046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      raise NoThreadTimeDataException(
221cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)          'This record\'s async_event does not contain thread time data. '
222cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)          'Event data: %s' % repr(self._async_event))
223cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if not timeline_slice.has_thread_timestamps:
22446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      raise NoThreadTimeDataException(
225cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)          'slice does not contain thread time data')
226cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
227cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if timeline_slice.parent_thread == self._async_event.start_thread:
228cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      return self._GetOverlappedThreadTimeForSliceInSameThread(
229cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)          timeline_slice)
230cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    else:
231cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      return self._GetOverlappedThreadTimeForSliceInDifferentThread(
232cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)          timeline_slice)
233cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
234cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  def _GetOverlappedThreadTimeForSliceInSameThread(self, timeline_slice):
235cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    return timeline_bounds.Bounds.GetOverlap(
236cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        timeline_slice.thread_start, timeline_slice.thread_end,
237cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        self._async_event.thread_start, self._async_event.thread_end)
238cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
239cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  def _GetOverlappedThreadTimeForSliceInDifferentThread(self, timeline_slice):
240cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    # In case timeline_slice's parent thread is not the parent thread of the
241cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    # async slice that issues this record, we assume that events are descheduled
242cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    # uniformly. The overlap duration in thread time is then computed by
243cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    # multiplying the overlap wall-time duration of timeline_slice and the
244cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    # record's async slice with their thread_duration/duration ratios.
245cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    overlapped_walltime_duration = timeline_bounds.Bounds.GetOverlap(
246cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        timeline_slice.start, timeline_slice.end,
247cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        self.start, self.end)
248cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if timeline_slice.duration == 0 or self._async_event.duration == 0:
249cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      return 0
250cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    timeline_slice_scheduled_ratio = (
251cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        timeline_slice.thread_duration / float(timeline_slice.duration))
252cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    record_scheduled_ratio = (
253cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        self._async_event.thread_duration / float(self._async_event.duration))
254cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    return (overlapped_walltime_duration * timeline_slice_scheduled_ratio *
255cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            record_scheduled_ratio)
25646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
25746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  def __repr__(self):
258116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    flags_str = ','.join(self._flags)
259116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return ('TimelineInteractionRecord(label=\'%s\', start=%f, end=%f,' +
26046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)            ' flags=%s, async_event=%s)') % (
261116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                self.label,
26246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                self.start,
26346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                self.end,
26446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                flags_str,
26546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                repr(self._async_event))
266