From d21af03f9f5e1d92fdea6696a755cc89dd4cb9c8 Mon Sep 17 00:00:00 2001 From: "dominikg@chromium.org" Date: Mon, 18 Nov 2013 08:53:07 +0000 Subject: [PATCH] Telemetry: Filter invalid frame lengths from SurfaceFlinger data. We use the SurfaceFlinger data to compute the lengths of the frames (normalized to vsyncs). Sometimes the data is inconsistent and we get two frames that are less than 1 vsync apart. Filter out these frame lengths (threshold length is currently 0.5 vsyncs) and print a warning. BUG=318093 Review URL: https://codereview.chromium.org/71353006 git-svn-id: http://src.chromium.org/svn/trunk/src/build@235675 4ff67af0-8c30-449e-8e8b-ad334ec8d88c --- android/pylib/perf/surface_stats_collector.py | 17 ++++- .../perf/surface_stats_collector_unittest.py | 62 +++++++++++++++++++ 2 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 android/pylib/perf/surface_stats_collector_unittest.py diff --git a/android/pylib/perf/surface_stats_collector.py b/android/pylib/perf/surface_stats_collector.py index c38b1b417..9d92b4376 100644 --- a/android/pylib/perf/surface_stats_collector.py +++ b/android/pylib/perf/surface_stats_collector.py @@ -13,6 +13,8 @@ import threading _SURFACE_TEXTURE_TIMESTAMPS_MESSAGE = 'SurfaceTexture update timestamps' _SURFACE_TEXTURE_TIMESTAMP_RE = '\d+' +_MIN_NORMALIZED_FRAME_LENGTH = 0.5 + class SurfaceStatsCollector(object): """Collects surface stats for a SurfaceView from the output of SurfaceFlinger. @@ -79,8 +81,11 @@ class SurfaceStatsCollector(object): ] @staticmethod - def _GetNormalizedDeltas(data, refresh_period): + def _GetNormalizedDeltas(data, refresh_period, min_normalized_delta=None): deltas = [t2 - t1 for t1, t2 in zip(data, data[1:])] + if min_normalized_delta != None: + deltas = filter(lambda d: d / refresh_period >= min_normalized_delta, + deltas) return (deltas, [delta / refresh_period for delta in deltas]) @staticmethod @@ -90,7 +95,13 @@ class SurfaceStatsCollector(object): seconds = timestamps[-1] - timestamps[0] frame_lengths, normalized_frame_lengths = \ - SurfaceStatsCollector._GetNormalizedDeltas(timestamps, refresh_period) + SurfaceStatsCollector._GetNormalizedDeltas( + timestamps, refresh_period, _MIN_NORMALIZED_FRAME_LENGTH) + if len(frame_lengths) < frame_count - 1: + logging.warning('Skipping frame lengths that are too short.') + frame_count = len(frame_lengths) + 1 + if len(frame_lengths) == 0: + raise Exception('No valid frames lengths found.') length_changes, normalized_changes = \ SurfaceStatsCollector._GetNormalizedDeltas( frame_lengths, refresh_period) @@ -101,7 +112,7 @@ class SurfaceStatsCollector(object): return [ SurfaceStatsCollector.Result( 'avg_surface_fps' + result_suffix, - int(round(frame_count / seconds)), 'fps'), + int(round((frame_count - 1) / seconds)), 'fps'), SurfaceStatsCollector.Result( 'jank_count' + result_suffix, jank_count, 'janks'), SurfaceStatsCollector.Result( diff --git a/android/pylib/perf/surface_stats_collector_unittest.py b/android/pylib/perf/surface_stats_collector_unittest.py new file mode 100644 index 000000000..982b55ddf --- /dev/null +++ b/android/pylib/perf/surface_stats_collector_unittest.py @@ -0,0 +1,62 @@ +# Copyright 2013 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Unittests for SurfaceStatsCollector.""" + +import unittest + +from surface_stats_collector import SurfaceStatsCollector + +class TestSurfaceStatsCollector(unittest.TestCase): + @staticmethod + def _CreateUniformTimestamps(base, num, delta): + return [base + i * delta for i in range(1, num + 1)] + + @staticmethod + def _CreateDictionaryFromResults(results): + dictionary = {} + for result in results: + dictionary[result.name] = result + return dictionary + + def setUp(self): + self.refresh_period = 0.1 + + def testOneFrameDelta(self): + timestamps = self._CreateUniformTimestamps(0, 10, self.refresh_period) + results = self._CreateDictionaryFromResults( + SurfaceStatsCollector._CalculateResults( + self.refresh_period, timestamps, '')) + + self.assertEquals(results['avg_surface_fps'].value, + int(round(1 / self.refresh_period))) + self.assertEquals(results['jank_count'].value, 0) + self.assertEquals(results['max_frame_delay'].value, 1) + self.assertEquals(len(results['frame_lengths'].value), len(timestamps) - 1) + + def testAllFramesTooShort(self): + timestamps = self._CreateUniformTimestamps(0, 10, self.refresh_period / 100) + self.assertRaises(Exception, + SurfaceStatsCollector._CalculateResults, + [self.refresh_period, timestamps, '']) + + def testSomeFramesTooShort(self): + timestamps = self._CreateUniformTimestamps(0, 5, self.refresh_period) + # The following timestamps should be skipped. + timestamps += self._CreateUniformTimestamps(timestamps[4], + 5, + self.refresh_period / 100) + timestamps += self._CreateUniformTimestamps(timestamps[4], + 5, + self.refresh_period) + + results = self._CreateDictionaryFromResults( + SurfaceStatsCollector._CalculateResults( + self.refresh_period, timestamps, '')) + + self.assertEquals(len(results['frame_lengths'].value), 9) + + +if __name__ == '__main__': + unittest.main()