| # 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') |
| |
| |
| 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 |