| OLD | NEW |
| 1 # Copyright 2017 The Chromium Authors. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """Main Python API for analyzing binary size.""" | 5 """Main Python API for analyzing binary size.""" |
| 6 | 6 |
| 7 import argparse | 7 import argparse |
| 8 import calendar | 8 import calendar |
| 9 import collections | 9 import collections |
| 10 import datetime | 10 import datetime |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 85 """ | 85 """ |
| 86 found_prefixes = set() | 86 found_prefixes = set() |
| 87 for symbol in raw_symbols: | 87 for symbol in raw_symbols: |
| 88 if symbol.full_name.startswith('*'): | 88 if symbol.full_name.startswith('*'): |
| 89 # See comment in _CalculatePadding() about when this | 89 # See comment in _CalculatePadding() about when this |
| 90 # can happen. | 90 # can happen. |
| 91 symbol.template_name = symbol.full_name | 91 symbol.template_name = symbol.full_name |
| 92 symbol.name = symbol.full_name | 92 symbol.name = symbol.full_name |
| 93 continue | 93 continue |
| 94 | 94 |
| 95 # Remove [clone] suffix, and set flag accordingly. |
| 96 # Search from left-to-right, as multiple [clone]s can exist. |
| 97 # Example name suffixes: |
| 98 # [clone .part.322] # GCC |
| 99 # [clone .isra.322] # GCC |
| 100 # [clone .constprop.1064] # GCC |
| 101 # [clone .11064] # clang |
| 102 # http://unix.stackexchange.com/questions/223013/function-symbol-gets-part-s
uffix-after-compilation |
| 103 idx = symbol.full_name.find(' [clone ') |
| 104 if idx != -1: |
| 105 symbol.full_name = symbol.full_name[:idx] |
| 106 symbol.flags |= models.FLAG_CLONE |
| 107 |
| 95 # E.g.: vtable for FOO | 108 # E.g.: vtable for FOO |
| 96 idx = symbol.full_name.find(' for ', 0, 30) | 109 idx = symbol.full_name.find(' for ', 0, 30) |
| 97 if idx != -1: | 110 if idx != -1: |
| 98 found_prefixes.add(symbol.full_name[:idx + 4]) | 111 found_prefixes.add(symbol.full_name[:idx + 4]) |
| 99 symbol.full_name = ( | 112 symbol.full_name = ( |
| 100 symbol.full_name[idx + 5:] + ' [' + symbol.full_name[:idx] + ']') | 113 symbol.full_name[idx + 5:] + ' [' + symbol.full_name[:idx] + ']') |
| 101 | 114 |
| 102 # E.g.: virtual thunk to FOO | 115 # E.g.: virtual thunk to FOO |
| 103 idx = symbol.full_name.find(' to ', 0, 30) | 116 idx = symbol.full_name.find(' to ', 0, 30) |
| 104 if idx != -1: | 117 if idx != -1: |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 157 # We don't have source info for prebuilt .a files. | 170 # We don't have source info for prebuilt .a files. |
| 158 if not os.path.isabs(object_path) and not object_path.startswith('..'): | 171 if not os.path.isabs(object_path) and not object_path.startswith('..'): |
| 159 source_path = source_mapper.FindSourceForPath(object_path) | 172 source_path = source_mapper.FindSourceForPath(object_path) |
| 160 if source_path: | 173 if source_path: |
| 161 return _NormalizeSourcePath(source_path) | 174 return _NormalizeSourcePath(source_path) |
| 162 return False, '' | 175 return False, '' |
| 163 | 176 |
| 164 | 177 |
| 165 def _ExtractSourcePaths(raw_symbols, source_mapper): | 178 def _ExtractSourcePaths(raw_symbols, source_mapper): |
| 166 """Fills in the |source_path| attribute.""" | 179 """Fills in the |source_path| attribute.""" |
| 167 logging.debug('Parsed %d .ninja files.', source_mapper.parsed_file_count) | |
| 168 for symbol in raw_symbols: | 180 for symbol in raw_symbols: |
| 169 object_path = symbol.object_path | 181 object_path = symbol.object_path |
| 170 if object_path and not symbol.source_path: | 182 if object_path and not symbol.source_path: |
| 171 symbol.generated_source, symbol.source_path = ( | 183 symbol.generated_source, symbol.source_path = ( |
| 172 _SourcePathForObjectPath(object_path, source_mapper)) | 184 _SourcePathForObjectPath(object_path, source_mapper)) |
| 173 | 185 |
| 174 | 186 |
| 175 def _ComputeAnscestorPath(path_list): | 187 def _ComputeAnscestorPath(path_list): |
| 176 """Returns the common anscestor of the given paths.""" | 188 """Returns the common anscestor of the given paths.""" |
| 177 # Ignore missing paths. | 189 # Ignore missing paths. |
| (...skipping 240 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 418 output_directory: Build output directory. If None, source_paths and symbol | 430 output_directory: Build output directory. If None, source_paths and symbol |
| 419 alias information will not be recorded. | 431 alias information will not be recorded. |
| 420 """ | 432 """ |
| 421 source_mapper = None | 433 source_mapper = None |
| 422 if output_directory: | 434 if output_directory: |
| 423 # Start by finding the elf_object_paths, so that nm can run on them while | 435 # Start by finding the elf_object_paths, so that nm can run on them while |
| 424 # the linker .map is being parsed. | 436 # the linker .map is being parsed. |
| 425 logging.info('Parsing ninja files.') | 437 logging.info('Parsing ninja files.') |
| 426 source_mapper, elf_object_paths = ninja_parser.Parse( | 438 source_mapper, elf_object_paths = ninja_parser.Parse( |
| 427 output_directory, elf_path) | 439 output_directory, elf_path) |
| 440 logging.debug('Parsed %d .ninja files.', source_mapper.parsed_file_count) |
| 428 assert not elf_path or elf_object_paths, ( | 441 assert not elf_path or elf_object_paths, ( |
| 429 'Failed to find link command in ninja files for ' + | 442 'Failed to find link command in ninja files for ' + |
| 430 os.path.relpath(elf_path, output_directory)) | 443 os.path.relpath(elf_path, output_directory)) |
| 431 | 444 |
| 432 if elf_path: | 445 if elf_path: |
| 433 # Run nm on the elf file to retrieve the list of symbol names per-address. | 446 # Run nm on the elf file to retrieve the list of symbol names per-address. |
| 434 # This list is required because the .map file contains only a single name | 447 # This list is required because the .map file contains only a single name |
| 435 # for each address, yet multiple symbols are often coalesced when they are | 448 # for each address, yet multiple symbols are often coalesced when they are |
| 436 # identical. This coalescing happens mainly for small symbols and for C++ | 449 # identical. This coalescing happens mainly for small symbols and for C++ |
| 437 # templates. Such symbols make up ~500kb of libchrome.so on Android. | 450 # templates. Such symbols make up ~500kb of libchrome.so on Android. |
| (...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 688 logging.warning('Packed section not present: %s', packed_section_name) | 701 logging.warning('Packed section not present: %s', packed_section_name) |
| 689 else: | 702 else: |
| 690 size_info.section_sizes['%s (unpacked)' % packed_section_name] = ( | 703 size_info.section_sizes['%s (unpacked)' % packed_section_name] = ( |
| 691 unstripped_section_sizes.get(packed_section_name)) | 704 unstripped_section_sizes.get(packed_section_name)) |
| 692 | 705 |
| 693 logging.info('Recording metadata: \n %s', | 706 logging.info('Recording metadata: \n %s', |
| 694 '\n '.join(describe.DescribeMetadata(size_info.metadata))) | 707 '\n '.join(describe.DescribeMetadata(size_info.metadata))) |
| 695 logging.info('Saving result to %s', args.size_file) | 708 logging.info('Saving result to %s', args.size_file) |
| 696 file_format.SaveSizeInfo(size_info, args.size_file) | 709 file_format.SaveSizeInfo(size_info, args.size_file) |
| 697 logging.info('Done') | 710 logging.info('Done') |
| OLD | NEW |