17dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch# Copyright 2013 The Chromium Authors. All rights reserved.
27dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch# Use of this source code is governed by a BSD-style license that can be
37dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch# found in the LICENSE file.
47dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
57dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochimport logging
67dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochimport sys
77dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
87dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochfrom lib.bucket import BUCKET_ID, COMMITTED, ALLOC_COUNT, FREE_COUNT
97dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochfrom lib.policy import PolicySet
107dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochfrom lib.subcommand import SubCommand
117dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
127dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
137dbb3d5cf0c15f500944d211057644d6a2f37371Ben MurdochLOGGER = logging.getLogger('dmprof')
147dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
157dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
167dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochclass ExpandCommand(SubCommand):
177dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  def __init__(self):
187dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    super(ExpandCommand, self).__init__(
197dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        'Usage: %prog expand <dump> <policy> <component> <depth>')
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self._parser.add_option('--alternative-dirs', dest='alternative_dirs',
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            metavar='/path/on/target@/path/on/host[:...]',
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            help='Read files in /path/on/host/ instead of '
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                 'files in /path/on/target/.')
247dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
257dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  def do(self, sys_argv):
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    options, args = self._parse_args(sys_argv, 4)
277dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    dump_path = args[1]
287dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    target_policy = args[2]
297dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    component_name = args[3]
307dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    depth = args[4]
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    alternative_dirs_dict = {}
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
337dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    policy_set = PolicySet.load(SubCommand._parse_policy_list(target_policy))
345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if not policy_set[target_policy].find_rule(component_name):
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      sys.stderr.write("ERROR: Component %s not found in policy %s\n"
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          % (component_name, target_policy))
375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return 1
385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if options.alternative_dirs:
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      for alternative_dir_pair in options.alternative_dirs.split(':'):
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        target_path, host_path = alternative_dir_pair.split('@', 1)
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        alternative_dirs_dict[target_path] = host_path
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    (bucket_set, dump) = SubCommand.load_basic_files(
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        dump_path, False, alternative_dirs=alternative_dirs_dict)
457dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
467dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    ExpandCommand._output(dump, policy_set[target_policy], bucket_set,
477dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                          component_name, int(depth), sys.stdout)
487dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    return 0
497dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
507dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  @staticmethod
517dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  def _output(dump, policy, bucket_set, component_name, depth, out):
527dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    """Prints all stacktraces in a given component of given depth.
537dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
547dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    Args:
557dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        dump: A Dump object.
567dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        policy: A Policy object.
577dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        bucket_set: A BucketSet object.
587dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        component_name: A name of component for filtering.
597dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        depth: An integer representing depth to be printed.
607dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        out: An IO object to output.
617dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    """
627dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    sizes = {}
637dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
647dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    ExpandCommand._accumulate(
657dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        dump, policy, bucket_set, component_name, depth, sizes)
667dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
677dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    sorted_sizes_list = sorted(
687dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        sizes.iteritems(), key=(lambda x: x[1]), reverse=True)
697dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    total = 0
707dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    # TODO(dmikurube): Better formatting.
717dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    for size_pair in sorted_sizes_list:
727dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      out.write('%10d %s\n' % (size_pair[1], size_pair[0]))
737dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      total += size_pair[1]
747dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    LOGGER.info('total: %d\n' % total)
757dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
767dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  @staticmethod
777dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  def _add_size(precedence, bucket, depth, committed, sizes):
787dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    stacktrace_sequence = precedence
797dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    for function, sourcefile in zip(
807dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        bucket.symbolized_stackfunction[
817dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            0 : min(len(bucket.symbolized_stackfunction), 1 + depth)],
827dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        bucket.symbolized_stacksourcefile[
837dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            0 : min(len(bucket.symbolized_stacksourcefile), 1 + depth)]):
847dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      stacktrace_sequence += '%s(@%s) ' % (function, sourcefile)
857dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    if not stacktrace_sequence in sizes:
867dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      sizes[stacktrace_sequence] = 0
877dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    sizes[stacktrace_sequence] += committed
887dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
897dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  @staticmethod
907dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  def _accumulate(dump, policy, bucket_set, component_name, depth, sizes):
917dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    rule = policy.find_rule(component_name)
927dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    if not rule:
937dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      pass
947dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    elif rule.allocator_type == 'malloc':
957dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      for line in dump.iter_stacktrace:
967dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        words = line.split()
977dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        bucket = bucket_set.get(int(words[BUCKET_ID]))
987dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        if not bucket or bucket.allocator_type == 'malloc':
997dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          component_match = policy.find_malloc(bucket)
1007dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        elif bucket.allocator_type == 'mmap':
1017dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          continue
1027dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        else:
1037dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          assert False
1047dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        if component_match == component_name:
1057dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          precedence = ''
1067dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          precedence += '(alloc=%d) ' % int(words[ALLOC_COUNT])
1077dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          precedence += '(free=%d) ' % int(words[FREE_COUNT])
1087dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          if bucket.typeinfo:
1097dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            precedence += '(type=%s) ' % bucket.symbolized_typeinfo
1107dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            precedence += '(type.name=%s) ' % bucket.typeinfo_name
1117dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          ExpandCommand._add_size(precedence, bucket, depth,
1127dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                                  int(words[COMMITTED]), sizes)
1137dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    elif rule.allocator_type == 'mmap':
1147dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      for _, region in dump.iter_map:
1157dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        if region[0] != 'hooked':
1167dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          continue
1177dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        component_match, bucket = policy.find_mmap(region, bucket_set)
1187dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        if component_match == component_name:
1197dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          ExpandCommand._add_size('', bucket, depth,
1207dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                                  region[1]['committed'], sizes)
121