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.
9 _BASE_PATH
= os
.path
.dirname(os
.path
.dirname(os
.path
.abspath(__file__
)))
10 _FIND_RUNTIME_SYMBOLS_PATH
= os
.path
.join(_BASE_PATH
,
12 'find_runtime_symbols')
13 _TOOLS_LINUX_PATH
= os
.path
.join(_BASE_PATH
,
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
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.
45 def __init__(self
, prefix
, alternative_dirs
=None):
47 self
._prepared
_symbol
_data
_sources
_path
= None
48 self
._loaded
_symbol
_data
_sources
= None
49 self
._alternative
_dirs
= alternative_dirs
or {}
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|.
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
,
67 use_source_file_name
=True))
68 if self
._prepared
_symbol
_data
_sources
_path
:
69 LOGGER
.info(' Prepared symbol mapping.')
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.')
76 LOGGER
.warn(' Failed to prepare symbol mapping.')
80 """Returns the prepared symbol data sources.
83 The prepared symbol data sources. None if failed.
85 if not self
._prepared
_symbol
_data
_sources
_path
and not self
.prepare():
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
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():
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()'.
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:
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.')
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|.
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.
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
):
186 items
= line
.rstrip().split(None, 1)
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
]))
193 LOGGER
.info('The symbol cache file is invalid: %s' % e
)