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 logging
6import os
7
8from lib.symbol import FUNCTION_SYMBOLS, SOURCEFILE_SYMBOLS, TYPEINFO_SYMBOLS
9
10
11LOGGER = logging.getLogger('dmprof')
12
13# Indexes in dumped heap profile dumps.
14VIRTUAL, COMMITTED, ALLOC_COUNT, FREE_COUNT, _, BUCKET_ID = range(6)
15
16
17class Bucket(object):
18  """Represents a bucket, which is a unit of memory block classification."""
19
20  def __init__(self, stacktrace, allocator_type, typeinfo, typeinfo_name):
21    self._stacktrace = stacktrace
22    self._allocator_type = allocator_type
23    self._typeinfo = typeinfo
24    self._typeinfo_name = typeinfo_name
25
26    self._symbolized_stackfunction = stacktrace
27    self._symbolized_joined_stackfunction = ''
28    self._symbolized_stacksourcefile = stacktrace
29    self._symbolized_joined_stacksourcefile = ''
30    self._symbolized_typeinfo = typeinfo_name
31
32    self.component_cache = ''
33
34  def __str__(self):
35    result = []
36    result.append(self._allocator_type)
37    if self._symbolized_typeinfo == 'no typeinfo':
38      result.append('tno_typeinfo')
39    else:
40      result.append('t' + self._symbolized_typeinfo)
41    result.append('n' + self._typeinfo_name)
42    result.extend(['%s(@%s)' % (function, sourcefile)
43                   for function, sourcefile
44                   in zip(self._symbolized_stackfunction,
45                          self._symbolized_stacksourcefile)])
46    return ' '.join(result)
47
48  def symbolize(self, symbol_mapping_cache):
49    """Makes a symbolized stacktrace and typeinfo with |symbol_mapping_cache|.
50
51    Args:
52        symbol_mapping_cache: A SymbolMappingCache object.
53    """
54    # TODO(dmikurube): Fill explicitly with numbers if symbol not found.
55    self._symbolized_stackfunction = [
56        symbol_mapping_cache.lookup(FUNCTION_SYMBOLS, address)
57        for address in self._stacktrace]
58    self._symbolized_joined_stackfunction = ' '.join(
59        self._symbolized_stackfunction)
60    self._symbolized_stacksourcefile = [
61        symbol_mapping_cache.lookup(SOURCEFILE_SYMBOLS, address)
62        for address in self._stacktrace]
63    self._symbolized_joined_stacksourcefile = ' '.join(
64        self._symbolized_stacksourcefile)
65    if not self._typeinfo:
66      self._symbolized_typeinfo = 'no typeinfo'
67    else:
68      self._symbolized_typeinfo = symbol_mapping_cache.lookup(
69          TYPEINFO_SYMBOLS, self._typeinfo)
70      if not self._symbolized_typeinfo:
71        self._symbolized_typeinfo = 'no typeinfo'
72
73  def clear_component_cache(self):
74    self.component_cache = ''
75
76  @property
77  def stacktrace(self):
78    return self._stacktrace
79
80  @property
81  def allocator_type(self):
82    return self._allocator_type
83
84  @property
85  def typeinfo(self):
86    return self._typeinfo
87
88  @property
89  def typeinfo_name(self):
90    return self._typeinfo_name
91
92  @property
93  def symbolized_stackfunction(self):
94    return self._symbolized_stackfunction
95
96  @property
97  def symbolized_joined_stackfunction(self):
98    return self._symbolized_joined_stackfunction
99
100  @property
101  def symbolized_stacksourcefile(self):
102    return self._symbolized_stacksourcefile
103
104  @property
105  def symbolized_joined_stacksourcefile(self):
106    return self._symbolized_joined_stacksourcefile
107
108  @property
109  def symbolized_typeinfo(self):
110    return self._symbolized_typeinfo
111
112
113class BucketSet(object):
114  """Represents a set of bucket."""
115  def __init__(self):
116    self._buckets = {}
117    self._code_addresses = set()
118    self._typeinfo_addresses = set()
119
120  def load(self, prefix):
121    """Loads all related bucket files.
122
123    Args:
124        prefix: A prefix string for bucket file names.
125    """
126    LOGGER.info('Loading bucket files.')
127
128    n = 0
129    skipped = 0
130    while True:
131      path = '%s.%04d.buckets' % (prefix, n)
132      if not os.path.exists(path) or not os.stat(path).st_size:
133        if skipped > 10:
134          break
135        n += 1
136        skipped += 1
137        continue
138      LOGGER.info('  %s' % path)
139      with open(path, 'r') as f:
140        self._load_file(f)
141      n += 1
142      skipped = 0
143
144  def _load_file(self, bucket_f):
145    for line in bucket_f:
146      words = line.split()
147      typeinfo = None
148      typeinfo_name = ''
149      stacktrace_begin = 2
150      for index, word in enumerate(words):
151        if index < 2:
152          continue
153        if word[0] == 't':
154          typeinfo = int(word[1:], 16)
155          self._typeinfo_addresses.add(typeinfo)
156        elif word[0] == 'n':
157          typeinfo_name = word[1:]
158        else:
159          stacktrace_begin = index
160          break
161      stacktrace = [int(address, 16) for address in words[stacktrace_begin:]]
162      for frame in stacktrace:
163        self._code_addresses.add(frame)
164      self._buckets[int(words[0])] = Bucket(
165          stacktrace, words[1], typeinfo, typeinfo_name)
166
167  def __iter__(self):
168    for bucket_id, bucket_content in self._buckets.iteritems():
169      yield bucket_id, bucket_content
170
171  def __getitem__(self, bucket_id):
172    return self._buckets[bucket_id]
173
174  def get(self, bucket_id):
175    return self._buckets.get(bucket_id)
176
177  def symbolize(self, symbol_mapping_cache):
178    for bucket_content in self._buckets.itervalues():
179      bucket_content.symbolize(symbol_mapping_cache)
180
181  def clear_component_cache(self):
182    for bucket_content in self._buckets.itervalues():
183      bucket_content.clear_component_cache()
184
185  def iter_addresses(self, symbol_type):
186    if symbol_type in [FUNCTION_SYMBOLS, SOURCEFILE_SYMBOLS]:
187      for function in self._code_addresses:
188        yield function
189    else:
190      for function in self._typeinfo_addresses:
191        yield function
192