command_line.py revision 0529e5d033099cbfc42635f6f6183833b09dff6e
1# Copyright 2014 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5"""Command line frontend for Memory Inspector""" 6 7import json 8import memory_inspector 9import optparse 10import os 11import time 12 13from memory_inspector.classification import mmap_classifier 14from memory_inspector.core import backends 15from memory_inspector.data import serialization 16 17 18def main(): 19 COMMANDS = ['devices', 'ps', 'stats', 'mmaps', 'classified_mmaps'] 20 usage = ('%prog [options] ' + ' | '.join(COMMANDS)) 21 parser = optparse.OptionParser(usage=usage) 22 parser.add_option('-b', '--backend', help='Backend name ' 23 '(e.g., Android)', type='string', default='Android') 24 parser.add_option('-s', '--device_id', help='Device ' 25 'id (e.g., Android serial)', type='string') 26 parser.add_option('-p', '--process_id', help='Target process id', 27 type='int') 28 parser.add_option('-m', '--filter_process_name', help='Process ' 29 'name to match', type='string') 30 parser.add_option('-r', '--mmap_rule', 31 help='mmap rule', type='string', 32 default= 33 'classification_rules/default/mmap-android.py') 34 (options, args) = parser.parse_args() 35 36 memory_inspector.RegisterAllBackends() 37 38 if not args or args[0] not in COMMANDS: 39 parser.print_help() 40 return -1 41 42 if args[0] == 'devices': 43 _ListDevices(options.backend) 44 return 0 45 46 number_of_devices = 0 47 if options.device_id: 48 device_id = options.device_id 49 number_of_devices = 1 50 else: 51 for device in backends.ListDevices(): 52 if device.backend.name == options.backend: 53 number_of_devices += 1 54 device_id = device.id 55 56 if number_of_devices == 0: 57 print "No devices connected" 58 return -1 59 60 if number_of_devices > 1: 61 print ('More than 1 device connected. You need to provide' 62 ' --device_id') 63 return -1 64 65 device = backends.GetDevice(options.backend, device_id) 66 if not device: 67 print 'Device', device_id, 'does not exist' 68 return -1 69 70 device.Initialize() 71 if args[0] == 'ps': 72 if not options.filter_process_name: 73 print 'Listing all processes' 74 else: 75 print ('Listing processes matching ' 76 + options.filter_process_name.lower()) 77 print '' 78 print '%-10s : %-50s : %12s %12s %12s' % ( 79 'Process ID', 'Process Name', 'RUN_TIME', 'THREADS', 80 'MEM_RSS_KB') 81 print '' 82 for process in device.ListProcesses(): 83 if (not options.filter_process_name or 84 options.filter_process_name.lower() in process.name.lower()): 85 stats = process.GetStats() 86 run_time_min, run_time_sec = divmod(stats.run_time, 60) 87 print '%10s : %-50s : %6s m %2s s %8s %12s' % ( 88 process.pid, process.name, run_time_min, run_time_sec, 89 stats.threads, stats.vm_rss) 90 return 0 91 92 if not options.process_id: 93 print 'You need to provide --process_id' 94 return -1 95 96 process = device.GetProcess(options.process_id) 97 98 if not process: 99 print 'Cannot find process [%d] on device %s' % ( 100 options.process_id, device.id) 101 return -1 102 elif args[0] == 'stats': 103 _ListProcessStats(process) 104 return 0 105 elif args[0] == 'mmaps': 106 _ListProcessMmaps(process) 107 return 0 108 elif args[0] == 'classified_mmaps': 109 _ListProcessClassifiedMmaps(process, options.mmap_rule) 110 return 0 111 112 113def _ListDevices(backend_name): 114 print 'Device list:' 115 print '' 116 for device in backends.ListDevices(): 117 if device.backend.name == backend_name: 118 print '%-16s : %s' % (device.id, device.name) 119 120 121def _ListProcessStats(process): 122 """Prints process stats periodically 123 """ 124 print 'Stats for process: [%d] %s' % (process.pid, process.name) 125 print '%-10s : %-50s : %12s %12s %13s %12s %14s' % ( 126 'Process ID', 'Process Name', 'RUN_TIME', 'THREADS', 127 'CPU_USAGE', 'MEM_RSS_KB', 'PAGE_FAULTS') 128 print '' 129 while True: 130 stats = process.GetStats() 131 run_time_min, run_time_sec = divmod(stats.run_time, 60) 132 print '%10s : %-50s : %6s m %2s s %8s %12s %13s %11s' % ( 133 process.pid, process.name, run_time_min, run_time_sec, 134 stats.threads, stats.cpu_usage, stats.vm_rss, stats.page_faults) 135 time.sleep(1) 136 137 138def _ListProcessMmaps(process): 139 """Prints process memory maps 140 """ 141 print 'Memory Maps for process: [%d] %s' % (process.pid, process.name) 142 print '%-10s %-10s %6s %12s %12s %13s %13s %-40s' % ( 143 'START', 'END', 'FLAGS', 'PRIV.DIRTY', 'PRIV.CLEAN', 144 'SHARED DIRTY', 'SHARED CLEAN', 'MAPPED_FILE') 145 print '%38s %12s %12s %13s' % ('(kb)', '(kb)', '(kb)', '(kb)') 146 print '' 147 maps = process.DumpMemoryMaps() 148 for entry in maps.entries: 149 print '%-10x %-10x %6s %12s %12s %13s %13s %-40s' % ( 150 entry.start, entry.end, entry.prot_flags, 151 entry.priv_dirty_bytes / 1024, entry.priv_clean_bytes / 1024, 152 entry.shared_dirty_bytes / 1024, 153 entry.shared_clean_bytes / 1024, entry.mapped_file) 154 155 156def _ListProcessClassifiedMmaps(process, mmap_rule): 157 """Prints process classified memory maps 158 """ 159 maps = process.DumpMemoryMaps() 160 if not os.path.exists(mmap_rule): 161 print 'File', mmap_rule, 'not found' 162 return 163 with open(mmap_rule) as f: 164 rules = mmap_classifier.LoadRules(f.read()) 165 classified_results_tree = mmap_classifier.Classify(maps, rules) 166 print json.dumps(classified_results_tree, cls=serialization.Encoder) 167 168 169