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 5 from telemetry.util import statistics 6 from telemetry.value import improvement_direction 7 from telemetry.value import scalar 8 from telemetry.web_perf.metrics import timeline_based_metric 9 10 import logging 11 12 class V8EventStat(object): 13 14 def __init__(self, src_event_name, result_name, result_description): 15 self.src_event_name = src_event_name 16 self.result_name = result_name 17 self.result_description = result_description 18 self.thread_duration = 0.0 19 self.thread_duration_inside_idle = 0.0 20 self.idle_task_overrun_duration = 0.0 21 self.max_thread_duration = 0.0 22 self.count = 0 23 24 @property 25 def thread_duration_outside_idle(self): 26 return self.thread_duration - self.thread_duration_inside_idle 27 28 @property 29 def percentage_thread_duration_during_idle(self): 30 return statistics.DivideIfPossibleOrZero( 31 100 * self.thread_duration_inside_idle, self.thread_duration) 32 33 class V8GCLatency(timeline_based_metric.TimelineBasedMetric): 34 _RENDERER_MAIN_THREAD = 'CrRendererMain' 35 _IDLE_TASK_PARENT = 'SingleThreadIdleTaskRunner::RunTask' 36 37 def __init__(self): 38 super(V8GCLatency, self).__init__() 39 40 def AddResults(self, model, renderer_thread, interaction_records, results): 41 self.VerifyNonOverlappedRecords(interaction_records) 42 self._AddV8MetricsToResults(model, interaction_records, results) 43 44 def _AddV8MetricsToResults(self, model, 45 interaction_records, results): 46 self._AddV8EventStatsToResults(model, interaction_records, results) 47 48 def _AddV8EventStatsToResults(self, model, interactions, results): 49 v8_event_stats = [ 50 V8EventStat('V8.GCIncrementalMarking', 51 'v8_gc_incremental_marking', 52 'incremental marking steps'), 53 V8EventStat('V8.GCScavenger', 54 'v8_gc_scavenger', 55 'scavenges'), 56 V8EventStat('V8.GCCompactor', 57 'v8_gc_mark_compactor', 58 'mark-sweep-compactor'), 59 V8EventStat('V8.GCFinalizeMC', 60 'v8_gc_finalize_incremental', 61 'finalization of incremental marking'), 62 V8EventStat('V8.GCFinalizeMCReduceMemory', 63 'v8_gc_finalize_incremental_reduce_memory', 64 'finalization of incremental marking with memory reducer')] 65 label = interactions[0].label 66 name_to_v8_stat = {x.src_event_name : x for x in v8_event_stats} 67 thread_time_not_available = False 68 for event in model.IterAllSlices(): 69 if (not timeline_based_metric.IsEventInInteractions(event, interactions) 70 or not event.name in name_to_v8_stat): 71 continue 72 event_stat = name_to_v8_stat[event.name] 73 if event.thread_duration is None: 74 thread_time_not_available = True 75 event_duration = event.duration 76 else: 77 event_duration = event.thread_duration 78 event_stat.thread_duration += event_duration 79 event_stat.max_thread_duration = max(event_stat.max_thread_duration, 80 event_duration) 81 event_stat.count += 1 82 83 parent_idle_task = self._ParentIdleTask(event) 84 if parent_idle_task: 85 allotted_idle_time = parent_idle_task.args['allotted_time_ms'] 86 idle_task_wall_overrun = 0 87 if event.duration > allotted_idle_time: 88 idle_task_wall_overrun = event.duration - allotted_idle_time 89 # Don't count time over the deadline as being inside idle time. 90 # Since the deadline should be relative to wall clock we compare 91 # allotted_time_ms with wall duration instead of thread duration, and 92 # then assume the thread duration was inside idle for the same 93 # percentage of time. 94 inside_idle = event_duration * statistics.DivideIfPossibleOrZero( 95 event.duration - idle_task_wall_overrun, event.duration) 96 event_stat.thread_duration_inside_idle += inside_idle 97 event_stat.idle_task_overrun_duration += idle_task_wall_overrun 98 99 if thread_time_not_available: 100 logging.warning( 101 'thread time is not available in trace data, switch to walltime') 102 103 for v8_event_stat in v8_event_stats: 104 results.AddValue(scalar.ScalarValue( 105 results.current_page, v8_event_stat.result_name, 'ms', 106 v8_event_stat.thread_duration, 107 description=('Total thread duration spent in %s' % 108 v8_event_stat.result_description), 109 tir_label=label, 110 improvement_direction=improvement_direction.DOWN)) 111 results.AddValue(scalar.ScalarValue( 112 results.current_page, '%s_max' % v8_event_stat.result_name, 'ms', 113 v8_event_stat.max_thread_duration, 114 description=('Max thread duration spent in %s' % 115 v8_event_stat.result_description), 116 tir_label=label)) 117 results.AddValue(scalar.ScalarValue( 118 results.current_page, '%s_count' % v8_event_stat.result_name, 'count', 119 v8_event_stat.count, 120 description=('Number of %s' % 121 v8_event_stat.result_description), 122 tir_label=label, 123 improvement_direction=improvement_direction.DOWN)) 124 average_thread_duration = statistics.DivideIfPossibleOrZero( 125 v8_event_stat.thread_duration, v8_event_stat.count) 126 results.AddValue(scalar.ScalarValue( 127 results.current_page, '%s_average' % v8_event_stat.result_name, 'ms', 128 average_thread_duration, 129 description=('Average thread duration spent in %s' % 130 v8_event_stat.result_description), 131 tir_label=label, 132 improvement_direction=improvement_direction.DOWN)) 133 results.AddValue(scalar.ScalarValue(results.current_page, 134 '%s_outside_idle' % v8_event_stat.result_name, 'ms', 135 v8_event_stat.thread_duration_outside_idle, 136 description=( 137 'Total thread duration spent in %s outside of idle tasks' % 138 v8_event_stat.result_description), 139 tir_label=label)) 140 results.AddValue(scalar.ScalarValue(results.current_page, 141 '%s_idle_deadline_overrun' % v8_event_stat.result_name, 'ms', 142 v8_event_stat.idle_task_overrun_duration, 143 description=('Total idle task deadline overrun for %s idle tasks' 144 % v8_event_stat.result_description), 145 tir_label=label, 146 improvement_direction=improvement_direction.DOWN)) 147 results.AddValue(scalar.ScalarValue(results.current_page, 148 '%s_percentage_idle' % v8_event_stat.result_name, 'idle%', 149 v8_event_stat.percentage_thread_duration_during_idle, 150 description=('Percentage of %s spent in idle time' % 151 v8_event_stat.result_description), 152 tir_label=label, 153 improvement_direction=improvement_direction.UP)) 154 155 # Add total metrics. 156 gc_total = sum(x.thread_duration for x in v8_event_stats) 157 gc_total_outside_idle = sum( 158 x.thread_duration_outside_idle for x in v8_event_stats) 159 gc_total_idle_deadline_overrun = sum( 160 x.idle_task_overrun_duration for x in v8_event_stats) 161 gc_total_percentage_idle = statistics.DivideIfPossibleOrZero( 162 100 * (gc_total - gc_total_outside_idle), gc_total) 163 164 results.AddValue(scalar.ScalarValue(results.current_page, 165 'v8_gc_total', 'ms', gc_total, 166 description='Total thread duration of all garbage collection events', 167 tir_label=label, 168 improvement_direction=improvement_direction.DOWN)) 169 results.AddValue(scalar.ScalarValue(results.current_page, 170 'v8_gc_total_outside_idle', 'ms', gc_total_outside_idle, 171 description=( 172 'Total thread duration of all garbage collection events outside of ' 173 'idle tasks'), 174 tir_label=label, 175 improvement_direction=improvement_direction.DOWN)) 176 results.AddValue(scalar.ScalarValue(results.current_page, 177 'v8_gc_total_idle_deadline_overrun', 'ms', 178 gc_total_idle_deadline_overrun, 179 description=( 180 'Total idle task deadline overrun for all idle tasks garbage ' 181 'collection events'), 182 tir_label=label, 183 improvement_direction=improvement_direction.DOWN)) 184 results.AddValue(scalar.ScalarValue(results.current_page, 185 'v8_gc_total_percentage_idle', 'idle%', gc_total_percentage_idle, 186 description=( 187 'Percentage of the thread duration of all garbage collection ' 188 'events spent inside of idle tasks'), 189 tir_label=label, 190 improvement_direction=improvement_direction.UP)) 191 192 def _ParentIdleTask(self, event): 193 parent = event.parent_slice 194 while parent: 195 if parent.name == self._IDLE_TASK_PARENT: 196 return parent 197 parent = parent.parent_slice 198 return None 199 200