1e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch# Copyright 2014 The Chromium Authors. All rights reserved. 2e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch# Use of this source code is governed by a BSD-style license that can be 3e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch# found in the LICENSE file. 4e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 5e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch"""Command line frontend for Memory Inspector""" 6e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 70529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochimport json 8e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochimport memory_inspector 9e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochimport optparse 100529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochimport os 11e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochimport time 12e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 13cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)from memory_inspector import constants 140529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochfrom memory_inspector.classification import mmap_classifier 15e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochfrom memory_inspector.core import backends 160529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochfrom memory_inspector.data import serialization 17e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 18e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 190529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochdef main(): 200529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch COMMANDS = ['devices', 'ps', 'stats', 'mmaps', 'classified_mmaps'] 210529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch usage = ('%prog [options] ' + ' | '.join(COMMANDS)) 22e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch parser = optparse.OptionParser(usage=usage) 23e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch parser.add_option('-b', '--backend', help='Backend name ' 24e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch '(e.g., Android)', type='string', default='Android') 25e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch parser.add_option('-s', '--device_id', help='Device ' 26a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch 'id (e.g., Android serial)', type='string') 27e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch parser.add_option('-p', '--process_id', help='Target process id', 28a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch type='int') 29e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch parser.add_option('-m', '--filter_process_name', help='Process ' 30e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 'name to match', type='string') 310529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch parser.add_option('-r', '--mmap_rule', 320529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch help='mmap rule', type='string', 33cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) default=os.path.join(constants.CLASSIFICATION_RULES_PATH, 34cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 'default', 'mmap-android.py')) 35e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch (options, args) = parser.parse_args() 36e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 37e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch memory_inspector.RegisterAllBackends() 38e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 390529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if not args or args[0] not in COMMANDS: 40e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch parser.print_help() 41e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch return -1 420529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 43e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch if args[0] == 'devices': 44e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch _ListDevices(options.backend) 45a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch return 0 46a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch 47a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch number_of_devices = 0 48a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch if options.device_id: 49a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch device_id = options.device_id 50a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch number_of_devices = 1 51a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch else: 52a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch for device in backends.ListDevices(): 53a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch if device.backend.name == options.backend: 54a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch number_of_devices += 1 55a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch device_id = device.id 56a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch 57a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch if number_of_devices == 0: 58a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch print "No devices connected" 59a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch return -1 60a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch 61a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch if number_of_devices > 1: 62a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch print ('More than 1 device connected. You need to provide' 63a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch ' --device_id') 64a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch return -1 65a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch 660529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch device = backends.GetDevice(options.backend, device_id) 670529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if not device: 680529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch print 'Device', device_id, 'does not exist' 690529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch return -1 70a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch 710529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch device.Initialize() 720529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if args[0] == 'ps': 730529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if not options.filter_process_name: 740529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch print 'Listing all processes' 75e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch else: 760529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch print ('Listing processes matching ' 770529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch + options.filter_process_name.lower()) 780529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch print '' 790529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch print '%-10s : %-50s : %12s %12s %12s' % ( 800529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 'Process ID', 'Process Name', 'RUN_TIME', 'THREADS', 810529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 'MEM_RSS_KB') 820529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch print '' 830529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch for process in device.ListProcesses(): 840529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if (not options.filter_process_name or 850529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch options.filter_process_name.lower() in process.name.lower()): 860529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch stats = process.GetStats() 870529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch run_time_min, run_time_sec = divmod(stats.run_time, 60) 880529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch print '%10s : %-50s : %6s m %2s s %8s %12s' % ( 891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci process.pid, _Truncate(process.name, 50), run_time_min, 901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci run_time_sec, stats.threads, stats.vm_rss) 91a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch return 0 920529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 930529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if not options.process_id: 940529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch print 'You need to provide --process_id' 95e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch return -1 96e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 970529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch process = device.GetProcess(options.process_id) 980529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 990529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if not process: 1000529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch print 'Cannot find process [%d] on device %s' % ( 1010529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch options.process_id, device.id) 1020529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch return -1 1030529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch elif args[0] == 'stats': 1040529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch _ListProcessStats(process) 1050529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch return 0 1060529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch elif args[0] == 'mmaps': 1070529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch _ListProcessMmaps(process) 1080529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch return 0 1090529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch elif args[0] == 'classified_mmaps': 1100529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch _ListProcessClassifiedMmaps(process, options.mmap_rule) 1110529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch return 0 1120529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 113e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 114e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochdef _ListDevices(backend_name): 115e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch print 'Device list:' 116e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch print '' 117e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch for device in backends.ListDevices(): 118e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch if device.backend.name == backend_name: 119e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch print '%-16s : %s' % (device.id, device.name) 120e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 121e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 1220529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochdef _ListProcessStats(process): 1230529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch """Prints process stats periodically 124e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch """ 1250529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch print 'Stats for process: [%d] %s' % (process.pid, process.name) 1260529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch print '%-10s : %-50s : %12s %12s %13s %12s %14s' % ( 1270529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 'Process ID', 'Process Name', 'RUN_TIME', 'THREADS', 1280529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 'CPU_USAGE', 'MEM_RSS_KB', 'PAGE_FAULTS') 129a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch print '' 1300529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch while True: 1310529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch stats = process.GetStats() 1320529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch run_time_min, run_time_sec = divmod(stats.run_time, 60) 1330529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch print '%10s : %-50s : %6s m %2s s %8s %12s %13s %11s' % ( 1341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci process.pid, _Truncate(process.name, 50), run_time_min, run_time_sec, 1350529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch stats.threads, stats.cpu_usage, stats.vm_rss, stats.page_faults) 1360529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch time.sleep(1) 137a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch 138a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch 1390529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochdef _ListProcessMmaps(process): 1400529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch """Prints process memory maps 1410529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch """ 1420529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch print 'Memory Maps for process: [%d] %s' % (process.pid, process.name) 1430529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch print '%-10s %-10s %6s %12s %12s %13s %13s %-40s' % ( 1440529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 'START', 'END', 'FLAGS', 'PRIV.DIRTY', 'PRIV.CLEAN', 1450529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 'SHARED DIRTY', 'SHARED CLEAN', 'MAPPED_FILE') 1460529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch print '%38s %12s %12s %13s' % ('(kb)', '(kb)', '(kb)', '(kb)') 147e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch print '' 1480529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch maps = process.DumpMemoryMaps() 1490529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch for entry in maps.entries: 1500529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch print '%-10x %-10x %6s %12s %12s %13s %13s %-40s' % ( 1510529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch entry.start, entry.end, entry.prot_flags, 1520529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch entry.priv_dirty_bytes / 1024, entry.priv_clean_bytes / 1024, 1530529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch entry.shared_dirty_bytes / 1024, 1540529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch entry.shared_clean_bytes / 1024, entry.mapped_file) 155e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 156e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 1570529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochdef _ListProcessClassifiedMmaps(process, mmap_rule): 1580529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch """Prints process classified memory maps 1590529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch """ 1600529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch maps = process.DumpMemoryMaps() 1610529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if not os.path.exists(mmap_rule): 1620529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch print 'File', mmap_rule, 'not found' 1630529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch return 1640529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch with open(mmap_rule) as f: 1650529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch rules = mmap_classifier.LoadRules(f.read()) 1660529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch classified_results_tree = mmap_classifier.Classify(maps, rules) 1670529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch print json.dumps(classified_results_tree, cls=serialization.Encoder) 168e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 169e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 1701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccidef _Truncate(name, max_length): 1711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if len(name) <= max_length: 1721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return name 1731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return '%s...' % name[0:(max_length - 3)] 174