mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-13 08:04:27 +00:00
feat(memmap): add tabular memory use data
Add support for tabulating static memory consumption data from ELF binaries. This relies on static symbols, defined in the linker files, that provide information about the memory ranges. Change-Id: Ie19cd2b80a7b591607640feeb84c63266963ea4d Signed-off-by: Harrison Mutai <harrison.mutai@arm.com>
This commit is contained in:
parent
fcb72e16ce
commit
d9d5eb138d
6 changed files with 132 additions and 2 deletions
|
@ -1269,6 +1269,9 @@ subsections:
|
|||
- title: Certificate Creation Tool
|
||||
scope: cert-create
|
||||
|
||||
- title: Memory Mapping Tool
|
||||
scope: memmap
|
||||
|
||||
deprecated:
|
||||
- cert_create
|
||||
|
||||
|
|
|
@ -113,6 +113,40 @@ For more detailed help instructions, run:
|
|||
|
||||
poetry run memory --help
|
||||
|
||||
Memory Footprint
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
The tool enables users to view static memory consumption. When the options
|
||||
``-f``, or ``--footprint`` are provided, the script analyses the ELF binaries in
|
||||
the build path to generate a table (per memory type), showing memory allocation
|
||||
and usage. This is the default output generated by the tool.
|
||||
|
||||
.. code:: shell
|
||||
|
||||
$ poetry run memory -f
|
||||
build-path: build/fvp/release
|
||||
+----------------------------------------------------------------------------+
|
||||
| Memory Usage (bytes) [RAM] |
|
||||
+-----------+------------+------------+------------+------------+------------+
|
||||
| Component | Start | Limit | Size | Free | Total |
|
||||
+-----------+------------+------------+------------+------------+------------+
|
||||
| BL1 | 4034000 | 4040000 | 7000 | 5000 | c000 |
|
||||
| BL2 | 4021000 | 4034000 | d000 | 6000 | 13000 |
|
||||
| BL2U | 4021000 | 4034000 | a000 | 9000 | 13000 |
|
||||
| BL31 | 4003000 | 4040000 | 1e000 | 1f000 | 3d000 |
|
||||
+-----------+------------+------------+------------+------------+------------+
|
||||
|
||||
+----------------------------------------------------------------------------+
|
||||
| Memory Usage (bytes) [ROM] |
|
||||
+-----------+------------+------------+------------+------------+------------+
|
||||
| Component | Start | Limit | Size | Free | Total |
|
||||
+-----------+------------+------------+------------+------------+------------+
|
||||
| BL1 | 0 | 4000000 | 5df0 | 3ffa210 | 4000000 |
|
||||
+-----------+------------+------------+------------+------------+------------+
|
||||
|
||||
The script relies on symbols in the symbol table to determine the start, end,
|
||||
and limit addresses of each bootloader stage.
|
||||
|
||||
--------------
|
||||
|
||||
*Copyright (c) 2023, Arm Limited. All rights reserved.*
|
||||
|
|
|
@ -50,6 +50,15 @@ class TfaBuildParser:
|
|||
reverse=True,
|
||||
)
|
||||
|
||||
def get_mem_usage_dict(self) -> dict:
|
||||
"""Returns map of memory usage per memory type for each module."""
|
||||
mem_map = {}
|
||||
for k, v in self._modules.items():
|
||||
mod_mem_map = v.get_elf_memory_layout()
|
||||
if len(mod_mem_map):
|
||||
mem_map[k] = mod_mem_map
|
||||
return mem_map
|
||||
|
||||
@property
|
||||
def module_names(self):
|
||||
"""Returns sorted list of module names."""
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
import re
|
||||
from typing import BinaryIO
|
||||
|
||||
from elftools.elf.elffile import ELFFile
|
||||
|
@ -28,6 +29,46 @@ class TfaElfParser:
|
|||
for sym in elf.get_section_by_name(".symtab").iter_symbols()
|
||||
}
|
||||
|
||||
self._memory_layout = self.get_memory_layout_from_symbols()
|
||||
|
||||
@property
|
||||
def symbols(self):
|
||||
return self._symbols.items()
|
||||
|
||||
def get_memory_layout_from_symbols(self, expr=None) -> dict:
|
||||
"""Retrieve information about the memory configuration from the symbol
|
||||
table.
|
||||
"""
|
||||
assert len(self._symbols), "Symbol table is empty!"
|
||||
|
||||
expr = r".*(.?R.M)_REGION.*(START|END|LENGTH)" if not expr else expr
|
||||
region_symbols = filter(lambda s: re.match(expr, s), self._symbols)
|
||||
memory_layout = {}
|
||||
|
||||
for symbol in region_symbols:
|
||||
region, _, attr = tuple(symbol.lower().strip("__").split("_"))
|
||||
if region not in memory_layout:
|
||||
memory_layout[region] = {}
|
||||
|
||||
# Retrieve the value of the symbol using the symbol as the key.
|
||||
memory_layout[region][attr] = self._symbols[symbol]
|
||||
|
||||
return memory_layout
|
||||
|
||||
def get_elf_memory_layout(self):
|
||||
"""Get the total memory consumed by this module from the memory
|
||||
configuration.
|
||||
{"rom": {"start": 0x0, "end": 0xFF, "length": ... }
|
||||
"""
|
||||
mem_dict = {}
|
||||
|
||||
for mem, attrs in self._memory_layout.items():
|
||||
limit = attrs["start"] + attrs["length"]
|
||||
mem_dict[mem] = {
|
||||
"start": attrs["start"],
|
||||
"limit": limit,
|
||||
"size": attrs["end"] - attrs["start"],
|
||||
"free": limit - attrs["end"],
|
||||
"total": attrs["length"],
|
||||
}
|
||||
return mem_dict
|
||||
|
|
|
@ -35,12 +35,17 @@ from memory.printer import TfaPrettyPrinter
|
|||
help="The target build type.",
|
||||
type=click.Choice(["debug", "release"], case_sensitive=False),
|
||||
)
|
||||
@click.option(
|
||||
"-f",
|
||||
"--footprint",
|
||||
is_flag=True,
|
||||
show_default=True,
|
||||
help="Generate a high level view of memory usage by memory types.",
|
||||
)
|
||||
@click.option(
|
||||
"-s",
|
||||
"--symbols",
|
||||
is_flag=True,
|
||||
show_default=True,
|
||||
default=True,
|
||||
help="Generate a map of important TF symbols.",
|
||||
)
|
||||
@click.option("-w", "--width", type=int, envvar="COLUMNS")
|
||||
|
@ -54,6 +59,7 @@ def main(
|
|||
root: Path,
|
||||
platform: str,
|
||||
build_type: str,
|
||||
footprint: bool,
|
||||
symbols: bool,
|
||||
width: int,
|
||||
d: bool,
|
||||
|
@ -64,6 +70,9 @@ def main(
|
|||
parser = TfaBuildParser(build_path)
|
||||
printer = TfaPrettyPrinter(columns=width, as_decimal=d)
|
||||
|
||||
if footprint or not symbols:
|
||||
printer.print_footprint(parser.get_mem_usage_dict())
|
||||
|
||||
if symbols:
|
||||
expr = (
|
||||
r"(.*)(TEXT|BSS|RODATA|STACKS|_OPS|PMF|XLAT|GOT|FCONF"
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
from prettytable import PrettyTable
|
||||
|
||||
|
||||
class TfaPrettyPrinter:
|
||||
"""A class for printing the memory layout of ELF files.
|
||||
|
@ -15,6 +17,7 @@ class TfaPrettyPrinter:
|
|||
|
||||
def __init__(self, columns: int = None, as_decimal: bool = False):
|
||||
self.term_size = columns if columns and columns > 120 else 120
|
||||
self._footprint = None
|
||||
self._symbol_map = None
|
||||
self.as_decimal = as_decimal
|
||||
|
||||
|
@ -50,6 +53,37 @@ class TfaPrettyPrinter:
|
|||
|
||||
return leading + sec_row_l + sec_row + sec_row_r
|
||||
|
||||
def print_footprint(
|
||||
self, app_mem_usage: dict, sort_key: str = None, fields: list = None
|
||||
):
|
||||
assert len(app_mem_usage), "Empty memory layout dictionary!"
|
||||
if not fields:
|
||||
fields = ["Component", "Start", "Limit", "Size", "Free", "Total"]
|
||||
|
||||
sort_key = fields[0] if not sort_key else sort_key
|
||||
|
||||
# Iterate through all the memory types, create a table for each
|
||||
# type, rows represent a single module.
|
||||
for mem in sorted(set(k for _, v in app_mem_usage.items() for k in v)):
|
||||
table = PrettyTable(
|
||||
sortby=sort_key,
|
||||
title=f"Memory Usage (bytes) [{mem.upper()}]",
|
||||
field_names=fields,
|
||||
)
|
||||
|
||||
for mod, vals in app_mem_usage.items():
|
||||
if mem in vals.keys():
|
||||
val = vals[mem]
|
||||
table.add_row(
|
||||
[
|
||||
mod.upper(),
|
||||
*self.format_args(
|
||||
*[val[k.lower()] for k in fields[1:]]
|
||||
),
|
||||
]
|
||||
)
|
||||
print(table, "\n")
|
||||
|
||||
def print_symbol_table(
|
||||
self,
|
||||
symbols: list,
|
||||
|
|
Loading…
Add table
Reference in a new issue