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