Last active
January 13, 2023 12:42
-
-
Save obfusk/98249517a329dc33911b71ed344e7db3 to your computer and use it in GitHub Desktop.
-> https://github.com/obfusk/reproducible-apk-tools | dump baseline.prof{,m}
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/python3 | |
| # encoding: utf-8 | |
| # SPDX-FileCopyrightText: 2023 FC Stegerman <[email protected]> | |
| # SPDX-License-Identifier: GPL-3.0-or-later | |
| import struct | |
| import zlib | |
| from typing import Any, BinaryIO, Tuple | |
| # https://android.googlesource.com/platform/tools/base | |
| # profgen/profgen/src/main/kotlin/com/android/tools/profgen/ArtProfileSerializer.kt | |
| PROF_MAGIC = b"pro\x00" | |
| PROFM_MAGIC = b"prm\x00" | |
| PROF_001_N = b"001\x00" | |
| PROF_005_O = b"005\x00" | |
| PROF_009_O_MR1 = b"009\x00" | |
| PROF_010_P = b"010\x00" | |
| PROF_015_S = b"015\x00" | |
| PROFM_001_N = b"001\x00" | |
| PROFM_002 = b"002\x00" | |
| INLINE_CACHE_MISSING_TYPES_ENCODING = 6 | |
| INLINE_CACHE_MEGAMORPHIC_ENCODING = 7 | |
| class Error(RuntimeError): | |
| pass | |
| # FIXME | |
| def dump_prof_profm(file: str, verbose: bool = False) -> None: | |
| with open(file, "rb") as fh: | |
| magic = fh.read(4) | |
| version = fh.read(4) | |
| if magic == PROF_MAGIC: | |
| if version == PROF_010_P: | |
| print("prof version=010 P") | |
| dump_prof_010_p(fh, verbose) | |
| else: | |
| raise Error(f"Unsupported prof version {version!r}") | |
| elif magic == PROFM_MAGIC: | |
| if version == PROFM_002: | |
| print("profm version=002") | |
| dump_profm_002(fh, verbose) | |
| else: | |
| raise Error(f"Unsupported profm version {version!r}") | |
| else: | |
| raise Error(f"Unsupported magic {magic!r}") | |
| def dump_prof_010_p(fh: BinaryIO, verbose: bool) -> None: | |
| num_dex_files, uncompressed_data_size, compressed_data_size = \ | |
| struct.unpack("<BII", fh.read(9)) | |
| print(f"num_dex_files={num_dex_files}") | |
| if verbose: | |
| print(f"uncompressed_data_size={uncompressed_data_size}") | |
| print(f"compressed_data_size={compressed_data_size}") | |
| data = zlib.decompress(fh.read(compressed_data_size)) | |
| # FIXME | |
| if fh.read(): | |
| raise Error("Expected EOF") | |
| if len(data) != uncompressed_data_size: | |
| raise Error("Uncompressed data size does not match") | |
| info = [] | |
| for i in range(num_dex_files): | |
| print(f"dex_data_header {i}") | |
| profile_key_size, num_type_ids, hot_method_region_size, \ | |
| dex_checksum, num_method_ids, data = _unpack("<HHIII", data) | |
| profile_key, data = _split(data, profile_key_size) | |
| info.append((num_type_ids, hot_method_region_size, num_method_ids)) | |
| print(f" profile_key={profile_key.decode()}") | |
| print(f" num_type_ids={num_type_ids}") | |
| if verbose: | |
| print(f" hot_method_region_size={hot_method_region_size}") | |
| print(f" dex_checksum=0x{dex_checksum:x}") | |
| print(f" num_method_ids={num_method_ids}") | |
| for i in range(num_dex_files): | |
| print(f"dex_data {i}") | |
| num_type_ids, hot_method_region_size, num_method_ids = info[i] | |
| region, data = _split(data, hot_method_region_size) | |
| num_hot_method_ids = 0 | |
| mi_delta = 0 | |
| while region: | |
| num_hot_method_ids += 1 | |
| method_id, num_inline_caches, region = _unpack("<HH", region) | |
| method_id += mi_delta | |
| mi_delta = method_id | |
| if verbose: | |
| print(f" method_id={method_id}") | |
| print(f" num_inline_caches={num_inline_caches}") | |
| # skip inline caches | |
| _skip_inline_caches(PROF_010_P, region, num_inline_caches) | |
| print(f" num_hot_method_ids={num_hot_method_ids}") | |
| ti_delta = 0 | |
| for _ in range(num_type_ids): | |
| type_id, data = _unpack("<H", data) | |
| type_id += ti_delta | |
| ti_delta = type_id | |
| if verbose: | |
| print(f" type_id={type_id}") | |
| # FIXME: skip bitmap | |
| bitmap_size = _bitmap_storage_size(num_method_ids) | |
| _bitmap, data = _split(data, bitmap_size) | |
| print(f" bitmap_size={bitmap_size}") | |
| if data: | |
| raise Error("Expected end of data") | |
| def dump_profm_002(fh: BinaryIO, verbose: bool) -> None: | |
| num_dex_files, uncompressed_data_size, compressed_data_size = \ | |
| struct.unpack("<HII", fh.read(10)) | |
| print(f"num_dex_files={num_dex_files}") | |
| if verbose: | |
| print(f"uncompressed_data_size={uncompressed_data_size}") | |
| print(f"compressed_data_size={compressed_data_size}") | |
| data = zlib.decompress(fh.read(compressed_data_size)) | |
| # FIXME | |
| if fh.read(): | |
| raise Error("Expected EOF") | |
| if len(data) != uncompressed_data_size: | |
| raise Error("Uncompressed data size does not match") | |
| for _ in range(num_dex_files): | |
| profile_idx, profile_key_size, data = _unpack("<HH", data) | |
| profile_key, data = _split(data, profile_key_size) | |
| print(f"profile_idx={profile_idx}") | |
| print(f" profile_key={profile_key.decode()}") | |
| num_type_ids, num_class_ids, data = _unpack("<IH", data) | |
| print(f" num_type_ids={num_type_ids}") | |
| print(f" num_class_ids={num_class_ids}") | |
| ci_delta = 0 | |
| for _ in range(num_class_ids): | |
| class_id, data = _unpack("<H", data) | |
| class_id += ci_delta | |
| ci_delta = class_id | |
| if verbose: | |
| print(f" class_id={class_id}") | |
| if data: | |
| raise Error("Expected end of data") | |
| def _skip_inline_caches(version: bytes, region: bytes, num_inline_caches: int) -> bytes: | |
| if version <= PROF_010_P: | |
| for _ in range(num_inline_caches): | |
| _dex_pc, dex_map_size, region = _unpack("<HB", region) | |
| if dex_map_size in (INLINE_CACHE_MISSING_TYPES_ENCODING, | |
| INLINE_CACHE_MEGAMORPHIC_ENCODING): | |
| continue | |
| for _ in range(dex_map_size): | |
| _dex_profile_idx, num_classes, region = _unpack("<BB", region) | |
| _, region = _split(region, 2 * num_classes) | |
| return region | |
| else: | |
| raise Error(f"Unsupported version {version!r}") | |
| def _bitmap_storage_size(num_method_ids: int) -> int: | |
| byte, bits = 8, num_method_ids * 2 | |
| return (bits + byte - 1 & -byte) // byte | |
| def _unpack(fmt: str, data: bytes) -> Any: | |
| size = fmt.count("B") + 2 * fmt.count("H") + 4 * fmt.count("I") | |
| return struct.unpack(fmt, data[:size]) + (data[size:],) | |
| def _split(data: bytes, size: int) -> Tuple[bytes, bytes]: | |
| return data[:size], data[size:] | |
| if __name__ == "__main__": | |
| import argparse | |
| parser = argparse.ArgumentParser(prog="dump-baseline.py") | |
| parser.add_argument("-v", "--verbose", action="store_true") | |
| parser.add_argument("file", metavar="FILE") | |
| args = parser.parse_args() | |
| dump_prof_profm(args.file, args.verbose) | |
| # vim: set tw=80 sw=4 sts=4 et fdm=marker : |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/python3 | |
| # encoding: utf-8 | |
| # SPDX-FileCopyrightText: 2023 FC Stegerman <[email protected]> | |
| # SPDX-License-Identifier: GPL-3.0-or-later | |
| import struct | |
| import zlib | |
| from typing import Any, BinaryIO, Tuple | |
| # https://android.googlesource.com/platform/tools/base | |
| # profgen/profgen/src/main/kotlin/com/android/tools/profgen/ArtProfileSerializer.kt | |
| PROF_MAGIC = b"pro\x00" | |
| PROFM_MAGIC = b"prm\x00" | |
| PROF_001_N = b"001\x00" | |
| PROF_005_O = b"005\x00" | |
| PROF_009_O_MR1 = b"009\x00" | |
| PROF_010_P = b"010\x00" | |
| PROF_015_S = b"015\x00" | |
| PROFM_001_N = b"001\x00" | |
| PROFM_002 = b"002\x00" | |
| class Error(RuntimeError): | |
| pass | |
| # FIXME | |
| def sort_prof_profm(input_file: str, output_file: str) -> None: | |
| with open(input_file, "rb") as fhi: | |
| magic = fhi.read(4) | |
| version = fhi.read(4) | |
| if magic == PROF_MAGIC: | |
| raise Error(f"Unsupported prof version {version!r}") | |
| elif magic == PROFM_MAGIC: | |
| if version == PROFM_002: | |
| with open(output_file, "wb") as fho: | |
| fho.write(PROFM_MAGIC + PROFM_002) | |
| fho.write(sort_profm_002(fhi)) | |
| else: | |
| raise Error(f"Unsupported profm version {version!r}") | |
| else: | |
| raise Error(f"Unsupported magic {magic!r}") | |
| def sort_profm_002(fh: BinaryIO) -> bytes: | |
| num_dex_files, uncompressed_data_size, compressed_data_size = \ | |
| struct.unpack("<HII", fh.read(10)) | |
| data = zlib.decompress(fh.read(compressed_data_size)) | |
| profiles = [] | |
| if fh.read(): | |
| raise Error("Expected EOF") | |
| if len(data) != uncompressed_data_size: | |
| raise Error("Uncompressed data size does not match") | |
| for _ in range(num_dex_files): | |
| profile = data[:4] | |
| profile_idx, profile_key_size, data = _unpack("<HH", data) | |
| profile_key, data = _split(data, profile_key_size) | |
| profile += profile_key + data[:6] | |
| num_type_ids, num_class_ids, data = _unpack("<IH", data) | |
| class_ids, data = _split(data, num_class_ids * 2) | |
| profile += class_ids | |
| profiles.append((profile_key, profile)) | |
| if data: | |
| raise Error("Expected end of data") | |
| srtd = b"".join(int.to_bytes(i, 2, "little") + p[1][2:] | |
| for i, p in enumerate(sorted(profiles))) | |
| cdata = zlib.compress(srtd, 1) | |
| hdr = struct.pack("<HII", num_dex_files, uncompressed_data_size, len(cdata)) | |
| return hdr + cdata | |
| def _unpack(fmt: str, data: bytes) -> Any: | |
| size = fmt.count("B") + 2 * fmt.count("H") + 4 * fmt.count("I") | |
| return struct.unpack(fmt, data[:size]) + (data[size:],) | |
| def _split(data: bytes, size: int) -> Tuple[bytes, bytes]: | |
| return data[:size], data[size:] | |
| if __name__ == "__main__": | |
| import argparse | |
| parser = argparse.ArgumentParser(prog="sort-baseline.py") | |
| parser.add_argument("input_file", metavar="INPUT_FILE") | |
| parser.add_argument("output_file", metavar="OUTPUT_FILE") | |
| args = parser.parse_args() | |
| sort_prof_profm(args.input_file, args.output_file) | |
| # vim: set tw=80 sw=4 sts=4 et fdm=marker : |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment