1#!/usr/bin/env python 2# 3# Copyright (c) 2013 The Chromium Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7"""Command line tool for continuously printing Android graphics surface 8statistics on the console. 9""" 10 11import collections 12import optparse 13import sys 14import time 15 16from pylib.device import device_utils 17from pylib.perf import surface_stats_collector 18from pylib.utils import run_tests_helper 19 20 21_FIELD_FORMAT = { 22 'jank_count (janks)': '%d', 23 'max_frame_delay (vsyncs)': '%d', 24 'avg_surface_fps (fps)': '%.2f', 25 'frame_lengths (vsyncs)': '%.3f', 26 'refresh_period (seconds)': '%.6f', 27} 28 29 30def _MergeResults(results, fields): 31 merged_results = collections.defaultdict(list) 32 for result in results: 33 if ((fields != ['all'] and not result.name in fields) or 34 result.value is None): 35 continue 36 name = '%s (%s)' % (result.name, result.unit) 37 if isinstance(result.value, list): 38 value = result.value 39 else: 40 value = [result.value] 41 merged_results[name] += value 42 for name, values in merged_results.iteritems(): 43 merged_results[name] = sum(values) / float(len(values)) 44 return merged_results 45 46 47def _GetTerminalHeight(): 48 try: 49 import fcntl, termios, struct 50 except ImportError: 51 return 0, 0 52 height, _, _, _ = struct.unpack('HHHH', 53 fcntl.ioctl(0, termios.TIOCGWINSZ, 54 struct.pack('HHHH', 0, 0, 0, 0))) 55 return height 56 57 58def _PrintColumnTitles(results): 59 for name in results.keys(): 60 print '%s ' % name, 61 print 62 for name in results.keys(): 63 print '%s ' % ('-' * len(name)), 64 print 65 66 67def _PrintResults(results): 68 for name, value in results.iteritems(): 69 value = _FIELD_FORMAT.get(name, '%s') % value 70 print value.rjust(len(name)) + ' ', 71 print 72 73 74def main(argv): 75 parser = optparse.OptionParser(usage='Usage: %prog [options]', 76 description=__doc__) 77 parser.add_option('-v', 78 '--verbose', 79 dest='verbose_count', 80 default=0, 81 action='count', 82 help='Verbose level (multiple times for more)') 83 parser.add_option('--device', 84 help='Serial number of device we should use.') 85 parser.add_option('-f', 86 '--fields', 87 dest='fields', 88 default='jank_count,max_frame_delay,avg_surface_fps,' 89 'frame_lengths', 90 help='Comma separated list of fields to display or "all".') 91 parser.add_option('-d', 92 '--delay', 93 dest='delay', 94 default=1, 95 type='float', 96 help='Time in seconds to sleep between updates.') 97 98 options, _ = parser.parse_args(argv) 99 run_tests_helper.SetLogLevel(options.verbose_count) 100 101 device = device_utils.DeviceUtils(options.device) 102 collector = surface_stats_collector.SurfaceStatsCollector(device) 103 collector.DisableWarningAboutEmptyData() 104 105 fields = options.fields.split(',') 106 row_count = None 107 108 try: 109 collector.Start() 110 while True: 111 time.sleep(options.delay) 112 results = collector.SampleResults() 113 results = _MergeResults(results, fields) 114 115 if not results: 116 continue 117 118 terminal_height = _GetTerminalHeight() 119 if row_count is None or (terminal_height and 120 row_count >= terminal_height - 3): 121 _PrintColumnTitles(results) 122 row_count = 0 123 124 _PrintResults(results) 125 row_count += 1 126 except KeyboardInterrupt: 127 sys.exit(0) 128 finally: 129 collector.Stop() 130 131 132if __name__ == '__main__': 133 main(sys.argv) 134