[Session restore] Rename group name Enabled to Restore.
[chromium-blink-merge.git] / tools / cygprofile / patch_orderfile.py
blob88abc62c889c79ab7f0558de03b2779e0ab02604
1 #!/usr/bin/python
2 # Copyright 2013 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 """Patch an orderfile.
8 Starting with a list of symbols in a binary and an orderfile (ordered list of
9 symbols), matches the symbols in the orderfile and augments each symbol with the
10 symbols residing at the same address (due to having identical code).
12 Note: It is possible to have.
13 - Several symbols mapping to the same offset in the binary.
14 - Several offsets for a given symbol (because we strip the ".clone." suffix)
16 TODO(lizeb): Since the suffix ".clone." is only used with -O3 that we don't
17 currently use, simplify the logic by removing the suffix handling.
19 The general pipeline is:
20 1. Get the symbol infos (name, offset, size, section) from the binary
21 2. Get the symbol names from the orderfile
22 3. Find the orderfile symbol names in the symbols coming from the binary
23 4. For each symbol found, get all the symbols at the same address
24 5. Output them to an updated orderfile, with several different prefixes
25 """
27 import collections
28 import logging
29 import optparse
30 import sys
32 import symbol_extractor
34 # Prefixes for the symbols. We strip them from the incoming symbols, and add
35 # them back in the output file.
36 _PREFIXES = ('.text.startup.', '.text.hot.', '.text.unlikely.', '.text.')
39 def _RemoveClone(name):
40 """Return name up to the ".clone." marker."""
41 clone_index = name.find('.clone.')
42 if clone_index != -1:
43 return name[:clone_index]
44 return name
47 def _GroupSymbolInfos(symbol_infos):
48 """Group the symbol infos by name and offset.
50 Args:
51 symbol_infos: an iterable of SymbolInfo
53 Returns:
54 The same output as _GroupSymbolInfosFromBinary.
55 """
56 # Map the addresses to symbols.
57 offset_to_symbol_infos = collections.defaultdict(list)
58 name_to_symbol_infos = collections.defaultdict(list)
59 for symbol in symbol_infos:
60 symbol = symbol_extractor.SymbolInfo(name=_RemoveClone(symbol.name),
61 offset=symbol.offset,
62 size=symbol.size,
63 section=symbol.section)
64 offset_to_symbol_infos[symbol.offset].append(symbol)
65 name_to_symbol_infos[symbol.name].append(symbol)
66 return (dict(offset_to_symbol_infos), dict(name_to_symbol_infos))
69 def _GroupSymbolInfosFromBinary(binary_filename):
70 """Group all the symbols from a binary by name and offset.
72 Args:
73 binary_filename: path to the binary.
75 Returns:
76 A tuple of dict:
77 (offset_to_symbol_infos, name_to_symbol_infos):
78 - offset_to_symbol_infos: {offset: [symbol_info1, ...]}
79 - name_to_symbol_infos: {name: [symbol_info1, ...]}
80 """
81 symbol_infos = symbol_extractor.SymbolInfosFromBinary(binary_filename)
82 return _GroupSymbolInfos(symbol_infos)
85 def _StripPrefix(line):
86 """Get the symbol from a line with a linker section name.
88 Args:
89 line: a line from an orderfile, usually in the form:
90 .text.SymbolName
92 Returns:
93 The symbol, SymbolName in the example above.
94 """
95 line = line.rstrip('\n')
96 for prefix in _PREFIXES:
97 if line.startswith(prefix):
98 return line[len(prefix):]
99 return line # Unprefixed case
102 def _GetSymbolsFromStream(lines):
103 """Get the symbols from an iterable of lines.
104 Filters out wildcards and lines which do not correspond to symbols.
106 Args:
107 lines: iterable of lines from an orderfile.
109 Returns:
110 Same as GetSymbolsFromOrderfile
112 # TODO(lizeb): Retain the prefixes later in the processing stages.
113 symbols = []
114 unique_symbols = set()
115 for line in lines:
116 line = _StripPrefix(line)
117 name = _RemoveClone(line)
118 if name == '' or name == '*' or name == '.text':
119 continue
120 if not line in unique_symbols:
121 symbols.append(line)
122 unique_symbols.add(line)
123 return symbols
126 def GetSymbolsFromOrderfile(filename):
127 """Return the symbols from an orderfile.
129 Args:
130 filename: The name of the orderfile.
132 Returns:
133 A list of symbol names.
135 with open(filename, 'r') as f:
136 return _GetSymbolsFromStream(f.xreadlines())
138 def _SymbolsWithSameOffset(profiled_symbol, name_to_symbol_info,
139 offset_to_symbol_info):
140 """Expand a profiled symbol to include all symbols which share an offset
141 with that symbol.
142 Args:
143 profiled_symbol: the string symbol name to be expanded.
144 name_to_symbol_info: {name: [symbol_info1], ...}, as returned by
145 GetSymbolInfosFromBinary
146 offset_to_symbol_info: {offset: [symbol_info1, ...], ...}
148 Returns:
149 A list of symbol names, or an empty list if profiled_symbol was not in
150 name_to_symbol_info.
152 if not profiled_symbol in name_to_symbol_info:
153 return []
154 symbol_infos = name_to_symbol_info[profiled_symbol]
155 expanded = []
156 for symbol_info in symbol_infos:
157 expanded += (s.name for s in offset_to_symbol_info[symbol_info.offset])
158 return expanded
160 def _ExpandSymbols(profiled_symbols, name_to_symbol_infos,
161 offset_to_symbol_infos):
162 """Expand all of the symbols in profiled_symbols to include any symbols which
163 share the same address.
165 Args:
166 profiled_symbols: Symbols to match
167 name_to_symbol_infos: {name: [symbol_info1], ...}, as returned by
168 GetSymbolInfosFromBinary
169 offset_to_symbol_infos: {offset: [symbol_info1, ...], ...}
171 Returns:
172 A list of the symbol names.
174 found_symbols = 0
175 missing_symbols = []
176 all_symbols = []
177 for name in profiled_symbols:
178 expansion = _SymbolsWithSameOffset(name,
179 name_to_symbol_infos, offset_to_symbol_infos)
180 if expansion:
181 found_symbols += 1
182 all_symbols += expansion
183 else:
184 all_symbols.append(name)
185 missing_symbols.append(name)
186 logging.info('symbols found: %d\n' % found_symbols)
187 if missing_symbols > 0:
188 logging.warning('%d missing symbols.' % len(missing_symbols))
189 missing_symbols_to_show = min(100, len(missing_symbols))
190 logging.warning('First %d missing symbols:\n%s' % (
191 missing_symbols_to_show,
192 '\n'.join(missing_symbols[:missing_symbols_to_show])))
193 return all_symbols
196 def _PrintSymbolsWithPrefixes(symbol_names, output_file):
197 """For each symbol, outputs it to output_file with the prefixes."""
198 unique_outputs = set()
199 for name in symbol_names:
200 for prefix in _PREFIXES:
201 linker_section = prefix + name
202 if not linker_section in unique_outputs:
203 output_file.write(linker_section + '\n')
204 unique_outputs.add(linker_section)
207 def main(argv):
208 parser = optparse.OptionParser(usage=
209 'usage: %prog [options] <unpatched_orderfile> <library>')
210 parser.add_option('--target-arch', action='store', dest='arch',
211 default='arm',
212 choices=['arm', 'arm64', 'x86', 'x86_64', 'x64', 'mips'],
213 help='The target architecture for the library.')
214 options, argv = parser.parse_args(argv)
215 if len(argv) != 3:
216 parser.print_help()
217 return 1
218 orderfile_filename = argv[1]
219 binary_filename = argv[2]
220 symbol_extractor.SetArchitecture(options.arch)
221 (offset_to_symbol_infos, name_to_symbol_infos) = _GroupSymbolInfosFromBinary(
222 binary_filename)
223 profiled_symbols = GetSymbolsFromOrderfile(orderfile_filename)
224 expanded_symbols = _ExpandSymbols(
225 profiled_symbols, name_to_symbol_infos, offset_to_symbol_infos)
226 _PrintSymbolsWithPrefixes(expanded_symbols, sys.stdout)
227 # The following is needed otherwise Gold only applies a partial sort.
228 print '.text' # gets methods not in a section, such as assembly
229 print '.text.*' # gets everything else
230 return 0
233 if __name__ == '__main__':
234 logging.basicConfig(level=logging.INFO)
235 sys.exit(main(sys.argv))