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 copy
6import logging
7import sys
8
9from lib.range_dict import ExclusiveRangeDict
10from lib.policy import PolicySet
11from lib.subcommand import SubCommand
12
13
14LOGGER = logging.getLogger('dmprof')
15
16
17class MapCommand(SubCommand):
18  def __init__(self):
19    super(MapCommand, self).__init__('Usage: %prog map <first-dump> <policy>')
20
21  def do(self, sys_argv, out=sys.stdout):
22    _, args = self._parse_args(sys_argv, 2)
23    dump_path = args[1]
24    target_policy = args[2]
25    (bucket_set, dumps) = SubCommand.load_basic_files(dump_path, True)
26    policy_set = PolicySet.load(SubCommand._parse_policy_list(target_policy))
27
28    MapCommand._output(dumps, bucket_set, policy_set[target_policy], out)
29    return 0
30
31  @staticmethod
32  def _output(dumps, bucket_set, policy, out):
33    """Prints all stacktraces in a given component of given depth.
34
35    Args:
36        dumps: A list of Dump objects.
37        bucket_set: A BucketSet object.
38        policy: A Policy object.
39        out: An IO object to output.
40    """
41    max_dump_count = 0
42    range_dict = ExclusiveRangeDict(ListAttribute)
43    for dump in dumps:
44      max_dump_count = max(max_dump_count, dump.count)
45      for key, value in dump.iter_map:
46        for begin, end, attr in range_dict.iter_range(key[0], key[1]):
47          attr[dump.count] = value
48
49    max_dump_count_digit = len(str(max_dump_count))
50    for begin, end, attr in range_dict.iter_range():
51      out.write('%x-%x\n' % (begin, end))
52      if len(attr) < max_dump_count:
53        attr[max_dump_count] = None
54      for index, value in enumerate(attr[1:]):
55        out.write('  #%0*d: ' % (max_dump_count_digit, index + 1))
56        if not value:
57          out.write('None\n')
58        elif value[0] == 'hooked':
59          component_match, _ = policy.find_mmap(value, bucket_set)
60          out.write('%s @ %d\n' % (component_match, value[1]['bucket_id']))
61        else:
62          component_match = policy.find_unhooked(value)
63          region_info = value[1]
64          size = region_info['committed']
65          out.write('%s [%d bytes] %s%s%s%s %s\n' % (
66              component_match, size, value[1]['vma']['readable'],
67              value[1]['vma']['writable'], value[1]['vma']['executable'],
68              value[1]['vma']['private'], value[1]['vma']['name']))
69
70
71class ListAttribute(ExclusiveRangeDict.RangeAttribute):
72  """Represents a list for an attribute in range_dict.ExclusiveRangeDict."""
73  def __init__(self):
74    super(ListAttribute, self).__init__()
75    self._list = []
76
77  def __str__(self):
78    return str(self._list)
79
80  def __repr__(self):
81    return 'ListAttribute' + str(self._list)
82
83  def __len__(self):
84    return len(self._list)
85
86  def __iter__(self):
87    for x in self._list:
88      yield x
89
90  def __getitem__(self, index):
91    return self._list[index]
92
93  def __setitem__(self, index, value):
94    if index >= len(self._list):
95      self._list.extend([None] * (index + 1 - len(self._list)))
96    self._list[index] = value
97
98  def copy(self):
99    new_list = ListAttribute()
100    for index, item in enumerate(self._list):
101      new_list[index] = copy.deepcopy(item)
102    return new_list
103