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 5import collections 6 7from telemetry.value import list_of_scalar_values 8from telemetry.value import scalar 9 10 11class TraceEventStatsInput(object): 12 """Input for the TraceEventStats. 13 Using this object with TraceEventStats will include two metrics, one with a 14 list of times of the given event, and one for the count of the events, named 15 `metric_name + '-count'`. 16 Args: 17 event_category: The category of the event to track. 18 event_name: The name of the event to track. 19 metric_name: The name of the metric name, which accumulates all of the 20 times of the events. 21 metric_description: Description of the metric. 22 units: Units for the metric. 23 process_name: (optional) The name of the process to inspect for the trace 24 events. Defaults to 'Renderer'. 25 """ 26 def __init__(self, event_category, event_name, metric_name, 27 metric_description, units, process_name='Renderer'): 28 self.event_category = event_category 29 self.event_name = event_name 30 self.metric_name = metric_name 31 self.metric_description = metric_description 32 self.units = units 33 self.process_name = process_name 34 self.event_id = TraceEventStatsInput.GetEventId(event_category, event_name) 35 assert process_name is not None 36 37 @staticmethod 38 def GetEventId(event_category, event_name): 39 return event_category + '^SERIALIZE-DELIM^' + event_name 40 41class TraceEventStats(object): 42 """Reports durations and counts of given trace events. 43 """ 44 45 def __init__(self, trace_event_aggregator_inputs=None): 46 self._inputs_by_process_name = collections.defaultdict(list) 47 self._metrics = set() 48 self._IndexNewInputs(trace_event_aggregator_inputs) 49 50 def AddInput(self, trace_event_aggregator_input): 51 self._IndexNewInputs([trace_event_aggregator_input]) 52 53 def _IndexNewInputs(self, input_list): 54 if not input_list: 55 return 56 for input_obj in input_list: 57 name = input_obj.metric_name 58 # We check here to make sure we don't have a duplicate metric 59 assert name not in self._metrics 60 assert (name + '-count') not in self._metrics 61 self._metrics.add(name) 62 self._metrics.add(name + '-count') 63 64 self._inputs_by_process_name[input_obj.process_name].append(input_obj) 65 66 @staticmethod 67 def ThreadDurationIfPresent(event): 68 if event.thread_duration: 69 return event.thread_duration 70 else: 71 return event.duration 72 73 def AddResults(self, model, renderer_process, interactions, results): 74 del renderer_process # unused 75 assert interactions 76 for p in model.GetAllProcesses(): 77 if p.name not in self._inputs_by_process_name: 78 continue 79 80 inputs = self._inputs_by_process_name[p.name] 81 input_ids = {i.event_id for i in inputs} 82 83 def InputIdPredicate(e, ids): 84 return TraceEventStatsInput.GetEventId(e.category, e.name) in ids 85 86 self._AddResultsInternal( 87 p.IterAllEvents( 88 recursive=True, 89 event_type_predicate=lambda t: True, 90 event_predicate= 91 lambda e, ids=input_ids: InputIdPredicate(e, ids)), 92 interactions, 93 results, 94 inputs) 95 96 # We assume events have been filtered already. 'events' is an iterator. 97 def _AddResultsInternal(self, events, interactions, results, inputs): 98 times_by_event_id = collections.defaultdict(list) 99 100 for event in events: 101 if not any(interaction.start <= event.start <= interaction.end 102 for interaction in interactions): 103 continue 104 event_id = TraceEventStatsInput.GetEventId(event.category, event.name) 105 times_by_event_id[event_id].append(self.ThreadDurationIfPresent(event)) 106 107 if not times_by_event_id: 108 return 109 110 inputs_by_event_id = dict([[input_obj.event_id, input_obj] 111 for input_obj in inputs]) 112 113 for (event_name, times) in times_by_event_id.iteritems(): 114 input_for_event = inputs_by_event_id[event_name] 115 name = input_for_event.metric_name 116 results.AddValue(scalar.ScalarValue( 117 page=results.current_page, 118 tir_label=interactions[0].label, 119 name=name + '-count', 120 units='count', 121 value=len(times), 122 description='The number of times ' + name + ' was recorded.')) 123 if len(times) == 0: 124 continue 125 results.AddValue(list_of_scalar_values.ListOfScalarValues( 126 page=results.current_page, 127 tir_label=interactions[0].label, 128 name=name, 129 units=input_for_event.units, 130 values=times, 131 description=input_for_event.metric_description)) 132