Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5728)

Unified Diff: build/android/pylib/perf/surface_stats_collector.py

Issue 809753002: Revert of telemetry: Clean up SurfaceFlinger statistics (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | build/android/surface_stats.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: build/android/pylib/perf/surface_stats_collector.py
diff --git a/build/android/pylib/perf/surface_stats_collector.py b/build/android/pylib/perf/surface_stats_collector.py
index c7e7527a1e25c2ef663f5f80c09f6f80696bbba7..499b0c6fd25bad3fdb3e8a89a227097654e56d5f 100644
--- a/build/android/pylib/perf/surface_stats_collector.py
+++ b/build/android/pylib/perf/surface_stats_collector.py
@@ -15,6 +15,8 @@
_SURFACE_TEXTURE_TIMESTAMPS_MESSAGE = 'SurfaceTexture update timestamps'
_SURFACE_TEXTURE_TIMESTAMP_RE = r'\d+'
+_MIN_NORMALIZED_FRAME_LENGTH = 0.5
+
class SurfaceStatsCollector(object):
"""Collects surface stats for a SurfaceView from the output of SurfaceFlinger.
@@ -22,6 +24,11 @@
Args:
device: A DeviceUtils instance.
"""
+ class Result(object):
+ def __init__(self, name, value, unit):
+ self.name = name
+ self.value = value
+ self.unit = unit
def __init__(self, device):
# TODO(jbudorick) Remove once telemetry gets switched over.
@@ -29,10 +36,12 @@
device = device_utils.DeviceUtils(device)
self._device = device
self._collector_thread = None
+ self._use_legacy_method = False
self._surface_before = None
self._get_data_event = None
self._data_queue = None
self._stop_event = None
+ self._results = []
self._warn_about_empty_data = True
def DisableWarningAboutEmptyData(self):
@@ -48,16 +57,110 @@
self._collector_thread = threading.Thread(target=self._CollectorThread)
self._collector_thread.start()
else:
- raise Exception('SurfaceFlinger not supported on this device.')
+ self._use_legacy_method = True
+ self._surface_before = self._GetSurfaceStatsLegacy()
def Stop(self):
- assert self._collector_thread
- (refresh_period, timestamps) = self._GetDataFromThread()
+ self._StorePerfResults()
if self._collector_thread:
self._stop_event.set()
self._collector_thread.join()
self._collector_thread = None
- return (refresh_period, timestamps)
+
+ def SampleResults(self):
+ self._StorePerfResults()
+ results = self.GetResults()
+ self._results = []
+ return results
+
+ def GetResults(self):
+ return self._results or self._GetEmptyResults()
+
+ @staticmethod
+ def _GetEmptyResults():
+ return [
+ SurfaceStatsCollector.Result('refresh_period', None, 'seconds'),
+ SurfaceStatsCollector.Result('jank_count', None, 'janks'),
+ SurfaceStatsCollector.Result('max_frame_delay', None, 'vsyncs'),
+ SurfaceStatsCollector.Result('frame_lengths', None, 'vsyncs'),
+ SurfaceStatsCollector.Result('avg_surface_fps', None, 'fps')
+ ]
+
+ @staticmethod
+ 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 = [d for d in deltas
+ if d / refresh_period >= min_normalized_delta]
+ return (deltas, [delta / refresh_period for delta in deltas])
+
+ @staticmethod
+ def _CalculateResults(refresh_period, timestamps, result_suffix):
+ """Returns a list of SurfaceStatsCollector.Result."""
+ frame_count = len(timestamps)
+ seconds = timestamps[-1] - timestamps[0]
+
+ frame_lengths, normalized_frame_lengths = \
+ 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.')
+ _, normalized_changes = \
+ SurfaceStatsCollector._GetNormalizedDeltas(
+ frame_lengths, refresh_period)
+ jankiness = [max(0, round(change)) for change in normalized_changes]
+ pause_threshold = 20
+ jank_count = sum(1 for change in jankiness
+ if change > 0 and change < pause_threshold)
+ return [
+ SurfaceStatsCollector.Result(
+ 'avg_surface_fps' + result_suffix,
+ int(round((frame_count - 1) / seconds)), 'fps'),
+ SurfaceStatsCollector.Result(
+ 'jank_count' + result_suffix, jank_count, 'janks'),
+ SurfaceStatsCollector.Result(
+ 'max_frame_delay' + result_suffix,
+ round(max(normalized_frame_lengths)),
+ 'vsyncs'),
+ SurfaceStatsCollector.Result(
+ 'frame_lengths' + result_suffix, normalized_frame_lengths,
+ 'vsyncs'),
+ ]
+
+ @staticmethod
+ def _CalculateBuckets(refresh_period, timestamps):
+ results = []
+ for pct in [0.99, 0.5]:
+ sliced = timestamps[min(int(-pct * len(timestamps)), -3) : ]
+ results += SurfaceStatsCollector._CalculateResults(
+ refresh_period, sliced, '_' + str(int(pct * 100)))
+ return results
+
+ def _StorePerfResults(self):
+ if self._use_legacy_method:
+ surface_after = self._GetSurfaceStatsLegacy()
+ td = surface_after['timestamp'] - self._surface_before['timestamp']
+ seconds = td.seconds + td.microseconds / 1e6
+ frame_count = (surface_after['page_flip_count'] -
+ self._surface_before['page_flip_count'])
+ self._results.append(SurfaceStatsCollector.Result(
+ 'avg_surface_fps', int(round(frame_count / seconds)), 'fps'))
+ return
+
+ # Non-legacy method.
+ assert self._collector_thread
+ (refresh_period, timestamps) = self._GetDataFromThread()
+ if not refresh_period or not len(timestamps) >= 3:
+ if self._warn_about_empty_data:
+ logging.warning('Surface stat data is empty')
+ return
+ self._results.append(SurfaceStatsCollector.Result(
+ 'refresh_period', refresh_period, 'seconds'))
+ self._results += self._CalculateResults(refresh_period, timestamps, '')
+ self._results += self._CalculateBuckets(refresh_period, timestamps)
def _CollectorThread(self):
last_timestamp = 0
@@ -116,21 +219,13 @@
'dumpsys SurfaceFlinger --latency-clear SurfaceView')
return not len(results)
- def GetSurfaceFlingerPid(self):
- results = self._device.RunShellCommand('ps | grep surfaceflinger')
- if not results:
- raise Exception('Unable to get surface flinger process id')
- pid = results[0].split()[1]
- return pid
-
def _GetSurfaceFlingerFrameData(self):
"""Returns collected SurfaceFlinger frame timing data.
Returns:
A tuple containing:
- - The display's nominal refresh period in milliseconds.
- - A list of timestamps signifying frame presentation times in
- milliseconds.
+ - The display's nominal refresh period in seconds.
+ - A list of timestamps signifying frame presentation times in seconds.
The return value may be (None, None) if there was no data collected (for
example, if the app was closed before the collector thread has finished).
"""
@@ -169,8 +264,8 @@
return (None, None)
timestamps = []
- nanoseconds_per_millisecond = 1e6
- refresh_period = long(results[0]) / nanoseconds_per_millisecond
+ nanoseconds_per_second = 1e9
+ refresh_period = long(results[0]) / nanoseconds_per_second
# If a fence associated with a frame is still pending when we query the
# latency data, SurfaceFlinger gives the frame a timestamp of INT64_MAX.
@@ -185,7 +280,33 @@
timestamp = long(fields[1])
if timestamp == pending_fence_timestamp:
continue
- timestamp /= nanoseconds_per_millisecond
+ timestamp /= nanoseconds_per_second
timestamps.append(timestamp)
return (refresh_period, timestamps)
+
+ def _GetSurfaceStatsLegacy(self):
+ """Legacy method (before JellyBean), returns the current Surface index
+ and timestamp.
+
+ Calculate FPS by measuring the difference of Surface index returned by
+ SurfaceFlinger in a period of time.
+
+ Returns:
+ Dict of {page_flip_count (or 0 if there was an error), timestamp}.
+ """
+ results = self._device.RunShellCommand('service call SurfaceFlinger 1013')
+ assert len(results) == 1
+ match = re.search(r'^Result: Parcel\((\w+)', results[0])
+ cur_surface = 0
+ if match:
+ try:
+ cur_surface = int(match.group(1), 16)
+ except Exception:
+ logging.error('Failed to parse current surface from ' + match.group(1))
+ else:
+ logging.warning('Failed to call SurfaceFlinger surface ' + results[0])
+ return {
+ 'page_flip_count': cur_surface,
+ 'timestamp': datetime.datetime.now(),
+ }
« no previous file with comments | « no previous file | build/android/surface_stats.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698