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