Home | History | Annotate | Download | only in subcommands
      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 
      5 import json
      6 import logging
      7 import sys
      8 
      9 from lib.ordered_dict import OrderedDict
     10 from lib.subcommand import SubCommand
     11 from lib.sorter import MallocUnit, MMapUnit, SorterSet, UnhookedUnit, UnitSet
     12 
     13 
     14 LOGGER = logging.getLogger('dmprof')
     15 
     16 
     17 class CatCommand(SubCommand):
     18   def __init__(self):
     19     super(CatCommand, self).__init__('Usage: %prog cat <first-dump>')
     20     self._parser.add_option('--alternative-dirs', dest='alternative_dirs',
     21                             metavar='/path/on/target@/path/on/host[:...]',
     22                             help='Read files in /path/on/host/ instead of '
     23                                  'files in /path/on/target/.')
     24     self._parser.add_option('--indent', dest='indent', action='store_true',
     25                             help='Indent the output.')
     26 
     27   def do(self, sys_argv):
     28     options, args = self._parse_args(sys_argv, 1)
     29     dump_path = args[1]
     30     # TODO(dmikurube): Support shared memory.
     31     alternative_dirs_dict = {}
     32     if options.alternative_dirs:
     33       for alternative_dir_pair in options.alternative_dirs.split(':'):
     34         target_path, host_path = alternative_dir_pair.split('@', 1)
     35         alternative_dirs_dict[target_path] = host_path
     36     (bucket_set, dumps) = SubCommand.load_basic_files(
     37         dump_path, True, alternative_dirs=alternative_dirs_dict)
     38 
     39     # Load all sorters.
     40     sorters = SorterSet()
     41 
     42     json_root = OrderedDict()
     43     json_root['version'] = 1
     44     json_root['run_id'] = None
     45     json_root['roots'] = []
     46     for sorter in sorters:
     47       if sorter.root:
     48         json_root['roots'].append([sorter.world, sorter.name])
     49     json_root['default_template'] = 'l2'
     50     json_root['templates'] = sorters.templates.as_dict()
     51 
     52     orders = OrderedDict()
     53     orders['worlds'] = OrderedDict()
     54     for world in ['vm', 'malloc']:
     55       orders['worlds'][world] = OrderedDict()
     56       orders['worlds'][world]['breakdown'] = OrderedDict()
     57       for sorter in sorters.iter_world(world):
     58         order = []
     59         for rule in sorter.iter_rule():
     60           if rule.name not in order:
     61             order.append(rule.name)
     62         orders['worlds'][world]['breakdown'][sorter.name] = order
     63     json_root['orders'] = orders
     64 
     65     json_root['snapshots'] = []
     66 
     67     for dump in dumps:
     68       if json_root['run_id'] and json_root['run_id'] != dump.run_id:
     69         LOGGER.error('Inconsistent heap profile dumps.')
     70         json_root['run_id'] = ''
     71       else:
     72         json_root['run_id'] = dump.run_id
     73 
     74       LOGGER.info('Sorting a dump %s...' % dump.path)
     75       json_root['snapshots'].append(
     76           self._fill_snapshot(dump, bucket_set, sorters))
     77 
     78     if options.indent:
     79       json.dump(json_root, sys.stdout, indent=2)
     80     else:
     81       json.dump(json_root, sys.stdout)
     82     print ''
     83 
     84   @staticmethod
     85   def _fill_snapshot(dump, bucket_set, sorters):
     86     root = OrderedDict()
     87     root['time'] = dump.time
     88     root['worlds'] = OrderedDict()
     89     root['worlds']['vm'] = CatCommand._fill_world(
     90         dump, bucket_set, sorters, 'vm')
     91     root['worlds']['malloc'] = CatCommand._fill_world(
     92         dump, bucket_set, sorters, 'malloc')
     93     return root
     94 
     95   @staticmethod
     96   def _fill_world(dump, bucket_set, sorters, world):
     97     root = OrderedDict()
     98 
     99     root['name'] = world
    100     if world == 'vm':
    101       root['unit_fields'] = ['size', 'reserved']
    102     elif world == 'malloc':
    103       root['unit_fields'] = ['size', 'alloc_count', 'free_count']
    104 
    105     # Make { vm | malloc } units with their sizes.
    106     root['units'] = OrderedDict()
    107     unit_set = UnitSet(world)
    108     if world == 'vm':
    109       for unit in CatCommand._iterate_vm_unit(dump, None, bucket_set):
    110         unit_set.append(unit)
    111       for unit in unit_set:
    112         root['units'][unit.unit_id] = [unit.committed, unit.reserved]
    113     elif world == 'malloc':
    114       for unit in CatCommand._iterate_malloc_unit(dump, bucket_set):
    115         unit_set.append(unit)
    116       for unit in unit_set:
    117         root['units'][unit.unit_id] = [
    118             unit.size, unit.alloc_count, unit.free_count]
    119 
    120     # Iterate for { vm | malloc } sorters.
    121     root['breakdown'] = OrderedDict()
    122     for sorter in sorters.iter_world(world):
    123       LOGGER.info('  Sorting with %s:%s.' % (sorter.world, sorter.name))
    124       breakdown = OrderedDict()
    125       for rule in sorter.iter_rule():
    126         category = OrderedDict()
    127         category['name'] = rule.name
    128         subs = []
    129         for sub_world, sub_breakdown in rule.iter_subs():
    130           subs.append([sub_world, sub_breakdown])
    131         if subs:
    132           category['subs'] = subs
    133         if rule.hidden:
    134           category['hidden'] = True
    135         category['units'] = []
    136         breakdown[rule.name] = category
    137       for unit in unit_set:
    138         found = sorter.find(unit)
    139         if found:
    140           # Note that a bucket which doesn't match any rule is just dropped.
    141           breakdown[found.name]['units'].append(unit.unit_id)
    142       root['breakdown'][sorter.name] = breakdown
    143 
    144     return root
    145 
    146   @staticmethod
    147   def _iterate_vm_unit(dump, pfn_dict, bucket_set):
    148     unit_id = 0
    149     for _, region in dump.iter_map:
    150       unit_id += 1
    151       if region[0] == 'unhooked':
    152         if pfn_dict and dump.pageframe_length:
    153           for pageframe in region[1]['pageframe']:
    154             yield UnhookedUnit(unit_id, pageframe.size, pageframe.size,
    155                                region, pageframe, pfn_dict)
    156         else:
    157           yield UnhookedUnit(unit_id,
    158                              int(region[1]['committed']),
    159                              int(region[1]['reserved']),
    160                              region)
    161       elif region[0] == 'hooked':
    162         if pfn_dict and dump.pageframe_length:
    163           for pageframe in region[1]['pageframe']:
    164             yield MMapUnit(unit_id,
    165                            pageframe.size,
    166                            pageframe.size,
    167                            region, bucket_set, pageframe, pfn_dict)
    168         else:
    169           yield MMapUnit(unit_id,
    170                          int(region[1]['committed']),
    171                          int(region[1]['reserved']),
    172                          region,
    173                          bucket_set)
    174       else:
    175         LOGGER.error('Unrecognized mapping status: %s' % region[0])
    176 
    177   @staticmethod
    178   def _iterate_malloc_unit(dump, bucket_set):
    179     for bucket_id, _, committed, allocs, frees in dump.iter_stacktrace:
    180       bucket = bucket_set.get(bucket_id)
    181       if bucket and bucket.allocator_type == 'malloc':
    182         yield MallocUnit(bucket_id, committed, allocs, frees, bucket)
    183       elif not bucket:
    184         # 'Not-found' buckets are all assumed as malloc buckets.
    185         yield MallocUnit(bucket_id, committed, allocs, frees, None)
    186