Revert of Remove OneClickSigninHelper since it is no longer used. (patchset #5 id...
[chromium-blink-merge.git] / tools / deep_memory_profiler / lib / symbol.py
blob86dd44b28a54d51a5d6f68609ab60508df014c7c
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.
5 import logging
6 import os
7 import sys
9 _BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
10 _FIND_RUNTIME_SYMBOLS_PATH = os.path.join(_BASE_PATH,
11 os.pardir,
12 'find_runtime_symbols')
13 _TOOLS_LINUX_PATH = os.path.join(_BASE_PATH,
14 os.pardir,
15 'linux')
16 sys.path.append(_FIND_RUNTIME_SYMBOLS_PATH)
17 sys.path.append(_TOOLS_LINUX_PATH)
19 import find_runtime_symbols
20 import prepare_symbol_info
21 import procfs # pylint: disable=W0611,F0401
23 LOGGER = logging.getLogger('dmprof')
25 FUNCTION_SYMBOLS = find_runtime_symbols.FUNCTION_SYMBOLS
26 SOURCEFILE_SYMBOLS = find_runtime_symbols.SOURCEFILE_SYMBOLS
27 TYPEINFO_SYMBOLS = find_runtime_symbols.TYPEINFO_SYMBOLS
30 class SymbolDataSources(object):
31 """Manages symbol data sources in a process.
33 The symbol data sources consist of maps (/proc/<pid>/maps), nm, readelf and
34 so on. They are collected into a directory '|prefix|.symmap' from the binary
35 files by 'prepare()' with tools/find_runtime_symbols/prepare_symbol_info.py.
37 Binaries are not mandatory to profile. The prepared data sources work in
38 place of the binary even if the binary has been overwritten with another
39 binary.
41 Note that loading the symbol data sources takes a long time. They are often
42 very big. So, the 'dmprof' profiler is designed to use 'SymbolMappingCache'
43 which caches actually used symbols.
44 """
45 def __init__(self, prefix, alternative_dirs=None):
46 self._prefix = prefix
47 self._prepared_symbol_data_sources_path = None
48 self._loaded_symbol_data_sources = None
49 self._alternative_dirs = alternative_dirs or {}
51 def prepare(self):
52 """Prepares symbol data sources by extracting mapping from a binary.
54 The prepared symbol data sources are stored in a directory. The directory
55 name is stored in |self._prepared_symbol_data_sources_path|.
57 Returns:
58 True if succeeded.
59 """
60 LOGGER.info('Preparing symbol mapping...')
61 self._prepared_symbol_data_sources_path, used_tempdir = (
62 prepare_symbol_info.prepare_symbol_info(
63 self._prefix + '.maps',
64 output_dir_path=self._prefix + '.symmap',
65 alternative_dirs=self._alternative_dirs,
66 use_tempdir=True,
67 use_source_file_name=True))
68 if self._prepared_symbol_data_sources_path:
69 LOGGER.info(' Prepared symbol mapping.')
70 if used_tempdir:
71 LOGGER.warn(' Using a temporary directory for symbol mapping.')
72 LOGGER.warn(' Delete it by yourself.')
73 LOGGER.warn(' Or, move the directory by yourself to use it later.')
74 return True
75 else:
76 LOGGER.warn(' Failed to prepare symbol mapping.')
77 return False
79 def get(self):
80 """Returns the prepared symbol data sources.
82 Returns:
83 The prepared symbol data sources. None if failed.
84 """
85 if not self._prepared_symbol_data_sources_path and not self.prepare():
86 return None
87 if not self._loaded_symbol_data_sources:
88 LOGGER.info('Loading symbol mapping...')
89 self._loaded_symbol_data_sources = (
90 find_runtime_symbols.RuntimeSymbolsInProcess.load(
91 self._prepared_symbol_data_sources_path))
92 return self._loaded_symbol_data_sources
94 def path(self):
95 """Returns the path of the prepared symbol data sources if possible."""
96 if not self._prepared_symbol_data_sources_path and not self.prepare():
97 return None
98 return self._prepared_symbol_data_sources_path
101 class SymbolFinder(object):
102 """Finds corresponding symbols from addresses.
104 This class does only 'find()' symbols from a specified |address_list|.
105 It is introduced to make a finder mockable.
107 def __init__(self, symbol_type, symbol_data_sources):
108 self._symbol_type = symbol_type
109 self._symbol_data_sources = symbol_data_sources
111 def find(self, address_list):
112 return find_runtime_symbols.find_runtime_symbols(
113 self._symbol_type, self._symbol_data_sources.get(), address_list)
116 class SymbolMappingCache(object):
117 """Caches mapping from actually used addresses to symbols.
119 'update()' updates the cache from the original symbol data sources via
120 'SymbolFinder'. Symbols can be looked up by the method 'lookup()'.
122 def __init__(self):
123 self._symbol_mapping_caches = {
124 FUNCTION_SYMBOLS: {},
125 SOURCEFILE_SYMBOLS: {},
126 TYPEINFO_SYMBOLS: {},
129 def update(self, symbol_type, bucket_set, symbol_finder, cache_f):
130 """Updates symbol mapping cache on memory and in a symbol cache file.
132 It reads cached symbol mapping from a symbol cache file |cache_f| if it
133 exists. Unresolved addresses are then resolved and added to the cache
134 both on memory and in the symbol cache file with using 'SymbolFinder'.
136 A cache file is formatted as follows:
137 <Address> <Symbol>
138 <Address> <Symbol>
139 <Address> <Symbol>
142 Args:
143 symbol_type: A type of symbols to update. It should be one of
144 FUNCTION_SYMBOLS, SOURCEFILE_SYMBOLS and TYPEINFO_SYMBOLS.
145 bucket_set: A BucketSet object.
146 symbol_finder: A SymbolFinder object to find symbols.
147 cache_f: A readable and writable IO object of the symbol cache file.
149 cache_f.seek(0, os.SEEK_SET)
150 self._load(cache_f, symbol_type)
152 unresolved_addresses = sorted(
153 address for address in bucket_set.iter_addresses(symbol_type)
154 if address not in self._symbol_mapping_caches[symbol_type])
156 if not unresolved_addresses:
157 LOGGER.info('No need to resolve any more addresses.')
158 return
160 cache_f.seek(0, os.SEEK_END)
161 LOGGER.info('Loading %d unresolved addresses.' %
162 len(unresolved_addresses))
163 symbol_dict = symbol_finder.find(unresolved_addresses)
165 for address, symbol in symbol_dict.iteritems():
166 stripped_symbol = symbol.strip() or '?'
167 self._symbol_mapping_caches[symbol_type][address] = stripped_symbol
168 cache_f.write('%x %s\n' % (address, stripped_symbol))
170 def lookup(self, symbol_type, address):
171 """Looks up a symbol for a given |address|.
173 Args:
174 symbol_type: A type of symbols to update. It should be one of
175 FUNCTION_SYMBOLS, SOURCEFILE_SYMBOLS and TYPEINFO_SYMBOLS.
176 address: An integer that represents an address.
178 Returns:
179 A string that represents a symbol.
181 return self._symbol_mapping_caches[symbol_type].get(address)
183 def _load(self, cache_f, symbol_type):
184 try:
185 for line in cache_f:
186 items = line.rstrip().split(None, 1)
187 if len(items) == 1:
188 items.append('??')
189 self._symbol_mapping_caches[symbol_type][int(items[0], 16)] = items[1]
190 LOGGER.info('Loaded %d entries from symbol cache.' %
191 len(self._symbol_mapping_caches[symbol_type]))
192 except IOError as e:
193 LOGGER.info('The symbol cache file is invalid: %s' % e)