| # Copyright 2013 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import optparse |
| import time |
| |
| from metrics import v8_object_stats |
| from telemetry.page import page_measurement |
| from telemetry.value import scalar |
| |
| # V8 statistics counter names. These can be retrieved using |
| # v8_object_stats.V8ObjectStatsMetric.GetV8StatsTable. |
| _V8_BYTES_COMMITTED = [ |
| 'V8.MemoryNewSpaceBytesCommitted', |
| 'V8.MemoryOldPointerSpaceBytesCommitted', |
| 'V8.MemoryOldDataSpaceBytesCommitted', |
| 'V8.MemoryCodeSpaceBytesCommitted', |
| 'V8.MemoryMapSpaceBytesCommitted', |
| 'V8.MemoryCellSpaceBytesCommitted', |
| 'V8.MemoryPropertyCellSpaceBytesCommitted', |
| 'V8.MemoryLoSpaceBytesCommitted', |
| ] |
| _V8_BYTES_USED = [ |
| 'V8.MemoryNewSpaceBytesUsed', |
| 'V8.MemoryOldPointerSpaceBytesUsed', |
| 'V8.MemoryOldDataSpaceBytesUsed', |
| 'V8.MemoryCodeSpaceBytesUsed', |
| 'V8.MemoryMapSpaceBytesUsed', |
| 'V8.MemoryCellSpaceBytesUsed', |
| 'V8.MemoryPropertyCellSpaceBytesUsed', |
| 'V8.MemoryLoSpaceBytesUsed', |
| ] |
| _V8_MEMORY_ALLOCATED = [ |
| 'V8.OsMemoryAllocated', |
| ] |
| |
| |
| class Endure(page_measurement.PageMeasurement): |
| options = {'skip_navigate_on_repeat': True} |
| |
| def __init__(self): |
| super(Endure, self).__init__('RunEndure') |
| # Browser object, saved so that browser.memory_stats can be accessed. |
| self._browser = None |
| |
| # Dictionary of trace name to lists of y-values, for making summary values. |
| self._y_values = {} |
| |
| # Number of page repetitions since the start of the test. |
| self._iterations_elapsed = 0 |
| |
| # Start time of the test, used to report total time. |
| self._start_time = None |
| |
| @classmethod |
| def AddCommandLineArgs(cls, parser): |
| group = optparse.OptionGroup(parser, 'Endure options') |
| group.add_option('--perf-stats-interval', |
| dest='perf_stats_interval', |
| default=1, |
| type='int', |
| help='Number of iterations per sampling of statistics.') |
| parser.add_option_group(group) |
| |
| def DidStartBrowser(self, browser): |
| """Initializes the measurement after the browser is started.""" |
| self._browser = browser |
| self._start_time = time.time() |
| |
| def CustomizeBrowserOptions(self, options): |
| """Adds extra command-line options to the browser.""" |
| v8_object_stats.V8ObjectStatsMetric.CustomizeBrowserOptions(options) |
| |
| def MeasurePage(self, page, tab, results): |
| """Takes a sample and adds a result if enough time has passed.""" |
| self._iterations_elapsed += 1 |
| if self._iterations_elapsed % int(self.options.perf_stats_interval) == 0: |
| self._SampleStats(tab, results) |
| |
| def _SampleStats(self, tab, results): |
| """Records information and add it to the results.""" |
| |
| def AddPoint(trace_name, units_y, value_y, chart_name=None): |
| """Adds one data point to the results object.""" |
| if chart_name: |
| trace_name = '%s.%s' % (chart_name, trace_name) |
| else: |
| assert '.' not in trace_name, ( |
| 'Trace names cannot contain "." with an empty chart_name since this' |
| ' is used to delimit chart_name.trace_name.') |
| results.AddValue(scalar.ScalarValue( |
| results.current_page, trace_name + '_X', 'iterations', |
| self._iterations_elapsed, important=False)) |
| results.AddValue(scalar.ScalarValue( |
| results.current_page, trace_name + '_Y', units_y, value_y, |
| important=False)) |
| |
| # Save the value so that summary stats can be calculated. |
| if trace_name not in self._y_values: |
| self._y_values[trace_name] = { |
| 'units': units_y, |
| 'chart_name': chart_name, |
| 'values': [], |
| } |
| self._y_values[trace_name]['values'].append(value_y) |
| |
| # DOM nodes and event listeners. |
| dom_stats = tab.dom_stats |
| dom_node_count = dom_stats['node_count'] |
| event_listener_count = dom_stats['event_listener_count'] |
| AddPoint('dom_nodes', 'count', dom_node_count, chart_name='object_counts') |
| AddPoint('event_listeners', 'count', event_listener_count, |
| chart_name='object_counts') |
| |
| # Browser and renderer virtual memory stats. |
| memory_stats = self._browser.memory_stats |
| def BrowserVMStats(statistic_name): |
| """Get VM stats from the Browser object in KB.""" |
| return memory_stats[statistic_name].get('VM', 0) / 1024.0 |
| AddPoint('browser_vm', 'KB', BrowserVMStats('Browser'), |
| chart_name='vm_stats') |
| AddPoint('renderer_vm', 'KB', BrowserVMStats('Renderer'), |
| chart_name='vm_stats') |
| AddPoint('gpu_vm', 'KB', BrowserVMStats('Gpu'), chart_name='vm_stats') |
| |
| # V8 counter stats. |
| def V8StatsSum(counters): |
| """Given a list of V8 counter names, get the sum of the values in KB.""" |
| stats = v8_object_stats.V8ObjectStatsMetric.GetV8StatsTable(tab, counters) |
| return sum(stats.values()) / 1024.0 |
| AddPoint('v8_memory_committed', 'KB', V8StatsSum(_V8_BYTES_COMMITTED), |
| chart_name='v8_counter_stats') |
| AddPoint('v8_memory_used', 'KB', V8StatsSum(_V8_BYTES_USED), |
| chart_name='v8_counter_stats') |
| AddPoint('v8_memory_allocated', 'KB', V8StatsSum(_V8_MEMORY_ALLOCATED), |
| chart_name='v8_counter_stats') |
| |
| def DidRunTest(self, browser, results): |
| """Adds summary results (single number for one test run).""" |
| # Report test run length. |
| results.AddSummaryValue(scalar.ScalarValue(None, 'total_iterations', |
| 'iterations', |
| self._iterations_elapsed, |
| important=False)) |
| results.AddSummaryValue(scalar.ScalarValue(None, 'total_time', 'seconds', |
| time.time() - self._start_time, |
| important=False)) |
| |
| # Add summary stats which could be monitored for anomalies. |
| for trace_name in self._y_values: |
| units = self._y_values[trace_name]['units'] |
| chart_name = self._y_values[trace_name]['chart_name'] |
| values = self._y_values[trace_name]['values'] |
| value_name = '%s.%s_max' % (chart_name, trace_name) |
| results.AddSummaryValue( |
| scalar.ScalarValue(None, value_name, units, max(values))) |