| 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 |
| 11 import gzip | 11 import gzip |
| 12 import logging | 12 import logging |
| 13 import os | 13 import os |
| 14 import multiprocessing | |
| 15 import posixpath | 14 import posixpath |
| 16 import re | 15 import re |
| 17 import subprocess | 16 import subprocess |
| 18 import sys | 17 import sys |
| 19 import tempfile | 18 import tempfile |
| 20 import zipfile | 19 import zipfile |
| 21 | 20 |
| 22 import describe | 21 import describe |
| 23 import file_format | 22 import file_format |
| 24 import function_signature | 23 import function_signature |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 148 def _NormalizeSourcePath(path): | 147 def _NormalizeSourcePath(path): |
| 149 if path.startswith('gen/'): | 148 if path.startswith('gen/'): |
| 150 # Convert gen/third_party/... -> third_party/... | 149 # Convert gen/third_party/... -> third_party/... |
| 151 return path[4:] | 150 return path[4:] |
| 152 if path.startswith('../../'): | 151 if path.startswith('../../'): |
| 153 # Convert ../../third_party/... -> third_party/... | 152 # Convert ../../third_party/... -> third_party/... |
| 154 return path[6:] | 153 return path[6:] |
| 155 return path | 154 return path |
| 156 | 155 |
| 157 | 156 |
| 158 def _ExtractSourcePaths(symbols, output_directory): | 157 def _ExtractSourcePaths(symbols, source_mapper): |
| 159 """Fills in the .source_path attribute of all symbols. | 158 """Fills in the .source_path attribute of all symbols.""" |
| 160 | 159 logging.debug('Parsed %d .ninja files.', source_mapper.parsed_file_count) |
| 161 Returns True if source paths were found. | |
| 162 """ | |
| 163 mapper = ninja_parser.SourceFileMapper(output_directory) | |
| 164 not_found_paths = set() | |
| 165 | 160 |
| 166 for symbol in symbols: | 161 for symbol in symbols: |
| 167 object_path = symbol.object_path | 162 object_path = symbol.object_path |
| 168 if symbol.source_path or not object_path: | 163 if symbol.source_path or not object_path: |
| 169 continue | 164 continue |
| 170 # We don't have source info for prebuilt .a files. | 165 # We don't have source info for prebuilt .a files. |
| 171 if not os.path.isabs(object_path) and not object_path.startswith('..'): | 166 if not os.path.isabs(object_path) and not object_path.startswith('..'): |
| 172 source_path = mapper.FindSourceForPath(object_path) | 167 source_path = source_mapper.FindSourceForPath(object_path) |
| 173 if source_path: | 168 if source_path: |
| 174 symbol.source_path = _NormalizeSourcePath(source_path) | 169 symbol.source_path = _NormalizeSourcePath(source_path) |
| 175 elif object_path not in not_found_paths: | |
| 176 not_found_paths.add(object_path) | |
| 177 logging.warning('Could not find source path for %s', object_path) | |
| 178 logging.debug('Parsed %d .ninja files.', mapper.GetParsedFileCount()) | |
| 179 return len(not_found_paths) == 0 | |
| 180 | 170 |
| 181 | 171 |
| 182 def _CalculatePadding(symbols): | 172 def _CalculatePadding(symbols): |
| 183 """Populates the |padding| field based on symbol addresses. | 173 """Populates the |padding| field based on symbol addresses. |
| 184 | 174 |
| 185 Symbols must already be sorted by |address|. | 175 Symbols must already be sorted by |address|. |
| 186 """ | 176 """ |
| 187 seen_sections = [] | 177 seen_sections = [] |
| 188 for i, symbol in enumerate(symbols[1:]): | 178 for i, symbol in enumerate(symbols[1:]): |
| 189 prev_symbol = symbols[i] | 179 prev_symbol = symbols[i] |
| (...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 342 _CalculatePadding(size_info.raw_symbols) | 332 _CalculatePadding(size_info.raw_symbols) |
| 343 logging.info('Grouping decomposed functions') | 333 logging.info('Grouping decomposed functions') |
| 344 size_info.symbols = models.SymbolGroup( | 334 size_info.symbols = models.SymbolGroup( |
| 345 _ClusterSymbols(size_info.raw_symbols)) | 335 _ClusterSymbols(size_info.raw_symbols)) |
| 346 logging.info('Processed %d symbols', len(size_info.raw_symbols)) | 336 logging.info('Processed %d symbols', len(size_info.raw_symbols)) |
| 347 | 337 |
| 348 | 338 |
| 349 def CreateSizeInfo(map_path, lazy_paths=None, no_source_paths=False, | 339 def CreateSizeInfo(map_path, lazy_paths=None, no_source_paths=False, |
| 350 raw_only=False): | 340 raw_only=False): |
| 351 """Creates a SizeInfo from the given map file.""" | 341 """Creates a SizeInfo from the given map file.""" |
| 352 if not no_source_paths: | |
| 353 # output_directory needed for source file information. | |
| 354 lazy_paths.VerifyOutputDirectory() | |
| 355 # tool_prefix needed for c++filt. | 342 # tool_prefix needed for c++filt. |
| 356 lazy_paths.VerifyToolPrefix() | 343 lazy_paths.VerifyToolPrefix() |
| 357 | 344 |
| 345 if not no_source_paths: |
| 346 # Parse .ninja files at the same time as parsing the .map file. |
| 347 source_mapper_result = helpers.ForkAndCall( |
| 348 ninja_parser.Parse, lazy_paths.VerifyOutputDirectory()) |
| 349 |
| 358 with _OpenMaybeGz(map_path) as map_file: | 350 with _OpenMaybeGz(map_path) as map_file: |
| 359 section_sizes, raw_symbols = ( | 351 section_sizes, raw_symbols = ( |
| 360 linker_map_parser.MapFileParser().Parse(map_file)) | 352 linker_map_parser.MapFileParser().Parse(map_file)) |
| 361 | 353 |
| 362 if not no_source_paths: | 354 if not no_source_paths: |
| 363 logging.info('Extracting source paths from .ninja files') | 355 logging.info('Extracting source paths from .ninja files') |
| 364 all_found = _ExtractSourcePaths(raw_symbols, lazy_paths.output_directory) | 356 source_mapper = source_mapper_result.get() |
| 365 assert all_found, ( | 357 _ExtractSourcePaths(raw_symbols, source_mapper) |
| 358 assert source_mapper.unmatched_paths_count == 0, ( |
| 366 'One or more source file paths could not be found. Likely caused by ' | 359 'One or more source file paths could not be found. Likely caused by ' |
| 367 '.ninja files being generated at a different time than the .map file.') | 360 '.ninja files being generated at a different time than the .map file.') |
| 368 | 361 |
| 369 logging.info('Stripping linker prefixes from symbol names') | 362 logging.info('Stripping linker prefixes from symbol names') |
| 370 _StripLinkerAddedSymbolPrefixes(raw_symbols) | 363 _StripLinkerAddedSymbolPrefixes(raw_symbols) |
| 371 # Map file for some reason doesn't unmangle all names. | 364 # Map file for some reason doesn't unmangle all names. |
| 372 # Unmangle prints its own log statement. | 365 # Unmangle prints its own log statement. |
| 373 _UnmangleRemainingSymbols(raw_symbols, lazy_paths.tool_prefix) | 366 _UnmangleRemainingSymbols(raw_symbols, lazy_paths.tool_prefix) |
| 374 logging.info('Normalizing object paths') | 367 logging.info('Normalizing object paths') |
| 375 _NormalizeObjectPaths(raw_symbols) | 368 _NormalizeObjectPaths(raw_symbols) |
| (...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 531 models.METADATA_ELF_ARCHITECTURE: architecture, | 524 models.METADATA_ELF_ARCHITECTURE: architecture, |
| 532 models.METADATA_ELF_FILENAME: relative_to_out(elf_path), | 525 models.METADATA_ELF_FILENAME: relative_to_out(elf_path), |
| 533 models.METADATA_ELF_MTIME: timestamp, | 526 models.METADATA_ELF_MTIME: timestamp, |
| 534 models.METADATA_ELF_BUILD_ID: build_id, | 527 models.METADATA_ELF_BUILD_ID: build_id, |
| 535 models.METADATA_GN_ARGS: gn_args, | 528 models.METADATA_GN_ARGS: gn_args, |
| 536 } | 529 } |
| 537 | 530 |
| 538 if apk_path: | 531 if apk_path: |
| 539 metadata[models.METADATA_APK_FILENAME] = relative_to_out(apk_path) | 532 metadata[models.METADATA_APK_FILENAME] = relative_to_out(apk_path) |
| 540 # Extraction takes around 1 second, so do it in parallel. | 533 # Extraction takes around 1 second, so do it in parallel. |
| 541 pool_of_one = multiprocessing.Pool(1) | 534 apk_elf_result = helpers.ForkAndCall( |
| 542 apk_elf_result = pool_of_one.apply_async( | 535 _ElfInfoFromApk, apk_path, apk_so_path, lazy_paths.tool_prefix) |
| 543 _ElfInfoFromApk, (apk_path, apk_so_path, lazy_paths.tool_prefix)) | |
| 544 pool_of_one.close() | |
| 545 | 536 |
| 546 size_info = CreateSizeInfo( | 537 size_info = CreateSizeInfo( |
| 547 map_path, lazy_paths, no_source_paths=args.no_source_paths, raw_only=True) | 538 map_path, lazy_paths, no_source_paths=args.no_source_paths, raw_only=True) |
| 548 | 539 |
| 549 if metadata: | 540 if metadata: |
| 550 size_info.metadata = metadata | 541 size_info.metadata = metadata |
| 551 logging.debug('Validating section sizes') | 542 logging.debug('Validating section sizes') |
| 552 elf_section_sizes = _SectionSizesFromElf(elf_path, lazy_paths.tool_prefix) | 543 elf_section_sizes = _SectionSizesFromElf(elf_path, lazy_paths.tool_prefix) |
| 553 for k, v in elf_section_sizes.iteritems(): | 544 for k, v in elf_section_sizes.iteritems(): |
| 554 assert v == size_info.section_sizes.get(k), ( | 545 assert v == size_info.section_sizes.get(k), ( |
| (...skipping 19 matching lines...) Expand all Loading... |
| 574 logging.warning('Packed section not present: %s', packed_section_name) | 565 logging.warning('Packed section not present: %s', packed_section_name) |
| 575 else: | 566 else: |
| 576 size_info.section_sizes['%s (unpacked)' % packed_section_name] = ( | 567 size_info.section_sizes['%s (unpacked)' % packed_section_name] = ( |
| 577 unstripped_section_sizes.get(packed_section_name)) | 568 unstripped_section_sizes.get(packed_section_name)) |
| 578 | 569 |
| 579 logging.info('Recording metadata: \n %s', | 570 logging.info('Recording metadata: \n %s', |
| 580 '\n '.join(describe.DescribeMetadata(size_info.metadata))) | 571 '\n '.join(describe.DescribeMetadata(size_info.metadata))) |
| 581 logging.info('Saving result to %s', args.size_file) | 572 logging.info('Saving result to %s', args.size_file) |
| 582 file_format.SaveSizeInfo(size_info, args.size_file) | 573 file_format.SaveSizeInfo(size_info, args.size_file) |
| 583 logging.info('Done') | 574 logging.info('Done') |
| OLD | NEW |