# SPDX-License-Identifier: BSD-2-Clause """ This module provides functions for the generation of memory benchmark documentation. """ # Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de) # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. import os import re from typing import Dict, List, Tuple from rtemsspec.items import Item, ItemMapper from rtemsspec.sphinxcontent import get_label, get_reference, SphinxContent from rtemsspec.util import run_command _SECTION = re.compile( r"^\s*\d+\s+(\S+)\s+([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s+[0-9a-fA-F]+" r"\s+[0-9a-fA-F]+\s+.*$") _SECTION_MAP = { ".ARM.attributes": None, ".ARM.exidx": ".rodata", ".bss": ".bss", ".comment": None, ".data": ".data", ".debug_abbrev": None, ".debug_aranges": None, ".debug_frame": None, ".debug_info": None, ".debug_line": None, ".debug_loc": None, ".debug_ranges": None, ".debug_str": None, ".eh_frame": ".rodata", ".fini": ".text", ".fini_array": ".rodata", ".init": ".text", ".init_array": ".rodata", ".nocachenoload": None, ".robarrier": None, ".rodata": ".rodata", ".rtemsroset": ".rodata", ".rtemsrwset": ".data", ".rtemsstack": ".rtemsstack", ".rwbarrier": None, ".stack": None, ".start": ".text", ".text": ".text", ".vector": ".vector", ".work": None, ".xbarrier": None, } def _do_gather_items(items: List[Item], item: Item) -> None: if item.type == "test-suite": items.append(item) for child in item.children("validation"): _do_gather_items(items, child) for child in item.children("requirement-refinement"): _do_gather_items(items, child) def _gather_items(root: Item) -> List[Item]: items = [] # type: List[Item] _do_gather_items(items, root) return items def _get_sections(item: Item, path: str) -> Dict[str, Tuple[int, int]]: name = os.path.basename(item.uid).replace("mem-", "") module = os.path.basename(os.path.dirname(os.path.dirname(item.uid))) elf = f"{path}/mem-{module}-{name}.norun.exe" stdout = [] # type: List[str] status = run_command(["objdump", "-h", elf], stdout=stdout) assert status == 0 sections = {} # type: Dict[str, Tuple[int, int]] for line in stdout: match = _SECTION.search(line) if match: name = match.group(1) size = int(match.group(2), 16) section = _SECTION_MAP[name] if size != 0 and section: start = int(match.group(3), 16) end = start + size info = sections.get(section, (2**64, 0)) sections[section] = (min(info[0], start), max(info[1], end)) return sections def _get_label(item: Item) -> str: return get_label(f"MemBenchmark {item.uid[1:]}") def _generate_table(content: SphinxContent, items: List[Item], path: str) -> None: rows = [] # type: List[Tuple[str, ...]] for index, item in enumerate(items): sections = _get_sections(item, path) name = (get_reference(_get_label(item), item.uid), ) if index == 0: keys = ("spec", ) + tuple(sections.keys()) base = {key: info[1] - info[0] for key, info in sections.items()} rows.append(keys) rows.append(name + tuple(map(str, base.values()))) else: rows.append(name + tuple(f"{info[1] - info[0] - base[key]:+}" for key, info in sections.items())) pivot = items[0] section = f"Benchmarks Based on: {pivot.spec}" with content.section(section): content.wrap(f"""The following memory benchmarks are based on the memory benchmark defined by {get_reference(_get_label(pivot), pivot.spec)}.""") content.add_simple_table(rows) def _generate_paragraphs(content: SphinxContent, items: List[Item], mapper: ItemMapper) -> None: for item in items: section = f"Benchmark: {item.spec}" with content.section(section, label=_get_label(item)): content.wrap(mapper.substitute(item["test-brief"], item)) content.wrap(mapper.substitute(item["test-description"], item)) def generate(content: SphinxContent, root: Item, mapper: ItemMapper, table_pivots: List[str], path: str) -> None: """ Generates memory benchmark documentation for items dependent on the root item and executables in the path. """ for pivot in table_pivots: items = _gather_items(root.map(pivot)) _generate_table(content, items, path) items = _gather_items(root) _generate_paragraphs(content, items, mapper)