1#
2# Copyright (c) 2023, Arm Limited. All rights reserved.
3#
4# SPDX-License-Identifier: BSD-3-Clause
5#
6
7import re
8from pathlib import Path
9
10from memory.elfparser import TfaElfParser
11from memory.mapparser import TfaMapParser
12
13
14class TfaBuildParser:
15    """A class for performing analysis on the memory layout of a TF-A build."""
16
17    def __init__(self, path: Path, map_backend=False):
18        self._modules = dict()
19        self._path = path
20        self.map_backend = map_backend
21        self._parse_modules()
22
23    def __getitem__(self, module: str):
24        """Returns an TfaElfParser instance indexed by module."""
25        return self._modules[module]
26
27    def _parse_modules(self):
28        """Parse the build files using the selected backend."""
29        backend = TfaElfParser
30        files = list(self._path.glob("**/*.elf"))
31        io_perms = "rb"
32
33        if self.map_backend or len(files) == 0:
34            backend = TfaMapParser
35            files = self._path.glob("**/*.map")
36            io_perms = "r"
37
38        for file in files:
39            module_name = file.name.split("/")[-1].split(".")[0]
40            with open(file, io_perms) as f:
41                self._modules[module_name] = backend(f)
42
43        if not len(self._modules):
44            raise FileNotFoundError(
45                f"failed to find files to analyse in path {self._path}!"
46            )
47
48    @property
49    def symbols(self) -> list:
50        return [
51            (*sym, k) for k, v in self._modules.items() for sym in v.symbols
52        ]
53
54    @staticmethod
55    def filter_symbols(symbols: list, regex: str = None) -> list:
56        """Returns a map of symbols to modules."""
57        regex = r".*" if not regex else regex
58        return sorted(
59            filter(lambda s: re.match(regex, s[0]), symbols),
60            key=lambda s: (-s[1], s[0]),
61            reverse=True,
62        )
63
64    def get_mem_usage_dict(self) -> dict:
65        """Returns map of memory usage per memory type for each module."""
66        mem_map = {}
67        for k, v in self._modules.items():
68            mod_mem_map = v.get_memory_layout()
69            if len(mod_mem_map):
70                mem_map[k] = mod_mem_map
71        return mem_map
72
73    def get_mem_tree_as_dict(self) -> dict:
74        """Returns _tree of modules, segments and segments and their total
75        memory usage."""
76        return {
77            k: {
78                "name": k,
79                **v.get_mod_mem_usage_dict(),
80                **{"children": v.get_seg_map_as_dict()},
81            }
82            for k, v in self._modules.items()
83        }
84
85    @property
86    def module_names(self):
87        """Returns sorted list of module names."""
88        return sorted(self._modules.keys())
89