| 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 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 88 symbol.name = non_anonymous | 88 symbol.name = non_anonymous |
| 89 symbol.full_name = symbol.full_name.replace( | 89 symbol.full_name = symbol.full_name.replace( |
| 90 '(anonymous namespace)::', '') | 90 '(anonymous namespace)::', '') |
| 91 | 91 |
| 92 if symbol.section != 't' and '(' in symbol.name: | 92 if symbol.section != 't' and '(' in symbol.name: |
| 93 # Pretty rare. Example: | 93 # Pretty rare. Example: |
| 94 # blink::CSSValueKeywordsHash::findValueImpl(char const*)::value_word_list | 94 # blink::CSSValueKeywordsHash::findValueImpl(char const*)::value_word_list |
| 95 symbol.full_name = symbol.name | 95 symbol.full_name = symbol.name |
| 96 symbol.name = re.sub(r'\(.*\)', '', symbol.full_name) | 96 symbol.name = re.sub(r'\(.*\)', '', symbol.full_name) |
| 97 | 97 |
| 98 # Don't bother storing both if they are the same. |
| 99 if symbol.full_name == symbol.name: |
| 100 symbol.full_name = '' |
| 101 |
| 98 logging.debug('Found name prefixes of: %r', found_prefixes) | 102 logging.debug('Found name prefixes of: %r', found_prefixes) |
| 99 | 103 |
| 100 | 104 |
| 101 def _NormalizeObjectPaths(symbol_group): | 105 def _NormalizeObjectPaths(symbol_group): |
| 102 """Ensures that all paths are formatted in a useful way.""" | 106 """Ensures that all paths are formatted in a useful way.""" |
| 103 for symbol in symbol_group: | 107 for symbol in symbol_group: |
| 104 path = symbol.object_path | 108 path = symbol.object_path |
| 105 if path.startswith('obj/'): | 109 if path.startswith('obj/'): |
| 106 # Convert obj/third_party/... -> third_party/... | 110 # Convert obj/third_party/... -> third_party/... |
| 107 path = path[4:] | 111 path = path[4:] |
| 108 elif path.startswith('../../'): | 112 elif path.startswith('../../'): |
| 109 # Convert ../../third_party/... -> third_party/... | 113 # Convert ../../third_party/... -> third_party/... |
| 110 path = path[6:] | 114 path = path[6:] |
| 111 if path.endswith(')'): | 115 if path.endswith(')'): |
| 112 # Convert foo/bar.a(baz.o) -> foo/bar.a/(baz.o) | 116 # Convert foo/bar.a(baz.o) -> foo/bar.a/baz.o |
| 113 start_idx = path.index('(') | 117 start_idx = path.index('(') |
| 114 path = os.path.join(path[:start_idx], path[start_idx:]) | 118 path = os.path.join(path[:start_idx], path[start_idx + 1:-1]) |
| 115 symbol.object_path = path | 119 symbol.object_path = path |
| 116 | 120 |
| 117 | 121 |
| 118 def _NormalizeSourcePath(path): | 122 def _NormalizeSourcePath(path): |
| 119 if path.startswith('gen/'): | 123 if path.startswith('gen/'): |
| 120 # Convert gen/third_party/... -> third_party/... | 124 # Convert gen/third_party/... -> third_party/... |
| 121 return path[4:] | 125 return path[4:] |
| 122 if path.startswith('../../'): | 126 if path.startswith('../../'): |
| 123 # Convert ../../third_party/... -> third_party/... | 127 # Convert ../../third_party/... -> third_party/... |
| 124 return path[6:] | 128 return path[6:] |
| (...skipping 16 matching lines...) Expand all Loading... |
| 141 else: | 145 else: |
| 142 logging.warning('Could not find source path for %s', object_path) | 146 logging.warning('Could not find source path for %s', object_path) |
| 143 logging.debug('Parsed %d .ninja files.', mapper.GetParsedFileCount()) | 147 logging.debug('Parsed %d .ninja files.', mapper.GetParsedFileCount()) |
| 144 | 148 |
| 145 | 149 |
| 146 def _RemoveDuplicatesAndCalculatePadding(symbol_group): | 150 def _RemoveDuplicatesAndCalculatePadding(symbol_group): |
| 147 """Removes symbols at the same address and calculates the |padding| field. | 151 """Removes symbols at the same address and calculates the |padding| field. |
| 148 | 152 |
| 149 Symbols must already be sorted by |address|. | 153 Symbols must already be sorted by |address|. |
| 150 """ | 154 """ |
| 151 to_remove = set() | 155 to_remove = [] |
| 152 all_symbols = symbol_group.symbols | 156 for i, symbol in enumerate(symbol_group[1:]): |
| 153 for i, symbol in enumerate(all_symbols[1:]): | 157 prev_symbol = symbol_group[i] |
| 154 prev_symbol = all_symbols[i] | |
| 155 if prev_symbol.section_name != symbol.section_name: | 158 if prev_symbol.section_name != symbol.section_name: |
| 156 continue | 159 continue |
| 157 if symbol.address > 0 and prev_symbol.address > 0: | 160 if symbol.address > 0 and prev_symbol.address > 0: |
| 158 # Fold symbols that are at the same address (happens in nm output). | 161 # Fold symbols that are at the same address (happens in nm output). |
| 159 if symbol.address == prev_symbol.address: | 162 if symbol.address == prev_symbol.address: |
| 160 symbol.size = max(prev_symbol.size, symbol.size) | 163 symbol.size = max(prev_symbol.size, symbol.size) |
| 161 to_remove.add(i + 1) | 164 to_remove.add(symbol) |
| 162 continue | 165 continue |
| 163 # Even with symbols at the same address removed, overlaps can still | 166 # Even with symbols at the same address removed, overlaps can still |
| 164 # happen. In this case, padding will be negative (and this is fine). | 167 # happen. In this case, padding will be negative (and this is fine). |
| 165 padding = symbol.address - prev_symbol.end_address | 168 padding = symbol.address - prev_symbol.end_address |
| 166 # These thresholds were found by manually auditing arm32 Chrome. | 169 # These thresholds were found by manually auditing arm32 Chrome. |
| 167 # E.g.: Set them to 0 and see what warnings get logged. | 170 # E.g.: Set them to 0 and see what warnings get logged. |
| 168 # TODO(agrieve): See if these thresholds make sense for architectures | 171 # TODO(agrieve): See if these thresholds make sense for architectures |
| 169 # other than arm32. | 172 # other than arm32. |
| 170 if (symbol.section in 'rd' and padding >= 256 or | 173 if (symbol.section in 'rd' and padding >= 256 or |
| 171 symbol.section in 't' and padding >= 64): | 174 symbol.section in 't' and padding >= 64): |
| 172 # For nm data, this is caused by data that has no associated symbol. | 175 # For nm data, this is caused by data that has no associated symbol. |
| 173 # The linker map file lists them with no name, but with a file. | 176 # The linker map file lists them with no name, but with a file. |
| 174 # Example: | 177 # Example: |
| 175 # .data 0x02d42764 0x120 .../V8SharedWorkerGlobalScope.o | 178 # .data 0x02d42764 0x120 .../V8SharedWorkerGlobalScope.o |
| 176 # Where as most look like: | 179 # Where as most look like: |
| 177 # .data.MANGLED_NAME... | 180 # .data.MANGLED_NAME... |
| 178 logging.debug('Large padding of %d between:\n A) %r\n B) %r' % ( | 181 logging.debug('Large padding of %d between:\n A) %r\n B) %r' % ( |
| 179 padding, prev_symbol, symbol)) | 182 padding, prev_symbol, symbol)) |
| 180 continue | 183 continue |
| 181 symbol.padding = padding | 184 symbol.padding = padding |
| 182 symbol.size += padding | 185 symbol.size += padding |
| 183 assert symbol.size >= 0, 'Symbol has negative size: ' + ( | 186 assert symbol.size >= 0, 'Symbol has negative size: ' + ( |
| 184 '%r\nprev symbol: %r' % (symbol, prev_symbol)) | 187 '%r\nprev symbol: %r' % (symbol, prev_symbol)) |
| 185 # Map files have no overlaps, so worth special-casing the no-op case. | 188 # Map files have no overlaps, so worth special-casing the no-op case. |
| 186 if to_remove: | 189 if to_remove: |
| 187 logging.info('Removing %d overlapping symbols', len(to_remove)) | 190 logging.info('Removing %d overlapping symbols', len(to_remove)) |
| 188 symbol_group.symbols = ( | 191 symbol_group -= models.SymbolGroup(to_remove) |
| 189 [s for i, s in enumerate(all_symbols) if i not in to_remove]) | |
| 190 | 192 |
| 191 | 193 |
| 192 def AddOptions(parser): | 194 def AddOptions(parser): |
| 193 parser.add_argument('--tool-prefix', default='', | 195 parser.add_argument('--tool-prefix', default='', |
| 194 help='Path prefix for c++filt.') | 196 help='Path prefix for c++filt.') |
| 195 parser.add_argument('--output-directory', | 197 parser.add_argument('--output-directory', |
| 196 help='Path to the root build directory.') | 198 help='Path to the root build directory.') |
| 197 | 199 |
| 198 | 200 |
| 199 def _DetectToolPrefix(tool_prefix, input_file, output_directory=None): | 201 def _DetectToolPrefix(tool_prefix, input_file, output_directory=None): |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 306 logging.info('Recording metadata: %s', | 308 logging.info('Recording metadata: %s', |
| 307 describe.DescribeSizeInfoMetadata(size_info)) | 309 describe.DescribeSizeInfoMetadata(size_info)) |
| 308 logging.info('Saving result to %s', args.output_file) | 310 logging.info('Saving result to %s', args.output_file) |
| 309 file_format.SaveSizeInfo(size_info, args.output_file) | 311 file_format.SaveSizeInfo(size_info, args.output_file) |
| 310 | 312 |
| 311 logging.info('Done') | 313 logging.info('Done') |
| 312 | 314 |
| 313 | 315 |
| 314 if __name__ == '__main__': | 316 if __name__ == '__main__': |
| 315 sys.exit(main(sys.argv)) | 317 sys.exit(main(sys.argv)) |
| OLD | NEW |