1# Copyright 2016 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. 4from telemetry.util import statistics 5from telemetry.value import scalar 6from telemetry.web_perf.metrics import timeline_based_metric 7 8 9class V8ExecutionMetric(timeline_based_metric.TimelineBasedMetric): 10 """ This Metric aggregates various V8 runtime measurements.""" 11 _EVENTS = ('v8.run', 'v8.compile', 'V8.Execute', 'WindowProxy::initialize',) 12 _RENDERER_MAIN_THREAD = 'CrRendererMain' 13 14 def __init__(self): 15 super(V8ExecutionMetric, self).__init__() 16 self._stats = [ 17 V8TotalTimeStats('v8_execution_time_total', ['V8.Execute']), 18 V8SelfTimeStats('v8_execution_time_self', ['V8.Execute']), 19 V8SelfTimeStats('v8_parse_lazy_total', 20 ['V8.ParseLazy', 'V8.ParseLazyMicroSeconds']), 21 V8SelfTimeStats('v8_compile_fullcode_total', 22 ['V8.CompileFullCode']), 23 V8SelfTimeStats('v8_compile_ignition_total', 24 ['V8.CompileIgnition']), 25 V8TotalTimeStats('v8_recompile_total', 26 ['V8.RecompileSynchronous', 27 'V8.RecompileConcurrent']), 28 V8TotalTimeStats('v8_recompile_synchronous_total', 29 ['V8.RecompileSynchronous']), 30 V8TotalTimeStats('v8_recompile_concurrent_total', 31 ['V8.RecompileConcurrent']), 32 V8TotalTimeStats('v8_optimize_code_total', ['V8.OptimizeCode']), 33 V8TotalTimeStats('v8_deoptimize_code_total', ['V8.DeoptimizeCode']), 34 V8OptimizeParseLazyStats('v8_optimize_parse_lazy_total'), 35 ] 36 self._name_to_stats = {} 37 for stat in self._stats: 38 for event_name in stat.event_names: 39 if event_name not in self._name_to_stats: 40 self._name_to_stats[event_name] = [stat] 41 else: 42 self._name_to_stats[event_name].append(stat) 43 44 def AddResults(self, timeline_model, renderer_thread, interactions, results): 45 self.VerifyNonOverlappedRecords(interactions) 46 self._ResetMetrics() 47 self._CollectEvents(timeline_model, interactions) 48 self._AddMetricResults(results, interactions[0].label) 49 50 def _ResetMetrics(self): 51 for metric in self._stats: 52 metric.Reset() 53 54 def _CollectEvents(self, timeline_model, interactions): 55 for event in timeline_model.IterAllSlices(): 56 if not timeline_based_metric.IsEventInInteractions(event, interactions): 57 continue 58 self._CollectEvent(event) 59 60 def _CollectEvent(self, event): 61 if event.name not in self._name_to_stats: 62 return 63 for stat in self._name_to_stats[event.name]: 64 stat.CollectEvent(event) 65 66 def _AddMetricResults(self, results, label): 67 for stat in self._stats: 68 stat.AddResults(results, label) 69 70 71class V8TimeStats(object): 72 def __init__(self, name, event_names, description=None): 73 self.name = name 74 self.event_names = event_names 75 self.description = description 76 self.durations = [] 77 78 def Reset(self): 79 self.durations = [] 80 81 def Duration(self): 82 return sum(self.durations) 83 84 def Count(self): 85 return len(self.durations) 86 87 def Average(self): 88 return statistics.DivideIfPossibleOrZero(self.Duration(), self.Count()) 89 90 def AddResults(self, results, label): 91 results.AddValue( 92 scalar.ScalarValue( 93 results.current_page, 94 self.name, 'ms', self.Duration(), 95 description=self.description, 96 tir_label=label)) 97 results.AddValue( 98 scalar.ScalarValue( 99 results.current_page, 100 "%s_count" % self.name, 'count', self.Count(), 101 description=self.description, 102 tir_label=label)) 103 results.AddValue( 104 scalar.ScalarValue( 105 results.current_page, 106 "%s_average" % self.name, 'ms', self.Average(), 107 description=self.description, 108 tir_label=label)) 109 110 def CollectEvent(self, event): 111 raise NotImplementedError() 112 113 114class V8TotalTimeStats(V8TimeStats): 115 def CollectEvent(self, event): 116 self.durations.append(event.duration) 117 118 119class V8SelfTimeStats(V8TimeStats): 120 def CollectEvent(self, event): 121 self.durations.append(event.self_time) 122 123 124class V8OptimizeParseLazyStats(V8TimeStats): 125 def __init__(self, name): 126 super(V8OptimizeParseLazyStats, self).__init__( 127 name, 128 ['V8.ParseLazy', 'V8.ParseLazyMicroSeconds'], 129 'Time spent in lazy-parsing due to optimizing code') 130 131 def CollectEvent(self, event): 132 if event.parent_slice is None or \ 133 event.parent_slice.name != "V8.OptimizeCode": 134 return 135 self.durations.append(event.self_time) 136