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 keychain_metric
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 def __init__(self
, page_repeat
, pageset_repeat
, cold_load_percent
=50,
34 record_v8_object_stats
=False, report_speed_index
=False,
35 clear_cache_before_each_run
=False):
36 super(PageCycler
, self
).__init
__(
37 clear_cache_before_each_run
=clear_cache_before_each_run
)
39 with
open(os
.path
.join(os
.path
.dirname(__file__
),
40 'page_cycler.js'), 'r') as f
:
41 self
._page
_cycler
_js
= f
.read()
43 self
._record
_v
8_object
_stats
= record_v8_object_stats
44 self
._report
_speed
_index
= report_speed_index
45 self
._speedindex
_metric
= speedindex
.SpeedIndexMetric()
46 self
._memory
_metric
= None
47 self
._power
_metric
= None
48 self
._cpu
_metric
= None
49 self
._v
8_object
_stats
_metric
= None
50 self
._has
_loaded
_page
= collections
.defaultdict(int)
51 self
._initial
_renderer
_url
= None # to avoid cross-renderer navigation
53 cold_runs_percent_set
= (cold_load_percent
!= None)
54 # Handle requests for cold cache runs
55 if (cold_runs_percent_set
and
56 (cold_load_percent
< 0 or cold_load_percent
> 100)):
57 raise Exception('cold-load-percent must be in the range [0-100]')
59 # Make sure _cold_run_start_index is an integer multiple of page_repeat.
60 # Without this, --pageset_shuffle + --page_repeat could lead to
61 # assertion failures on _started_warm in WillNavigateToPage.
62 if cold_runs_percent_set
:
63 number_warm_pageset_runs
= int(
64 (int(pageset_repeat
) - 1) * (100 - cold_load_percent
) / 100)
65 number_warm_runs
= number_warm_pageset_runs
* page_repeat
66 self
._cold
_run
_start
_index
= number_warm_runs
+ page_repeat
67 self
._discard
_first
_result
= (not cold_load_percent
or
68 self
._discard
_first
_result
)
70 self
._cold
_run
_start
_index
= pageset_repeat
* page_repeat
72 def WillStartBrowser(self
, platform
):
73 """Initialize metrics once right before the browser has been launched."""
74 self
._power
_metric
= power
.PowerMetric(platform
)
76 def DidStartBrowser(self
, browser
):
77 """Initialize metrics once right after the browser has been launched."""
78 self
._memory
_metric
= memory
.MemoryMetric(browser
)
79 self
._cpu
_metric
= cpu
.CpuMetric(browser
)
80 if self
._record
_v
8_object
_stats
:
81 self
._v
8_object
_stats
_metric
= v8_object_stats
.V8ObjectStatsMetric()
83 def WillNavigateToPage(self
, page
, tab
):
85 # For legacy page cyclers which use the filesystem, do an initial
86 # navigate to avoid paying for a cross-renderer navigation.
87 initial_url
= tab
.browser
.http_server
.UrlOf('nonexistent.html')
88 if self
._initial
_renderer
_url
!= initial_url
:
89 self
._initial
_renderer
_url
= initial_url
90 tab
.Navigate(self
._initial
_renderer
_url
)
92 page
.script_to_evaluate_on_commit
= self
._page
_cycler
_js
93 if self
.ShouldRunCold(page
.url
):
94 tab
.ClearCache(force
=True)
95 if self
._report
_speed
_index
:
96 self
._speedindex
_metric
.Start(page
, tab
)
97 self
._cpu
_metric
.Start(page
, tab
)
98 self
._power
_metric
.Start(page
, tab
)
100 def DidNavigateToPage(self
, page
, tab
):
101 self
._memory
_metric
.Start(page
, tab
)
102 if self
._record
_v
8_object
_stats
:
103 self
._v
8_object
_stats
_metric
.Start(page
, tab
)
105 def CustomizeBrowserOptions(self
, options
):
106 memory
.MemoryMetric
.CustomizeBrowserOptions(options
)
107 power
.PowerMetric
.CustomizeBrowserOptions(options
)
108 options
.AppendExtraBrowserArgs('--js-flags=--expose_gc')
110 if self
._record
_v
8_object
_stats
:
111 v8_object_stats
.V8ObjectStatsMetric
.CustomizeBrowserOptions(options
)
112 if self
._report
_speed
_index
:
113 self
._speedindex
_metric
.CustomizeBrowserOptions(options
)
115 keychain_metric
.KeychainMetric
.CustomizeBrowserOptions(options
)
117 def ValidateAndMeasurePage(self
, page
, tab
, results
):
118 tab
.WaitForJavaScriptExpression('__pc_load_time', 60)
120 chart_name_prefix
= ('cold_' if self
.IsRunCold(page
.url
) else
123 results
.AddValue(scalar
.ScalarValue(
124 results
.current_page
, '%stimes.page_load_time' % chart_name_prefix
,
125 'ms', tab
.EvaluateJavaScript('__pc_load_time'),
126 description
='Average page load time. Measured from '
127 'performance.timing.navigationStart until the completion '
128 'time of a layout after the window.load event. Cold times '
129 'are the times when the page is loaded cold, i.e. without '
130 'loading it before, and warm times are times when the '
131 'page is loaded after being loaded previously.'))
133 self
._has
_loaded
_page
[page
.url
] += 1
135 self
._power
_metric
.Stop(page
, tab
)
136 self
._memory
_metric
.Stop(page
, tab
)
137 self
._memory
_metric
.AddResults(tab
, results
)
138 self
._power
_metric
.AddResults(tab
, results
)
140 self
._cpu
_metric
.Stop(page
, tab
)
141 self
._cpu
_metric
.AddResults(tab
, results
)
142 if self
._record
_v
8_object
_stats
:
143 self
._v
8_object
_stats
_metric
.Stop(page
, tab
)
144 self
._v
8_object
_stats
_metric
.AddResults(tab
, results
)
146 if self
._report
_speed
_index
:
147 def SpeedIndexIsFinished():
148 return self
._speedindex
_metric
.IsFinished(tab
)
149 util
.WaitFor(SpeedIndexIsFinished
, 60)
150 self
._speedindex
_metric
.Stop(page
, tab
)
151 self
._speedindex
_metric
.AddResults(
152 tab
, results
, chart_name
=chart_name_prefix
+'speed_index')
153 keychain_metric
.KeychainMetric().AddResults(tab
, results
)
155 def IsRunCold(self
, url
):
156 return (self
.ShouldRunCold(url
) or
157 self
._has
_loaded
_page
[url
] == 0)
159 def ShouldRunCold(self
, url
):
160 # We do the warm runs first for two reasons. The first is so we can
161 # preserve any initial profile cache for as long as possible.
162 # The second is that, if we did cold runs first, we'd have a transition
163 # page set during which we wanted the run for each URL to both
164 # contribute to the cold data and warm the catch for the following
165 # warm run, and clearing the cache before the load of the following
166 # URL would eliminate the intended warmup for the previous URL.
167 return (self
._has
_loaded
_page
[url
] >= self
._cold
_run
_start
_index
)