android: Add interactive surface statistics viewer
Add a simple tool for viewing graphics surface statistics interactively. Sample output: avg_surface_fps (fps) frame_lengths (vsyncs) jank_count (janks) max_frame_delay (vsyncs) --------------------- ---------------------- ------------------ ------------------------ 58.00 1.158 1 4 55.00 1.215 2 7 57.00 1.174 2 3 56.00 1.192 2 6 56.00 1.196 3 3 54.00 1.239 1 8 BUG=https://b/8364918 Review URL: https://chromiumcodereview.appspot.com/13046007 git-svn-id: http://src.chromium.org/svn/trunk/src/build@190930 4ff67af0-8c30-449e-8e8b-ad334ec8d88c
This commit is contained in:
Родитель
467f42371f
Коммит
194e4ec1d9
|
@ -37,6 +37,10 @@ class SurfaceStatsCollector(object):
|
|||
self._data_queue = None
|
||||
self._stop_event = None
|
||||
self._results = []
|
||||
self._warn_about_empty_data = True
|
||||
|
||||
def DisableWarningAboutEmptyData(self):
|
||||
self._warn_about_empty_data = False
|
||||
|
||||
def Start(self):
|
||||
assert not self._collector_thread
|
||||
|
@ -58,6 +62,12 @@ class SurfaceStatsCollector(object):
|
|||
self._collector_thread.join()
|
||||
self._collector_thread = None
|
||||
|
||||
def SampleResults(self):
|
||||
self._StorePerfResults()
|
||||
results = self._results
|
||||
self._results = []
|
||||
return results
|
||||
|
||||
def GetResults(self):
|
||||
return self._results
|
||||
|
||||
|
@ -77,7 +87,8 @@ class SurfaceStatsCollector(object):
|
|||
assert self._collector_thread
|
||||
(refresh_period, timestamps) = self._GetDataFromThread()
|
||||
if not refresh_period or not len(timestamps) >= 3:
|
||||
logging.warning('Surface stat data is empty')
|
||||
if self._warn_about_empty_data:
|
||||
logging.warning('Surface stat data is empty')
|
||||
return
|
||||
frame_count = len(timestamps)
|
||||
seconds = timestamps[-1] - timestamps[0]
|
||||
|
@ -208,11 +219,19 @@ class SurfaceStatsCollector(object):
|
|||
nanoseconds_per_second = 1e9
|
||||
refresh_period = long(results[0]) / nanoseconds_per_second
|
||||
|
||||
# SurfaceFlinger sometimes gives an invalid timestamp for the very latest
|
||||
# frame if it is queried while the frame is still being presented. We ignore
|
||||
# these timestamps.
|
||||
bad_timestamp = (1 << 63) - 1
|
||||
|
||||
for line in results[1:]:
|
||||
fields = line.split()
|
||||
if len(fields) != 3:
|
||||
continue
|
||||
timestamp = long(fields[1]) / nanoseconds_per_second
|
||||
timestamp = long(fields[1])
|
||||
if timestamp == bad_timestamp:
|
||||
continue
|
||||
timestamp /= nanoseconds_per_second
|
||||
timestamps.append(timestamp)
|
||||
|
||||
return (refresh_period, timestamps)
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (c) 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.
|
||||
|
||||
"""Command line tool for continuously printing Android graphics surface
|
||||
statistics on the console.
|
||||
"""
|
||||
|
||||
import collections
|
||||
import optparse
|
||||
import sys
|
||||
import time
|
||||
|
||||
from pylib import android_commands, surface_stats_collector
|
||||
from pylib.utils import run_tests_helper
|
||||
|
||||
|
||||
_FIELD_FORMAT = {
|
||||
'jank_count (janks)': '%d',
|
||||
'max_frame_delay (vsyncs)': '%d',
|
||||
'avg_surface_fps (fps)': '%.2f',
|
||||
'frame_lengths (vsyncs)': '%.3f',
|
||||
'refresh_period (seconds)': '%.6f',
|
||||
}
|
||||
|
||||
|
||||
def _MergeResults(results, fields):
|
||||
merged_results = collections.defaultdict(list)
|
||||
for result in results:
|
||||
if fields != ['all'] and not result.name in fields:
|
||||
continue
|
||||
name = '%s (%s)' % (result.name, result.unit)
|
||||
if isinstance(result.value, list):
|
||||
value = result.value
|
||||
else:
|
||||
value = [result.value]
|
||||
merged_results[name] += value
|
||||
for name, values in merged_results.iteritems():
|
||||
merged_results[name] = sum(values) / float(len(values))
|
||||
return merged_results
|
||||
|
||||
|
||||
def _GetTerminalHeight():
|
||||
try:
|
||||
import fcntl, termios, struct
|
||||
except ImportError:
|
||||
return 0, 0
|
||||
height, _, _, _ = struct.unpack('HHHH',
|
||||
fcntl.ioctl(0, termios.TIOCGWINSZ,
|
||||
struct.pack('HHHH', 0, 0, 0, 0)))
|
||||
return height
|
||||
|
||||
|
||||
def _PrintColumnTitles(results):
|
||||
for name in results.keys():
|
||||
print '%s ' % name,
|
||||
print
|
||||
for name in results.keys():
|
||||
print '%s ' % ('-' * len(name)),
|
||||
print
|
||||
|
||||
|
||||
def _PrintResults(results):
|
||||
for name, value in results.iteritems():
|
||||
value = _FIELD_FORMAT.get(name, '%s') % value
|
||||
print value.rjust(len(name)) + ' ',
|
||||
print
|
||||
|
||||
|
||||
def main(argv):
|
||||
parser = optparse.OptionParser(usage='Usage: %prog [options]',
|
||||
description=__doc__)
|
||||
parser.add_option('-v',
|
||||
'--verbose',
|
||||
dest='verbose_count',
|
||||
default=0,
|
||||
action='count',
|
||||
help='Verbose level (multiple times for more)')
|
||||
parser.add_option('--device',
|
||||
help='Serial number of device we should use.')
|
||||
parser.add_option('-f',
|
||||
'--fields',
|
||||
dest='fields',
|
||||
default='jank_count,max_frame_delay,avg_surface_fps,'
|
||||
'frame_lengths',
|
||||
help='Comma separated list of fields to display or "all".')
|
||||
parser.add_option('-d',
|
||||
'--delay',
|
||||
dest='delay',
|
||||
default=1,
|
||||
type='float',
|
||||
help='Time in seconds to sleep between updates.')
|
||||
|
||||
options, args = parser.parse_args(argv)
|
||||
run_tests_helper.SetLogLevel(options.verbose_count)
|
||||
|
||||
adb = android_commands.AndroidCommands(options.device)
|
||||
collector = surface_stats_collector.SurfaceStatsCollector(adb)
|
||||
collector.DisableWarningAboutEmptyData()
|
||||
|
||||
fields = options.fields.split(',')
|
||||
row_count = None
|
||||
|
||||
try:
|
||||
collector.Start()
|
||||
while True:
|
||||
time.sleep(options.delay)
|
||||
results = collector.SampleResults()
|
||||
results = _MergeResults(results, fields)
|
||||
|
||||
if not results:
|
||||
continue
|
||||
|
||||
terminal_height = _GetTerminalHeight()
|
||||
if row_count is None or (terminal_height and
|
||||
row_count >= terminal_height - 3):
|
||||
_PrintColumnTitles(results)
|
||||
row_count = 0
|
||||
|
||||
_PrintResults(results)
|
||||
row_count += 1
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(0)
|
||||
finally:
|
||||
collector.Stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv)
|
Загрузка…
Ссылка в новой задаче