# 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 re import time from metrics import v8_object_stats from telemetry.page import page_measurement _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): def __init__(self): super(Endure, self).__init__('endure') # Browser object, saved so that memory stats can be gotten later. self._browser = None # Timestamp for the time when the test starts. self._start_time = None # Timestamp of the last statistics sample. self._last_sample_time = 0 # Number of page repetitions that have currently been done. self._iterations = 0 # Number of page repetitions at the point of the last statistics sample. self._last_sample_iterations = 0 # One of these variables will be set when the perf stats interval option # is parsed, and the other shall remain as None. self._interval_seconds = None self._interval_iterations = None def AddCommandLineOptions(self, parser): # TODO(tdu): When ProcessCommandLine is added to replace this method, # move the logic in _ParseIntervalOption there to ProcessCommandLine. group = optparse.OptionGroup(parser, 'Endure options') group.add_option('--perf-stats-interval', dest='perf_stats_interval', default='20s', type='string', help='Interval between sampling of statistics, either in ' 'seconds (specified by appending \'s\') or in number ' 'of iterations') parser.add_option_group(group) def DidStartBrowser(self, browser): # Save the Browser object so that memory_stats can be gotten later. self._browser = browser def CustomizeBrowserOptions(self, options): v8_object_stats.V8ObjectStatsMetric.CustomizeBrowserOptions(options) def CanRunForPage(self, page): return hasattr(page, 'endure') def WillRunPageRepeats(self, page): """Set-up before starting a new page.""" # Reset the starting time for each new page. self._start_time = time.time() # Prefix the page name so it can be picked up by the buildbot script that # parses Endure output. if page.name and not page.display_name.startswith('endure_'): page.name = 'endure_' + page.name def MeasurePage(self, page, tab, results): """Sample perf information if enough seconds or iterations have passed.""" # Parse the interval option, setting either or seconds or iterations. # This is done here because self.options is not set when any of the above # methods are run. self._ParseIntervalOption() # Check whether the sample interval is specified in seconds or iterations, # and take a sample if it's time. self._iterations += 1 if self._interval_seconds: now = time.time() seconds_elapsed = int(round(now - self._last_sample_time)) # Note: the time since last sample must be at least as many seconds # as specified; it will usually be more, it will never be less. if seconds_elapsed >= self._interval_seconds: total_seconds = int(round(now - self._start_time)) self._SampleStats(tab, results, seconds=total_seconds) self._last_sample_time = now else: iterations_elapsed = self._iterations - self._last_sample_iterations if iterations_elapsed >= self._interval_iterations: self._SampleStats(tab, results, iterations=self._iterations) self._last_sample_iterations = self._iterations def _ParseIntervalOption(self): """Parse the perf stats interval option that was passed in.""" if self._interval_seconds or self._interval_iterations: return interval = self.options.perf_stats_interval match = re.match('([0-9]+)([sS]?)$', interval) assert match, ('Invalid value for --perf-stats-interval: %s' % interval) if match.group(2): self._interval_seconds = int(match.group(1)) else: self._interval_iterations = int(match.group(1)) assert self._interval_seconds or self._interval_iterations def _SampleStats(self, tab, results, seconds=None, iterations=None): """Record memory information and add it to the results.""" def AddPoint(trace_name, units_y, value_y): """Add one data point to the results object.""" if seconds: results.Add(trace_name + '_X', 'seconds', seconds) else: assert iterations, 'Neither seconds nor iterations given.' results.Add(trace_name + '_X', 'iterations', iterations) results.Add(trace_name + '_Y', units_y, 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) AddPoint('event_listeners', 'count', event_listener_count) # 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')) AddPoint('renderer_vm', 'KB', BrowserVMStats('Renderer')) AddPoint('gpu_vm', 'KB', BrowserVMStats('Gpu')) # V8 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)) AddPoint('v8_memory_used', 'KB', V8StatsSum(_V8_BYTES_USED)) AddPoint('v8_memory_allocated', 'KB', V8StatsSum(_V8_MEMORY_ALLOCATED))