Index: tools/deep_memory_profiler/lib/bucket.py |
diff --git a/tools/deep_memory_profiler/lib/bucket.py b/tools/deep_memory_profiler/lib/bucket.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..51af5b2ddd82bc18f1420c4202fa80f5f039f754 |
--- /dev/null |
+++ b/tools/deep_memory_profiler/lib/bucket.py |
@@ -0,0 +1,191 @@ |
+# Copyright 2013 The Chromium Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+import logging |
+import os |
+ |
+from lib.symbol import FUNCTION_SYMBOLS, SOURCEFILE_SYMBOLS, TYPEINFO_SYMBOLS |
+ |
+ |
+LOGGER = logging.getLogger('dmprof') |
+ |
+# Indexes in dumped heap profile dumps. |
+VIRTUAL, COMMITTED, ALLOC_COUNT, FREE_COUNT, _, BUCKET_ID = range(6) |
+ |
+ |
+class Bucket(object): |
+ """Represents a bucket, which is a unit of memory block classification.""" |
+ |
+ def __init__(self, stacktrace, allocator_type, typeinfo, typeinfo_name): |
+ self._stacktrace = stacktrace |
+ self._allocator_type = allocator_type |
+ self._typeinfo = typeinfo |
+ self._typeinfo_name = typeinfo_name |
+ |
+ self._symbolized_stackfunction = stacktrace |
+ self._symbolized_joined_stackfunction = '' |
+ self._symbolized_stacksourcefile = stacktrace |
+ self._symbolized_joined_stacksourcefile = '' |
+ self._symbolized_typeinfo = typeinfo_name |
+ |
+ self.component_cache = '' |
+ |
+ def __str__(self): |
+ result = [] |
+ result.append(self._allocator_type) |
+ if self._symbolized_typeinfo == 'no typeinfo': |
+ result.append('tno_typeinfo') |
+ else: |
+ result.append('t' + self._symbolized_typeinfo) |
+ result.append('n' + self._typeinfo_name) |
+ result.extend(['%s(@%s)' % (function, sourcefile) |
+ for function, sourcefile |
+ in zip(self._symbolized_stackfunction, |
+ self._symbolized_stacksourcefile)]) |
+ return ' '.join(result) |
+ |
+ def symbolize(self, symbol_mapping_cache): |
+ """Makes a symbolized stacktrace and typeinfo with |symbol_mapping_cache|. |
+ |
+ Args: |
+ symbol_mapping_cache: A SymbolMappingCache object. |
+ """ |
+ # TODO(dmikurube): Fill explicitly with numbers if symbol not found. |
+ self._symbolized_stackfunction = [ |
+ symbol_mapping_cache.lookup(FUNCTION_SYMBOLS, address) |
+ for address in self._stacktrace] |
+ self._symbolized_joined_stackfunction = ' '.join( |
+ self._symbolized_stackfunction) |
+ self._symbolized_stacksourcefile = [ |
+ symbol_mapping_cache.lookup(SOURCEFILE_SYMBOLS, address) |
+ for address in self._stacktrace] |
+ self._symbolized_joined_stacksourcefile = ' '.join( |
+ self._symbolized_stacksourcefile) |
+ if not self._typeinfo: |
+ self._symbolized_typeinfo = 'no typeinfo' |
+ else: |
+ self._symbolized_typeinfo = symbol_mapping_cache.lookup( |
+ TYPEINFO_SYMBOLS, self._typeinfo) |
+ if not self._symbolized_typeinfo: |
+ self._symbolized_typeinfo = 'no typeinfo' |
+ |
+ def clear_component_cache(self): |
+ self.component_cache = '' |
+ |
+ @property |
+ def stacktrace(self): |
+ return self._stacktrace |
+ |
+ @property |
+ def allocator_type(self): |
+ return self._allocator_type |
+ |
+ @property |
+ def typeinfo(self): |
+ return self._typeinfo |
+ |
+ @property |
+ def typeinfo_name(self): |
+ return self._typeinfo_name |
+ |
+ @property |
+ def symbolized_stackfunction(self): |
+ return self._symbolized_stackfunction |
+ |
+ @property |
+ def symbolized_joined_stackfunction(self): |
+ return self._symbolized_joined_stackfunction |
+ |
+ @property |
+ def symbolized_stacksourcefile(self): |
+ return self._symbolized_stacksourcefile |
+ |
+ @property |
+ def symbolized_joined_stacksourcefile(self): |
+ return self._symbolized_joined_stacksourcefile |
+ |
+ @property |
+ def symbolized_typeinfo(self): |
+ return self._symbolized_typeinfo |
+ |
+ |
+class BucketSet(object): |
+ """Represents a set of bucket.""" |
+ def __init__(self): |
+ self._buckets = {} |
+ self._code_addresses = set() |
+ self._typeinfo_addresses = set() |
+ |
+ def load(self, prefix): |
+ """Loads all related bucket files. |
+ |
+ Args: |
+ prefix: A prefix string for bucket file names. |
+ """ |
+ LOGGER.info('Loading bucket files.') |
+ |
+ n = 0 |
+ skipped = 0 |
+ while True: |
+ path = '%s.%04d.buckets' % (prefix, n) |
+ if not os.path.exists(path) or not os.stat(path).st_size: |
+ if skipped > 10: |
+ break |
+ n += 1 |
+ skipped += 1 |
+ continue |
+ LOGGER.info(' %s' % path) |
+ with open(path, 'r') as f: |
+ self._load_file(f) |
+ n += 1 |
+ skipped = 0 |
+ |
+ def _load_file(self, bucket_f): |
+ for line in bucket_f: |
+ words = line.split() |
+ typeinfo = None |
+ typeinfo_name = '' |
+ stacktrace_begin = 2 |
+ for index, word in enumerate(words): |
+ if index < 2: |
+ continue |
+ if word[0] == 't': |
+ typeinfo = int(word[1:], 16) |
+ self._typeinfo_addresses.add(typeinfo) |
+ elif word[0] == 'n': |
+ typeinfo_name = word[1:] |
+ else: |
+ stacktrace_begin = index |
+ break |
+ stacktrace = [int(address, 16) for address in words[stacktrace_begin:]] |
+ for frame in stacktrace: |
+ self._code_addresses.add(frame) |
+ self._buckets[int(words[0])] = Bucket( |
+ stacktrace, words[1], typeinfo, typeinfo_name) |
+ |
+ def __iter__(self): |
+ for bucket_id, bucket_content in self._buckets.iteritems(): |
+ yield bucket_id, bucket_content |
+ |
+ def __getitem__(self, bucket_id): |
+ return self._buckets[bucket_id] |
+ |
+ def get(self, bucket_id): |
+ return self._buckets.get(bucket_id) |
+ |
+ def symbolize(self, symbol_mapping_cache): |
+ for bucket_content in self._buckets.itervalues(): |
+ bucket_content.symbolize(symbol_mapping_cache) |
+ |
+ def clear_component_cache(self): |
+ for bucket_content in self._buckets.itervalues(): |
+ bucket_content.clear_component_cache() |
+ |
+ def iter_addresses(self, symbol_type): |
+ if symbol_type in [FUNCTION_SYMBOLS, SOURCEFILE_SYMBOLS]: |
+ for function in self._code_addresses: |
+ yield function |
+ else: |
+ for function in self._typeinfo_addresses: |
+ yield function |