# Copyright 2013 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import logging import sys from lib.bucket import BUCKET_ID, COMMITTED, ALLOC_COUNT, FREE_COUNT from lib.policy import PolicySet from lib.subcommand import SubCommand LOGGER = logging.getLogger('dmprof') class ExpandCommand(SubCommand): def __init__(self): super(ExpandCommand, self).__init__( 'Usage: %prog expand ') def do(self, sys_argv): _, args = self._parse_args(sys_argv, 4) dump_path = args[1] target_policy = args[2] component_name = args[3] depth = args[4] (bucket_set, dump) = SubCommand.load_basic_files(dump_path, False) policy_set = PolicySet.load(SubCommand._parse_policy_list(target_policy)) ExpandCommand._output(dump, policy_set[target_policy], bucket_set, component_name, int(depth), sys.stdout) return 0 @staticmethod def _output(dump, policy, bucket_set, component_name, depth, out): """Prints all stacktraces in a given component of given depth. Args: dump: A Dump object. policy: A Policy object. bucket_set: A BucketSet object. component_name: A name of component for filtering. depth: An integer representing depth to be printed. out: An IO object to output. """ sizes = {} ExpandCommand._accumulate( dump, policy, bucket_set, component_name, depth, sizes) sorted_sizes_list = sorted( sizes.iteritems(), key=(lambda x: x[1]), reverse=True) total = 0 # TODO(dmikurube): Better formatting. for size_pair in sorted_sizes_list: out.write('%10d %s\n' % (size_pair[1], size_pair[0])) total += size_pair[1] LOGGER.info('total: %d\n' % total) @staticmethod def _add_size(precedence, bucket, depth, committed, sizes): stacktrace_sequence = precedence for function, sourcefile in zip( bucket.symbolized_stackfunction[ 0 : min(len(bucket.symbolized_stackfunction), 1 + depth)], bucket.symbolized_stacksourcefile[ 0 : min(len(bucket.symbolized_stacksourcefile), 1 + depth)]): stacktrace_sequence += '%s(@%s) ' % (function, sourcefile) if not stacktrace_sequence in sizes: sizes[stacktrace_sequence] = 0 sizes[stacktrace_sequence] += committed @staticmethod def _accumulate(dump, policy, bucket_set, component_name, depth, sizes): rule = policy.find_rule(component_name) if not rule: pass elif rule.allocator_type == 'malloc': for line in dump.iter_stacktrace: words = line.split() bucket = bucket_set.get(int(words[BUCKET_ID])) if not bucket or bucket.allocator_type == 'malloc': component_match = policy.find_malloc(bucket) elif bucket.allocator_type == 'mmap': continue else: assert False if component_match == component_name: precedence = '' precedence += '(alloc=%d) ' % int(words[ALLOC_COUNT]) precedence += '(free=%d) ' % int(words[FREE_COUNT]) if bucket.typeinfo: precedence += '(type=%s) ' % bucket.symbolized_typeinfo precedence += '(type.name=%s) ' % bucket.typeinfo_name ExpandCommand._add_size(precedence, bucket, depth, int(words[COMMITTED]), sizes) elif rule.allocator_type == 'mmap': for _, region in dump.iter_map: if region[0] != 'hooked': continue component_match, bucket = policy.find_mmap(region, bucket_set) if component_match == component_name: ExpandCommand._add_size('', bucket, depth, region[1]['committed'], sizes)