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