You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trezor-firmware/core/tools/size/groups.py

172 lines
6.0 KiB

#!/usr/bin/env python3
"""
Grouping symbols in binary into coherent categories.
"""
from __future__ import annotations
import sys
from pathlib import Path
from typing import Callable
from binsize import BinarySize, DataRow, StatisticsPlugin, set_root_dir
HERE = Path(__file__).resolve().parent
CORE_DIR = HERE.parent.parent
if len(sys.argv) > 1:
BIN_TO_ANALYZE = sys.argv[1]
else:
BIN_TO_ANALYZE = CORE_DIR / "build/firmware/firmware.elf" # type: ignore
FILE_TO_SAVE = HERE / "size_binary_firmware_elf_results.txt"
def _categories_func(row: DataRow) -> str | None:
# Defined inside the function so it can be seen in the function definition
# (which is optionally printed)
CATEGORIES: dict[str, Callable[[DataRow], bool]] = {
"UI": lambda row: (
row.source_definition.startswith(
("src/trezor/ui/", "embed/extmod/modtrezorui/")
)
),
"Crypto": lambda row: (
row.source_definition.startswith(
(
"vendor/trezor-crypto/",
"src/trezor/crypto/",
"embed/extmod/modtrezorcrypto/",
)
)
),
"Secp256": lambda row: (
row.source_definition.startswith("vendor/secp256k1-zkp/")
),
"Storage": lambda row: (
row.source_definition.startswith(("src/storage/", "vendor/trezor-storage/"))
),
"Micropython": lambda row: row.source_definition.startswith(
"vendor/micropython/"
),
"Bitcoin app": lambda row: row.source_definition.startswith(
"src/apps/bitcoin/"
),
"Ethereum app": lambda row: row.source_definition.startswith(
"src/apps/ethereum/"
),
"Monero app": lambda row: row.source_definition.startswith("src/apps/monero/"),
"Cardano app": lambda row: row.source_definition.startswith(
"src/apps/cardano/"
),
"Management app": lambda row: row.source_definition.startswith(
"src/apps/management/"
),
"Common apps": lambda row: row.source_definition.startswith("src/apps/common/"),
"Webauthn app": lambda row: row.source_definition.startswith(
"src/apps/webauthn/"
),
"Altcoin apps": lambda row: (
row.source_definition.startswith(
(
"src/apps/nem/",
"src/apps/stellar/",
"src/apps/eos/",
"src/apps/tezos/",
"src/apps/ripple/",
"src/apps/zcash/",
"src/apps/binance/",
)
)
),
"Other apps": lambda row: row.source_definition.startswith("src/apps/"),
"Rest of src/": lambda row: row.source_definition.startswith("src/"),
"Fonts": lambda row: row.source_definition.startswith("embed/lib/fonts/"),
"Embed firmware": lambda row: row.source_definition.startswith(
"embed/firmware/"
),
"Trezorhal": lambda row: row.source_definition.startswith("embed/trezorhal/"),
"Trezorio": lambda row: row.source_definition.startswith(
"embed/extmod/modtrezorio/"
),
"Trezorconfig": lambda row: row.source_definition.startswith(
"embed/extmod/modtrezorconfig/"
),
"Trezorutils": lambda row: row.source_definition.startswith(
"embed/extmod/modtrezorutils/"
),
"Embed extmod": lambda row: row.source_definition.startswith("embed/extmod/"),
"Embed lib": lambda row: row.source_definition.startswith("embed/lib/"),
"Rust": lambda row: (
row.language == "Rust"
or row.source_definition.startswith(("embed/rust/", "/cargo/registry"))
or row.symbol_name.startswith(("trezor_tjpgdec", "qrcodegen"))
or ".cargo/registry" in row.symbol_name
),
"Frozen modules": lambda row: row.symbol_name.startswith("frozen_module"),
".bootloader": lambda row: row.symbol_name == ".bootloader",
".rodata + qstr + misc": lambda row: (
row.symbol_name.startswith(
(".rodata", "mp_qstr", "str1", "*fill*", ".text", "OUTLINED_FUNCTION")
)
or _has_32_hex(row.symbol_name)
),
}
for category, func in CATEGORIES.items():
if func(row):
return category
return None
def _has_32_hex(text: str) -> bool:
if "." in text:
text = text.split(".")[0]
return len(text) == 32 and all(c in "0123456789abcdef" for c in text)
def show_categories_statistics(
STATS: StatisticsPlugin, include_categories_func: bool = False
) -> None:
STATS.show(include_none=True, include_categories_func=include_categories_func)
def show_data_with_categories(
STATS: StatisticsPlugin, file_to_save: str | Path | None = None
) -> None:
STATS.show_data_with_categories(file_to_save, include_none=True)
def show_only_one_category(
BS: BinarySize, category: str | None, file_to_save: str | Path | None = None
) -> None:
BS.filter(lambda row: _categories_func(row) == category).show(
file_to_save, debug=True
)
def show_raw_bloaty_data() -> None:
BinarySize().load_file(BIN_TO_ANALYZE, sections=(".flash", ".flash2")).show(
HERE / "size_binary_firmware_elf_results_no_aggregation.txt"
)
if __name__ == "__main__":
set_root_dir(str(CORE_DIR))
BS = (
BinarySize()
.load_file(BIN_TO_ANALYZE, sections=(".flash", ".flash2"))
.use_map_file(
CORE_DIR / "build/firmware/firmware.map", sections=(".flash", ".flash2")
)
.add_basic_info()
.aggregate()
.sort()
.add_definitions()
)
STATS = StatisticsPlugin(BS, _categories_func)
show_categories_statistics(STATS, include_categories_func=True)
show_data_with_categories(STATS, FILE_TO_SAVE)
show_only_one_category(BS, None, HERE / "size_binary_firmware_elf_results_None.txt")