1 # Copyright 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.
5 """The page cycler measurement.
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.
14 Finally, various memory and IO statistics are gathered at the very end of
21 from metrics import cpu
22 from metrics import iometric
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_test
29 from telemetry.value import scalar
32 class PageCycler(page_test.PageTest):
33 options = {'pageset_repeat': 10}
35 def __init__(self, *args, **kwargs):
36 super(PageCycler, self).__init__(*args, **kwargs)
38 with open(os.path.join(os.path.dirname(__file__),
39 'page_cycler.js'), 'r') as f:
40 self._page_cycler_js = f.read()
42 self._speedindex_metric = speedindex.SpeedIndexMetric()
43 self._memory_metric = None
44 self._power_metric = None
45 self._cpu_metric = None
46 self._v8_object_stats_metric = None
47 self._has_loaded_page = collections.defaultdict(int)
48 self._initial_renderer_url = None # to avoid cross-renderer navigation
51 def AddCommandLineArgs(cls, parser):
52 parser.add_option('--v8-object-stats',
54 help='Enable detailed V8 object statistics.')
56 parser.add_option('--report-speed-index',
58 help='Enable the speed index metric.')
60 parser.add_option('--cold-load-percent', type='int', default=50,
61 help='%d of page visits for which a cold load is forced')
64 def ProcessCommandLineArgs(cls, parser, args):
65 cls._record_v8_object_stats = args.v8_object_stats
66 cls._report_speed_index = args.report_speed_index
68 cold_runs_percent_set = (args.cold_load_percent != None)
69 # Handle requests for cold cache runs
70 if (cold_runs_percent_set and
71 (args.cold_load_percent < 0 or args.cold_load_percent > 100)):
72 raise Exception('--cold-load-percent must be in the range [0-100]')
74 # Make sure _cold_run_start_index is an integer multiple of page_repeat.
75 # Without this, --pageset_shuffle + --page_repeat could lead to
76 # assertion failures on _started_warm in WillNavigateToPage.
77 if cold_runs_percent_set:
78 number_warm_pageset_runs = int(
79 (int(args.pageset_repeat) - 1) * (100 - args.cold_load_percent) / 100)
80 number_warm_runs = number_warm_pageset_runs * args.page_repeat
81 cls._cold_run_start_index = number_warm_runs + args.page_repeat
82 cls.discard_first_result = (not args.cold_load_percent or
83 cls.discard_first_result)
85 cls._cold_run_start_index = args.pageset_repeat * args.page_repeat
87 def WillStartBrowser(self, platform):
88 """Initialize metrics once right before the browser has been launched."""
89 self._power_metric = power.PowerMetric(platform)
91 def DidStartBrowser(self, browser):
92 """Initialize metrics once right after the browser has been launched."""
93 self._memory_metric = memory.MemoryMetric(browser)
94 self._cpu_metric = cpu.CpuMetric(browser)
95 if self._record_v8_object_stats:
96 self._v8_object_stats_metric = v8_object_stats.V8ObjectStatsMetric()
98 def WillNavigateToPage(self, page, tab):
100 # For legacy page cyclers which use the filesystem, do an initial
101 # navigate to avoid paying for a cross-renderer navigation.
102 initial_url = tab.browser.http_server.UrlOf('nonexistent.html')
103 if self._initial_renderer_url != initial_url:
104 self._initial_renderer_url = initial_url
105 tab.Navigate(self._initial_renderer_url)
107 page.script_to_evaluate_on_commit = self._page_cycler_js
108 if self.ShouldRunCold(page.url):
109 tab.ClearCache(force=True)
110 if self._report_speed_index:
111 self._speedindex_metric.Start(page, tab)
112 self._cpu_metric.Start(page, tab)
113 self._power_metric.Start(page, tab)
115 def DidNavigateToPage(self, page, tab):
116 self._memory_metric.Start(page, tab)
117 if self._record_v8_object_stats:
118 self._v8_object_stats_metric.Start(page, tab)
120 def CustomizeBrowserOptions(self, options):
121 memory.MemoryMetric.CustomizeBrowserOptions(options)
122 power.PowerMetric.CustomizeBrowserOptions(options)
123 iometric.IOMetric.CustomizeBrowserOptions(options)
124 options.AppendExtraBrowserArgs('--js-flags=--expose_gc')
126 if self._record_v8_object_stats:
127 v8_object_stats.V8ObjectStatsMetric.CustomizeBrowserOptions(options)
128 if self._report_speed_index:
129 self._speedindex_metric.CustomizeBrowserOptions(options)
131 def ValidateAndMeasurePage(self, page, tab, results):
132 tab.WaitForJavaScriptExpression('__pc_load_time', 60)
134 chart_name_prefix = ('cold_' if self.IsRunCold(page.url) else
137 results.AddValue(scalar.ScalarValue(
138 results.current_page, '%stimes.page_load_time' % chart_name_prefix,
139 'ms', tab.EvaluateJavaScript('__pc_load_time'),
140 description='Average page load time. Measured from '
141 'performance.timing.navigationStart until the completion '
142 'time of a layout after the window.load event. Cold times '
143 'are the times when the page is loaded cold, i.e. without '
144 'loading it before, and warm times are times when the '
145 'page is loaded after being loaded previously.'))
147 self._has_loaded_page[page.url] += 1
149 self._power_metric.Stop(page, tab)
150 self._memory_metric.Stop(page, tab)
151 self._memory_metric.AddResults(tab, results)
152 self._power_metric.AddResults(tab, results)
154 self._cpu_metric.Stop(page, tab)
155 self._cpu_metric.AddResults(tab, results)
156 if self._record_v8_object_stats:
157 self._v8_object_stats_metric.Stop(page, tab)
158 self._v8_object_stats_metric.AddResults(tab, results)
160 if self._report_speed_index:
161 def SpeedIndexIsFinished():
162 return self._speedindex_metric.IsFinished(tab)
163 util.WaitFor(SpeedIndexIsFinished, 60)
164 self._speedindex_metric.Stop(page, tab)
165 self._speedindex_metric.AddResults(
166 tab, results, chart_name=chart_name_prefix+'speed_index')
168 def DidRunTest(self, browser, results):
169 iometric.IOMetric().AddSummaryResults(browser, results)
171 def IsRunCold(self, url):
172 return (self.ShouldRunCold(url) or
173 self._has_loaded_page[url] == 0)
175 def ShouldRunCold(self, url):
176 # We do the warm runs first for two reasons. The first is so we can
177 # preserve any initial profile cache for as long as possible.
178 # The second is that, if we did cold runs first, we'd have a transition
179 # page set during which we wanted the run for each URL to both
180 # contribute to the cold data and warm the catch for the following
181 # warm run, and clearing the cache before the load of the following
182 # URL would eliminate the intended warmup for the previous URL.
183 return (self._has_loaded_page[url] >= self._cold_run_start_index)