1# Copyright 2015 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5from telemetry.value import improvement_direction
6from telemetry.value import list_of_scalar_values
7from telemetry.web_perf.metrics import timeline_based_metric
8
9
10WRITE_EVENT_NAME = 'Registry::RegisterBlob'
11READ_EVENT_NAME = 'BlobRequest'
12
13
14class BlobTimelineMetric(timeline_based_metric.TimelineBasedMetric):
15  """BlobTimelineMetric reports timing information about blob storage.
16
17  The following metrics are added to the results:
18    * blob write times (blob_writes)
19    * blob read times (blob_reads)
20  """
21
22  def __init__(self):
23    super(BlobTimelineMetric, self).__init__()
24
25  @staticmethod
26  def IsWriteEvent(event):
27    return event.name == WRITE_EVENT_NAME
28
29  @staticmethod
30  def IsReadEvent(event):
31    return event.name == READ_EVENT_NAME
32
33  @staticmethod
34  def IsEventInInteraction(event, interaction):
35    return interaction.start <= event.start <= interaction.end
36
37  @staticmethod
38  def ThreadDurationIfPresent(event):
39    if event.thread_duration:
40      return event.thread_duration
41    else:
42      return event.duration
43
44  def AddResults(self, model, renderer_thread, interactions, results):
45    assert interactions
46
47    write_events = []
48    read_events = []
49    for event in model.IterAllEvents(
50        event_predicate=lambda e: self.IsWriteEvent(e) or self.IsReadEvent(e)):
51      if self.IsReadEvent(event):
52        read_events.append(event)
53      else:
54        write_events.append(event)
55
56    # Only these private methods are tested for mocking simplicity.
57    self._AddWriteResultsInternal(write_events, interactions, results)
58    self._AddReadResultsInternal(read_events, interactions, results)
59
60  def _AddWriteResultsInternal(self, events, interactions, results):
61    writes = []
62    for event in events:
63      if (self.IsWriteEvent(event) and
64          any(self.IsEventInInteraction(event, interaction)
65              for interaction in interactions)):
66        writes.append(self.ThreadDurationIfPresent(event))
67    if writes:
68      results.AddValue(list_of_scalar_values.ListOfScalarValues(
69          page=results.current_page,
70          tir_label=interactions[0].label,
71          name='blob-writes',
72          units='ms',
73          values=writes,
74          description='List of durations of blob writes.',
75          improvement_direction=improvement_direction.DOWN))
76    else:
77      results.AddValue(list_of_scalar_values.ListOfScalarValues(
78          page=results.current_page,
79          tir_label=interactions[0].label,
80          name='blob-writes',
81          units='ms',
82          values=None,
83          none_value_reason='No blob write events found for this interaction.',
84          improvement_direction=improvement_direction.DOWN))
85
86  def _AddReadResultsInternal(self, events, interactions, results):
87    reads = dict()
88    for event in events:
89      if (not self.IsReadEvent(event) or
90          not any(self.IsEventInInteraction(event, interaction)
91                 for interaction in interactions)):
92        continue
93      # Every blob has unique UUID.  To get the total time for reading
94      # a blob, we add up the time of all events with the same blob UUID.
95      uuid = event.args['uuid']
96      if uuid not in reads:
97        reads[uuid] = 0
98      reads[uuid] += self.ThreadDurationIfPresent(event)
99
100    if reads:
101      results.AddValue(list_of_scalar_values.ListOfScalarValues(
102          page=results.current_page,
103          tir_label=interactions[0].label,
104          name='blob-reads',
105          units='ms',
106          values=reads.values(),
107          description='List of read times for blobs.',
108          improvement_direction=improvement_direction.DOWN))
109    else:
110      results.AddValue(list_of_scalar_values.ListOfScalarValues(
111          page=results.current_page,
112          tir_label=interactions[0].label,
113          name='blob-reads',
114          units='ms',
115          values=None,
116          none_value_reason='No blob read events found for this interaction.',
117          improvement_direction=improvement_direction.DOWN))
118