def Run(args, parser): if not args.size_file.endswith('.size'): parser.error('size_file must end with .size') (output_directory, tool_prefix, apk_path, apk_so_path, elf_path, map_path) = ( DeduceMainPaths(args, parser)) metadata = CreateMetadata(map_path, elf_path, apk_path, tool_prefix, output_directory) apk_elf_result = None if apk_path and elf_path: # Extraction takes around 1 second, so do it in parallel. apk_elf_result = concurrent.ForkAndCall( _ElfInfoFromApk, (apk_path, apk_so_path, tool_prefix)) section_sizes, raw_symbols = CreateSectionSizesAndSymbols( map_path=map_path, tool_prefix=tool_prefix, elf_path=elf_path, apk_path=apk_path, output_directory=output_directory, track_string_literals=args.track_string_literals, metadata=metadata, apk_elf_result=apk_elf_result, pak_files=args.pak_file, pak_info_file=args.pak_info_file) size_info = CreateSizeInfo( section_sizes, raw_symbols, metadata=metadata, normalize_names=False) if logging.getLogger().isEnabledFor(logging.INFO): for line in describe.DescribeSizeInfoCoverage(size_info): logging.info(line) logging.info('Recorded info for %d symbols', len(size_info.raw_symbols)) logging.info('Recording metadata: \n %s', '\n '.join(describe.DescribeMetadata(size_info.metadata))) logging.info('Saving result to %s', args.size_file) file_format.SaveSizeInfo(size_info, args.size_file) size_in_mb = os.path.getsize(args.size_file) / 1024.0 / 1024.0 logging.info('Done. File size is %.2fMiB.', size_in_mb)
def CollectAliasesByAddressAsync(elf_path, tool_prefix): """Calls CollectAliasesByAddress in a helper process. Returns a Result.""" def decode(encoded): return concurrent.DecodeDictOfLists(encoded, key_transform=int) return concurrent.ForkAndCall( _CollectAliasesByAddressAsyncHelper, (elf_path, tool_prefix), decode_func=decode)
def testForkAndCall_normal(self): parent_pid = os.getpid() result = concurrent.ForkAndCall( _ForkTestHelper, (self, parent_pid, 1, 2, Unpicklable())) self.assertEquals(3, result.get())
def testForkAndCall_exception(self): parent_pid = os.getpid() result = concurrent.ForkAndCall(_ForkTestHelper, (self, parent_pid, 1, 'a')) self.assertRaises(TypeError, result.get)
def Run(args, parser): if not args.size_file.endswith('.size'): parser.error('size_file must end with .size') elf_path = args.elf_file map_path = args.map_file apk_path = args.apk_file any_input = apk_path or elf_path or map_path if not any_input: parser.error( 'Most pass at least one of --apk-file, --elf-file, --map-file') lazy_paths = paths.LazyPaths(tool_prefix=args.tool_prefix, output_directory=args.output_directory, any_path_within_output_directory=any_input) if apk_path: with zipfile.ZipFile(apk_path) as z: lib_infos = [ f for f in z.infolist() if f.filename.endswith('.so') and f.file_size > 0 ] assert lib_infos, 'APK has no .so files.' # TODO(agrieve): Add support for multiple .so files, and take into account # secondary architectures. apk_so_path = max(lib_infos, key=lambda x: x.file_size).filename logging.debug('Sub-apk path=%s', apk_so_path) if not elf_path and lazy_paths.output_directory: elf_path = os.path.join( lazy_paths.output_directory, 'lib.unstripped', os.path.basename(apk_so_path.replace('crazy.', ''))) logging.debug('Detected --elf-file=%s', elf_path) if map_path: if not map_path.endswith('.map') and not map_path.endswith('.map.gz'): parser.error('Expected --map-file to end with .map or .map.gz') else: map_path = elf_path + '.map' if not os.path.exists(map_path): map_path += '.gz' if not os.path.exists(map_path): parser.error('Could not find .map(.gz)? file. Use --map-file.') tool_prefix = lazy_paths.VerifyToolPrefix() output_directory = None if not args.no_source_paths: output_directory = lazy_paths.VerifyOutputDirectory() metadata = CreateMetadata(map_path, elf_path, apk_path, tool_prefix, output_directory) if apk_path and elf_path: # Extraction takes around 1 second, so do it in parallel. apk_elf_result = concurrent.ForkAndCall( _ElfInfoFromApk, (apk_path, apk_so_path, tool_prefix)) size_info = CreateSizeInfo(map_path, elf_path, tool_prefix, output_directory, normalize_names=False) if metadata: size_info.metadata = metadata if apk_path: logging.debug('Extracting section sizes from .so within .apk') unstripped_section_sizes = size_info.section_sizes apk_build_id, size_info.section_sizes = apk_elf_result.get() assert apk_build_id == metadata[models.METADATA_ELF_BUILD_ID], ( 'BuildID for %s within %s did not match the one at %s' % (apk_so_path, apk_path, elf_path)) packed_section_name = None architecture = metadata[models.METADATA_ELF_ARCHITECTURE] # Packing occurs enabled only arm32 & arm64. if architecture == 'arm': packed_section_name = '.rel.dyn' elif architecture == 'arm64': packed_section_name = '.rela.dyn' if packed_section_name: logging.debug('Recording size of unpacked relocations') if packed_section_name not in size_info.section_sizes: logging.warning('Packed section not present: %s', packed_section_name) else: size_info.section_sizes[ '%s (unpacked)' % packed_section_name] = ( unstripped_section_sizes.get(packed_section_name)) logging.info('Recording metadata: \n %s', '\n '.join(describe.DescribeMetadata(size_info.metadata))) logging.info('Saving result to %s', args.size_file) file_format.SaveSizeInfo(size_info, args.size_file) logging.info('Done')
def CreateSectionSizesAndSymbols( map_path=None, tool_prefix=None, output_directory=None, elf_path=None, apk_path=None, track_string_literals=True, metadata=None, apk_so_path=None, pak_files=None, pak_info_file=None, knobs=SectionSizeKnobs()): """Creates sections sizes and symbols for a SizeInfo. Args: map_path: Path to the linker .map(.gz) file to parse. elf_path: Path to the corresponding unstripped ELF file. Used to find symbol aliases and inlined functions. Can be None. tool_prefix: Prefix for c++filt & nm (required). output_directory: Build output directory. If None, source_paths and symbol alias information will not be recorded. track_string_literals: Whether to break down "** merge string" sections into smaller symbols (requires output_directory). """ if apk_path and elf_path: # Extraction takes around 1 second, so do it in parallel. apk_elf_result = concurrent.ForkAndCall( _ElfInfoFromApk, (apk_path, apk_so_path, tool_prefix)) source_mapper = None elf_object_paths = None if output_directory: # Start by finding the elf_object_paths, so that nm can run on them while # the linker .map is being parsed. logging.info('Parsing ninja files.') source_mapper, elf_object_paths = ninja_parser.Parse( output_directory, elf_path) logging.debug('Parsed %d .ninja files.', source_mapper.parsed_file_count) assert not elf_path or elf_object_paths, ( 'Failed to find link command in ninja files for ' + os.path.relpath(elf_path, output_directory)) section_sizes, raw_symbols = _ParseElfInfo( map_path, elf_path, tool_prefix, output_directory, track_string_literals, elf_object_paths) elf_overhead_size = _CalculateElfOverhead(section_sizes, elf_path) pak_symbols_by_id = None if apk_path: pak_symbols_by_id = _FindPakSymbolsFromApk(apk_path, output_directory, knobs) if elf_path: section_sizes, elf_overhead_size = _ParseApkElfSectionSize( section_sizes, metadata, apk_elf_result) raw_symbols.extend( _ParseDexSymbols(section_sizes, apk_path, output_directory)) raw_symbols.extend( _ParseApkOtherSymbols(section_sizes, apk_path, apk_so_path)) elif pak_files and pak_info_file: pak_symbols_by_id = _FindPakSymbolsFromFiles( pak_files, pak_info_file, output_directory) if elf_path: elf_overhead_symbol = models.Symbol( models.SECTION_OTHER, elf_overhead_size, full_name='Overhead: ELF file') prev = section_sizes.setdefault(models.SECTION_OTHER, 0) section_sizes[models.SECTION_OTHER] = prev + elf_overhead_size raw_symbols.append(elf_overhead_symbol) if pak_symbols_by_id: object_paths = (p for p in source_mapper.IterAllPaths() if p.endswith('.o')) pak_raw_symbols = _ParsePakSymbols( section_sizes, object_paths, output_directory, pak_symbols_by_id) raw_symbols.extend(pak_raw_symbols) _ExtractSourcePathsAndNormalizeObjectPaths(raw_symbols, source_mapper) logging.info('Converting excessive aliases into shared-path symbols') _CompactLargeAliasesIntoSharedSymbols(raw_symbols, knobs) logging.debug('Connecting nm aliases') _ConnectNmAliases(raw_symbols) return section_sizes, raw_symbols
def Run(args, parser): if not args.size_file.endswith('.size'): parser.error('size_file must end with .size') elf_path = args.elf_file map_path = args.map_file apk_path = args.apk_file pak_files = args.pak_file pak_info_file = args.pak_info_file any_input = apk_path or elf_path or map_path if not any_input: parser.error('Most pass at least one of --apk-file, --elf-file, --map-file') output_directory_finder = path_util.OutputDirectoryFinder( value=args.output_directory, any_path_within_output_directory=any_input) if apk_path: with zipfile.ZipFile(apk_path) as z: lib_infos = [f for f in z.infolist() if f.filename.endswith('.so') and f.file_size > 0] assert lib_infos, 'APK has no .so files.' # TODO(agrieve): Add support for multiple .so files, and take into account # secondary architectures. apk_so_path = max(lib_infos, key=lambda x:x.file_size).filename logging.debug('Sub-apk path=%s', apk_so_path) if not elf_path and output_directory_finder.Tentative(): elf_path = os.path.join( output_directory_finder.Tentative(), 'lib.unstripped', os.path.basename(apk_so_path.replace('crazy.', ''))) logging.debug('Detected --elf-file=%s', elf_path) if map_path: if not map_path.endswith('.map') and not map_path.endswith('.map.gz'): parser.error('Expected --map-file to end with .map or .map.gz') else: map_path = elf_path + '.map' if not os.path.exists(map_path): map_path += '.gz' if not os.path.exists(map_path): parser.error('Could not find .map(.gz)? file. Ensure you have built with ' 'is_official_build=true, or use --map-file to point me a ' 'linker map file.') linker_name = _DetectLinkerName(map_path) tool_prefix_finder = path_util.ToolPrefixFinder( value=args.tool_prefix, output_directory_finder=output_directory_finder, linker_name=linker_name) tool_prefix = tool_prefix_finder.Finalized() output_directory = None if not args.no_source_paths: output_directory = output_directory_finder.Finalized() metadata = CreateMetadata(map_path, elf_path, apk_path, tool_prefix, output_directory) if apk_path and elf_path: # Extraction takes around 1 second, so do it in parallel. apk_elf_result = concurrent.ForkAndCall( _ElfInfoFromApk, (apk_path, apk_so_path, tool_prefix)) section_sizes, raw_symbols = CreateSectionSizesAndSymbols( map_path, elf_path, tool_prefix, output_directory, track_string_literals=args.track_string_literals) if apk_path: AddApkInfo(section_sizes, raw_symbols, apk_path, output_directory, metadata, apk_elf_result) elif pak_files and pak_info_file: AddPakSymbolsFromFiles( section_sizes, raw_symbols, pak_files, pak_info_file) size_info = CreateSizeInfo( section_sizes, raw_symbols, metadata=metadata, normalize_names=False) if logging.getLogger().isEnabledFor(logging.INFO): for line in describe.DescribeSizeInfoCoverage(size_info): logging.info(line) logging.info('Recorded info for %d symbols', len(size_info.raw_symbols)) logging.info('Recording metadata: \n %s', '\n '.join(describe.DescribeMetadata(size_info.metadata))) logging.info('Saving result to %s', args.size_file) file_format.SaveSizeInfo(size_info, args.size_file) size_in_mb = os.path.getsize(args.size_file) / 1024.0 / 1024.0 logging.info('Done. File size is %.2fMiB.', size_in_mb)