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