1# Copyright 2013 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 5import logging 6import sys 7 8from lib.policy import PolicySet 9from lib.subcommand import SubCommand 10 11 12LOGGER = logging.getLogger('dmprof') 13 14 15class ExpandCommand(SubCommand): 16 def __init__(self): 17 super(ExpandCommand, self).__init__( 18 'Usage: %prog expand <dump> <policy> <component> <depth>') 19 self._parser.add_option('--alternative-dirs', dest='alternative_dirs', 20 metavar='/path/on/target@/path/on/host[:...]', 21 help='Read files in /path/on/host/ instead of ' 22 'files in /path/on/target/.') 23 24 def do(self, sys_argv): 25 options, args = self._parse_args(sys_argv, 4) 26 dump_path = args[1] 27 target_policy = args[2] 28 component_name = args[3] 29 depth = args[4] 30 alternative_dirs_dict = {} 31 32 policy_set = PolicySet.load(SubCommand._parse_policy_list(target_policy)) 33 if not policy_set[target_policy].find_rule(component_name): 34 sys.stderr.write("ERROR: Component %s not found in policy %s\n" 35 % (component_name, target_policy)) 36 return 1 37 38 if options.alternative_dirs: 39 for alternative_dir_pair in options.alternative_dirs.split(':'): 40 target_path, host_path = alternative_dir_pair.split('@', 1) 41 alternative_dirs_dict[target_path] = host_path 42 (bucket_set, dump) = SubCommand.load_basic_files( 43 dump_path, False, alternative_dirs=alternative_dirs_dict) 44 45 ExpandCommand._output(dump, policy_set[target_policy], bucket_set, 46 component_name, int(depth), sys.stdout) 47 return 0 48 49 @staticmethod 50 def _output(dump, policy, bucket_set, component_name, depth, out): 51 """Prints all stacktraces in a given component of given depth. 52 53 Args: 54 dump: A Dump object. 55 policy: A Policy object. 56 bucket_set: A BucketSet object. 57 component_name: A name of component for filtering. 58 depth: An integer representing depth to be printed. 59 out: An IO object to output. 60 """ 61 sizes = {} 62 63 ExpandCommand._accumulate( 64 dump, policy, bucket_set, component_name, depth, sizes) 65 66 sorted_sizes_list = sorted( 67 sizes.iteritems(), key=(lambda x: x[1]), reverse=True) 68 total = 0 69 # TODO(dmikurube): Better formatting. 70 for size_pair in sorted_sizes_list: 71 out.write('%10d %s\n' % (size_pair[1], size_pair[0])) 72 total += size_pair[1] 73 LOGGER.info('total: %d\n' % total) 74 75 @staticmethod 76 def _add_size(precedence, bucket, depth, committed, sizes): 77 stacktrace_sequence = precedence 78 for function, sourcefile in zip( 79 bucket.symbolized_stackfunction[ 80 0 : min(len(bucket.symbolized_stackfunction), 1 + depth)], 81 bucket.symbolized_stacksourcefile[ 82 0 : min(len(bucket.symbolized_stacksourcefile), 1 + depth)]): 83 stacktrace_sequence += '%s(@%s) ' % (function, sourcefile) 84 if not stacktrace_sequence in sizes: 85 sizes[stacktrace_sequence] = 0 86 sizes[stacktrace_sequence] += committed 87 88 @staticmethod 89 def _accumulate(dump, policy, bucket_set, component_name, depth, sizes): 90 rule = policy.find_rule(component_name) 91 if not rule: 92 pass 93 elif rule.allocator_type == 'malloc': 94 for bucket_id, _, committed, allocs, frees in dump.iter_stacktrace: 95 bucket = bucket_set.get(bucket_id) 96 if not bucket or bucket.allocator_type == 'malloc': 97 component_match = policy.find_malloc(bucket) 98 elif bucket.allocator_type == 'mmap': 99 continue 100 else: 101 assert False 102 if component_match == component_name: 103 precedence = '' 104 precedence += '(alloc=%d) ' % allocs 105 precedence += '(free=%d) ' % frees 106 if bucket.typeinfo: 107 precedence += '(type=%s) ' % bucket.symbolized_typeinfo 108 precedence += '(type.name=%s) ' % bucket.typeinfo_name 109 ExpandCommand._add_size(precedence, bucket, depth, committed, sizes) 110 elif rule.allocator_type == 'mmap': 111 for _, region in dump.iter_map: 112 if region[0] != 'hooked': 113 continue 114 component_match, bucket = policy.find_mmap(region, bucket_set) 115 if component_match == component_name: 116 ExpandCommand._add_size('', bucket, depth, 117 region[1]['committed'], sizes) 118 elif rule.allocator_type == 'unhooked': 119 for addr, region in dump.iter_map: 120 if region[0] != 'unhooked': 121 continue 122 component_match = policy.find_unhooked(region) 123 if component_match == component_name: 124 precedence = '' 125 precedence += '%s-' % hex(addr[0])[2:] 126 precedence += '%s' % hex(addr[1])[2:] 127 precedence += ' %s' % region[1]['vma']['readable'] 128 precedence += '%s' % region[1]['vma']['writable'] 129 precedence += '%s' % region[1]['vma']['executable'] 130 precedence += '%s' % region[1]['vma']['private'] 131 precedence += ' %s' % region[1]['vma']['offset'] 132 precedence += ' %s:' % region[1]['vma']['major'] 133 precedence += '%s' % region[1]['vma']['minor'] 134 precedence += ' %s' % region[1]['vma']['inode'] 135 precedence += ' %s' % region[1]['vma']['name'] 136 if not precedence in sizes: 137 sizes[precedence] = 0 138 sizes[precedence] += region[1]['committed']