Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / tools / perf / measurements / endure.py
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 optparse
6 import re
7 import time
8
9 from metrics import v8_object_stats
10 from telemetry.page import page_measurement
11
12 # V8 statistics counter names. These can be retrieved using
13 # v8_object_stats.V8ObjectStatsMetric.GetV8StatsTable.
14 _V8_BYTES_COMMITTED = [
15     'V8.MemoryNewSpaceBytesCommitted',
16     'V8.MemoryOldPointerSpaceBytesCommitted',
17     'V8.MemoryOldDataSpaceBytesCommitted',
18     'V8.MemoryCodeSpaceBytesCommitted',
19     'V8.MemoryMapSpaceBytesCommitted',
20     'V8.MemoryCellSpaceBytesCommitted',
21     'V8.MemoryPropertyCellSpaceBytesCommitted',
22     'V8.MemoryLoSpaceBytesCommitted',
23 ]
24 _V8_BYTES_USED = [
25     'V8.MemoryNewSpaceBytesUsed',
26     'V8.MemoryOldPointerSpaceBytesUsed',
27     'V8.MemoryOldDataSpaceBytesUsed',
28     'V8.MemoryCodeSpaceBytesUsed',
29     'V8.MemoryMapSpaceBytesUsed',
30     'V8.MemoryCellSpaceBytesUsed',
31     'V8.MemoryPropertyCellSpaceBytesUsed',
32     'V8.MemoryLoSpaceBytesUsed',
33 ]
34 _V8_MEMORY_ALLOCATED = [
35     'V8.OsMemoryAllocated',
36 ]
37
38
39 class Endure(page_measurement.PageMeasurement):
40   def __init__(self):
41     super(Endure, self).__init__('endure')
42     # Browser object, saved so that browser.memory_stats can be accessed.
43     self._browser = None
44
45     # Dict of trace name to lists of y-values, for making summary values.
46     self._y_values = {}
47
48     # Time of test start and last sample, in seconds since the epoch.
49     self._start_time = None
50     self._last_sample_time = 0
51
52     # Number of page repetitions currently, and at the last sample.
53     self._iterations = 0
54     self._last_sample_iterations = 0
55
56     # Interval between stats sampling. One of these attributes will be set when
57     # the perf stats interval option is parsed. The other shall remain as None.
58     self._interval_seconds = None
59     self._interval_iterations = None
60
61   def AddCommandLineOptions(self, parser):
62     # TODO(tdu): When ProcessCommandLine is added to replace this method,
63     # move the logic in _ParseIntervalOption there to ProcessCommandLine.
64     group = optparse.OptionGroup(parser, 'Endure options')
65     group.add_option('--perf-stats-interval',
66                      dest='perf_stats_interval',
67                      default='20s',
68                      type='string',
69                      help='Interval between sampling of statistics, either in '
70                           'seconds (specified by appending \'s\') or in number '
71                           'of iterations')
72     parser.add_option_group(group)
73
74   def DidStartBrowser(self, browser):
75     """Saves the Browser object. Called after the browser is started."""
76     self._browser = browser
77
78   def CustomizeBrowserOptions(self, options):
79     """Adds extra command-line options to the browser."""
80     v8_object_stats.V8ObjectStatsMetric.CustomizeBrowserOptions(options)
81
82   def CanRunForPage(self, page):
83     """Checks whether a page has the required 'endure' property."""
84     return hasattr(page, 'endure')
85
86   def WillRunPageRepeats(self, page):
87     """Set-up before starting a new page."""
88     # Reset the starting time for each new page.
89     self._start_time = time.time()
90
91     # Prefix the page name so it can be picked up by the buildbot script that
92     # parses Endure output.
93     if page.name and not page.display_name.startswith('endure_'):
94       page.name = 'endure_' + page.name
95
96   def MeasurePage(self, page, tab, results):
97     """Takes a sample and adds a result if enough time has passed."""
98     # Parse the interval option, setting either or seconds or iterations.
99     # This is done here because self.options is not set when any of the above
100     # methods are run.
101     self._ParseIntervalOption()
102
103     # Check whether the sample interval is specified in seconds or iterations,
104     # and take a sample if it's time.
105     self._iterations += 1
106     if self._interval_seconds:
107       now = time.time()
108       seconds_elapsed = int(round(now - self._last_sample_time))
109       # Note: the time since last sample must be at least as many seconds
110       # as specified; it will usually be more, it will never be less.
111       if seconds_elapsed >= self._interval_seconds:
112         total_seconds = int(round(now - self._start_time))
113         self._SampleStats(tab, results, seconds=total_seconds)
114         self._last_sample_time = now
115     else:
116       iterations_elapsed = self._iterations - self._last_sample_iterations
117       if iterations_elapsed >= self._interval_iterations:
118         self._SampleStats(tab, results, iterations=self._iterations)
119         self._last_sample_iterations = self._iterations
120
121   def _ParseIntervalOption(self):
122     """Parses the --perf-stats-interval option that was passed in."""
123     if self._interval_seconds or self._interval_iterations:
124       return
125     interval = self.options.perf_stats_interval
126     match = re.match('([0-9]+)([sS]?)$', interval)
127     assert match, ('Invalid value for --perf-stats-interval: %s' % interval)
128     if match.group(2):
129       self._interval_seconds = int(match.group(1))
130     else:
131       self._interval_iterations = int(match.group(1))
132     assert self._interval_seconds or self._interval_iterations
133
134   def _SampleStats(self, tab, results, seconds=None, iterations=None):
135     """Records information and add it to the results."""
136
137     def AddPoint(trace_name, units_y, value_y, chart_name=None):
138       """Adds one data point to the results object."""
139       if seconds:
140         results.Add(trace_name + '_X', 'seconds', seconds,
141                     data_type='unimportant', chart_name=chart_name)
142       else:
143         assert iterations, 'Neither seconds nor iterations given.'
144         results.Add(trace_name + '_X', 'iterations', iterations,
145                     data_type='unimportant', chart_name=chart_name)
146       # Add the result as 'unimportant' because we want it to be unmonitored
147       # by default.
148       results.Add(trace_name + '_Y', units_y, value_y, data_type='unimportant',
149                   chart_name=chart_name)
150       # Save the value in a list so that summary stats can be calculated.
151       if trace_name not in self._y_values:
152         self._y_values[trace_name] = {
153             'units': units_y,
154             'chart_name': chart_name,
155             'values': [],
156         }
157       self._y_values[trace_name]['values'].append(value_y)
158
159     # DOM nodes and event listeners
160     dom_stats = tab.dom_stats
161     dom_node_count = dom_stats['node_count']
162     event_listener_count = dom_stats['event_listener_count']
163     AddPoint('dom_nodes', 'count', dom_node_count, chart_name='object_counts')
164     AddPoint('event_listeners', 'count', event_listener_count,
165              chart_name='object_counts')
166
167     # Browser and renderer virtual memory stats
168     memory_stats = self._browser.memory_stats
169     def BrowserVMStats(statistic_name):
170       """Get VM stats from the Browser object in KB."""
171       return memory_stats[statistic_name].get('VM', 0) / 1024.0
172     AddPoint('browser_vm', 'KB', BrowserVMStats('Browser'),
173              chart_name='vm_stats')
174     AddPoint('renderer_vm', 'KB', BrowserVMStats('Renderer'),
175              chart_name='vm_stats')
176     AddPoint('gpu_vm', 'KB', BrowserVMStats('Gpu'), chart_name='vm_stats')
177
178     # V8 stats
179     def V8StatsSum(counters):
180       """Given a list of V8 counter names, get the sum of the values in KB."""
181       stats = v8_object_stats.V8ObjectStatsMetric.GetV8StatsTable(tab, counters)
182       return sum(stats.values()) / 1024.0
183     AddPoint('v8_memory_committed', 'KB', V8StatsSum(_V8_BYTES_COMMITTED),
184              chart_name='v8_counter_stats')
185     AddPoint('v8_memory_used', 'KB', V8StatsSum(_V8_BYTES_USED),
186              chart_name='v8_counter_stats')
187     AddPoint('v8_memory_allocated', 'KB', V8StatsSum(_V8_MEMORY_ALLOCATED),
188              chart_name='v8_counter_stats')
189
190   def DidRunTest(self, browser, results):
191     """Adds summary results (single number for one test run)."""
192     # Keep track of total test run length in terms of seconds and page
193     # repetitions, so that test run length can be known regardless of
194     # whether it is specified in seconds or iterations.
195     results.AddSummary('total_iterations', 'iterations', self._iterations,
196                        data_type='unimportant')
197     results.AddSummary('total_time', 'seconds', time.time() - self._start_time,
198                        data_type='unimportant')
199
200     # Add summary stats which could be monitored for anomalies.
201     for trace_name in self._y_values:
202       units = self._y_values[trace_name]['units']
203       chart_name = self._y_values[trace_name]['chart_name']
204       values = self._y_values[trace_name]['values']
205       results.AddSummary(trace_name + '_max', units, max(values),
206                          chart_name=chart_name)
207