| 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 306 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 317 name_list = aliases_by_address.get(s.address) | 317 name_list = aliases_by_address.get(s.address) |
| 318 if name_list: | 318 if name_list: |
| 319 if s.full_name not in name_list: | 319 if s.full_name not in name_list: |
| 320 logging.warning('Name missing from aliases: %s %s', s.full_name, | 320 logging.warning('Name missing from aliases: %s %s', s.full_name, |
| 321 name_list) | 321 name_list) |
| 322 continue | 322 continue |
| 323 replacements.append((i, name_list)) | 323 replacements.append((i, name_list)) |
| 324 num_new_symbols += len(name_list) - 1 | 324 num_new_symbols += len(name_list) - 1 |
| 325 | 325 |
| 326 if float(num_new_symbols) / len(raw_symbols) < .05: | 326 if float(num_new_symbols) / len(raw_symbols) < .05: |
| 327 # TODO(agrieve): Figure out if there's a way to get alias information from |
| 328 # clang-compiled nm. |
| 327 logging.warning('Number of aliases is oddly low (%.0f%%). It should ' | 329 logging.warning('Number of aliases is oddly low (%.0f%%). It should ' |
| 328 'usually be around 25%%. Ensure --tool-prefix is correct.', | 330 'usually be around 25%%. Ensure --tool-prefix is correct. ' |
| 331 'Ignore this if you compiled with clang.', |
| 329 float(num_new_symbols) / len(raw_symbols) * 100) | 332 float(num_new_symbols) / len(raw_symbols) * 100) |
| 330 | 333 |
| 331 # Step 2: Create new symbols as siblings to each existing one. | 334 # Step 2: Create new symbols as siblings to each existing one. |
| 332 logging.debug('Creating %d aliases', num_new_symbols) | 335 logging.debug('Creating %d aliases', num_new_symbols) |
| 333 src_cursor_end = len(raw_symbols) | 336 src_cursor_end = len(raw_symbols) |
| 334 raw_symbols += [None] * num_new_symbols | 337 raw_symbols += [None] * num_new_symbols |
| 335 dst_cursor_end = len(raw_symbols) | 338 dst_cursor_end = len(raw_symbols) |
| 336 for src_index, name_list in reversed(replacements): | 339 for src_index, name_list in reversed(replacements): |
| 337 # Copy over symbols that come after the current one. | 340 # Copy over symbols that come after the current one. |
| 338 chunk_size = src_cursor_end - src_index - 1 | 341 chunk_size = src_cursor_end - src_index - 1 |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 397 gn_args = _ParseGnArgs(os.path.join(output_directory, 'args.gn')) | 400 gn_args = _ParseGnArgs(os.path.join(output_directory, 'args.gn')) |
| 398 metadata[models.METADATA_MAP_FILENAME] = relative_to_out(map_path) | 401 metadata[models.METADATA_MAP_FILENAME] = relative_to_out(map_path) |
| 399 metadata[models.METADATA_ELF_FILENAME] = relative_to_out(elf_path) | 402 metadata[models.METADATA_ELF_FILENAME] = relative_to_out(elf_path) |
| 400 metadata[models.METADATA_GN_ARGS] = gn_args | 403 metadata[models.METADATA_GN_ARGS] = gn_args |
| 401 | 404 |
| 402 if apk_path: | 405 if apk_path: |
| 403 metadata[models.METADATA_APK_FILENAME] = relative_to_out(apk_path) | 406 metadata[models.METADATA_APK_FILENAME] = relative_to_out(apk_path) |
| 404 return metadata | 407 return metadata |
| 405 | 408 |
| 406 | 409 |
| 407 def CreateSizeInfo(map_path, elf_path, tool_prefix, output_directory, | 410 def CreateSizeInfo(map_path, elf_path, tool_prefix, output_directory): |
| 408 raw_only=False): | |
| 409 """Creates a SizeInfo. | 411 """Creates a SizeInfo. |
| 410 | 412 |
| 411 Args: | 413 Args: |
| 412 map_path: Path to the linker .map(.gz) file to parse. | 414 map_path: Path to the linker .map(.gz) file to parse. |
| 413 elf_path: Path to the corresponding unstripped ELF file. Used to find symbol | 415 elf_path: Path to the corresponding unstripped ELF file. Used to find symbol |
| 414 aliases and inlined functions. Can be None. | 416 aliases and inlined functions. Can be None. |
| 415 tool_prefix: Prefix for c++filt & nm (required). | 417 tool_prefix: Prefix for c++filt & nm (required). |
| 416 output_directory: Build output directory. If None, source_paths and symbol | 418 output_directory: Build output directory. If None, source_paths and symbol |
| 417 alias information will not be recorded. | 419 alias information will not be recorded. |
| 418 raw_only: Fill in just the information required for creating a .size file. | |
| 419 """ | 420 """ |
| 420 source_mapper = None | 421 source_mapper = None |
| 421 if output_directory: | 422 if output_directory: |
| 422 # Start by finding the elf_object_paths, so that nm can run on them while | 423 # Start by finding the elf_object_paths, so that nm can run on them while |
| 423 # the linker .map is being parsed. | 424 # the linker .map is being parsed. |
| 424 logging.info('Parsing ninja files.') | 425 logging.info('Parsing ninja files.') |
| 425 source_mapper, elf_object_paths = ninja_parser.Parse( | 426 source_mapper, elf_object_paths = ninja_parser.Parse( |
| 426 output_directory, elf_path) | 427 output_directory, elf_path) |
| 427 assert not elf_path or elf_object_paths, ( | 428 assert not elf_path or elf_object_paths, ( |
| 428 'Failed to find link command in ninja files for ' + | 429 'Failed to find link command in ninja files for ' + |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 500 _ComputeAnscestorPathsAndNormalizeObjectPaths( | 501 _ComputeAnscestorPathsAndNormalizeObjectPaths( |
| 501 raw_symbols, object_paths_by_name, source_mapper) | 502 raw_symbols, object_paths_by_name, source_mapper) |
| 502 | 503 |
| 503 if not elf_path or not output_directory: | 504 if not elf_path or not output_directory: |
| 504 logging.info('Normalizing object paths.') | 505 logging.info('Normalizing object paths.') |
| 505 for symbol in raw_symbols: | 506 for symbol in raw_symbols: |
| 506 symbol.object_path = _NormalizeObjectPath(symbol.object_path) | 507 symbol.object_path = _NormalizeObjectPath(symbol.object_path) |
| 507 | 508 |
| 508 size_info = models.SizeInfo(section_sizes, raw_symbols) | 509 size_info = models.SizeInfo(section_sizes, raw_symbols) |
| 509 | 510 |
| 510 # Name normalization not strictly required, but makes for smaller files. | 511 # When creating the .size file, name normalization is not strictly required, |
| 511 if raw_only: | 512 # but makes for smaller files. |
| 512 logging.info('Normalizing symbol names') | 513 # Padding not required either, but it is useful to check for large padding and |
| 513 _NormalizeNames(size_info.raw_symbols) | 514 # log a warning. |
| 514 else: | 515 _PostProcessSizeInfo(size_info) |
| 515 _PostProcessSizeInfo(size_info) | |
| 516 | 516 |
| 517 if logging.getLogger().isEnabledFor(logging.DEBUG): | 517 if logging.getLogger().isEnabledFor(logging.INFO): |
| 518 # Padding is reported in size coverage logs. | |
| 519 if raw_only: | |
| 520 _CalculatePadding(size_info.raw_symbols) | |
| 521 for line in describe.DescribeSizeInfoCoverage(size_info): | 518 for line in describe.DescribeSizeInfoCoverage(size_info): |
| 522 logging.info(line) | 519 logging.info(line) |
| 523 logging.info('Recorded info for %d symbols', len(size_info.raw_symbols)) | 520 logging.info('Recorded info for %d symbols', len(size_info.raw_symbols)) |
| 524 return size_info | 521 return size_info |
| 525 | 522 |
| 526 | 523 |
| 527 def _DetectGitRevision(directory): | 524 def _DetectGitRevision(directory): |
| 528 try: | 525 try: |
| 529 git_rev = subprocess.check_output( | 526 git_rev = subprocess.check_output( |
| 530 ['git', '-C', directory, 'rev-parse', 'HEAD']) | 527 ['git', '-C', directory, 'rev-parse', 'HEAD']) |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 657 if not args.no_source_paths: | 654 if not args.no_source_paths: |
| 658 output_directory = lazy_paths.VerifyOutputDirectory() | 655 output_directory = lazy_paths.VerifyOutputDirectory() |
| 659 | 656 |
| 660 metadata = CreateMetadata(map_path, elf_path, apk_path, tool_prefix, | 657 metadata = CreateMetadata(map_path, elf_path, apk_path, tool_prefix, |
| 661 output_directory) | 658 output_directory) |
| 662 if apk_path and elf_path: | 659 if apk_path and elf_path: |
| 663 # Extraction takes around 1 second, so do it in parallel. | 660 # Extraction takes around 1 second, so do it in parallel. |
| 664 apk_elf_result = concurrent.ForkAndCall( | 661 apk_elf_result = concurrent.ForkAndCall( |
| 665 _ElfInfoFromApk, (apk_path, apk_so_path, tool_prefix)) | 662 _ElfInfoFromApk, (apk_path, apk_so_path, tool_prefix)) |
| 666 | 663 |
| 667 size_info = CreateSizeInfo( | 664 size_info = CreateSizeInfo(map_path, elf_path, tool_prefix, output_directory) |
| 668 map_path, elf_path, tool_prefix, output_directory, raw_only=True) | |
| 669 | 665 |
| 670 if metadata: | 666 if metadata: |
| 671 size_info.metadata = metadata | 667 size_info.metadata = metadata |
| 672 | 668 |
| 673 if apk_path: | 669 if apk_path: |
| 674 logging.debug('Extracting section sizes from .so within .apk') | 670 logging.debug('Extracting section sizes from .so within .apk') |
| 675 unstripped_section_sizes = size_info.section_sizes | 671 unstripped_section_sizes = size_info.section_sizes |
| 676 apk_build_id, size_info.section_sizes = apk_elf_result.get() | 672 apk_build_id, size_info.section_sizes = apk_elf_result.get() |
| 677 assert apk_build_id == metadata[models.METADATA_ELF_BUILD_ID], ( | 673 assert apk_build_id == metadata[models.METADATA_ELF_BUILD_ID], ( |
| 678 'BuildID for %s within %s did not match the one at %s' % | 674 'BuildID for %s within %s did not match the one at %s' % |
| (...skipping 13 matching lines...) Expand all Loading... |
| 692 logging.warning('Packed section not present: %s', packed_section_name) | 688 logging.warning('Packed section not present: %s', packed_section_name) |
| 693 else: | 689 else: |
| 694 size_info.section_sizes['%s (unpacked)' % packed_section_name] = ( | 690 size_info.section_sizes['%s (unpacked)' % packed_section_name] = ( |
| 695 unstripped_section_sizes.get(packed_section_name)) | 691 unstripped_section_sizes.get(packed_section_name)) |
| 696 | 692 |
| 697 logging.info('Recording metadata: \n %s', | 693 logging.info('Recording metadata: \n %s', |
| 698 '\n '.join(describe.DescribeMetadata(size_info.metadata))) | 694 '\n '.join(describe.DescribeMetadata(size_info.metadata))) |
| 699 logging.info('Saving result to %s', args.size_file) | 695 logging.info('Saving result to %s', args.size_file) |
| 700 file_format.SaveSizeInfo(size_info, args.size_file) | 696 file_format.SaveSizeInfo(size_info, args.size_file) |
| 701 logging.info('Done') | 697 logging.info('Done') |
| OLD | NEW |