Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / tools / cygprofile / patch_orderfile.py
blob7906804406841c9c693d6ca560d89d823c30373a
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 cygprofile_utils
33 import symbol_extractor
35 # Prefixes for the symbols. We strip them from the incoming symbols, and add
36 # them back in the output file.
37 _PREFIXES = ('.text.startup.', '.text.hot.', '.text.unlikely.', '.text.')
40 def _RemoveClone(name):
41 """Return name up to the ".clone." marker."""
42 clone_index = name.find('.clone.')
43 if clone_index != -1:
44 return name[:clone_index]
45 return name
48 def _GroupSymbolInfos(symbol_infos):
49 """Group the symbol infos by name and offset.
51 Args:
52 symbol_infos: an iterable of SymbolInfo
54 Returns:
55 The same output as _GroupSymbolInfosFromBinary.
56 """
57 # Map the addresses to symbols.
58 offset_to_symbol_infos = collections.defaultdict(list)
59 name_to_symbol_infos = collections.defaultdict(list)
60 for symbol in symbol_infos:
61 symbol = symbol_extractor.SymbolInfo(name=_RemoveClone(symbol.name),
62 offset=symbol.offset,
63 size=symbol.size,
64 section=symbol.section)
65 offset_to_symbol_infos[symbol.offset].append(symbol)
66 name_to_symbol_infos[symbol.name].append(symbol)
67 return (dict(offset_to_symbol_infos), dict(name_to_symbol_infos))
70 def _GroupSymbolInfosFromBinary(binary_filename):
71 """Group all the symbols from a binary by name and offset.
73 Args:
74 binary_filename: path to the binary.
76 Returns:
77 A tuple of dict:
78 (offset_to_symbol_infos, name_to_symbol_infos):
79 - offset_to_symbol_infos: {offset: [symbol_info1, ...]}
80 - name_to_symbol_infos: {name: [symbol_info1, ...]}
81 """
82 symbol_infos = symbol_extractor.SymbolInfosFromBinary(binary_filename)
83 return _GroupSymbolInfos(symbol_infos)
86 def _StripPrefix(line):
87 """Get the symbol from a line with a linker section name.
89 Args:
90 line: a line from an orderfile, usually in the form:
91 .text.SymbolName
93 Returns:
94 The symbol, SymbolName in the example above.
95 """
96 line = line.rstrip('\n')
97 for prefix in _PREFIXES:
98 if line.startswith(prefix):
99 return line[len(prefix):]
100 return line # Unprefixed case
103 def _GetSymbolsFromStream(lines):
104 """Get the symbols from an iterable of lines.
105 Filters out wildcards and lines which do not correspond to symbols.
107 Args:
108 lines: iterable of lines from an orderfile.
110 Returns:
111 Same as GetSymbolsFromOrderfile
113 # TODO(lizeb): Retain the prefixes later in the processing stages.
114 symbols = []
115 unique_symbols = set()
116 for line in lines:
117 line = _StripPrefix(line)
118 name = _RemoveClone(line)
119 if name == '' or name == '*' or name == '.text':
120 continue
121 if not line in unique_symbols:
122 symbols.append(line)
123 unique_symbols.add(line)
124 return symbols
127 def GetSymbolsFromOrderfile(filename):
128 """Return the symbols from an orderfile.
130 Args:
131 filename: The name of the orderfile.
133 Returns:
134 A list of symbol names.
136 with open(filename, 'r') as f:
137 return _GetSymbolsFromStream(f.xreadlines())
139 def _SymbolsWithSameOffset(profiled_symbol, name_to_symbol_info,
140 offset_to_symbol_info):
141 """Expand a profiled symbol to include all symbols which share an offset
142 with that symbol.
143 Args:
144 profiled_symbol: the string symbol name to be expanded.
145 name_to_symbol_info: {name: [symbol_info1], ...}, as returned by
146 GetSymbolInfosFromBinary
147 offset_to_symbol_info: {offset: [symbol_info1, ...], ...}
149 Returns:
150 A list of symbol names, or an empty list if profiled_symbol was not in
151 name_to_symbol_info.
153 if not profiled_symbol in name_to_symbol_info:
154 return []
155 symbol_infos = name_to_symbol_info[profiled_symbol]
156 expanded = []
157 for symbol_info in symbol_infos:
158 expanded += (s.name for s in offset_to_symbol_info[symbol_info.offset])
159 return expanded
161 def _ExpandSymbols(profiled_symbols, name_to_symbol_infos,
162 offset_to_symbol_infos):
163 """Expand all of the symbols in profiled_symbols to include any symbols which
164 share the same address.
166 Args:
167 profiled_symbols: Symbols to match
168 name_to_symbol_infos: {name: [symbol_info1], ...}, as returned by
169 GetSymbolInfosFromBinary
170 offset_to_symbol_infos: {offset: [symbol_info1, ...], ...}
172 Returns:
173 A list of the symbol names.
175 found_symbols = 0
176 missing_symbols = []
177 all_symbols = []
178 for name in profiled_symbols:
179 expansion = _SymbolsWithSameOffset(name,
180 name_to_symbol_infos, offset_to_symbol_infos)
181 if expansion:
182 found_symbols += 1
183 all_symbols += expansion
184 else:
185 all_symbols.append(name)
186 missing_symbols.append(name)
187 logging.info('symbols found: %d\n' % found_symbols)
188 if missing_symbols > 0:
189 logging.warning('%d missing symbols.' % len(missing_symbols))
190 missing_symbols_to_show = min(100, len(missing_symbols))
191 logging.warning('First %d missing symbols:\n%s' % (
192 missing_symbols_to_show,
193 '\n'.join(missing_symbols[:missing_symbols_to_show])))
194 return all_symbols
197 def _PrintSymbolsWithPrefixes(symbol_names, output_file):
198 """For each symbol, outputs it to output_file with the prefixes."""
199 unique_outputs = set()
200 for name in symbol_names:
201 for prefix in _PREFIXES:
202 linker_section = prefix + name
203 if not linker_section in unique_outputs:
204 output_file.write(linker_section + '\n')
205 unique_outputs.add(linker_section)
208 def main(argv):
209 parser = optparse.OptionParser(usage=
210 'usage: %prog [options] <unpatched_orderfile> <library>')
211 parser.add_option('--target-arch', action='store', dest='arch',
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 not options.arch:
216 options.arch = cygprofile_utils.DetectArchitecture()
217 if len(argv) != 3:
218 parser.print_help()
219 return 1
220 orderfile_filename = argv[1]
221 binary_filename = argv[2]
222 symbol_extractor.SetArchitecture(options.arch)
223 (offset_to_symbol_infos, name_to_symbol_infos) = _GroupSymbolInfosFromBinary(
224 binary_filename)
225 profiled_symbols = GetSymbolsFromOrderfile(orderfile_filename)
226 expanded_symbols = _ExpandSymbols(
227 profiled_symbols, name_to_symbol_infos, offset_to_symbol_infos)
228 _PrintSymbolsWithPrefixes(expanded_symbols, sys.stdout)
229 # The following is needed otherwise Gold only applies a partial sort.
230 print '.text' # gets methods not in a section, such as assembly
231 print '.text.*' # gets everything else
232 return 0
235 if __name__ == '__main__':
236 logging.basicConfig(level=logging.INFO)
237 sys.exit(main(sys.argv))