refactor(memmap): apply additional type hints
This is a non-functional change which adds type hints wherever they
might be helpful. Outside of human readability, this can also help
static analysis tools and IDEs identify additional errors.
Change-Id: Ie63e7f1cdeb893ca7d8a703aff56361eaa4e8f1b
Signed-off-by: Chris Kay <chris.kay@arm.com>
diff --git a/tools/memory/src/memory/buildparser.py b/tools/memory/src/memory/buildparser.py
index d62249f..8caffd8 100755
--- a/tools/memory/src/memory/buildparser.py
+++ b/tools/memory/src/memory/buildparser.py
@@ -6,6 +6,7 @@
import re
from pathlib import Path
+from typing import Any, Dict, List, Tuple, Union
from memory.elfparser import TfaElfParser
from memory.mapparser import TfaMapParser
@@ -14,17 +15,17 @@
class TfaBuildParser:
"""A class for performing analysis on the memory layout of a TF-A build."""
- def __init__(self, path: Path, map_backend=False):
- self._modules = dict()
- self._path = path
- self.map_backend = map_backend
+ def __init__(self, path: Path, map_backend: bool = False) -> None:
+ self._modules: Dict[str, Union[TfaElfParser, TfaMapParser]] = {}
+ self._path: Path = path
+ self.map_backend: bool = map_backend
self._parse_modules()
- def __getitem__(self, module: str):
+ def __getitem__(self, module: str) -> Union[TfaElfParser, TfaMapParser]:
"""Returns an TfaElfParser instance indexed by module."""
return self._modules[module]
- def _parse_modules(self):
+ def _parse_modules(self) -> None:
"""Parse the build files using the selected backend."""
backend = TfaElfParser
files = list(self._path.glob("**/*.elf"))
@@ -46,11 +47,13 @@
)
@property
- def symbols(self) -> list:
+ def symbols(self) -> List[Tuple[str, int, str]]:
return [(*sym, k) for k, v in self._modules.items() for sym in v.symbols]
@staticmethod
- def filter_symbols(symbols: list, regex: str) -> list:
+ def filter_symbols(
+ symbols: List[Tuple[str, int, str]], regex: str
+ ) -> List[Tuple[str, int, str]]:
"""Returns a map of symbols to modules."""
return sorted(
@@ -59,16 +62,16 @@
reverse=True,
)
- def get_mem_usage_dict(self) -> dict:
+ def get_mem_usage_dict(self) -> Dict[str, Dict[str, Dict[str, int]]]:
"""Returns map of memory usage per memory type for each module."""
- mem_map = {}
+ mem_map: Dict[str, Dict[str, Dict[str, int]]] = {}
for k, v in self._modules.items():
mod_mem_map = v.get_memory_layout()
if len(mod_mem_map):
mem_map[k] = mod_mem_map
return mem_map
- def get_mem_tree_as_dict(self) -> dict:
+ def get_mem_tree_as_dict(self) -> Dict[str, Dict[str, Any]]:
"""Returns _tree of modules, segments and segments and their total
memory usage."""
return {
@@ -81,6 +84,6 @@
}
@property
- def module_names(self):
+ def module_names(self) -> List[str]:
"""Returns sorted list of module names."""
return sorted(self._modules.keys())
diff --git a/tools/memory/src/memory/elfparser.py b/tools/memory/src/memory/elfparser.py
index 10d1123..559461e 100644
--- a/tools/memory/src/memory/elfparser.py
+++ b/tools/memory/src/memory/elfparser.py
@@ -6,9 +6,10 @@
import re
from dataclasses import asdict, dataclass
-from typing import BinaryIO
+from typing import BinaryIO, Dict, ItemsView, Iterable, List, Optional, Tuple, Union
from elftools.elf.elffile import ELFFile
+from elftools.elf.sections import Section
from elftools.elf.segments import Segment
@@ -18,7 +19,7 @@
start: int
end: int
size: int
- children: list
+ children: List["TfaMemObject"]
class TfaElfParser:
@@ -29,29 +30,35 @@
the contents an ELF file.
"""
- def __init__(self, elf_file: BinaryIO):
- self._segments = {}
- self._memory_layout = {}
+ def __init__(self, elf_file: BinaryIO) -> None:
+ self._segments: Dict[int, TfaMemObject] = {}
+ self._memory_layout: Dict[str, Dict[str, int]] = {}
elf = ELFFile(elf_file)
- self._symbols = {
+ self._symbols: Dict[str, int] = {
sym.name: sym.entry["st_value"]
for sym in elf.get_section_by_name(".symtab").iter_symbols()
}
self.set_segment_section_map(elf.iter_segments(), elf.iter_sections())
self._memory_layout = self.get_memory_layout_from_symbols()
- self._start = elf["e_entry"]
+ self._start: int = elf["e_entry"]
+ self._size: int
+ self._free: int
self._size, self._free = self._get_mem_usage()
- self._end = self._start + self._size
+ self._end: int = self._start + self._size
@property
- def symbols(self):
+ def symbols(self) -> ItemsView[str, int]:
return self._symbols.items()
@staticmethod
- def tfa_mem_obj_factory(elf_obj, name=None, children=None):
+ def tfa_mem_obj_factory(
+ elf_obj: Union[Segment, Section],
+ name: Optional[str] = None,
+ children: Optional[List[TfaMemObject]] = None,
+ ) -> TfaMemObject:
"""Converts a pyelfparser Segment or Section to a TfaMemObject."""
# Ensure each segment is provided a name since they aren't in the
# program header.
@@ -75,7 +82,7 @@
[] if not children else children,
)
- def _get_mem_usage(self) -> (int, int):
+ def _get_mem_usage(self) -> Tuple[int, int]:
"""Get total size and free space for this component."""
size = free = 0
@@ -90,7 +97,11 @@
return size, free
- def set_segment_section_map(self, segments, sections):
+ def set_segment_section_map(
+ self,
+ segments: Iterable[Segment],
+ sections: Iterable[Section],
+ ) -> None:
"""Set segment to section mappings."""
segments = list(filter(lambda seg: seg["p_type"] == "PT_LOAD", segments))
@@ -104,7 +115,7 @@
self._segments[n].children.append(self.tfa_mem_obj_factory(sec))
- def get_memory_layout_from_symbols(self) -> dict:
+ def get_memory_layout_from_symbols(self) -> Dict[str, Dict[str, int]]:
"""Retrieve information about the memory configuration from the symbol
table.
"""
@@ -112,7 +123,7 @@
expr = r".*(.?R.M)_REGION.*(START|END|LENGTH)"
region_symbols = filter(lambda s: re.match(expr, s), self._symbols)
- memory_layout = {}
+ memory_layout: Dict[str, Dict[str, int]] = {}
for symbol in region_symbols:
region, _, attr = tuple(symbol.lower().strip("__").split("_"))
@@ -124,16 +135,16 @@
return memory_layout
- def get_seg_map_as_dict(self):
+ def get_seg_map_as_dict(self) -> List[Dict[str, int]]:
"""Get a dictionary of segments and their section mappings."""
return [asdict(v) for k, v in self._segments.items()]
- def get_memory_layout(self):
+ def get_memory_layout(self) -> Dict[str, Dict[str, int]]:
"""Get the total memory consumed by this module from the memory
configuration.
{"rom": {"start": 0x0, "end": 0xFF, "length": ... }
"""
- mem_dict = {}
+ mem_dict: Dict[str, Dict[str, int]] = {}
for mem, attrs in self._memory_layout.items():
limit = attrs["start"] + attrs["length"]
@@ -146,7 +157,7 @@
}
return mem_dict
- def get_mod_mem_usage_dict(self):
+ def get_mod_mem_usage_dict(self) -> Dict[str, int]:
"""Get the total memory consumed by the module, this combines the
information in the memory configuration.
"""
diff --git a/tools/memory/src/memory/mapparser.py b/tools/memory/src/memory/mapparser.py
index 93442b4..37d0c12 100644
--- a/tools/memory/src/memory/mapparser.py
+++ b/tools/memory/src/memory/mapparser.py
@@ -5,7 +5,7 @@
#
from re import match, search
-from typing import TextIO
+from typing import Dict, ItemsView, TextIO
class TfaMapParser:
@@ -16,17 +16,17 @@
are supported at this stage.
"""
- def __init__(self, map_file: TextIO):
- self._symbols = self.read_symbols(map_file)
+ def __init__(self, map_file: TextIO) -> None:
+ self._symbols: Dict[str, int] = self.read_symbols(map_file)
@property
- def symbols(self):
+ def symbols(self) -> ItemsView[str, int]:
return self._symbols.items()
@staticmethod
- def read_symbols(file: TextIO) -> dict:
+ def read_symbols(file: TextIO) -> Dict[str, int]:
pattern = r"\b(0x\w*)\s*(\w*)\s="
- symbols = {}
+ symbols: Dict[str, int] = {}
for line in file.readlines():
match = search(pattern, line)
@@ -37,14 +37,14 @@
return symbols
- def get_memory_layout(self) -> dict:
+ def get_memory_layout(self) -> Dict[str, Dict[str, int]]:
"""Get the total memory consumed by this module from the memory
configuration.
{"rom": {"start": 0x0, "end": 0xFF, "length": ... }
"""
assert len(self._symbols), "Symbol table is empty!"
expr = r".*(.?R.M)_REGION.*(START|END|LENGTH)"
- memory_layout = {}
+ memory_layout: Dict[str, Dict[str, int]] = {}
region_symbols = filter(lambda s: match(expr, s), self._symbols)
diff --git a/tools/memory/src/memory/memmap.py b/tools/memory/src/memory/memmap.py
index f3ade87..553e51f 100755
--- a/tools/memory/src/memory/memmap.py
+++ b/tools/memory/src/memory/memmap.py
@@ -81,14 +81,14 @@
root: Path,
platform: str,
build_type: str,
- footprint: str,
+ footprint: bool,
tree: bool,
symbols: bool,
depth: int,
width: int,
d: bool,
no_elf_images: bool,
-):
+) -> None:
build_path = root if root else Path("build/", platform, build_type)
click.echo(f"build-path: {build_path.resolve()}")
diff --git a/tools/memory/src/memory/printer.py b/tools/memory/src/memory/printer.py
index dda7917..3690853 100755
--- a/tools/memory/src/memory/printer.py
+++ b/tools/memory/src/memory/printer.py
@@ -4,6 +4,8 @@
# SPDX-License-Identifier: BSD-3-Clause
#
+from typing import Any, Dict, List, Optional, Tuple
+
from anytree import RenderTree
from anytree.importer import DictImporter
from prettytable import PrettyTable
@@ -17,19 +19,29 @@
structured and consumed.
"""
- def __init__(self, columns: int, as_decimal: bool = False):
- self.term_size = columns
- self._tree = None
- self._footprint = None
- self._symbol_map = None
- self.as_decimal = as_decimal
+ def __init__(self, columns: int, as_decimal: bool = False) -> None:
+ self.term_size: int = columns
+ self._tree: Optional[List[str]] = None
+ self._symbol_map: Optional[List[str]] = None
+ self.as_decimal: bool = as_decimal
- def format_args(self, *args, width=10, fmt=None):
+ def format_args(
+ self,
+ *args: Any,
+ width: int = 10,
+ fmt: Optional[str] = None,
+ ) -> List[Any]:
if not fmt and type(args[0]) is int:
fmt = f">{width}x" if not self.as_decimal else f">{width}"
return [f"{arg:{fmt}}" if fmt else arg for arg in args]
- def format_row(self, leading, *args, width=10, fmt=None):
+ def format_row(
+ self,
+ leading: str,
+ *args: Any,
+ width: int = 10,
+ fmt: Optional[str] = None,
+ ) -> str:
formatted_args = self.format_args(*args, width=width, fmt=fmt)
return leading + " ".join(formatted_args)
@@ -41,7 +53,7 @@
columns: int,
width: int,
is_edge: bool = False,
- ):
+ ) -> str:
empty_col = "{:{}{}}"
# Some symbols are longer than the column width, truncate them until
@@ -58,7 +70,9 @@
return leading + sec_row_l + sec_row + sec_row_r
- def print_footprint(self, app_mem_usage: dict):
+ def print_footprint(
+ self, app_mem_usage: Dict[str, Dict[str, Dict[str, int]]]
+ ) -> None:
assert len(app_mem_usage), "Empty memory layout dictionary!"
fields = ["Component", "Start", "Limit", "Size", "Free", "Total"]
@@ -86,10 +100,10 @@
def print_symbol_table(
self,
- symbols: list,
- modules: list,
+ symbols: List[Tuple[str, int, str]],
+ modules: List[str],
start: int = 12,
- ):
+ ) -> None:
assert len(symbols), "Empty symbol list!"
modules = sorted(modules)
col_width = int((self.term_size - start) / len(modules))
@@ -125,8 +139,13 @@
print("\n".join(self._symbol_map))
def print_mem_tree(
- self, mem_map_dict, modules, depth=1, min_pad=12, node_right_pad=12
- ):
+ self,
+ mem_map_dict: Dict[str, Any],
+ modules: List[str],
+ depth: int = 1,
+ min_pad: int = 12,
+ node_right_pad: int = 12,
+ ) -> None:
# Start column should have some padding between itself and its data
# values.
anchor = min_pad + node_right_pad * (depth - 1)