OLD | NEW |
1 # Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 import collections | 4 |
5 import json | 5 import json |
6 import logging | 6 import logging |
7 | 7 |
| 8 from metrics import histogram_util |
8 from metrics import Metric | 9 from metrics import Metric |
9 from metrics import histogram_util | |
10 | |
11 from telemetry.core import util | 10 from telemetry.core import util |
12 | 11 |
13 | 12 |
14 class StartupMetric(Metric): | 13 class StartupMetric(Metric): |
15 "A metric for browser startup time." | 14 """A metric for browser startup time. |
16 | 15 |
17 HISTOGRAMS_TO_RECORD = { | 16 User visible metrics: |
18 'messageloop_start_time' : | 17 process_creation_to_window_display: Time from browser process creation to |
19 'Startup.BrowserMessageLoopStartTimeFromMainEntry', | 18 the time that the browser window initially becomes visible. |
20 'window_display_time' : 'Startup.BrowserWindowDisplay', | 19 process_creation_to_foreground_tab_loaded: Time from browser process |
21 'open_tabs_time' : 'Startup.BrowserOpenTabs'} | 20 creation to the time that the foreground tab is fully loaded. |
| 21 process_creation_to_all_tabs_loaded: Time from the browser process creation |
| 22 to the time that all tabs have fully loaded. |
| 23 |
| 24 Critical code progression: |
| 25 process_creation_to_main: Time from process creation to the execution of the |
| 26 browser's main() entry. |
| 27 main_to_messageloop_start: Time from main() entry to the start of the UI |
| 28 thread's message loop. |
| 29 """ |
22 | 30 |
23 def Start(self, page, tab): | 31 def Start(self, page, tab): |
24 raise NotImplementedError() | 32 raise NotImplementedError() |
25 | 33 |
26 def Stop(self, page, tab): | 34 def Stop(self, page, tab): |
27 raise NotImplementedError() | 35 raise NotImplementedError() |
28 | 36 |
29 def _GetBrowserMainEntryTime(self, tab): | 37 def _GetBrowserMainEntryTime(self, tab): |
30 """Returns the main entry time (in ms) of the browser.""" | 38 """Returns the main entry time (in ms) of the browser.""" |
31 histogram_type = histogram_util.BROWSER_HISTOGRAM | |
32 high_bytes = histogram_util.GetHistogramSum( | 39 high_bytes = histogram_util.GetHistogramSum( |
33 histogram_type, | 40 histogram_util.BROWSER_HISTOGRAM, |
34 'Startup.BrowserMainEntryTimeAbsoluteHighWord', | 41 'Startup.BrowserMainEntryTimeAbsoluteHighWord', |
35 tab) | 42 tab) |
36 low_bytes = histogram_util.GetHistogramSum( | 43 low_bytes = histogram_util.GetHistogramSum( |
37 histogram_type, | 44 histogram_util.BROWSER_HISTOGRAM, |
38 'Startup.BrowserMainEntryTimeAbsoluteLowWord', | 45 'Startup.BrowserMainEntryTimeAbsoluteLowWord', |
39 tab) | 46 tab) |
40 if high_bytes == 0 and low_bytes == 0: | 47 if high_bytes == 0 and low_bytes == 0: |
41 return None | 48 return None |
42 return (int(high_bytes) << 32) | (int(low_bytes) << 1) | 49 return (int(high_bytes) << 32) | (int(low_bytes) << 1) |
43 | 50 |
44 def _RecordTabLoadTimes(self, tab, browser_main_entry_time_ms, results): | 51 def _GetTabLoadTimes(self, browser): |
45 """Records the tab load times for the browser. """ | 52 """Returns a tuple of (foreground_tab_load_time, all_tabs_load_time).""" |
46 tab_load_times = [] | 53 foreground_tab_load_time = 0 |
47 TabLoadTime = collections.namedtuple( | 54 all_tabs_load_time = 0 |
48 'TabLoadTime', | 55 for i in xrange(len(browser.tabs)): |
49 ['load_start_ms', 'load_duration_ms']) | |
50 | |
51 def RecordTabLoadTime(t): | |
52 try: | 56 try: |
53 t.WaitForDocumentReadyStateToBeComplete() | 57 tab = browser.tabs[i] |
54 | 58 tab.WaitForDocumentReadyStateToBeComplete() |
55 result = t.EvaluateJavaScript( | 59 result = json.loads(tab.EvaluateJavaScript( |
56 'statsCollectionController.tabLoadTiming()') | 60 'statsCollectionController.tabLoadTiming()')) |
57 result = json.loads(result) | 61 load_time = result['load_start_ms'] + result['load_duration_ms'] |
58 | 62 all_tabs_load_time = max(all_tabs_load_time, load_time) |
59 if 'load_start_ms' not in result or 'load_duration_ms' not in result: | 63 if tab == browser.foreground_tab: |
60 raise Exception("Outdated Chrome version, " | 64 foreground_tab_load_time = load_time |
61 "statsCollectionController.tabLoadTiming() not present") | |
62 if result['load_duration_ms'] is None: | |
63 tab_title = t.EvaluateJavaScript('document.title') | |
64 print "Page: ", tab_title, " didn't finish loading." | |
65 return | |
66 | |
67 tab_load_times.append(TabLoadTime( | |
68 int(result['load_start_ms']), | |
69 int(result['load_duration_ms']))) | |
70 except util.TimeoutException: | 65 except util.TimeoutException: |
71 # Low memory Android devices may not be able to load more than | 66 # Low memory Android devices may not be able to load more than |
72 # one tab at a time, so may timeout when the test attempts to | 67 # one tab at a time, so may timeout when the test attempts to |
73 # access a background tab. Ignore these tabs. | 68 # access a background tab. Ignore these tabs. |
74 logging.error("Tab timed out on JavaScript access") | 69 logging.error('Tab timed out on JavaScript access') |
75 | 70 return foreground_tab_load_time, all_tabs_load_time |
76 # Only measure the foreground tab. We can't measure all tabs on Android | |
77 # because on Android the data of the background tabs is loaded on demand, | |
78 # when the user switches to them, rather than during startup. In view of | |
79 # this, to get the same measures on all platform, we only measure the | |
80 # foreground tab on all platforms. | |
81 | |
82 RecordTabLoadTime(tab.browser.foreground_tab) | |
83 | |
84 foreground_tab_stats = tab_load_times[0] | |
85 foreground_tab_load_complete = ((foreground_tab_stats.load_start_ms + | |
86 foreground_tab_stats.load_duration_ms) - browser_main_entry_time_ms) | |
87 results.Add( | |
88 'foreground_tab_load_complete', 'ms', foreground_tab_load_complete) | |
89 | 71 |
90 def AddResults(self, tab, results): | 72 def AddResults(self, tab, results): |
91 get_histogram_js = 'statsCollectionController.getBrowserHistogram("%s")' | 73 absolute_browser_main_entry_ms = self._GetBrowserMainEntryTime(tab) |
92 | 74 |
93 for display_name, histogram_name in self.HISTOGRAMS_TO_RECORD.iteritems(): | 75 process_creation_to_window_display_ms = histogram_util.GetHistogramSum( |
94 result = tab.EvaluateJavaScript(get_histogram_js % histogram_name) | 76 histogram_util.BROWSER_HISTOGRAM, 'Startup.BrowserWindowDisplay', tab) |
95 result = json.loads(result) | 77 absolute_foreground_tab_loaded_ms, absolute_all_tabs_loaded_ms = \ |
96 measured_time = 0 | 78 self._GetTabLoadTimes(tab.browser) |
| 79 process_creation_to_messageloop_start_ms = histogram_util.GetHistogramSum( |
| 80 histogram_util.BROWSER_HISTOGRAM, 'Startup.BrowserMessageLoopStartTime', |
| 81 tab) |
| 82 main_to_messageloop_start_ms = histogram_util.GetHistogramSum( |
| 83 histogram_util.BROWSER_HISTOGRAM, |
| 84 'Startup.BrowserMessageLoopStartTimeFromMainEntry', |
| 85 tab) |
| 86 process_creation_to_main = (process_creation_to_messageloop_start_ms - |
| 87 main_to_messageloop_start_ms) |
97 | 88 |
98 if 'sum' in result: | 89 # User visible. |
99 # For all the histograms logged here, there's a single entry so sum | 90 results.Add('process_creation_to_window_display', 'ms', |
100 # is the exact value for that entry. | 91 process_creation_to_window_display_ms) |
101 measured_time = result['sum'] | 92 results.Add('process_creation_to_foreground_tab_loaded', 'ms', |
102 elif 'buckets' in result: | 93 absolute_foreground_tab_loaded_ms - |
103 measured_time = \ | 94 absolute_browser_main_entry_ms + process_creation_to_main) |
104 (result['buckets'][0]['high'] + result['buckets'][0]['low']) / 2 | 95 results.Add('process_creation_to_all_tabs_loaded', 'ms', |
| 96 absolute_all_tabs_loaded_ms - |
| 97 absolute_browser_main_entry_ms + process_creation_to_main) |
105 | 98 |
106 results.Add(display_name, 'ms', measured_time) | 99 # Critical code progression. |
107 | 100 results.Add('process_creation_to_main', 'ms', |
108 # Get tab load times. | 101 process_creation_to_main) |
109 browser_main_entry_time_ms = self._GetBrowserMainEntryTime(tab) | 102 results.Add('main_to_messageloop_start', 'ms', |
110 if (browser_main_entry_time_ms is None): | 103 main_to_messageloop_start_ms) |
111 print "Outdated Chrome version, browser main entry time not supported." | |
112 return | |
113 self._RecordTabLoadTimes(tab, browser_main_entry_time_ms, results) | |
OLD | NEW |