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.
5 """Command line frontend for Memory Inspector"""
8 import memory_inspector
13 from memory_inspector import constants
14 from memory_inspector.classification import mmap_classifier
15 from memory_inspector.core import backends
16 from memory_inspector.data import serialization
20 COMMANDS = ['devices', 'ps', 'stats', 'mmaps', 'classified_mmaps']
21 usage = ('%prog [options] ' + ' | '.join(COMMANDS))
22 parser = optparse.OptionParser(usage=usage)
23 parser.add_option('-b', '--backend', help='Backend name '
24 '(e.g., Android)', type='string', default='Android')
25 parser.add_option('-s', '--device_id', help='Device '
26 'id (e.g., Android serial)', type='string')
27 parser.add_option('-p', '--process_id', help='Target process id',
29 parser.add_option('-m', '--filter_process_name', help='Process '
30 'name to match', type='string')
31 parser.add_option('-r', '--mmap_rule',
32 help='mmap rule', type='string',
33 default=os.path.join(constants.CLASSIFICATION_RULES_PATH,
34 'default', 'mmap-android.py'))
35 (options, args) = parser.parse_args()
37 memory_inspector.RegisterAllBackends()
39 if not args or args[0] not in COMMANDS:
43 if args[0] == 'devices':
44 _ListDevices(options.backend)
49 device_id = options.device_id
52 for device in backends.ListDevices():
53 if device.backend.name == options.backend:
54 number_of_devices += 1
57 if number_of_devices == 0:
58 print "No devices connected"
61 if number_of_devices > 1:
62 print ('More than 1 device connected. You need to provide'
66 device = backends.GetDevice(options.backend, device_id)
68 print 'Device', device_id, 'does not exist'
73 if not options.filter_process_name:
74 print 'Listing all processes'
76 print ('Listing processes matching '
77 + options.filter_process_name.lower())
79 print '%-10s : %-50s : %12s %12s %12s' % (
80 'Process ID', 'Process Name', 'RUN_TIME', 'THREADS',
83 for process in device.ListProcesses():
84 if (not options.filter_process_name or
85 options.filter_process_name.lower() in process.name.lower()):
86 stats = process.GetStats()
87 run_time_min, run_time_sec = divmod(stats.run_time, 60)
88 print '%10s : %-50s : %6s m %2s s %8s %12s' % (
89 process.pid, _Truncate(process.name, 50), run_time_min,
90 run_time_sec, stats.threads, stats.vm_rss)
93 if not options.process_id:
94 print 'You need to provide --process_id'
97 process = device.GetProcess(options.process_id)
100 print 'Cannot find process [%d] on device %s' % (
101 options.process_id, device.id)
103 elif args[0] == 'stats':
104 _ListProcessStats(process)
106 elif args[0] == 'mmaps':
107 _ListProcessMmaps(process)
109 elif args[0] == 'classified_mmaps':
110 _ListProcessClassifiedMmaps(process, options.mmap_rule)
114 def _ListDevices(backend_name):
117 for device in backends.ListDevices():
118 if device.backend.name == backend_name:
119 print '%-16s : %s' % (device.id, device.name)
122 def _ListProcessStats(process):
123 """Prints process stats periodically
125 print 'Stats for process: [%d] %s' % (process.pid, process.name)
126 print '%-10s : %-50s : %12s %12s %13s %12s %14s' % (
127 'Process ID', 'Process Name', 'RUN_TIME', 'THREADS',
128 'CPU_USAGE', 'MEM_RSS_KB', 'PAGE_FAULTS')
131 stats = process.GetStats()
132 run_time_min, run_time_sec = divmod(stats.run_time, 60)
133 print '%10s : %-50s : %6s m %2s s %8s %12s %13s %11s' % (
134 process.pid, _Truncate(process.name, 50), run_time_min, run_time_sec,
135 stats.threads, stats.cpu_usage, stats.vm_rss, stats.page_faults)
139 def _ListProcessMmaps(process):
140 """Prints process memory maps
142 print 'Memory Maps for process: [%d] %s' % (process.pid, process.name)
143 print '%-10s %-10s %6s %12s %12s %13s %13s %-40s' % (
144 'START', 'END', 'FLAGS', 'PRIV.DIRTY', 'PRIV.CLEAN',
145 'SHARED DIRTY', 'SHARED CLEAN', 'MAPPED_FILE')
146 print '%38s %12s %12s %13s' % ('(kb)', '(kb)', '(kb)', '(kb)')
148 maps = process.DumpMemoryMaps()
149 for entry in maps.entries:
150 print '%-10x %-10x %6s %12s %12s %13s %13s %-40s' % (
151 entry.start, entry.end, entry.prot_flags,
152 entry.priv_dirty_bytes / 1024, entry.priv_clean_bytes / 1024,
153 entry.shared_dirty_bytes / 1024,
154 entry.shared_clean_bytes / 1024, entry.mapped_file)
157 def _ListProcessClassifiedMmaps(process, mmap_rule):
158 """Prints process classified memory maps
160 maps = process.DumpMemoryMaps()
161 if not os.path.exists(mmap_rule):
162 print 'File', mmap_rule, 'not found'
164 with open(mmap_rule) as f:
165 rules = mmap_classifier.LoadRules(f.read())
166 classified_results_tree = mmap_classifier.Classify(maps, rules)
167 print json.dumps(classified_results_tree, cls=serialization.Encoder)
170 def _Truncate(name, max_length):
171 if len(name) <= max_length:
173 return '%s...' % name[0:(max_length - 3)]