1# Copyright 2013 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 optparse 6import time 7 8from metrics import v8_object_stats 9from telemetry.page import page_measurement 10from telemetry.value import scalar 11 12# V8 statistics counter names. These can be retrieved using 13# v8_object_stats.V8ObjectStatsMetric.GetV8StatsTable. 14_V8_BYTES_COMMITTED = [ 15 'V8.MemoryNewSpaceBytesCommitted', 16 'V8.MemoryOldPointerSpaceBytesCommitted', 17 'V8.MemoryOldDataSpaceBytesCommitted', 18 'V8.MemoryCodeSpaceBytesCommitted', 19 'V8.MemoryMapSpaceBytesCommitted', 20 'V8.MemoryCellSpaceBytesCommitted', 21 'V8.MemoryPropertyCellSpaceBytesCommitted', 22 'V8.MemoryLoSpaceBytesCommitted', 23] 24_V8_BYTES_USED = [ 25 'V8.MemoryNewSpaceBytesUsed', 26 'V8.MemoryOldPointerSpaceBytesUsed', 27 'V8.MemoryOldDataSpaceBytesUsed', 28 'V8.MemoryCodeSpaceBytesUsed', 29 'V8.MemoryMapSpaceBytesUsed', 30 'V8.MemoryCellSpaceBytesUsed', 31 'V8.MemoryPropertyCellSpaceBytesUsed', 32 'V8.MemoryLoSpaceBytesUsed', 33] 34_V8_MEMORY_ALLOCATED = [ 35 'V8.OsMemoryAllocated', 36] 37 38 39class Endure(page_measurement.PageMeasurement): 40 options = {'skip_navigate_on_repeat': True} 41 42 def __init__(self): 43 super(Endure, self).__init__('RunEndure') 44 # Browser object, saved so that browser.memory_stats can be accessed. 45 self._browser = None 46 47 # Dictionary of trace name to lists of y-values, for making summary values. 48 self._y_values = {} 49 50 # Number of page repetitions since the start of the test. 51 self._iterations_elapsed = 0 52 53 # Start time of the test, used to report total time. 54 self._start_time = None 55 56 @classmethod 57 def AddCommandLineArgs(cls, parser): 58 group = optparse.OptionGroup(parser, 'Endure options') 59 group.add_option('--perf-stats-interval', 60 dest='perf_stats_interval', 61 default=1, 62 type='int', 63 help='Number of iterations per sampling of statistics.') 64 parser.add_option_group(group) 65 66 def DidStartBrowser(self, browser): 67 """Initializes the measurement after the browser is started.""" 68 self._browser = browser 69 self._start_time = time.time() 70 71 def CustomizeBrowserOptions(self, options): 72 """Adds extra command-line options to the browser.""" 73 v8_object_stats.V8ObjectStatsMetric.CustomizeBrowserOptions(options) 74 75 def MeasurePage(self, page, tab, results): 76 """Takes a sample and adds a result if enough time has passed.""" 77 self._iterations_elapsed += 1 78 if self._iterations_elapsed % int(self.options.perf_stats_interval) == 0: 79 self._SampleStats(tab, results) 80 81 def _SampleStats(self, tab, results): 82 """Records information and add it to the results.""" 83 84 def AddPoint(trace_name, units_y, value_y, chart_name=None): 85 """Adds one data point to the results object.""" 86 if chart_name: 87 trace_name = '%s.%s' % (chart_name, trace_name) 88 else: 89 assert '.' not in trace_name, ( 90 'Trace names cannot contain "." with an empty chart_name since this' 91 ' is used to delimit chart_name.trace_name.') 92 results.AddValue(scalar.ScalarValue( 93 results.current_page, trace_name + '_X', 'iterations', 94 self._iterations_elapsed, important=False)) 95 results.AddValue(scalar.ScalarValue( 96 results.current_page, trace_name + '_Y', units_y, value_y, 97 important=False)) 98 99 # Save the value so that summary stats can be calculated. 100 if trace_name not in self._y_values: 101 self._y_values[trace_name] = { 102 'units': units_y, 103 'chart_name': chart_name, 104 'values': [], 105 } 106 self._y_values[trace_name]['values'].append(value_y) 107 108 # DOM nodes and event listeners. 109 dom_stats = tab.dom_stats 110 dom_node_count = dom_stats['node_count'] 111 event_listener_count = dom_stats['event_listener_count'] 112 AddPoint('dom_nodes', 'count', dom_node_count, chart_name='object_counts') 113 AddPoint('event_listeners', 'count', event_listener_count, 114 chart_name='object_counts') 115 116 # Browser and renderer virtual memory stats. 117 memory_stats = self._browser.memory_stats 118 def BrowserVMStats(statistic_name): 119 """Get VM stats from the Browser object in KB.""" 120 return memory_stats[statistic_name].get('VM', 0) / 1024.0 121 AddPoint('browser_vm', 'KB', BrowserVMStats('Browser'), 122 chart_name='vm_stats') 123 AddPoint('renderer_vm', 'KB', BrowserVMStats('Renderer'), 124 chart_name='vm_stats') 125 AddPoint('gpu_vm', 'KB', BrowserVMStats('Gpu'), chart_name='vm_stats') 126 127 # V8 counter stats. 128 def V8StatsSum(counters): 129 """Given a list of V8 counter names, get the sum of the values in KB.""" 130 stats = v8_object_stats.V8ObjectStatsMetric.GetV8StatsTable(tab, counters) 131 return sum(stats.values()) / 1024.0 132 AddPoint('v8_memory_committed', 'KB', V8StatsSum(_V8_BYTES_COMMITTED), 133 chart_name='v8_counter_stats') 134 AddPoint('v8_memory_used', 'KB', V8StatsSum(_V8_BYTES_USED), 135 chart_name='v8_counter_stats') 136 AddPoint('v8_memory_allocated', 'KB', V8StatsSum(_V8_MEMORY_ALLOCATED), 137 chart_name='v8_counter_stats') 138 139 def DidRunTest(self, browser, results): 140 """Adds summary results (single number for one test run).""" 141 # Report test run length. 142 results.AddSummaryValue(scalar.ScalarValue(None, 'total_iterations', 143 'iterations', 144 self._iterations_elapsed, 145 important=False)) 146 results.AddSummaryValue(scalar.ScalarValue(None, 'total_time', 'seconds', 147 time.time() - self._start_time, 148 important=False)) 149 150 # Add summary stats which could be monitored for anomalies. 151 for trace_name in self._y_values: 152 units = self._y_values[trace_name]['units'] 153 chart_name = self._y_values[trace_name]['chart_name'] 154 values = self._y_values[trace_name]['values'] 155 value_name = '%s.%s_max' % (chart_name, trace_name) 156 results.AddSummaryValue( 157 scalar.ScalarValue(None, value_name, units, max(values))) 158