# 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 sys from metrics import histogram_util from metrics import Metric _HISTOGRAMS = [ {'name': 'V8.MemoryExternalFragmentationTotal', 'units': 'percent', 'type': histogram_util.RENDERER_HISTOGRAM}, {'name': 'V8.MemoryHeapSampleTotalCommitted', 'units': 'kb', 'type': histogram_util.RENDERER_HISTOGRAM}, {'name': 'V8.MemoryHeapSampleTotalUsed', 'units': 'kb', 'type': histogram_util.RENDERER_HISTOGRAM}, {'name': 'Memory.RendererUsed', 'units': 'kb', 'type': histogram_util.RENDERER_HISTOGRAM}, {'name': 'Memory.BrowserUsed', 'units': 'kb', 'type': histogram_util.BROWSER_HISTOGRAM}] class MemoryMetric(Metric): """MemoryMetric gathers memory statistics from the browser object. This includes both per-page histogram stats, most about javascript memory usage, and overall memory stats from the system for the whole test run.""" def __init__(self, browser): super(MemoryMetric, self).__init__() self._browser = browser self._start_commit_charge = self._browser.memory_stats['SystemCommitCharge'] self._memory_stats = None self._histogram_start = dict() self._histogram_delta = dict() @classmethod def CustomizeBrowserOptions(cls, options): options.AppendExtraBrowserArgs([ '--enable-stats-collection-bindings', '--enable-memory-benchmarking', # For a hard-coded set of Google pages (such as GMail), we produce # custom memory histograms (V8.Something_gmail) instead of the generic # histograms (V8.Something), if we detect that a renderer is only # rendering this page and no other pages. For this test, we need to # disable histogram customizing, so that we get the same generic # histograms produced for all pages. '--disable-histogram-customizer' ]) def Start(self, page, tab): """Start the per-page preparation for this metric. Here, this consists of recording the start value of all the histograms. """ for h in _HISTOGRAMS: histogram_data = histogram_util.GetHistogram( h['type'], h['name'], tab) # Histogram data may not be available if not histogram_data: continue self._histogram_start[h['name']] = histogram_data def Stop(self, page, tab): """Prepare the results for this page. The results are the differences between the current histogram values and the values when Start() was called. """ assert self._histogram_start, 'Must call Start() first' for h in _HISTOGRAMS: # Histogram data may not be available if h['name'] not in self._histogram_start: continue histogram_data = histogram_util.GetHistogram( h['type'], h['name'], tab) self._histogram_delta[h['name']] = histogram_util.SubtractHistogram( histogram_data, self._histogram_start[h['name']]) def AddResults(self, tab, results): """Add results for this page to the results object.""" assert self._histogram_delta, 'Must call Stop() first' for h in _HISTOGRAMS: # Histogram data may not be available if h['name'] not in self._histogram_start: continue results.Add(h['name'], h['units'], self._histogram_delta[h['name']], data_type='unimportant-histogram') def AddSummaryResults(self, results, trace_name=None): """Add summary (overall) results to the results object.""" self._memory_stats = self._browser.memory_stats if not self._memory_stats['Browser']: return metric = 'resident_set_size' if sys.platform == 'win32': metric = 'working_set' def AddSummariesForProcessTypes(process_types_memory, process_type_trace): """Add all summaries to the results for a given set of process types. Args: process_types_memory: A list of process types, e.g. Browser, 'Renderer' process_type_trace: The name of this set of process types in the output """ def AddSummary(value_name_memory, value_name_trace): """Add a summary to the results for a given statistic. Args: value_name_memory: Name of some statistic, e.g. VM, WorkingSetSize value_name_trace: Name of this statistic to be used in the output """ if len(process_types_memory) > 1 and value_name_memory.endswith('Peak'): return values = [] for process_type_memory in process_types_memory: stats = self._memory_stats[process_type_memory] if value_name_memory in stats: values.append(stats[value_name_memory]) if values: if trace_name: current_trace = '%s_%s' % (trace_name, process_type_trace) chart_name = value_name_trace else: current_trace = '%s_%s' % (value_name_trace, process_type_trace) chart_name = current_trace results.AddSummary(current_trace, 'bytes', sum(values), chart_name=chart_name, data_type='unimportant') AddSummary('VM', 'vm_final_size') AddSummary('WorkingSetSize', 'vm_%s_final_size' % metric) AddSummary('PrivateDirty', 'vm_private_dirty_final') AddSummary('ProportionalSetSize', 'vm_proportional_set_size_final') AddSummary('SharedDirty', 'vm_shared_dirty_final') AddSummary('VMPeak', 'vm_peak_size') AddSummary('WorkingSetSizePeak', '%s_peak_size' % metric) AddSummariesForProcessTypes(['Browser'], 'browser') AddSummariesForProcessTypes(['Renderer'], 'renderer') AddSummariesForProcessTypes(['Gpu'], 'gpu') AddSummariesForProcessTypes(['Browser', 'Renderer', 'Gpu'], 'total') end_commit_charge = self._memory_stats['SystemCommitCharge'] commit_charge_difference = end_commit_charge - self._start_commit_charge results.AddSummary(trace_name or 'commit_charge', 'kb', commit_charge_difference, chart_name='commit_charge', data_type='unimportant') results.AddSummary(trace_name or 'processes', 'count', self._memory_stats['ProcessCount'], chart_name='processes', data_type='unimportant')