Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / tools / perf / measurements / page_cycler.py
1 # Copyright (c) 2012 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 """The page cycler measurement.
6
7 This measurement registers a window load handler in which is forces a layout and
8 then records the value of performance.now(). This call to now() measures the
9 time from navigationStart (immediately after the previous page's beforeunload
10 event) until after the layout in the page's load event. In addition, two garbage
11 collections are performed in between the page loads (in the beforeunload event).
12 This extra garbage collection time is not included in the measurement times.
13
14 Finally, various memory and IO statistics are gathered at the very end of
15 cycling all pages.
16 """
17
18 import collections
19 import os
20
21 from metrics import cpu
22 from metrics import io
23 from metrics import memory
24 from metrics import power
25 from metrics import speedindex
26 from metrics import v8_object_stats
27 from telemetry.core import util
28 from telemetry.page import page_measurement
29
30 class PageCycler(page_measurement.PageMeasurement):
31   def __init__(self, *args, **kwargs):
32     super(PageCycler, self).__init__(*args, **kwargs)
33
34     with open(os.path.join(os.path.dirname(__file__),
35                            'page_cycler.js'), 'r') as f:
36       self._page_cycler_js = f.read()
37
38     self._record_v8_object_stats = False
39     self._report_speed_index = False
40     self._speedindex_metric = speedindex.SpeedIndexMetric()
41     self._memory_metric = None
42     self._power_metric = power.PowerMetric()
43     self._cpu_metric = None
44     self._v8_object_stats_metric = None
45     self._cold_run_start_index = None
46     self._has_loaded_page = collections.defaultdict(int)
47
48   def AddCommandLineOptions(self, parser):
49     # The page cyclers should default to 10 iterations. In order to change the
50     # default of an option, we must remove and re-add it.
51     # TODO: Remove this after transition to run_benchmark.
52     pageset_repeat_option = parser.get_option('--pageset-repeat')
53     pageset_repeat_option.default = 10
54     parser.remove_option('--pageset-repeat')
55     parser.add_option(pageset_repeat_option)
56
57     parser.add_option('--v8-object-stats',
58         action='store_true',
59         help='Enable detailed V8 object statistics.')
60
61     parser.add_option('--report-speed-index',
62         action='store_true',
63         help='Enable the speed index metric.')
64
65     parser.add_option('--cold-load-percent', type='int',
66                       help='%d of page visits for which a cold load is forced')
67
68   def DidStartBrowser(self, browser):
69     """Initialize metrics once right after the browser has been launched."""
70     self._memory_metric = memory.MemoryMetric(browser)
71     self._cpu_metric = cpu.CpuMetric(browser)
72     if self._record_v8_object_stats:
73       self._v8_object_stats_metric = v8_object_stats.V8ObjectStatsMetric()
74
75   def DidStartHTTPServer(self, tab):
76     # Avoid paying for a cross-renderer navigation on the first page on legacy
77     # page cyclers which use the filesystem.
78     tab.Navigate(tab.browser.http_server.UrlOf('nonexistent.html'))
79
80   def WillNavigateToPage(self, page, tab):
81     page.script_to_evaluate_on_commit = self._page_cycler_js
82     if self.ShouldRunCold(page.url):
83       tab.ClearCache(force=True)
84     if self._report_speed_index:
85       self._speedindex_metric.Start(page, tab)
86
87   def DidNavigateToPage(self, page, tab):
88     self._memory_metric.Start(page, tab)
89     self._power_metric.Start(page, tab)
90     # TODO(qyearsley): Uncomment the following line and move it to
91     # WillNavigateToPage once the cpu metric has been changed.
92     # This is being temporarily commented out to let the page cycler
93     # results return to how they were before the cpu metric was added.
94     # self._cpu_metric.Start(page, tab) See crbug.com/301714.
95     if self._record_v8_object_stats:
96       self._v8_object_stats_metric.Start(page, tab)
97
98   def CustomizeBrowserOptions(self, options):
99     memory.MemoryMetric.CustomizeBrowserOptions(options)
100     power.PowerMetric.CustomizeBrowserOptions(options)
101     io.IOMetric.CustomizeBrowserOptions(options)
102     options.AppendExtraBrowserArgs('--js-flags=--expose_gc')
103
104     if options.v8_object_stats:
105       self._record_v8_object_stats = True
106       v8_object_stats.V8ObjectStatsMetric.CustomizeBrowserOptions(options)
107
108     if options.report_speed_index:
109       self._report_speed_index = True
110       self._speedindex_metric.CustomizeBrowserOptions(options)
111
112     cold_runs_percent_set = (options.cold_load_percent != None)
113     # Handle requests for cold cache runs
114     if (cold_runs_percent_set and
115         (options.repeat_options.page_repeat_secs or
116          options.repeat_options.pageset_repeat_secs)):
117       raise Exception('--cold-load-percent is incompatible with timed repeat')
118
119     if (cold_runs_percent_set and
120         (options.cold_load_percent < 0 or options.cold_load_percent > 100)):
121       raise Exception('--cold-load-percent must be in the range [0-100]')
122
123     # Make sure _cold_run_start_index is an integer multiple of page_repeat.
124     # Without this, --pageset_shuffle + --page_repeat could lead to
125     # assertion failures on _started_warm in WillNavigateToPage.
126     if cold_runs_percent_set:
127       number_warm_pageset_runs = int(
128           (int(options.repeat_options.pageset_repeat_iters) - 1) *
129           (100 - options.cold_load_percent) / 100)
130       number_warm_runs = (number_warm_pageset_runs *
131                           options.repeat_options.page_repeat_iters)
132       self._cold_run_start_index = (number_warm_runs +
133           options.repeat_options.page_repeat_iters)
134       self.discard_first_result = (not options.cold_load_percent or
135                                    self.discard_first_result)
136     else:
137       self._cold_run_start_index = (
138           options.repeat_options.pageset_repeat_iters *
139           options.repeat_options.page_repeat_iters)
140
141   def MeasurePage(self, page, tab, results):
142     tab.WaitForJavaScriptExpression('__pc_load_time', 60)
143
144     chart_name_prefix = ('cold_' if self.IsRunCold(page.url) else
145                          'warm_')
146
147     results.Add('page_load_time', 'ms',
148                 int(float(tab.EvaluateJavaScript('__pc_load_time'))),
149                 chart_name=chart_name_prefix+'times')
150
151     self._has_loaded_page[page.url] += 1
152
153     self._power_metric.Stop(page, tab)
154     self._memory_metric.Stop(page, tab)
155     self._memory_metric.AddResults(tab, results)
156     self._power_metric.AddResults(tab, results)
157
158     # TODO(qyearsley): Uncomment the following line when CPU metric is
159     # changed. See crbug.com/301714.
160     # self._cpu_metric.Stop(page, tab)
161     # self._cpu_metric.AddResults(tab, results)
162     if self._record_v8_object_stats:
163       self._v8_object_stats_metric.Stop(page, tab)
164       self._v8_object_stats_metric.AddResults(tab, results)
165
166     if self._report_speed_index:
167       def SpeedIndexIsFinished():
168         return self._speedindex_metric.IsFinished(tab)
169       util.WaitFor(SpeedIndexIsFinished, 60)
170       self._speedindex_metric.Stop(page, tab)
171       self._speedindex_metric.AddResults(
172           tab, results, chart_name=chart_name_prefix+'speed_index')
173
174   def DidRunTest(self, browser, results):
175     io.IOMetric().AddSummaryResults(browser, results)
176
177   def IsRunCold(self, url):
178     return (self.ShouldRunCold(url) or
179             self._has_loaded_page[url] == 0)
180
181   def ShouldRunCold(self, url):
182     # We do the warm runs first for two reasons.  The first is so we can
183     # preserve any initial profile cache for as long as possible.
184     # The second is that, if we did cold runs first, we'd have a transition
185     # page set during which we wanted the run for each URL to both
186     # contribute to the cold data and warm the catch for the following
187     # warm run, and clearing the cache before the load of the following
188     # URL would eliminate the intended warmup for the previous URL.
189     return (self._has_loaded_page[url] >= self._cold_run_start_index)
190
191   def results_are_the_same_on_every_page(self):
192     return False