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.
7 from telemetry
.value
import histogram
8 from telemetry
.value
import histogram_util
9 from telemetry
.value
import scalar
11 from metrics
import Metric
16 'name': 'V8.MemoryExternalFragmentationTotal', 'units': 'percent',
17 'display_name': 'V8_MemoryExternalFragmentationTotal',
18 'type': histogram_util
.RENDERER_HISTOGRAM
,
19 'description': 'Total external memory fragmentation after each GC in '
23 'name': 'V8.MemoryHeapSampleTotalCommitted', 'units': 'kb',
24 'display_name': 'V8_MemoryHeapSampleTotalCommitted',
25 'type': histogram_util
.RENDERER_HISTOGRAM
,
26 'description': 'The total size of committed memory used by V8 after '
30 'name': 'V8.MemoryHeapSampleTotalUsed', 'units': 'kb',
31 'display_name': 'V8_MemoryHeapSampleTotalUsed',
32 'type': histogram_util
.RENDERER_HISTOGRAM
,
33 'description': 'The total size of live memory used by V8 after each '
37 'name': 'V8.MemoryHeapSampleMaximumCommitted', 'units': 'kb',
38 'display_name': 'V8_MemoryHeapSampleMaximumCommitted',
39 'type': histogram_util
.RENDERER_HISTOGRAM
42 'name': 'Memory.RendererUsed', 'units': 'kb',
43 'display_name': 'Memory_RendererUsed',
44 'type': histogram_util
.RENDERER_HISTOGRAM
47 'name': 'Memory.BrowserUsed', 'units': 'kb',
48 'display_name': 'Memory_BrowserUsed',
49 'type': histogram_util
.BROWSER_HISTOGRAM
53 class MemoryMetric(Metric
):
54 """MemoryMetric gathers memory statistics from the browser object.
56 This includes both per-page histogram stats, most about javascript
57 memory usage, and overall memory stats from the system for the whole
60 def __init__(self
, browser
):
61 super(MemoryMetric
, self
).__init
__()
62 self
._browser
= browser
63 start_memory_stats
= self
._browser
.memory_stats
64 self
._start
_commit
_charge
= None
65 if 'SystemCommitCharge' in start_memory_stats
:
66 self
._start
_commit
_charge
= start_memory_stats
['SystemCommitCharge']
67 self
._memory
_stats
= None
68 self
._histogram
_start
= dict()
69 self
._histogram
_delta
= dict()
72 def CustomizeBrowserOptions(cls
, options
):
73 options
.AppendExtraBrowserArgs([
74 '--enable-stats-collection-bindings',
75 # For a hard-coded set of Google pages (such as GMail), we produce
76 # custom memory histograms (V8.Something_gmail) instead of the generic
77 # histograms (V8.Something), if we detect that a renderer is only
78 # rendering this page and no other pages. For this test, we need to
79 # disable histogram customizing, so that we get the same generic
80 # histograms produced for all pages.
81 '--disable-histogram-customizer'
84 def Start(self
, page
, tab
):
85 """Start the per-page preparation for this metric.
87 Here, this consists of recording the start value of all the histograms.
90 histogram_data
= histogram_util
.GetHistogram(
91 h
['type'], h
['name'], tab
)
92 # Histogram data may not be available
93 if not histogram_data
:
95 self
._histogram
_start
[h
['name']] = histogram_data
97 def Stop(self
, page
, tab
):
98 """Prepare the results for this page.
100 The results are the differences between the current histogram values
101 and the values when Start() was called.
103 assert self
._histogram
_start
, 'Must call Start() first'
104 for h
in _HISTOGRAMS
:
105 # Histogram data may not be available
106 if h
['name'] not in self
._histogram
_start
:
108 histogram_data
= histogram_util
.GetHistogram(
109 h
['type'], h
['name'], tab
)
110 self
._histogram
_delta
[h
['name']] = histogram_util
.SubtractHistogram(
111 histogram_data
, self
._histogram
_start
[h
['name']])
113 # Optional argument trace_name is not in base class Metric.
114 # pylint: disable=W0221
115 def AddResults(self
, tab
, results
, trace_name
=None):
116 """Add results for this page to the results object."""
117 assert self
._histogram
_delta
, 'Must call Stop() first'
118 for h
in _HISTOGRAMS
:
119 # Histogram data may not be available
120 if h
['name'] not in self
._histogram
_start
:
122 results
.AddValue(histogram
.HistogramValue(
123 results
.current_page
, h
['display_name'], h
['units'],
124 raw_value_json
=self
._histogram
_delta
[h
['name']], important
=False,
125 description
=h
.get('description')))
126 self
._memory
_stats
= self
._browser
.memory_stats
127 if not self
._memory
_stats
['Browser']:
129 AddResultsForProcesses(results
, self
._memory
_stats
,
130 metric_trace_name
=trace_name
)
132 if self
._start
_commit
_charge
:
133 end_commit_charge
= self
._memory
_stats
['SystemCommitCharge']
134 commit_charge_difference
= end_commit_charge
- self
._start
_commit
_charge
135 results
.AddValue(scalar
.ScalarValue(
136 results
.current_page
,
137 'commit_charge.' + (trace_name
or 'commit_charge'),
138 'kb', commit_charge_difference
, important
=False,
139 description
='System commit charge (committed memory pages).'))
140 results
.AddValue(scalar
.ScalarValue(
141 results
.current_page
, 'processes.' + (trace_name
or 'processes'),
142 'count', self
._memory
_stats
['ProcessCount'], important
=False,
143 description
='Number of processes used by Chrome.'))
146 def AddResultsForProcesses(results
, memory_stats
, chart_trace_name
='final',
147 metric_trace_name
=None,
148 exclude_metrics
=None):
149 """Adds memory stats for browser, renderer and gpu processes.
152 results: A telemetry.results.PageTestResults object.
153 memory_stats: System memory stats collected.
154 chart_trace_name: Trace to identify memory metrics. Default is 'final'.
155 metric_trace_name: Trace to identify the metric results per test page.
156 exclude_metrics: List of memory metrics to exclude from results,
157 e.g. VM, WorkingSetSize, etc.
159 metric
= 'resident_set_size'
160 if sys
.platform
== 'win32':
161 metric
= 'working_set'
163 exclude_metrics
= exclude_metrics
or {}
165 def AddResultsForProcessTypes(process_types_memory
, process_type_trace
):
166 """Add all results for a given set of process types.
169 process_types_memory: A list of process types, e.g. Browser, 'Renderer'.
170 process_type_trace: The name of this set of process types in the output.
172 def AddResult(value_name_memory
, value_name_trace
, description
):
173 """Add a result for a given statistic.
176 value_name_memory: Name of some statistic, e.g. VM, WorkingSetSize.
177 value_name_trace: Name of this statistic to be used in the output.
179 if value_name_memory
in exclude_metrics
:
181 if len(process_types_memory
) > 1 and value_name_memory
.endswith('Peak'):
184 for process_type_memory
in process_types_memory
:
185 stats
= memory_stats
[process_type_memory
]
186 if value_name_memory
in stats
:
187 values
.append(stats
[value_name_memory
])
189 if metric_trace_name
:
190 current_trace
= '%s_%s' % (metric_trace_name
, process_type_trace
)
191 chart_name
= value_name_trace
193 current_trace
= '%s_%s' % (value_name_trace
, process_type_trace
)
194 chart_name
= current_trace
195 results
.AddValue(scalar
.ScalarValue(
196 results
.current_page
, '%s.%s' % (chart_name
, current_trace
), 'kb',
197 sum(values
) / 1024, important
=False, description
=description
))
199 AddResult('VM', 'vm_%s_size' % chart_trace_name
,
200 'Virtual Memory Size (address space allocated).')
201 AddResult('WorkingSetSize', 'vm_%s_%s_size' % (metric
, chart_trace_name
),
202 'Working Set Size (Windows) or Resident Set Size (other '
204 AddResult('PrivateDirty', 'vm_private_dirty_%s' % chart_trace_name
,
205 'Private Dirty is basically the amount of RAM inside the '
206 'process that can not be paged to disk (it is not backed by the '
207 'same data on disk), and is not shared with any other '
208 'processes. Another way to look at this is the RAM that will '
209 'become available to the system when that process goes away '
210 '(and probably quickly subsumed into caches and other uses of '
212 AddResult('ProportionalSetSize',
213 'vm_proportional_set_size_%s' % chart_trace_name
,
214 'The Proportional Set Size (PSS) number is a metric the kernel '
215 'computes that takes into account memory sharing -- basically '
216 'each page of RAM in a process is scaled by a ratio of the '
217 'number of other processes also using that page. This way you '
218 'can (in theory) add up the PSS across all processes to see '
219 'the total RAM they are using, and compare PSS between '
220 'processes to get a rough idea of their relative weight.')
221 AddResult('SharedDirty', 'vm_shared_dirty_%s' % chart_trace_name
,
222 'Shared Dirty is the amount of RAM outside the process that can '
223 'not be paged to disk, and is shared with other processes.')
224 AddResult('VMPeak', 'vm_peak_size',
225 'The peak Virtual Memory Size (address space allocated) usage '
226 'achieved by the * process.')
227 AddResult('WorkingSetSizePeak', '%s_peak_size' % metric
,
228 'Peak Working Set Size.')
230 AddResultsForProcessTypes(['Browser'], 'browser')
231 AddResultsForProcessTypes(['Renderer'], 'renderer')
232 AddResultsForProcessTypes(['Gpu'], 'gpu')
233 AddResultsForProcessTypes(['Browser', 'Renderer', 'Gpu'], 'total')