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