[Session restore] Rename group name Enabled to Restore.
[chromium-blink-merge.git] / tools / cygprofile / symbol_extractor.py
blobdbd625b2979e9cdb0b7f6f8a272f112e544d291b
1 #!/usr/bin/python
2 # Copyright 2015 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 """Utilities to get and manipulate symbols from a binary."""
8 import collections
9 import logging
10 import os
11 import re
12 import subprocess
13 import sys
15 sys.path.insert(
16 0, os.path.join(os.path.dirname(__file__), os.pardir, os.pardir,
17 'third_party', 'android_platform', 'development',
18 'scripts'))
19 import symbol
21 _MAX_WARNINGS_TO_PRINT = 200
23 SymbolInfo = collections.namedtuple('SymbolInfo', ('name', 'offset', 'size',
24 'section'))
26 def SetArchitecture(arch):
27 """Set the architecture for binaries to be symbolized."""
28 symbol.ARCH = arch
31 def _FromObjdumpLine(line):
32 """Create a SymbolInfo by parsing a properly formatted objdump output line.
34 Args:
35 line: line from objdump
37 Returns:
38 An instance of SymbolInfo if the line represents a symbol, None otherwise.
39 """
40 # All of the symbol lines we care about are in the form
41 # 0000000000 g F .text.foo 000000000 [.hidden] foo
42 # where g (global) might also be l (local) or w (weak).
43 parts = line.split()
44 if len(parts) < 6 or parts[2] != 'F':
45 return None
47 assert len(parts) == 6 or (len(parts) == 7 and parts[5] == '.hidden')
48 accepted_scopes = set(['g', 'l', 'w'])
49 assert parts[1] in accepted_scopes
51 offset = int(parts[0], 16)
52 section = parts[3]
53 size = int(parts[4], 16)
54 name = parts[-1].rstrip('\n')
55 assert re.match('^[a-zA-Z0-9_.]+$', name)
56 return SymbolInfo(name=name, offset=offset, section=section, size=size)
59 def _SymbolInfosFromStream(objdump_lines):
60 """Parses the output of objdump, and get all the symbols from a binary.
62 Args:
63 objdump_lines: An iterable of lines
65 Returns:
66 A list of SymbolInfo.
67 """
68 symbol_infos = []
69 for line in objdump_lines:
70 symbol_info = _FromObjdumpLine(line)
71 if symbol_info is not None:
72 symbol_infos.append(symbol_info)
73 return symbol_infos
76 def SymbolInfosFromBinary(binary_filename):
77 """Runs objdump to get all the symbols from a binary.
79 Args:
80 binary_filename: path to the binary.
82 Returns:
83 A list of SymbolInfo from the binary.
84 """
85 command = (symbol.ToolPath('objdump'), '-t', '-w', binary_filename)
86 p = subprocess.Popen(command, shell=False, stdout=subprocess.PIPE)
87 try:
88 result = _SymbolInfosFromStream(p.stdout)
89 return result
90 finally:
91 p.wait()
94 def GroupSymbolInfosByOffset(symbol_infos):
95 """Create a dict {offset: [symbol_info1, ...], ...}.
97 As several symbols can be at the same offset, this is a 1-to-many
98 relationship.
100 Args:
101 symbol_infos: iterable of SymbolInfo instances
103 Returns:
104 a dict {offset: [symbol_info1, ...], ...}
106 offset_to_symbol_infos = collections.defaultdict(list)
107 for symbol_info in symbol_infos:
108 offset_to_symbol_infos[symbol_info.offset].append(symbol_info)
109 return dict(offset_to_symbol_infos)
111 def GroupSymbolInfosByName(symbol_infos):
112 """Create a dict {name: [symbol_info1, ...], ...}.
114 A symbol can have several offsets, this is a 1-to-many relationship.
116 Args:
117 symbol_infos: iterable of SymbolInfo instances
119 Returns:
120 a dict {name: [symbol_info1, ...], ...}
122 name_to_symbol_infos = collections.defaultdict(list)
123 for symbol_info in symbol_infos:
124 name_to_symbol_infos[symbol_info.name].append(symbol_info)
125 return dict(name_to_symbol_infos)
127 def CreateNameToSymbolInfo(symbol_infos):
128 """Create a dict {name: symbol_info, ...}.
130 Args:
131 symbol_infos: iterable of SymbolInfo instances
133 Returns:
134 a dict {name: symbol_info, ...}
135 If a symbol name corresponds to more than one symbol_info, the symbol_info
136 with the lowest offset is chosen.
138 #TODO(azarchs): move the functionality in this method into check_orderfile.
139 symbol_infos_by_name = {}
140 collision_count = 0
141 for infos in GroupSymbolInfosByName(symbol_infos).itervalues():
142 first_symbol_info = min(infos, key=lambda x:x.offset)
143 symbol_infos_by_name[first_symbol_info.name] = first_symbol_info
144 if len(infos) > 1:
145 collision_count += 1
146 if collision_count <= _MAX_WARNINGS_TO_PRINT:
147 logging.warning('Symbol %s appears at %d offsets: %s' %
148 (first_symbol_info.name,
149 len(infos),
150 ','.join([hex(x.offset) for x in infos])))
151 if collision_count > _MAX_WARNINGS_TO_PRINT:
152 logging.warning('%d symbols at multiple offsets. First %d shown.' %
153 (collision_count, _MAX_WARNINGS_TO_PRINT))
154 return symbol_infos_by_name