Home | History | Annotate | Download | only in metrics
      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 
      5 import sys
      6 
      7 from metrics import histogram_util
      8 from metrics import Metric
      9 from telemetry.value import histogram
     10 from telemetry.value import scalar
     11 
     12 
     13 _HISTOGRAMS = [
     14     {'name': 'V8.MemoryExternalFragmentationTotal', 'units': 'percent',
     15      'display_name': 'V8_MemoryExternalFragmentationTotal',
     16      'type': histogram_util.RENDERER_HISTOGRAM},
     17     {'name': 'V8.MemoryHeapSampleTotalCommitted', 'units': 'kb',
     18      'display_name': 'V8_MemoryHeapSampleTotalCommitted',
     19      'type': histogram_util.RENDERER_HISTOGRAM},
     20     {'name': 'V8.MemoryHeapSampleTotalUsed', 'units': 'kb',
     21      'display_name': 'V8_MemoryHeapSampleTotalUsed',
     22      'type': histogram_util.RENDERER_HISTOGRAM},
     23     {'name': 'V8.MemoryHeapSampleMaximumCommitted', 'units': 'kb',
     24      'display_name': 'V8_MemoryHeapSampleMaximumCommitted',
     25      'type': histogram_util.RENDERER_HISTOGRAM},
     26     {'name': 'Memory.RendererUsed', 'units': 'kb',
     27      'display_name': 'Memory_RendererUsed',
     28      'type': histogram_util.RENDERER_HISTOGRAM},
     29     {'name': 'Memory.BrowserUsed', 'units': 'kb',
     30      'display_name': 'Memory_BrowserUsed',
     31      'type': histogram_util.BROWSER_HISTOGRAM}]
     32 
     33 class MemoryMetric(Metric):
     34   """MemoryMetric gathers memory statistics from the browser object.
     35 
     36   This includes both per-page histogram stats, most about javascript
     37   memory usage, and overall memory stats from the system for the whole
     38   test run."""
     39 
     40   def __init__(self, browser):
     41     super(MemoryMetric, self).__init__()
     42     self._browser = browser
     43     self._start_commit_charge = self._browser.memory_stats['SystemCommitCharge']
     44     self._memory_stats = None
     45     self._histogram_start = dict()
     46     self._histogram_delta = dict()
     47 
     48   @classmethod
     49   def CustomizeBrowserOptions(cls, options):
     50     options.AppendExtraBrowserArgs([
     51         '--enable-stats-collection-bindings',
     52         # For a hard-coded set of Google pages (such as GMail), we produce
     53         # custom memory histograms (V8.Something_gmail) instead of the generic
     54         # histograms (V8.Something), if we detect that a renderer is only
     55         # rendering this page and no other pages. For this test, we need to
     56         # disable histogram customizing, so that we get the same generic
     57         # histograms produced for all pages.
     58         '--disable-histogram-customizer'
     59     ])
     60 
     61   def Start(self, page, tab):
     62     """Start the per-page preparation for this metric.
     63 
     64     Here, this consists of recording the start value of all the histograms.
     65     """
     66     for h in _HISTOGRAMS:
     67       histogram_data = histogram_util.GetHistogram(
     68           h['type'], h['name'], tab)
     69       # Histogram data may not be available
     70       if not histogram_data:
     71         continue
     72       self._histogram_start[h['name']] = histogram_data
     73 
     74   def Stop(self, page, tab):
     75     """Prepare the results for this page.
     76 
     77     The results are the differences between the current histogram values
     78     and the values when Start() was called.
     79     """
     80     assert self._histogram_start, 'Must call Start() first'
     81     for h in _HISTOGRAMS:
     82       # Histogram data may not be available
     83       if h['name'] not in self._histogram_start:
     84         continue
     85       histogram_data = histogram_util.GetHistogram(
     86           h['type'], h['name'], tab)
     87       self._histogram_delta[h['name']] = histogram_util.SubtractHistogram(
     88           histogram_data, self._histogram_start[h['name']])
     89 
     90   # Optional argument trace_name is not in base class Metric.
     91   # pylint: disable=W0221
     92   def AddResults(self, tab, results, trace_name=None):
     93     """Add results for this page to the results object."""
     94     assert self._histogram_delta, 'Must call Stop() first'
     95     for h in _HISTOGRAMS:
     96       # Histogram data may not be available
     97       if h['name'] not in self._histogram_start:
     98         continue
     99       results.AddValue(histogram.HistogramValue(
    100           results.current_page, h['display_name'], h['units'],
    101           raw_value_json=self._histogram_delta[h['name']], important=False))
    102     self._memory_stats = self._browser.memory_stats
    103     if not self._memory_stats['Browser']:
    104       return
    105     AddResultsForProcesses(results, self._memory_stats,
    106                            metric_trace_name=trace_name)
    107 
    108     end_commit_charge = self._memory_stats['SystemCommitCharge']
    109     commit_charge_difference = end_commit_charge - self._start_commit_charge
    110     results.AddValue(scalar.ScalarValue(
    111         results.current_page,
    112         'commit_charge.' + (trace_name or 'commit_charge'),
    113         'kb',
    114         commit_charge_difference, important=False))
    115     results.AddValue(scalar.ScalarValue(
    116         results.current_page,
    117         'processes.' + (trace_name or 'processes'),
    118         'count',
    119         self._memory_stats['ProcessCount'],
    120         important=False))
    121 
    122 
    123 def AddResultsForProcesses(results, memory_stats, chart_trace_name='final',
    124                            metric_trace_name=None,
    125                            exclude_metrics=None):
    126   """Adds memory stats for browser, renderer and gpu processes.
    127 
    128   Args:
    129     results: A PageMeasurement results object.
    130     memory_stats: System memory stats collected.
    131     chart_trace_name: Trace to identify memory metrics. Default is 'final'.
    132     metric_trace_name: Trace to identify the metric results per test page.
    133     exclude_metrics: List of memory metrics to exclude from results,
    134                      e.g. VM, WorkingSetSize, etc.
    135   """
    136   metric = 'resident_set_size'
    137   if sys.platform == 'win32':
    138     metric = 'working_set'
    139 
    140   exclude_metrics = exclude_metrics or {}
    141 
    142   def AddResultsForProcessTypes(process_types_memory, process_type_trace):
    143     """Add all results for a given set of process types.
    144 
    145     Args:
    146       process_types_memory: A list of process types, e.g. Browser, 'Renderer'
    147       process_type_trace: The name of this set of process types in the output
    148     """
    149     def AddResult(value_name_memory, value_name_trace):
    150       """Add a result for a given statistic.
    151 
    152       Args:
    153         value_name_memory: Name of some statistic, e.g. VM, WorkingSetSize
    154         value_name_trace: Name of this statistic to be used in the output
    155       """
    156       if value_name_memory in exclude_metrics:
    157         return
    158       if len(process_types_memory) > 1 and value_name_memory.endswith('Peak'):
    159         return
    160       values = []
    161       for process_type_memory in process_types_memory:
    162         stats = memory_stats[process_type_memory]
    163         if value_name_memory in stats:
    164           values.append(stats[value_name_memory])
    165       if values:
    166         if metric_trace_name:
    167           current_trace = '%s_%s' % (metric_trace_name, process_type_trace)
    168           chart_name = value_name_trace
    169         else:
    170           current_trace = '%s_%s' % (value_name_trace, process_type_trace)
    171           chart_name = current_trace
    172         results.AddValue(scalar.ScalarValue(
    173             results.current_page, '%s.%s' % (chart_name, current_trace), 'kb',
    174             sum(values) / 1024, important=False))
    175 
    176     AddResult('VM', 'vm_%s_size' % chart_trace_name)
    177     AddResult('WorkingSetSize', 'vm_%s_%s_size' % (metric, chart_trace_name))
    178     AddResult('PrivateDirty', 'vm_private_dirty_%s' % chart_trace_name)
    179     AddResult('ProportionalSetSize',
    180               'vm_proportional_set_size_%s' % chart_trace_name)
    181     AddResult('SharedDirty', 'vm_shared_dirty_%s' % chart_trace_name)
    182     AddResult('VMPeak', 'vm_peak_size')
    183     AddResult('WorkingSetSizePeak', '%s_peak_size' % metric)
    184 
    185   AddResultsForProcessTypes(['Browser'], 'browser')
    186   AddResultsForProcessTypes(['Renderer'], 'renderer')
    187   AddResultsForProcessTypes(['Gpu'], 'gpu')
    188   AddResultsForProcessTypes(['Browser', 'Renderer', 'Gpu'], 'total')
    189