192 lines
5.5 KiB
Python
192 lines
5.5 KiB
Python
# 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
|