Home | History | Annotate | Download | only in metrics
      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.
      4 
      5 from metrics import Metric
      6 from metrics import rendering_stats
      7 from metrics import statistics
      8 from telemetry.core.timeline.model import MarkerMismatchError
      9 from telemetry.core.timeline.model import MarkerOverlapError
     10 from telemetry.page import page_measurement
     11 
     12 TIMELINE_MARKER = 'Smoothness'
     13 
     14 
     15 class NotEnoughFramesError(page_measurement.MeasurementFailure):
     16   def __init__(self):
     17     super(NotEnoughFramesError, self).__init__(
     18         'Page output less than two frames')
     19 
     20 
     21 class NoSupportedActionError(page_measurement.MeasurementFailure):
     22   def __init__(self):
     23     super(NoSupportedActionError, self).__init__(
     24         'None of the actions is supported by smoothness measurement')
     25 
     26 
     27 class SmoothnessMetric(Metric):
     28   def __init__(self):
     29     super(SmoothnessMetric, self).__init__()
     30     self._stats = None
     31     self._timeline_marker_names = []
     32 
     33   def AddTimelineMarkerNameToIncludeInMetric(self, timeline_marker_name):
     34     self._timeline_marker_names.append(timeline_marker_name)
     35 
     36   def Start(self, page, tab):
     37     tab.browser.StartTracing('webkit.console,benchmark', 60)
     38     tab.ExecuteJavaScript('console.time("' + TIMELINE_MARKER + '")')
     39 
     40   def Stop(self, page, tab):
     41     tab.ExecuteJavaScript('console.timeEnd("' + TIMELINE_MARKER + '")')
     42     timeline_model = tab.browser.StopTracing().AsTimelineModel()
     43     try:
     44       timeline_markers = timeline_model.FindTimelineMarkers(
     45           self._timeline_marker_names)
     46     except MarkerMismatchError as e:
     47       raise page_measurement.MeasurementFailure(str(e))
     48     except MarkerOverlapError as e:
     49       raise page_measurement.MeasurementFailure(str(e))
     50 
     51     renderer_process = timeline_model.GetRendererProcessFromTab(tab)
     52     self._stats = rendering_stats.RenderingStats(
     53         renderer_process, timeline_markers)
     54 
     55     if not self._stats.frame_times:
     56       raise NotEnoughFramesError()
     57 
     58   def SetStats(self, stats):
     59     """ Pass in a RenderingStats object directly. For unittests that don't call
     60         Start/Stop.
     61     """
     62     self._stats = stats
     63 
     64   def AddResults(self, tab, results):
     65     # List of raw frame times.
     66     results.Add('frame_times', 'ms', self._stats.frame_times)
     67 
     68     # Arithmetic mean of frame times.
     69     mean_frame_time = statistics.ArithmeticMean(self._stats.frame_times,
     70                                                 len(self._stats.frame_times))
     71     results.Add('mean_frame_time', 'ms', round(mean_frame_time, 3))
     72 
     73     # Absolute discrepancy of frame time stamps.
     74     jank = statistics.FrameDiscrepancy(self._stats.frame_timestamps)
     75     results.Add('jank', '', round(jank, 4))
     76 
     77     # Are we hitting 60 fps for 95 percent of all frames?
     78     # We use 19ms as a somewhat looser threshold, instead of 1000.0/60.0.
     79     percentile_95 = statistics.Percentile(self._stats.frame_times, 95.0)
     80     results.Add('mostly_smooth', '', 1.0 if percentile_95 < 19.0 else 0.0)
     81