| OLD | NEW |
| (Empty) |
| 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. | |
| 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 Finally, various memory and IO statistics are gathered at the very end of | |
| 14 cycling all pages. | |
| 15 """ | |
| 16 | |
| 17 import collections | |
| 18 import os | |
| 19 | |
| 20 from telemetry.core import util | |
| 21 from telemetry.page import legacy_page_test | |
| 22 from telemetry.value import scalar | |
| 23 | |
| 24 from metrics import cpu | |
| 25 from metrics import keychain_metric | |
| 26 from metrics import memory | |
| 27 from metrics import power | |
| 28 from metrics import speedindex | |
| 29 | |
| 30 | |
| 31 class PageCycler(legacy_page_test.LegacyPageTest): | |
| 32 | |
| 33 def __init__(self, page_repeat, pageset_repeat, cold_load_percent=50, | |
| 34 report_speed_index=False, clear_cache_before_each_run=False): | |
| 35 super(PageCycler, self).__init__( | |
| 36 clear_cache_before_each_run=clear_cache_before_each_run) | |
| 37 | |
| 38 with open(os.path.join(os.path.dirname(__file__), | |
| 39 'page_cycler.js'), 'r') as f: | |
| 40 self._page_cycler_js = f.read() | |
| 41 | |
| 42 self._report_speed_index = report_speed_index | |
| 43 self._speedindex_metric = speedindex.SpeedIndexMetric() | |
| 44 self._memory_metric = None | |
| 45 self._power_metric = None | |
| 46 self._cpu_metric = None | |
| 47 self._has_loaded_page = collections.defaultdict(int) | |
| 48 self._initial_renderer_url = None # to avoid cross-renderer navigation | |
| 49 | |
| 50 cold_runs_percent_set = (cold_load_percent != None) | |
| 51 # Handle requests for cold cache runs | |
| 52 if (cold_runs_percent_set and | |
| 53 (cold_load_percent < 0 or cold_load_percent > 100)): | |
| 54 raise Exception('cold-load-percent must be in the range [0-100]') | |
| 55 | |
| 56 # Make sure _cold_run_start_index is an integer multiple of page_repeat. | |
| 57 # Without this, --pageset_shuffle + --page_repeat could lead to | |
| 58 # assertion failures on _started_warm in WillNavigateToPage. | |
| 59 if cold_runs_percent_set: | |
| 60 number_warm_pageset_runs = int( | |
| 61 (int(pageset_repeat) - 1) * (100 - cold_load_percent) / 100) | |
| 62 number_warm_runs = number_warm_pageset_runs * page_repeat | |
| 63 self._cold_run_start_index = number_warm_runs + page_repeat | |
| 64 else: | |
| 65 self._cold_run_start_index = pageset_repeat * page_repeat | |
| 66 | |
| 67 def WillStartBrowser(self, platform): | |
| 68 """Initialize metrics once right before the browser has been launched.""" | |
| 69 self._power_metric = power.PowerMetric(platform) | |
| 70 | |
| 71 def DidStartBrowser(self, browser): | |
| 72 """Initialize metrics once right after the browser has been launched.""" | |
| 73 self._memory_metric = memory.MemoryMetric(browser) | |
| 74 self._cpu_metric = cpu.CpuMetric(browser) | |
| 75 | |
| 76 def WillNavigateToPage(self, page, tab): | |
| 77 if page.is_file: | |
| 78 # For legacy page cyclers which use the filesystem, do an initial | |
| 79 # navigate to avoid paying for a cross-renderer navigation. | |
| 80 initial_url = tab.browser.platform.http_server.UrlOf('nonexistent.html') | |
| 81 if self._initial_renderer_url != initial_url: | |
| 82 self._initial_renderer_url = initial_url | |
| 83 tab.Navigate(self._initial_renderer_url) | |
| 84 | |
| 85 page.script_to_evaluate_on_commit = self._page_cycler_js | |
| 86 if self.ShouldRunCold(page.url): | |
| 87 tab.ClearCache(force=True) | |
| 88 if self._report_speed_index: | |
| 89 self._speedindex_metric.Start(page, tab) | |
| 90 self._cpu_metric.Start(page, tab) | |
| 91 self._power_metric.Start(page, tab) | |
| 92 | |
| 93 def DidNavigateToPage(self, page, tab): | |
| 94 self._memory_metric.Start(page, tab) | |
| 95 | |
| 96 def CustomizeBrowserOptions(self, options): | |
| 97 memory.MemoryMetric.CustomizeBrowserOptions(options) | |
| 98 power.PowerMetric.CustomizeBrowserOptions(options) | |
| 99 options.AppendExtraBrowserArgs('--js-flags=--expose_gc') | |
| 100 | |
| 101 if self._report_speed_index: | |
| 102 self._speedindex_metric.CustomizeBrowserOptions(options) | |
| 103 | |
| 104 keychain_metric.KeychainMetric.CustomizeBrowserOptions(options) | |
| 105 | |
| 106 def ValidateAndMeasurePage(self, page, tab, results): | |
| 107 tab.WaitForJavaScriptExpression('__pc_load_time', 60) | |
| 108 | |
| 109 chart_name_prefix = ('cold_' if self.IsRunCold(page.url) else | |
| 110 'warm_') | |
| 111 | |
| 112 results.AddValue(scalar.ScalarValue( | |
| 113 results.current_page, '%stimes-page_load_time' % chart_name_prefix, | |
| 114 'ms', tab.EvaluateJavaScript('__pc_load_time'), | |
| 115 description='Average page load time. Measured from ' | |
| 116 'performance.timing.navigationStart until the completion ' | |
| 117 'time of a layout after the window.load event. Cold times ' | |
| 118 'are the times when the page is loaded cold, i.e. without ' | |
| 119 'loading it before, and warm times are times when the ' | |
| 120 'page is loaded after being loaded previously.')) | |
| 121 results.AddValue(scalar.ScalarValue( | |
| 122 results.current_page, '%stimes-time_to_onload' % chart_name_prefix, | |
| 123 'ms', tab.EvaluateJavaScript('performance.timing.loadEventStart' | |
| 124 '- performance.timing.navigationStart'), | |
| 125 description='Time to onload. This is temporary metric to check that ' | |
| 126 'PCv1 and PCv2 emit similar results')) | |
| 127 | |
| 128 # TODO(kouhei): Remove below. crbug.com/616342 | |
| 129 results.AddValue(scalar.ScalarValue( | |
| 130 results.current_page, '%stimes.page_load_time' % chart_name_prefix, | |
| 131 'ms', tab.EvaluateJavaScript('__pc_load_time'), | |
| 132 description='Average page load time. Measured from ' | |
| 133 'performance.timing.navigationStart until the completion ' | |
| 134 'time of a layout after the window.load event. Cold times ' | |
| 135 'are the times when the page is loaded cold, i.e. without ' | |
| 136 'loading it before, and warm times are times when the ' | |
| 137 'page is loaded after being loaded previously.')) | |
| 138 results.AddValue(scalar.ScalarValue( | |
| 139 results.current_page, '%stimes.time_to_onload' % chart_name_prefix, | |
| 140 'ms', tab.EvaluateJavaScript('performance.timing.loadEventStart' | |
| 141 '- performance.timing.navigationStart'), | |
| 142 description='Time to onload. This is temporary metric to check that ' | |
| 143 'PCv1 and PCv2 emit similar results')) | |
| 144 | |
| 145 self._has_loaded_page[page.url] += 1 | |
| 146 | |
| 147 self._power_metric.Stop(page, tab) | |
| 148 self._memory_metric.Stop(page, tab) | |
| 149 self._memory_metric.AddResults(tab, results) | |
| 150 self._power_metric.AddResults(tab, results) | |
| 151 | |
| 152 self._cpu_metric.Stop(page, tab) | |
| 153 self._cpu_metric.AddResults(tab, results) | |
| 154 | |
| 155 if self._report_speed_index: | |
| 156 def SpeedIndexIsFinished(): | |
| 157 return self._speedindex_metric.IsFinished(tab) | |
| 158 util.WaitFor(SpeedIndexIsFinished, 60) | |
| 159 self._speedindex_metric.Stop(page, tab) | |
| 160 self._speedindex_metric.AddResults( | |
| 161 tab, results, chart_name=chart_name_prefix + 'speed_index') | |
| 162 keychain_metric.KeychainMetric().AddResults(tab, results) | |
| 163 | |
| 164 def IsRunCold(self, url): | |
| 165 return self.ShouldRunCold(url) or self._has_loaded_page[url] == 0 | |
| 166 | |
| 167 def ShouldRunCold(self, url): | |
| 168 # We do the warm runs first for two reasons. The first is so we can | |
| 169 # preserve any initial profile cache for as long as possible. | |
| 170 # The second is that, if we did cold runs first, we'd have a transition | |
| 171 # page set during which we wanted the run for each URL to both | |
| 172 # contribute to the cold data and warm the catch for the following | |
| 173 # warm run, and clearing the cache before the load of the following | |
| 174 # URL would eliminate the intended warmup for the previous URL. | |
| 175 return self._has_loaded_page[url] >= self._cold_run_start_index | |
| 176 | |
| 177 def DidRunPage(self, platform): | |
| 178 del platform # unused | |
| 179 self._power_metric.Close() | |
| OLD | NEW |