| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2017 The Chromium Authors. All rights reserved. | 2 # Copyright 2017 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Main Python API for analyzing binary size.""" | 6 """Main Python API for analyzing binary size.""" |
| 7 | 7 |
| 8 import argparse | 8 import argparse |
| 9 import datetime | 9 import datetime |
| 10 import distutils.spawn | 10 import distutils.spawn |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 146 logging.warning('Could not find source path for %s', object_path) | 146 logging.warning('Could not find source path for %s', object_path) |
| 147 logging.debug('Parsed %d .ninja files.', mapper.GetParsedFileCount()) | 147 logging.debug('Parsed %d .ninja files.', mapper.GetParsedFileCount()) |
| 148 | 148 |
| 149 | 149 |
| 150 def _RemoveDuplicatesAndCalculatePadding(symbol_group): | 150 def _RemoveDuplicatesAndCalculatePadding(symbol_group): |
| 151 """Removes symbols at the same address and calculates the |padding| field. | 151 """Removes symbols at the same address and calculates the |padding| field. |
| 152 | 152 |
| 153 Symbols must already be sorted by |address|. | 153 Symbols must already be sorted by |address|. |
| 154 """ | 154 """ |
| 155 to_remove = [] | 155 to_remove = [] |
| 156 seen_sections = [] |
| 156 for i, symbol in enumerate(symbol_group[1:]): | 157 for i, symbol in enumerate(symbol_group[1:]): |
| 157 prev_symbol = symbol_group[i] | 158 prev_symbol = symbol_group[i] |
| 158 if prev_symbol.section_name != symbol.section_name: | 159 if prev_symbol.section_name != symbol.section_name: |
| 160 assert symbol.section_name not in seen_sections, ( |
| 161 'Input symbols must be sorted by section, then address.') |
| 162 seen_sections.append(symbol.section_name) |
| 159 continue | 163 continue |
| 160 if symbol.address > 0 and prev_symbol.address > 0: | 164 if symbol.address <= 0 or prev_symbol.address <= 0: |
| 161 # Fold symbols that are at the same address (happens in nm output). | 165 continue |
| 162 if symbol.address == prev_symbol.address: | 166 # Fold symbols that are at the same address (happens in nm output). |
| 163 symbol.size = max(prev_symbol.size, symbol.size) | 167 prev_is_padding_only = prev_symbol.size_without_padding == 0 |
| 164 to_remove.add(symbol) | 168 if symbol.address == prev_symbol.address and not prev_is_padding_only: |
| 165 continue | 169 symbol.size = max(prev_symbol.size, symbol.size) |
| 166 # Even with symbols at the same address removed, overlaps can still | 170 to_remove.add(symbol) |
| 167 # happen. In this case, padding will be negative (and this is fine). | 171 continue |
| 168 padding = symbol.address - prev_symbol.end_address | 172 # Even with symbols at the same address removed, overlaps can still |
| 169 # These thresholds were found by manually auditing arm32 Chrome. | 173 # happen. In this case, padding will be negative (and this is fine). |
| 170 # E.g.: Set them to 0 and see what warnings get logged. | 174 padding = symbol.address - prev_symbol.end_address |
| 171 # TODO(agrieve): See if these thresholds make sense for architectures | 175 # These thresholds were found by manually auditing arm32 Chrome. |
| 172 # other than arm32. | 176 # E.g.: Set them to 0 and see what warnings get logged. |
| 173 if (symbol.section in 'rd' and padding >= 256 or | 177 # TODO(agrieve): See if these thresholds make sense for architectures |
| 174 symbol.section in 't' and padding >= 64): | 178 # other than arm32. |
| 175 # For nm data, this is caused by data that has no associated symbol. | 179 if not symbol.name.startswith('*') and ( |
| 176 # The linker map file lists them with no name, but with a file. | 180 symbol.section in 'rd' and padding >= 256 or |
| 177 # Example: | 181 symbol.section in 't' and padding >= 64): |
| 178 # .data 0x02d42764 0x120 .../V8SharedWorkerGlobalScope.o | 182 # For nm data, this is caused by data that has no associated symbol. |
| 179 # Where as most look like: | 183 # The linker map file lists them with no name, but with a file. |
| 180 # .data.MANGLED_NAME... | 184 # Example: |
| 181 logging.debug('Large padding of %d between:\n A) %r\n B) %r' % ( | 185 # .data 0x02d42764 0x120 .../V8SharedWorkerGlobalScope.o |
| 182 padding, prev_symbol, symbol)) | 186 # Where as most look like: |
| 183 continue | 187 # .data.MANGLED_NAME... |
| 184 symbol.padding = padding | 188 logging.debug('Large padding of %d between:\n A) %r\n B) %r' % ( |
| 185 symbol.size += padding | 189 padding, prev_symbol, symbol)) |
| 186 assert symbol.size >= 0, 'Symbol has negative size: ' + ( | 190 continue |
| 187 '%r\nprev symbol: %r' % (symbol, prev_symbol)) | 191 symbol.padding = padding |
| 192 symbol.size += padding |
| 193 assert symbol.size >= 0, ( |
| 194 'Symbol has negative size (likely not sorted propertly): ' |
| 195 '%r\nprev symbol: %r' % (symbol, prev_symbol)) |
| 188 # Map files have no overlaps, so worth special-casing the no-op case. | 196 # Map files have no overlaps, so worth special-casing the no-op case. |
| 189 if to_remove: | 197 if to_remove: |
| 190 logging.info('Removing %d overlapping symbols', len(to_remove)) | 198 logging.info('Removing %d overlapping symbols', len(to_remove)) |
| 191 symbol_group -= models.SymbolGroup(to_remove) | 199 symbol_group -= models.SymbolGroup(to_remove) |
| 192 | 200 |
| 193 | 201 |
| 194 def AddOptions(parser): | 202 def AddOptions(parser): |
| 195 parser.add_argument('--tool-prefix', default='', | 203 parser.add_argument('--tool-prefix', default='', |
| 196 help='Path prefix for c++filt.') | 204 help='Path prefix for c++filt.') |
| 197 parser.add_argument('--output-directory', | 205 parser.add_argument('--output-directory', |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 308 logging.info('Recording metadata: %s', | 316 logging.info('Recording metadata: %s', |
| 309 describe.DescribeSizeInfoMetadata(size_info)) | 317 describe.DescribeSizeInfoMetadata(size_info)) |
| 310 logging.info('Saving result to %s', args.output_file) | 318 logging.info('Saving result to %s', args.output_file) |
| 311 file_format.SaveSizeInfo(size_info, args.output_file) | 319 file_format.SaveSizeInfo(size_info, args.output_file) |
| 312 | 320 |
| 313 logging.info('Done') | 321 logging.info('Done') |
| 314 | 322 |
| 315 | 323 |
| 316 if __name__ == '__main__': | 324 if __name__ == '__main__': |
| 317 sys.exit(main(sys.argv)) | 325 sys.exit(main(sys.argv)) |
| OLD | NEW |