refactor(memmap): create common image parser interface
This is another non-functional change, which factors the memory
footprint and symbol listing interfaces shared by the two image parsing
backends into a common `Image` interface.
Change-Id: I625a8e5ca77e38155b6ed545a33a8ae4c2cf133a
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 f811032..eaccd19 100755
--- a/tools/memory/src/memory/buildparser.py
+++ b/tools/memory/src/memory/buildparser.py
@@ -58,7 +58,7 @@
@staticmethod
def filter_symbols(
- symbols: Dict[str, Dict[str, int]], regex: str
+ images: Dict[str, Dict[str, int]], regex: str
) -> Dict[str, Dict[str, int]]:
"""Returns a map of symbols to modules."""
@@ -68,17 +68,12 @@
for symbol, symbol_value in symbols.items()
if re.match(regex, symbol)
}
- for image, symbols in symbols.items()
+ for image, symbols in images.items()
}
def get_mem_usage_dict(self) -> Dict[str, Dict[str, Region]]:
"""Returns map of memory usage per memory type for each module."""
- mem_map: Dict[str, Dict[str, Region]] = {}
- 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
+ return {k: v.footprint for k, v in self._modules.items()}
def get_mem_tree_as_dict(self) -> Dict[str, Dict[str, Any]]:
"""Returns _tree of modules, segments and segments and their total
diff --git a/tools/memory/src/memory/elfparser.py b/tools/memory/src/memory/elfparser.py
index e1b6cd3..33f36e3 100644
--- a/tools/memory/src/memory/elfparser.py
+++ b/tools/memory/src/memory/elfparser.py
@@ -20,7 +20,7 @@
from elftools.elf.sections import Section
from elftools.elf.segments import Segment
-from memory.image import Region
+from memory.image import Image, Region
@dataclass(frozen=True)
@@ -32,7 +32,7 @@
children: List["TfaMemObject"]
-class TfaElfParser:
+class TfaElfParser(Image):
"""A class representing an ELF file built for TF-A.
Provides a basic interface for reading the symbol table and other
@@ -59,6 +59,15 @@
self._size, self._free = self._get_mem_usage()
self._end: int = self._start + self._size
+ self._footprint: Dict[str, Region] = {}
+
+ for mem, attrs in self._memory_layout.items():
+ self._footprint[mem] = Region(
+ attrs["start"],
+ attrs["end"],
+ attrs["length"],
+ )
+
@property
def symbols(self) -> Dict[str, int]:
return self._symbols
@@ -164,6 +173,10 @@
return mem_dict
+ @property
+ def footprint(self) -> Dict[str, Region]:
+ return self._footprint
+
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/image.py b/tools/memory/src/memory/image.py
index b5653c7..cda1d8a 100644
--- a/tools/memory/src/memory/image.py
+++ b/tools/memory/src/memory/image.py
@@ -4,8 +4,9 @@
# SPDX-License-Identifier: BSD-3-Clause
#
+from abc import ABC, abstractmethod
from dataclasses import dataclass
-from typing import Optional
+from typing import Dict, Optional
@dataclass
@@ -56,3 +57,21 @@
return None
return self.limit - self.end
+
+
+class Image(ABC):
+ """An image under analysis."""
+
+ @property
+ @abstractmethod
+ def footprint(self) -> Dict[str, Region]:
+ """Get metrics about the memory regions that this image occupies."""
+
+ pass
+
+ @property
+ @abstractmethod
+ def symbols(self) -> Dict[str, int]:
+ """Get a dictionary of the image's symbols and their corresponding addresses."""
+
+ pass
diff --git a/tools/memory/src/memory/mapparser.py b/tools/memory/src/memory/mapparser.py
index eed75f0..24ee264 100644
--- a/tools/memory/src/memory/mapparser.py
+++ b/tools/memory/src/memory/mapparser.py
@@ -3,14 +3,15 @@
#
# SPDX-License-Identifier: BSD-3-Clause
#
+
from collections import defaultdict
from re import match, search
from typing import Dict, TextIO
-from memory.image import Region
+from memory.image import Image, Region
-class TfaMapParser:
+class TfaMapParser(Image):
"""A class representing a map file built for TF-A.
Provides a basic interface for reading the symbol table. The constructor
@@ -20,6 +21,20 @@
def __init__(self, map_file: TextIO) -> None:
self._symbols: Dict[str, int] = self.read_symbols(map_file)
+ assert self._symbols, "Symbol table is empty!"
+
+ self._footprint: Dict[str, Region] = defaultdict(Region)
+
+ expr = r".*(.?R.M)_REGION.*(START|END|LENGTH)"
+ for symbol in filter(lambda s: match(expr, s), self._symbols):
+ region, _, attr = symbol.lower().strip("__").split("_")
+
+ if attr == "start":
+ self._footprint[region].start = self._symbols[symbol]
+ elif attr == "end":
+ self._footprint[region].end = self._symbols[symbol]
+ if attr == "length":
+ self._footprint[region].length = self._symbols[symbol]
@property
def symbols(self) -> Dict[str, int]:
@@ -39,25 +54,6 @@
return symbols
- def get_memory_layout(self) -> Dict[str, Region]:
- """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: Dict[str, Region] = defaultdict(Region)
-
- region_symbols = filter(lambda s: match(expr, s), self._symbols)
-
- for symbol in region_symbols:
- region, _, attr = tuple(symbol.lower().strip("__").split("_"))
-
- if attr == "start":
- memory_layout[region].start = self._symbols[symbol]
- elif attr == "end":
- memory_layout[region].end = self._symbols[symbol]
- if attr == "length":
- memory_layout[region].length = self._symbols[symbol]
-
- return memory_layout
+ @property
+ def footprint(self) -> Dict[str, Region]:
+ return self._footprint