Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(234)

Side by Side Diff: tools/binary_size/parsers.py

Issue 2724253002: V1 of //tools/binary_size rewrite (Closed)
Patch Set: README tweaks, more cases for function parsing Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright 2017 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
7 import symbols
8
9
10 class ParseResult(object):
11 """Return value for Parse() methods."""
12 def __init__(self, symbol_list, section_sizes=None):
13 self.symbol_group = symbols.SymbolGroup(symbol_list)
14 self.section_sizes = section_sizes # E.g. {'.text': 0}
15
16
17 class MapFileParser(object):
18 """Parses a linker map file (tested only on files from gold linker)."""
19 # Map file writer for gold linker:
20 # https://github.com/gittup/binutils/blob/HEAD/gold/mapfile.cc
21
22 def __init__(self):
23 self._symbols = []
24 self._section_sizes = {}
25 self._lines = None
26
27 def Parse(self, lines):
28 """Parses a linker map file.
29
30 Args:
31 lines: Iterable of lines.
32
33 Returns:
34 A ParseResult object.
35 """
36 self._lines = iter(lines)
37 logging.info('Parsing common symbols')
38 self._ParseCommonSymbols()
39 logging.debug('.bss common entries: %d', len(self._symbols))
40 logging.info('Parsing section symbols')
41 self._ParseSections()
42 return ParseResult(self._symbols, self._section_sizes)
43
44 def _SkipToLineWithPrefix(self, prefix):
45 for l in self._lines:
46 if l.startswith(prefix):
47 return l
48
49 def _ParsePossiblyWrappedParts(self, line, count):
50 parts = line.split(None, count - 1)
51 if not parts:
52 return None
53 if len(parts) == count:
estevenson 2017/03/20 14:13:03 nit: you could change this to: if len(parts) != co
agrieve 2017/03/20 19:58:09 Done.
54 parts[-1] = parts[-1].rstrip()
55 return parts
56
57 line = next(self._lines)
58 parts.extend(line.split(None, count - len(parts) - 1))
59 assert len(parts) == count, 'parts: ' + ' '.join(parts)
60 parts[-1] = parts[-1].rstrip()
61 return parts
62
63 def _ParseCommonSymbols(self):
64 # Common symbol size file
65 #
66 # ff_cos_131072 0x40000 obj/third_party/<snip>
67 # ff_cos_131072_fixed
68 # 0x20000 obj/third_party/<snip>
69 self._SkipToLineWithPrefix('Common symbol')
70 next(self._lines) # Skip past blank line
71
72 name, size_str, path = None, None, None
73 for l in self._lines:
74 parts = self._ParsePossiblyWrappedParts(l, 3)
75 if not parts:
76 break
77 name, size_str, path = parts
78 self._symbols.append(
79 symbols.Symbol('.bss', 0, int(size_str[2:], 16), name, path))
80
81 def _ParseSections(self):
82 # .text 0x0028c600 0x22d3468
83 # .text.startup._GLOBAL__sub_I_bbr_sender.cc
84 # 0x0028c600 0x38 obj/net/net/bbr_sender.o
85 # .text._reset 0x00339d00 0xf0 obj/third_party/icu/icuuc/ucnv.o
86 # ** fill 0x0255fb00 0x02
87 # .text._ZN4base8AutoLockD2Ev
88 # 0x00290710 0xe obj/net/net/file_name.o
89 # 0x00290711 base::AutoLock::~AutoLock()
90 # 0x00290711 base::AutoLock::~AutoLock()
91 # .text._ZNK5blink15LayoutBlockFlow31mustSeparateMarginAfterForChildERK...
92 # 0xffffffffffffffff 0x46 obj/...
93 # 0x006808e1 blink::LayoutBlockFlow::...
94 # .bss
95 # .bss._ZGVZN11GrProcessor11initClassIDI10LightingFPEEvvE8kClassID
96 # 0x02d4b294 0x4 obj/skia/skia/SkLightingShader.o
97 # 0x02d4b294 guard variable for void GrProcessor::initClassID
98 # .data 0x0028c600 0x22d3468
99 # .data.rel.ro._ZTVN3gvr7android19ScopedJavaGlobalRefIP12_jfloatArrayEE
100 # 0x02d1e668 0x10 ../../third_party/.../libfoo.a(bar.o)
101 # 0x02d1e668 vtable for gvr::android::GlobalRef<_jfloatArray*>
102 # ** merge strings
103 # 0x0255fb00 0x1f2424
104 # ** merge constants
105 # 0x0255fb00 0x8
106 # ** common 0x02db5700 0x13ab48
107 self._SkipToLineWithPrefix('Memory map')
108 syms = self._symbols
109 while True:
110 line = self._SkipToLineWithPrefix('.')
111 if not line:
112 break
113 section_name = None
114 try:
115 # Parse section name and size.
116 parts = self._ParsePossiblyWrappedParts(line, 3)
117 if not parts:
118 break
119 section_name, address, size_str = parts
120 self._section_sizes[section_name] = int(size_str[2:], 16)
121 if (section_name in ('.bss', '.rodata', '.text') or
122 section_name.startswith('.data')):
123 logging.info('Parsing %s', section_name)
124 prefix_len = len(section_name) + 1 # + 1 for the trailing .
125 merge_symbol_start_address = 0
126 sym_count_at_start = len(syms)
127 line = next(self._lines)
128 # Parse section symbols.
129 while True:
130 if not line or line.isspace():
131 break
132 if line.startswith(' **'):
133 zero_index = line.find('0')
134 if zero_index == -1:
135 # Line wraps.
136 name = line.strip()
137 line = next(self._lines)
138 else:
139 # Line does not wrap.
140 name = line[:zero_index].strip()
141 line = line[zero_index:]
142 address_str, size_str = self._ParsePossiblyWrappedParts(line, 2)
143 line = next(self._lines)
144 # These bytes are already accounted for.
145 if name == '** common':
146 continue
147 address = int(address_str[2:], 16)
148 size = int(size_str[2:], 16)
149 path = None
150 syms.append(
151 symbols.Symbol(section_name, address, size, name, path))
152 else:
153 # A normal symbol entry.
154 subsection_name, address_str, size_str, path = (
155 self._ParsePossiblyWrappedParts(line, 4))
156 size = int(size_str[2:], 16)
157 if address_str == '0xffffffffffffffff':
158 # The section needs special handling (e.g., a merge section)
159 # It also generally has a large offset after it, so don't
160 # penalize the subsequent symbol for this gap (e.g. a 50kb gap).
161 # TODO(agrieve): Learn more about why this happens.
162 address = -1
163 if syms and syms[-1].address > 0:
164 merge_symbol_start_address = syms[-1].end_address
165 merge_symbol_start_address += size
166 else:
167 address = int(address_str[2:], 16)
168 # Finish off active address gap / merge section.
169 if merge_symbol_start_address:
170 merge_size = address - merge_symbol_start_address
171 sym = symbols.Symbol(
172 section_name, merge_symbol_start_address, merge_size,
173 '** merged symbol: %s' % syms[-1].name, syms[-1].path)
174 logging.debug('Merge symbol of size %d found at:\n %r',
175 merge_size, syms[-1])
176 syms.append(sym)
177 merge_symbol_start_address = 0
178 assert subsection_name.startswith(section_name), (
179 'subsection name was: ' + subsection_name)
180 mangled_name = subsection_name[prefix_len:]
181 name = None
182 while True:
183 line = next(self._lines).rstrip()
184 if not line or line.startswith(' .'):
185 break
186 # clang includes ** fill, but gcc does not.
187 if line.startswith(' ** fill'):
188 # Alignment explicitly recorded in map file. Rather than
189 # record padding based on these entries, we calculate it
190 # using addresses. We do this because fill lines are not
191 # present when compiling with gcc (only for clang).
192 continue
193 elif line.startswith(' **'):
194 break
195 elif name is None:
196 address_str2, name = self._ParsePossiblyWrappedParts(line, 2)
197 if address == -1:
198 address = int(address_str2[2:], 16) - 1
199
200 # Merge sym with no second line showing real address.
201 if address == -1:
202 address = syms[-1].end_address
203 syms.append(symbols.Symbol(section_name, address, size,
204 name or mangled_name, path))
205 logging.debug('Symbol count for %s: %d', section_name,
206 len(syms) - sym_count_at_start)
207 except:
208 logging.error('Problem line: %r', line)
209 logging.error('In section: %r', section_name)
210 raise
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698