summaryrefslogtreecommitdiffstats
path: root/rtemsspec/membench.py
diff options
context:
space:
mode:
Diffstat (limited to 'rtemsspec/membench.py')
-rw-r--r--rtemsspec/membench.py160
1 files changed, 160 insertions, 0 deletions
diff --git a/rtemsspec/membench.py b/rtemsspec/membench.py
new file mode 100644
index 00000000..a8bc670b
--- /dev/null
+++ b/rtemsspec/membench.py
@@ -0,0 +1,160 @@
+# 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)