| Index: tools/deep_memory_profiler/dmprof.py
|
| diff --git a/tools/deep_memory_profiler/dmprof.py b/tools/deep_memory_profiler/dmprof.py
|
| index e745ebd88754a889cf8e7d77d9b5613a95aa23b4..eef2feafda4934da8700fe6c0d5cfb2a67460594 100644
|
| --- a/tools/deep_memory_profiler/dmprof.py
|
| +++ b/tools/deep_memory_profiler/dmprof.py
|
| @@ -4,7 +4,7 @@
|
|
|
| """The deep heap profiler script for Chrome."""
|
|
|
| -from datetime import datetime
|
| +import datetime
|
| import json
|
| import logging
|
| import optparse
|
| @@ -20,10 +20,12 @@ FIND_RUNTIME_SYMBOLS_PATH = os.path.join(
|
| BASE_PATH, os.pardir, 'find_runtime_symbols')
|
| sys.path.append(FIND_RUNTIME_SYMBOLS_PATH)
|
|
|
| -from find_runtime_symbols import find_runtime_symbols_list
|
| -from find_runtime_symbols import find_runtime_typeinfo_symbols_list
|
| -from find_runtime_symbols import RuntimeSymbolsInProcess
|
| -from prepare_symbol_info import prepare_symbol_info
|
| +import find_runtime_symbols
|
| +import prepare_symbol_info
|
| +
|
| +from find_runtime_symbols import FUNCTION_SYMBOLS
|
| +from find_runtime_symbols import SOURCEFILE_SYMBOLS
|
| +from find_runtime_symbols import TYPEINFO_SYMBOLS
|
|
|
| BUCKET_ID = 5
|
| VIRTUAL = 0
|
| @@ -34,8 +36,6 @@ NULL_REGEX = re.compile('')
|
|
|
| LOGGER = logging.getLogger('dmprof')
|
| POLICIES_JSON_PATH = os.path.join(BASE_PATH, 'policies.json')
|
| -FUNCTION_ADDRESS = 'function'
|
| -TYPEINFO_ADDRESS = 'typeinfo'
|
|
|
|
|
| # Heap Profile Dump versions
|
| @@ -155,8 +155,12 @@ class SymbolDataSources(object):
|
| True if succeeded.
|
| """
|
| LOGGER.info('Preparing symbol mapping...')
|
| - self._prepared_symbol_data_sources_path, used_tempdir = prepare_symbol_info(
|
| - self._prefix + '.maps', self._prefix + '.symmap', True)
|
| + self._prepared_symbol_data_sources_path, used_tempdir = (
|
| + prepare_symbol_info.prepare_symbol_info(
|
| + self._prefix + '.maps',
|
| + output_dir_path=self._prefix + '.symmap',
|
| + use_tempdir=True,
|
| + use_source_file_name=True))
|
| if self._prepared_symbol_data_sources_path:
|
| LOGGER.info(' Prepared symbol mapping.')
|
| if used_tempdir:
|
| @@ -178,8 +182,9 @@ class SymbolDataSources(object):
|
| return None
|
| if not self._loaded_symbol_data_sources:
|
| LOGGER.info('Loading symbol mapping...')
|
| - self._loaded_symbol_data_sources = RuntimeSymbolsInProcess.load(
|
| - self._prepared_symbol_data_sources_path)
|
| + self._loaded_symbol_data_sources = (
|
| + find_runtime_symbols.RuntimeSymbolsInProcess.load(
|
| + self._prepared_symbol_data_sources_path))
|
| return self._loaded_symbol_data_sources
|
|
|
| def path(self):
|
| @@ -195,17 +200,13 @@ class SymbolFinder(object):
|
| This class does only 'find()' symbols from a specified |address_list|.
|
| It is introduced to make a finder mockable.
|
| """
|
| - _FIND_RUNTIME_SYMBOLS_FUNCTIONS = {
|
| - FUNCTION_ADDRESS: find_runtime_symbols_list,
|
| - TYPEINFO_ADDRESS: find_runtime_typeinfo_symbols_list,
|
| - }
|
| -
|
| - def __init__(self, address_type, symbol_data_sources):
|
| - self._finder_function = self._FIND_RUNTIME_SYMBOLS_FUNCTIONS[address_type]
|
| + def __init__(self, symbol_type, symbol_data_sources):
|
| + self._symbol_type = symbol_type
|
| self._symbol_data_sources = symbol_data_sources
|
|
|
| def find(self, address_list):
|
| - return self._finder_function(self._symbol_data_sources.get(), address_list)
|
| + return find_runtime_symbols.find_runtime_symbols(
|
| + self._symbol_type, self._symbol_data_sources.get(), address_list)
|
|
|
|
|
| class SymbolMappingCache(object):
|
| @@ -216,11 +217,12 @@ class SymbolMappingCache(object):
|
| """
|
| def __init__(self):
|
| self._symbol_mapping_caches = {
|
| - FUNCTION_ADDRESS: {},
|
| - TYPEINFO_ADDRESS: {},
|
| + FUNCTION_SYMBOLS: {},
|
| + SOURCEFILE_SYMBOLS: {},
|
| + TYPEINFO_SYMBOLS: {},
|
| }
|
|
|
| - def update(self, address_type, bucket_set, symbol_finder, cache_f):
|
| + def update(self, symbol_type, bucket_set, symbol_finder, cache_f):
|
| """Updates symbol mapping cache on memory and in a symbol cache file.
|
|
|
| It reads cached symbol mapping from a symbol cache file |cache_f| if it
|
| @@ -234,18 +236,18 @@ class SymbolMappingCache(object):
|
| ...
|
|
|
| Args:
|
| - address_type: A type of addresses to update.
|
| - It should be one of FUNCTION_ADDRESS or TYPEINFO_ADDRESS.
|
| + symbol_type: A type of symbols to update. It should be one of
|
| + FUNCTION_SYMBOLS, SOURCEFILE_SYMBOLS and TYPEINFO_SYMBOLS.
|
| bucket_set: A BucketSet object.
|
| symbol_finder: A SymbolFinder object to find symbols.
|
| cache_f: A readable and writable IO object of the symbol cache file.
|
| """
|
| cache_f.seek(0, os.SEEK_SET)
|
| - self._load(cache_f, address_type)
|
| + self._load(cache_f, symbol_type)
|
|
|
| unresolved_addresses = sorted(
|
| - address for address in bucket_set.iter_addresses(address_type)
|
| - if address not in self._symbol_mapping_caches[address_type])
|
| + address for address in bucket_set.iter_addresses(symbol_type)
|
| + if address not in self._symbol_mapping_caches[symbol_type])
|
|
|
| if not unresolved_addresses:
|
| LOGGER.info('No need to resolve any more addresses.')
|
| @@ -253,36 +255,36 @@ class SymbolMappingCache(object):
|
|
|
| cache_f.seek(0, os.SEEK_END)
|
| LOGGER.info('Loading %d unresolved addresses.' %
|
| - len(unresolved_addresses))
|
| - symbol_list = symbol_finder.find(unresolved_addresses)
|
| + len(unresolved_addresses))
|
| + symbol_dict = symbol_finder.find(unresolved_addresses)
|
|
|
| - for address, symbol in zip(unresolved_addresses, symbol_list):
|
| - stripped_symbol = symbol.strip() or '??'
|
| - self._symbol_mapping_caches[address_type][address] = stripped_symbol
|
| + for address, symbol in symbol_dict.iteritems():
|
| + stripped_symbol = symbol.strip() or '?'
|
| + self._symbol_mapping_caches[symbol_type][address] = stripped_symbol
|
| cache_f.write('%x %s\n' % (address, stripped_symbol))
|
|
|
| - def lookup(self, address_type, address):
|
| + def lookup(self, symbol_type, address):
|
| """Looks up a symbol for a given |address|.
|
|
|
| Args:
|
| - address_type: A type of addresses to lookup.
|
| - It should be one of FUNCTION_ADDRESS or TYPEINFO_ADDRESS.
|
| + symbol_type: A type of symbols to update. It should be one of
|
| + FUNCTION_SYMBOLS, SOURCEFILE_SYMBOLS and TYPEINFO_SYMBOLS.
|
| address: An integer that represents an address.
|
|
|
| Returns:
|
| A string that represents a symbol.
|
| """
|
| - return self._symbol_mapping_caches[address_type].get(address)
|
| + return self._symbol_mapping_caches[symbol_type].get(address)
|
|
|
| - def _load(self, cache_f, address_type):
|
| + def _load(self, cache_f, symbol_type):
|
| try:
|
| for line in cache_f:
|
| items = line.rstrip().split(None, 1)
|
| if len(items) == 1:
|
| items.append('??')
|
| - self._symbol_mapping_caches[address_type][int(items[0], 16)] = items[1]
|
| + self._symbol_mapping_caches[symbol_type][int(items[0], 16)] = items[1]
|
| LOGGER.info('Loaded %d entries from symbol cache.' %
|
| - len(self._symbol_mapping_caches[address_type]))
|
| + len(self._symbol_mapping_caches[symbol_type]))
|
| except IOError as e:
|
| LOGGER.info('The symbol cache file is invalid: %s' % e)
|
|
|
| @@ -290,14 +292,28 @@ class SymbolMappingCache(object):
|
| class Rule(object):
|
| """Represents one matching rule in a policy file."""
|
|
|
| - def __init__(self, name, mmap, stacktrace_pattern, typeinfo_pattern=None):
|
| + def __init__(self,
|
| + name,
|
| + mmap,
|
| + stackfunction_pattern=None,
|
| + stacksourcefile_pattern=None,
|
| + typeinfo_pattern=None):
|
| self._name = name
|
| self._mmap = mmap
|
| - self._stacktrace_pattern = re.compile(stacktrace_pattern + r'\Z')
|
| +
|
| + self._stackfunction_pattern = None
|
| + if stackfunction_pattern:
|
| + self._stackfunction_pattern = re.compile(
|
| + stackfunction_pattern + r'\Z')
|
| +
|
| + self._stacksourcefile_pattern = None
|
| + if stacksourcefile_pattern:
|
| + self._stacksourcefile_pattern = re.compile(
|
| + stacksourcefile_pattern + r'\Z')
|
| +
|
| + self._typeinfo_pattern = None
|
| if typeinfo_pattern:
|
| self._typeinfo_pattern = re.compile(typeinfo_pattern + r'\Z')
|
| - else:
|
| - self._typeinfo_pattern = None
|
|
|
| @property
|
| def name(self):
|
| @@ -308,8 +324,12 @@ class Rule(object):
|
| return self._mmap
|
|
|
| @property
|
| - def stacktrace_pattern(self):
|
| - return self._stacktrace_pattern
|
| + def stackfunction_pattern(self):
|
| + return self._stackfunction_pattern
|
| +
|
| + @property
|
| + def stacksourcefile_pattern(self):
|
| + return self._stacksourcefile_pattern
|
|
|
| @property
|
| def typeinfo_pattern(self):
|
| @@ -350,14 +370,18 @@ class Policy(object):
|
| if bucket.component_cache:
|
| return bucket.component_cache
|
|
|
| - stacktrace = bucket.symbolized_joined_stacktrace
|
| + stackfunction = bucket.symbolized_joined_stackfunction
|
| + stacksourcefile = bucket.symbolized_joined_stacksourcefile
|
| typeinfo = bucket.symbolized_typeinfo
|
| if typeinfo.startswith('0x'):
|
| typeinfo = bucket.typeinfo_name
|
|
|
| for rule in self._rules:
|
| if (bucket.mmap == rule.mmap and
|
| - rule.stacktrace_pattern.match(stacktrace) and
|
| + (not rule.stackfunction_pattern or
|
| + rule.stackfunction_pattern.match(stackfunction)) and
|
| + (not rule.stacksourcefile_pattern or
|
| + rule.stacksourcefile_pattern.match(stacksourcefile)) and
|
| (not rule.typeinfo_pattern or rule.typeinfo_pattern.match(typeinfo))):
|
| bucket.component_cache = rule.name
|
| return rule.name
|
| @@ -414,11 +438,15 @@ class Policy(object):
|
|
|
| rules = []
|
| for rule in policy['rules']:
|
| + stackfunction = rule.get('stackfunction') or rule.get('stacktrace')
|
| + stacksourcefile = rule.get('stacksourcefile')
|
| rules.append(Rule(
|
| rule['name'],
|
| rule['allocator'] == 'mmap',
|
| - rule['stacktrace'],
|
| + stackfunction,
|
| + stacksourcefile,
|
| rule['typeinfo'] if 'typeinfo' in rule else None))
|
| +
|
| return Policy(rules, policy['version'], policy['components'])
|
|
|
|
|
| @@ -493,8 +521,10 @@ class Bucket(object):
|
| self._typeinfo = typeinfo
|
| self._typeinfo_name = typeinfo_name
|
|
|
| - self._symbolized_stacktrace = stacktrace
|
| - self._symbolized_joined_stacktrace = ''
|
| + self._symbolized_stackfunction = stacktrace
|
| + self._symbolized_joined_stackfunction = ''
|
| + self._symbolized_stacksourcefile = stacktrace
|
| + self._symbolized_joined_stacksourcefile = ''
|
| self._symbolized_typeinfo = typeinfo_name
|
|
|
| self.component_cache = ''
|
| @@ -506,15 +536,21 @@ class Bucket(object):
|
| symbol_mapping_cache: A SymbolMappingCache object.
|
| """
|
| # TODO(dmikurube): Fill explicitly with numbers if symbol not found.
|
| - self._symbolized_stacktrace = [
|
| - symbol_mapping_cache.lookup(FUNCTION_ADDRESS, address)
|
| + 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_stacktrace = ' '.join(self._symbolized_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_ADDRESS, self._typeinfo)
|
| + TYPEINFO_SYMBOLS, self._typeinfo)
|
| if not self._symbolized_typeinfo:
|
| self._symbolized_typeinfo = 'no typeinfo'
|
|
|
| @@ -538,12 +574,20 @@ class Bucket(object):
|
| return self._typeinfo_name
|
|
|
| @property
|
| - def symbolized_stacktrace(self):
|
| - return self._symbolized_stacktrace
|
| + def symbolized_stackfunction(self):
|
| + return self._symbolized_stackfunction
|
|
|
| @property
|
| - def symbolized_joined_stacktrace(self):
|
| - return self._symbolized_joined_stacktrace
|
| + 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):
|
| @@ -554,10 +598,8 @@ class BucketSet(object):
|
| """Represents a set of bucket."""
|
| def __init__(self):
|
| self._buckets = {}
|
| - self._addresses = {
|
| - FUNCTION_ADDRESS: set(),
|
| - TYPEINFO_ADDRESS: set(),
|
| - }
|
| + self._code_addresses = set()
|
| + self._typeinfo_addresses = set()
|
|
|
| def load(self, prefix):
|
| """Loads all related bucket files.
|
| @@ -591,7 +633,7 @@ class BucketSet(object):
|
| continue
|
| if word[0] == 't':
|
| typeinfo = int(word[1:], 16)
|
| - self._addresses[TYPEINFO_ADDRESS].add(typeinfo)
|
| + self._typeinfo_addresses.add(typeinfo)
|
| elif word[0] == 'n':
|
| typeinfo_name = word[1:]
|
| else:
|
| @@ -599,7 +641,7 @@ class BucketSet(object):
|
| break
|
| stacktrace = [int(address, 16) for address in words[stacktrace_begin:]]
|
| for frame in stacktrace:
|
| - self._addresses[FUNCTION_ADDRESS].add(frame)
|
| + self._code_addresses.add(frame)
|
| self._buckets[int(words[0])] = Bucket(
|
| stacktrace, words[1] == 'mmap', typeinfo, typeinfo_name)
|
|
|
| @@ -621,9 +663,13 @@ class BucketSet(object):
|
| for bucket_content in self._buckets.itervalues():
|
| bucket_content.clear_component_cache()
|
|
|
| - def iter_addresses(self, address_type):
|
| - for function in self._addresses[address_type]:
|
| - yield function
|
| + 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
|
|
|
|
|
| class Dump(object):
|
| @@ -840,14 +886,18 @@ class Command(object):
|
| else:
|
| dump = Dump.load(dump_path)
|
| symbol_mapping_cache = SymbolMappingCache()
|
| - with open(prefix + '.funcsym', 'a+') as cache_f:
|
| + with open(prefix + '.cache.function', 'a+') as cache_f:
|
| + symbol_mapping_cache.update(
|
| + FUNCTION_SYMBOLS, bucket_set,
|
| + SymbolFinder(FUNCTION_SYMBOLS, symbol_data_sources), cache_f)
|
| + with open(prefix + '.cache.typeinfo', 'a+') as cache_f:
|
| symbol_mapping_cache.update(
|
| - FUNCTION_ADDRESS, bucket_set,
|
| - SymbolFinder(FUNCTION_ADDRESS, symbol_data_sources), cache_f)
|
| - with open(prefix + '.typesym', 'a+') as cache_f:
|
| + TYPEINFO_SYMBOLS, bucket_set,
|
| + SymbolFinder(TYPEINFO_SYMBOLS, symbol_data_sources), cache_f)
|
| + with open(prefix + '.cache.sourcefile', 'a+') as cache_f:
|
| symbol_mapping_cache.update(
|
| - TYPEINFO_ADDRESS, bucket_set,
|
| - SymbolFinder(TYPEINFO_ADDRESS, symbol_data_sources), cache_f)
|
| + SOURCEFILE_SYMBOLS, bucket_set,
|
| + SymbolFinder(SOURCEFILE_SYMBOLS, symbol_data_sources), cache_f)
|
| bucket_set.symbolize(symbol_mapping_cache)
|
| if multiple:
|
| return (bucket_set, dump_list)
|
| @@ -936,7 +986,7 @@ class StacktraceCommand(Command):
|
| continue
|
| for i in range(0, BUCKET_ID - 1):
|
| out.write(words[i] + ' ')
|
| - for frame in bucket.symbolized_stacktrace:
|
| + for frame in bucket.symbolized_stackfunction:
|
| out.write(frame + ' ')
|
| out.write('\n')
|
|
|
| @@ -1121,7 +1171,7 @@ class JSONCommand(PolicyCommands):
|
| component_sizes = PolicyCommands._apply_policy(
|
| dump, policy_set[label], bucket_set, dumps[0].time)
|
| component_sizes['dump_path'] = dump.path
|
| - component_sizes['dump_time'] = datetime.fromtimestamp(
|
| + component_sizes['dump_time'] = datetime.datetime.fromtimestamp(
|
| dump.time).strftime('%Y-%m-%d %H:%M:%S')
|
| json_base['policies'][label]['snapshots'].append(component_sizes)
|
|
|
| @@ -1197,6 +1247,7 @@ class ExpandCommand(Command):
|
| sorted_sizes_list = sorted(
|
| sizes.iteritems(), key=(lambda x: x[1]), reverse=True)
|
| total = 0
|
| + # TODO(dmikurube): Better formatting.
|
| for size_pair in sorted_sizes_list:
|
| out.write('%10d %s\n' % (size_pair[1], size_pair[0]))
|
| total += size_pair[1]
|
| @@ -1213,9 +1264,12 @@ class ExpandCommand(Command):
|
| if bucket.typeinfo:
|
| stacktrace_sequence += '(type=%s)' % bucket.symbolized_typeinfo
|
| stacktrace_sequence += ' (type.name=%s) ' % bucket.typeinfo_name
|
| - for stack in bucket.symbolized_stacktrace[
|
| - 0 : min(len(bucket.symbolized_stacktrace), 1 + depth)]:
|
| - stacktrace_sequence += stack + ' '
|
| + for function, sourcefile in zip(
|
| + bucket.symbolized_stackfunction[
|
| + 0 : min(len(bucket.symbolized_stackfunction), 1 + depth)],
|
| + bucket.symbolized_stacksourcefile[
|
| + 0 : min(len(bucket.symbolized_stacksourcefile), 1 + depth)]):
|
| + stacktrace_sequence += '%s(@%s) ' % (function, sourcefile)
|
| if not stacktrace_sequence in sizes:
|
| sizes[stacktrace_sequence] = 0
|
| sizes[stacktrace_sequence] += int(words[COMMITTED])
|
|
|