[rtems-central commit] membench: Support variant comparison

Sebastian Huber sebh at rtems.org
Tue Nov 21 13:35:40 UTC 2023


Module:    rtems-central
Branch:    master
Commit:    010b732a89352993eda688f2352375464fd712e3
Changeset: http://git.rtems.org/rtems-central/commit/?id=010b732a89352993eda688f2352375464fd712e3

Author:    Sebastian Huber <sebastian.huber at embedded-brains.de>
Date:      Tue Nov 21 11:13:15 2023 +0100

membench: Support variant comparison

---

 generate_membench.py                               |   5 +-
 membench.py                                        |  43 ++-
 rtemsspec/membench.py                              | 367 +++++++++++++++++++--
 rtemsspec/tests/spec-membench/r2.yml               |  15 +
 rtemsspec/tests/spec-membench/r3.yml               |  15 +
 rtemsspec/tests/spec-membench/r4.yml               |  15 +
 .../tests/spec-membench/rtems/val/mem-basic.yml    |  54 +++
 rtemsspec/tests/spec-membench/t1.yml               |   2 +-
 rtemsspec/tests/spec-membench/t2.yml               |  20 ++
 rtemsspec/tests/spec-membench/t3.yml               |  20 ++
 rtemsspec/tests/test_membench.py                   | 194 ++++++++++-
 11 files changed, 692 insertions(+), 58 deletions(-)

diff --git a/generate_membench.py b/generate_membench.py
index 90c2e6e8..73f1c784 100755
--- a/generate_membench.py
+++ b/generate_membench.py
@@ -31,7 +31,7 @@ import textwrap
 from typing import NamedTuple, List, Optional
 
 from rtemsspec.items import ItemCache
-from rtemsspec.membench import generate
+from rtemsspec.membench import gather_sections, generate
 from rtemsspec.sphinxcontent import SphinxContent, SphinxMapper
 from rtemsspec.util import create_argument_parser, init_logging, load_config
 
@@ -823,9 +823,10 @@ def _post_process(path: str) -> None:
     config = load_config("config.yml")
     item_cache = ItemCache(config["spec"])
     root = item_cache["/rtems/req/mem-basic"]
+    sections_by_uid = gather_sections(item_cache, path, "objdump", "gdb")
     content = SphinxContent()
     table_pivots = ["/rtems/req/mem-basic", "/rtems/req/mem-smp-1"]
-    generate(content, root, SphinxMapper(root), table_pivots, path)
+    generate(content, sections_by_uid, root, table_pivots, SphinxMapper(root))
     print(content)
 
 
diff --git a/membench.py b/membench.py
index a8e3b4c0..87a513e6 100755
--- a/membench.py
+++ b/membench.py
@@ -26,30 +26,57 @@
 # POSSIBILITY OF SUCH DAMAGE.
 
 import sys
+from typing import Dict, List, Optional
 
-from rtemsspec.items import ItemCache, item_is_enabled
-from rtemsspec.membench import generate
+from rtemsspec.items import ItemCache
+from rtemsspec.membench import gather_sections, generate, \
+    generate_variants_table, MembenchVariant, SectionsByUID
 from rtemsspec.sphinxcontent import SphinxContent, SphinxMapper
 from rtemsspec.util import create_argument_parser, init_logging, load_config
 
 
+def _split(value: Optional[str]) -> List[str]:
+    return value.split(",") if value else []
+
+
 def main() -> None:
     """ Generates a memory benchmark report. """
     parser = create_argument_parser()
     parser.add_argument(
         "builddir",
         metavar="BUILDDIR",
-        nargs=1,
+        nargs="+",
         help="the build directory containing the memory benchmark executables")
+    parser.add_argument(
+        "--enabled",
+        help=("a comma separated list of enabled options used to evaluate "
+              "enabled-by expressions"))
+    parser.add_argument("--variants",
+                        help="a comma separated list of variant names")
     args = parser.parse_args(sys.argv[1:])
     init_logging(args)
-    config = load_config("config.yml")
-    item_cache = ItemCache(config["spec"])
-    item_cache.set_enabled([], item_is_enabled)
+    config = load_config("config.yml")["spec"]
+    config["enabled"] = _split(args.enabled)
+    item_cache = ItemCache(config)
     content = SphinxContent()
     root = item_cache["/rtems/req/mem-basic"]
-    table_pivots = ["/rtems/req/mem-basic", "/rtems/req/mem-smp-1"]
-    generate(content, root, SphinxMapper(root), table_pivots, args.builddir[0])
+    table_pivots = ["/rtems/req/mem-smp-1"]
+    if len(args.builddir) == 1:
+        sections_by_uid = gather_sections(item_cache, args.builddir[0],
+                                          "objdump", "gdb")
+        generate(content, sections_by_uid, root, table_pivots,
+                 SphinxMapper(root))
+    else:
+        names = _split(args.variants)
+        assert len(names) == len(args.builddir)
+        sections_by_build_label: Dict[str, Dict[str, SectionsByUID]] = {}
+        variants: List[MembenchVariant] = []
+        for name, builddir in zip(names, args.builddir):
+            sections_by_build_label[name]["membench"] = gather_sections(
+                item_cache, builddir, "objdump", "gdb")
+            variants.append(MembenchVariant(name, name))
+        generate_variants_table(content, sections_by_build_label, root,
+                                variants)
     print(str(content))
 
 
diff --git a/rtemsspec/membench.py b/rtemsspec/membench.py
index 6453aad1..b395f728 100644
--- a/rtemsspec/membench.py
+++ b/rtemsspec/membench.py
@@ -4,7 +4,7 @@ This module provides functions for the generation of memory benchmark
 documentation.
 """
 
-# Copyright (C) 2021 embedded brains GmbH & Co. KG
+# Copyright (C) 2021, 2023 embedded brains GmbH & Co. KG
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions
@@ -29,12 +29,25 @@ documentation.
 
 import os
 import re
-from typing import Dict, List, Tuple
+from typing import Any, Dict, List, NamedTuple, Tuple
 
-from rtemsspec.items import Item, ItemMapper
-from rtemsspec.sphinxcontent import make_label, get_reference, SphinxContent
+from rtemsspec.items import Item, ItemCache, ItemMapper
+from rtemsspec.sphinxcontent import get_reference, SphinxContent
 from rtemsspec.util import run_command
 
+
+class MembenchVariant(NamedTuple):
+    """ Represents a static memory benchmark configuration. """
+    name: str
+    build_label: str
+
+
+SectionsByUID = Dict[str, Dict[str, int]]
+
+_SECTIONS = {".text": 0, ".rodata": 1, ".data": 2, ".bss": 3, ".noinit": 4}
+
+_SECTION_KEYS = tuple(sorted(_SECTIONS.keys(), key=lambda x: _SECTIONS[x]))
+
 _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+.*$")
@@ -53,6 +66,7 @@ _SECTION_MAP = {
     ".debug_line_str": None,
     ".debug_loc": None,
     ".debug_loclists": None,
+    ".debug_pubnames": None,
     ".debug_ranges": None,
     ".debug_rnglists": None,
     ".debug_str": None,
@@ -81,6 +95,184 @@ _SECTION_MAP = {
     ".xbarrier": None,
 }
 
+_OBJECT_SIZES = {
+    "/c/void-pointer": {
+        "commands": ["sizeof(void *)"],
+        "uid": "/rtems/val/mem-basic"
+    },
+    "/rtems/barrier/info": {
+        "commands": ["sizeof(_Barrier_Information)"],
+        "uid": "/rtems/barrier/val/mem-wait-rel-del"
+    },
+    "/rtems/barrier/obj": {
+        "commands": ["sizeof(Objects_Control *) + sizeof(Barrier_Control)"],
+        "uid": "/rtems/barrier/val/mem-wait-rel-del"
+    },
+    "/rtems/message/info": {
+        "commands": ["sizeof(_Message_queue_Information)"],
+        "uid": "/rtems/message/val/mem-snd-rcv-del"
+    },
+    "/rtems/message/obj": {
+        "commands":
+        ["sizeof(Objects_Control *) + sizeof(Message_queue_Control)"],
+        "uid": "/rtems/message/val/mem-snd-rcv-del"
+    },
+    "/rtems/part/info": {
+        "commands": ["sizeof(_Partition_Information)"],
+        "uid": "/rtems/part/val/mem-get-ret-del"
+    },
+    "/rtems/part/obj": {
+        "commands": ["sizeof(Objects_Control *) + sizeof(Partition_Control)"],
+        "uid": "/rtems/part/val/mem-get-ret-del"
+    },
+    "/rtems/priority/control": {
+        "commands": ["sizeof(Priority_Control)"],
+        "uid": "/rtems/sem/val/mem-obt-rel-del"
+    },
+    "/rtems/ratemon/info": {
+        "commands": ["sizeof(_Rate_monotonic_Information)"],
+        "uid": "/rtems/ratemon/val/mem-period-del"
+    },
+    "/rtems/ratemon/obj": {
+        "commands":
+        ["sizeof(Objects_Control *) + sizeof(Rate_monotonic_Control)"],
+        "uid": "/rtems/ratemon/val/mem-period-del"
+    },
+    "/rtems/sem/info": {
+        "commands": ["sizeof(_Semaphore_Information)"],
+        "uid": "/rtems/sem/val/mem-obt-rel-del"
+    },
+    "/rtems/sem/obj": {
+        "commands": ["sizeof(Objects_Control *) + sizeof(Semaphore_Control)"],
+        "uid": "/rtems/sem/val/mem-obt-rel-del"
+    },
+    "/rtems/sem/obj-mrsp": {
+        "commands": [
+            "sizeof(Objects_Control *) + sizeof(Objects_Control) + "
+            "sizeof(MRSP_Control)"
+        ],
+        "uid":
+        "/rtems/sem/val/mem-obt-rel-del"
+    },
+    "/rtems/task/info": {
+        "commands": ["sizeof(_RTEMS_tasks_Information)"],
+        "uid": "/rtems/val/mem-basic"
+    },
+    "/rtems/task/obj": {
+        "commands": [
+            "sizeof(Objects_Control *) + sizeof(Thread_Control) + "
+            "sizeof(RTEMS_API_Control) + sizeof(Thread_queue_Heads)",
+            "sizeof(Objects_Control *) + sizeof(Thread_Control) + "
+            "sizeof(RTEMS_API_Control)"
+        ],
+        "uid":
+        "/rtems/val/mem-basic"
+    },
+    "/rtems/timer/info": {
+        "commands": ["sizeof(_Timer_Information)"],
+        "uid": "/rtems/timer/val/mem-after"
+    },
+    "/rtems/timer/obj": {
+        "commands": ["sizeof(Objects_Control *) + sizeof(Timer_Control)"],
+        "uid": "/rtems/timer/val/mem-after"
+    },
+    "/rtems/userext/info": {
+        "commands": ["sizeof(_Extension_Information)"],
+        "uid": "/rtems/userext/val/mem-create"
+    },
+    "/rtems/userext/obj": {
+        "commands": ["sizeof(Objects_Control *) + sizeof(Extension_Control)"],
+        "uid": "/rtems/userext/val/mem-create"
+    },
+    "/scheduler/control": {
+        "commands": ["sizeof(Scheduler_Control)"],
+        "uid": "/rtems/val/mem-basic"
+    },
+    "/scheduler/priority/context": {
+        "commands": ["sizeof(Scheduler_priority_Context)"],
+        "uid": "/rtems/val/mem-basic"
+    },
+    "/scheduler/priority/node": {
+        "commands": ["sizeof(Scheduler_priority_Node)"],
+        "uid": "/rtems/val/mem-basic"
+    },
+    "/scheduler/smp/edf/context": {
+        "commands": [
+            "sizeof(Scheduler_EDF_SMP_Context) + "
+            "sizeof(Scheduler_EDF_SMP_Ready_queue)"
+        ],
+        "uid":
+        "/rtems/val/mem-smp-1"
+    },
+    "/scheduler/smp/edf/node": {
+        "commands": ["sizeof(Scheduler_EDF_SMP_Node)"],
+        "uid": "/rtems/val/mem-smp-1"
+    },
+    "/scheduler/smp/edf/ready-queue": {
+        "commands": ["sizeof(Scheduler_EDF_SMP_Ready_queue)"],
+        "uid": "/rtems/val/mem-smp-1"
+    },
+    "/score/chain/control": {
+        "commands": ["sizeof(Chain_Control)"],
+        "uid": "/rtems/val/mem-basic"
+    },
+    "/score/tq/priority-queue": {
+        "commands": ["sizeof(Thread_queue_Priority_queue)"],
+        "uid": "/rtems/val/mem-basic"
+    },
+}
+
+
+def _section_key(key_value: Tuple[str, Any]) -> int:
+    return _SECTIONS[key_value[0]]
+
+
+def _get_sections_of_item(sections_by_uid: SectionsByUID,
+                          item: Item) -> Tuple[int, ...]:
+    return tuple(size for _, size in sorted(
+        sections_by_uid.get(item.uid, {}).items(), key=_section_key))
+
+
+def gather_sections(item_cache: ItemCache, path: str, objdump: str,
+                    gdb: str) -> Dict[str, Dict[str, int]]:
+    """
+    Gathers the object sizes for all memory benchmarks of the item cache using
+    the programs of the path.
+    """
+    sections_by_uid: Dict[str, Dict[str, int]] = {}
+    for item in item_cache.items_by_type["memory-benchmark"]:
+        sections = _get_sections(item, path, objdump, gdb)
+        if sections:
+            sections_by_uid[item.uid] = sections
+    return sections_by_uid
+
+
+def gather_object_sizes(item_cache: ItemCache, path: str,
+                        gdb: str) -> Dict[str, int]:
+    """
+    Gathers the object sizes for all memory benchmarks of the item cache using
+    the programs of the path.
+    """
+    object_sizes: Dict[str, int] = {}
+    for key, value in _OBJECT_SIZES.items():
+        try:
+            item = item_cache[value["uid"]]
+        except KeyError:
+            continue
+        file = _get_path_to_test_suite_elf_file(item, path)
+        for command in value["commands"]:
+            cmd = [gdb, "-ex", f"p {command}", "--batch", ""]
+            cmd[-1] = file
+            stdout: List[str] = []
+            status = run_command(cmd, stdout=stdout)
+            if status == 0:
+                output = " ".join(stdout)
+                match = re.search(r"\$[0-9]+\s*=\s*([0-9]+)", output)
+                assert match
+                object_sizes[key] = int(match.group(1))
+                break
+    return object_sizes
+
 
 def _do_gather_test_suites(items: List[Item], item: Item) -> None:
     if item.type == "memory-benchmark":
@@ -91,26 +283,64 @@ def _do_gather_test_suites(items: List[Item], item: Item) -> None:
         _do_gather_test_suites(items, child)
 
 
-def gather_benchmarks(root: Item) -> List[Item]:
+def _gather_benchmarks(root: Item) -> List[Item]:
     """ Gather all test suite items related to the root item. """
     items: List[Item] = []
     _do_gather_test_suites(items, root)
     return items
 
 
-def get_path_to_test_suite_elf_file(item: Item, path: str) -> str:
+def _get_path_to_test_suite_elf_file(item: Item, path: str) -> str:
     """ Returns the path to the ELF file of the test suite. """
-    name = os.path.basename(item.uid).replace("mem-", "")
-    module = os.path.basename(os.path.dirname(os.path.dirname(item.uid)))
-    return f"{path}/mem-{module}-{name}.norun.exe"
+    name = item["test-target"]
+    return os.path.join(path, f"{name[:name.rfind('.')]}.norun.exe")
+
+
+def _try_add_workspace(gdb: str, elf: str,
+                       section_limits: Dict[str, Tuple[int, int]]) -> None:
+    if ".noinit" in section_limits:
+        return
+    # Maybe an older version of RTEMS
+    stdout: List[str] = []
+    cmd = [gdb, "-ex", "p Configuration.work_space_size", "--batch", elf]
+    status = run_command(cmd, stdout=stdout)
+    if status == 0 and stdout[0].startswith("$1 = "):
+        section_limits[".noinit"] = (0, int(stdout[0][5:]))
+
+
+def _get_size(section_limits: Dict[str, Tuple[int, int]], key: str) -> int:
+    limits = section_limits.get(key, (0, 0))
+    return limits[1] - limits[0]
 
 
-def _get_sections(item: Item, path: str) -> Dict[str, Tuple[int, int]]:
-    elf = get_path_to_test_suite_elf_file(item, path)
+def _run_objdump(objdump: str, elf: str) -> List[str]:
     stdout: List[str] = []
-    status = run_command(["objdump", "-h", elf], stdout=stdout)
-    assert status == 0
-    sections: Dict[str, Tuple[int, int]] = {}
+    status = run_command([objdump, "-h", elf], stdout=stdout)
+    if status != 0:
+        return []
+    return stdout
+
+
+def _make_sections(
+        section_limits: Dict[str, Tuple[int, int]]) -> Dict[str, int]:
+    sections: Dict[str, int] = {}
+    for key in _SECTIONS:
+        sections[key] = _get_size(section_limits, key)
+    sections[".noinit"] += _get_size(section_limits, ".vector")
+    return sections
+
+
+def _get_sections(item: Item, path_to_elf_files: str, objdump: str,
+                  gdb: str) -> Dict[str, int]:
+    """
+    Gets the memory benchmark sections of the program associated with the item
+    in the path to ELF files.
+    """
+    elf = _get_path_to_test_suite_elf_file(item, path_to_elf_files)
+    stdout = _run_objdump(objdump, elf)
+    if not stdout:
+        return {}
+    section_limits: Dict[str, Tuple[int, int]] = {}
     for line in stdout:
         match = _SECTION.search(line)
         if match:
@@ -120,29 +350,34 @@ def _get_sections(item: Item, path: str) -> Dict[str, Tuple[int, int]]:
             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
+                limits = section_limits.get(section, (2**64, 0))
+                section_limits[section] = (min(limits[0],
+                                               start), max(limits[1], end))
+    _try_add_workspace(gdb, elf, section_limits)
+    return _make_sections(section_limits)
 
 
 def _make_label(item: Item) -> str:
-    return make_label(f"MemBenchmark {item.uid[1:]}")
+    return f"Membench{item.ident}"
 
 
-def _generate_table(content: SphinxContent, items: List[Item],
-                    path: str) -> None:
+def _generate_table(content: SphinxContent, sections_by_uid: SectionsByUID,
+                    items: List[Item]) -> None:
     rows: List[Tuple[str, ...]] = []
     for index, item in enumerate(items):
-        sections = _get_sections(item, path)
-        name = (get_reference(_make_label(item), item.uid), )
+        sections = _get_sections_of_item(sections_by_uid, item)
+        spec = (get_reference(_make_label(item), item.uid), )
         if index == 0:
-            keys = ("spec", ) + tuple(sections.keys())
-            base = {key: info[1] - info[0] for key, info in sections.items()}
+            assert sections
+            keys = ("Specification", ) + _SECTION_KEYS
+            base = sections
             rows.append(keys)
-            rows.append(name + tuple(map(str, base.values())))
+            rows.append(spec + tuple(map(str, sections)))
+        elif sections:
+            rows.append(spec + tuple(f"{i - j:+}"
+                                     for i, j in zip(sections, base)))
         else:
-            rows.append(name + tuple(f"{info[1] - info[0] - base[key]:+}"
-                                     for key, info in sections.items()))
+            rows.append(spec + tuple("?") * len(keys))
 
     pivot = items[0]
     section = f"Benchmarks Based on: {pivot.spec}"
@@ -153,23 +388,83 @@ memory benchmark defined by
         content.add_simple_table(rows)
 
 
-def _generate_paragraphs(content: SphinxContent, items: List[Item],
-                         mapper: ItemMapper) -> None:
+_WARNING_NO_MEMBENCH = """.. topic:: WARNING
+
+    There are no results available for this static memory usage benchmark.
+"""
+
+
+def _generate_paragraphs(content: SphinxContent,
+                         sections_by_uid: SectionsByUID, mapper: ItemMapper,
+                         items: List[Item]) -> None:
     for item in items:
         section = f"Benchmark: {item.spec}"
         with content.section(section, label=_make_label(item)):
             content.wrap(mapper.substitute(item["test-brief"], item))
             content.wrap(mapper.substitute(item["test-description"], item))
+            sections = _get_sections_of_item(sections_by_uid, item)
+            if sections:
+                content.add_simple_table(
+                    [_SECTION_KEYS, tuple(map(str, sections))])
+            else:
+                content.add(_WARNING_NO_MEMBENCH)
+
+
+def _generate_tables(content: SphinxContent, sections_by_uid: SectionsByUID,
+                     root: Item, table_pivots: List[str]) -> List[Item]:
+    root_items = _gather_benchmarks(root)
+    _generate_table(content, sections_by_uid, root_items)
+    for pivot_uid in table_pivots:
+        pivot = root.map(pivot_uid)
+        if not pivot.enabled:
+            continue
+        pivot_items = _gather_benchmarks(pivot)
+        _generate_table(content, sections_by_uid, pivot_items)
+    return root_items
+
+
+def generate_tables(content: SphinxContent, sections_by_uid: SectionsByUID,
+                    root: Item, table_pivots: List[str]) -> None:
+    """ Generates memory benchmark tables. """
+    _generate_tables(content, sections_by_uid, root, table_pivots)
+
+
+def generate_variants_table(content: SphinxContent,
+                            sections_by_build_label: Dict[str,
+                                                          Dict[str,
+                                                               SectionsByUID]],
+                            root: Item,
+                            variants: List[MembenchVariant]) -> None:
+    """ Generates memory benchmark variant comparison tables. """
+    items = _gather_benchmarks(root)
+    rows: List[Tuple[str,
+                     ...]] = [("Specification", "Variant") + _SECTION_KEYS]
+    for item in items:
+        spec = get_reference(_make_label(item), item.uid)
+        for index, variant in enumerate(variants):
+            sections = _get_sections_of_item(
+                sections_by_build_label[variant.build_label]["membench"], item)
+            what = (spec, variant.name)
+            if index == 0:
+                assert sections
+                base = sections
+                rows.append(what + tuple(map(str, sections)))
+            else:
+                if sections:
+                    rows.append(what + tuple(f"{i - j:+}"
+                                             for i, j in zip(sections, base)))
+                else:
+                    rows.append(what + tuple("?") * len(base))
+            spec = ""
+    with content.latex_tiny("scriptsize"):
+        content.add_grid_table(rows, [35, 20, 9, 9, 9, 9, 9])
 
 
-def generate(content: SphinxContent, root: Item, mapper: ItemMapper,
-             table_pivots: List[str], path: str) -> None:
+def generate(content: SphinxContent, sections_by_uid: SectionsByUID,
+             root: Item, table_pivots: List[str], mapper: ItemMapper) -> None:
     """
     Generates memory benchmark documentation for items dependent on the root
     item and executables in the path.
     """
-    for pivot in table_pivots:
-        items = gather_benchmarks(root.map(pivot))
-        _generate_table(content, items, path)
-    items = gather_benchmarks(root)
-    _generate_paragraphs(content, items, mapper)
+    root_items = _generate_tables(content, sections_by_uid, root, table_pivots)
+    _generate_paragraphs(content, sections_by_uid, mapper, root_items)
diff --git a/rtemsspec/tests/spec-membench/r2.yml b/rtemsspec/tests/spec-membench/r2.yml
new file mode 100644
index 00000000..2ac74b9d
--- /dev/null
+++ b/rtemsspec/tests/spec-membench/r2.yml
@@ -0,0 +1,15 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2021 embedded brains GmbH & Co. KG
+enabled-by: true
+links:
+- role: requirement-refinement
+  uid: r0
+non-functional-type: quality
+rationale: null
+references: []
+requirement-type: non-functional
+text: |
+  The system shall provide a benchmark program to show the static memory usage
+  of a basic application configuration.
+type: requirement
diff --git a/rtemsspec/tests/spec-membench/r3.yml b/rtemsspec/tests/spec-membench/r3.yml
new file mode 100644
index 00000000..2ac74b9d
--- /dev/null
+++ b/rtemsspec/tests/spec-membench/r3.yml
@@ -0,0 +1,15 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2021 embedded brains GmbH & Co. KG
+enabled-by: true
+links:
+- role: requirement-refinement
+  uid: r0
+non-functional-type: quality
+rationale: null
+references: []
+requirement-type: non-functional
+text: |
+  The system shall provide a benchmark program to show the static memory usage
+  of a basic application configuration.
+type: requirement
diff --git a/rtemsspec/tests/spec-membench/r4.yml b/rtemsspec/tests/spec-membench/r4.yml
new file mode 100644
index 00000000..6ad6dc06
--- /dev/null
+++ b/rtemsspec/tests/spec-membench/r4.yml
@@ -0,0 +1,15 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2021 embedded brains GmbH & Co. KG
+enabled-by: false
+links:
+- role: requirement-refinement
+  uid: r0
+non-functional-type: quality
+rationale: null
+references: []
+requirement-type: non-functional
+text: |
+  The system shall provide a benchmark program to show the static memory usage
+  of a basic application configuration.
+type: requirement
diff --git a/rtemsspec/tests/spec-membench/rtems/val/mem-basic.yml b/rtemsspec/tests/spec-membench/rtems/val/mem-basic.yml
new file mode 100644
index 00000000..8cfcd1cf
--- /dev/null
+++ b/rtemsspec/tests/spec-membench/rtems/val/mem-basic.yml
@@ -0,0 +1,54 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2021 embedded brains GmbH & Co. KG
+enabled-by: true
+links: []
+test-brief: |
+  This static memory usage benchmark program facilitates a basic application
+  configuration.
+test-code: |
+  static void Init( rtems_task_argument arg )
+  {
+    (void) arg;
+
+    /* Nothing to do */
+  }
+
+  #define TASK_ATTRIBUTES RTEMS_DEFAULT_ATTRIBUTES
+
+  #define TASK_STORAGE_SIZE \
+    RTEMS_TASK_STORAGE_SIZE( \
+      RTEMS_MINIMUM_STACK_SIZE, \
+      TASK_ATTRIBUTES   )
+
+  #define CONFIGURE_APPLICATION_DOES_NOT_NEED_CLOCK_DRIVER
+
+  #define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 0
+
+  #define CONFIGURE_DISABLE_NEWLIB_REENTRANCY
+
+  #define CONFIGURE_APPLICATION_DISABLE_FILESYSTEM
+
+  #define CONFIGURE_IDLE_TASK_STORAGE_SIZE RTEMS_MINIMUM_STACK_SIZE
+
+  #define CONFIGURE_MAXIMUM_TASKS 1
+
+  #define CONFIGURE_RTEMS_INIT_TASKS_TABLE
+
+  #define CONFIGURE_INIT_TASK_ATTRIBUTES TASK_ATTRIBUTES
+
+  #define CONFIGURE_INIT_TASK_INITIAL_MODES RTEMS_DEFAULT_MODES
+
+  #define CONFIGURE_INIT_TASK_CONSTRUCT_STORAGE_SIZE TASK_STORAGE_SIZE
+
+  #define CONFIGURE_INIT
+
+  #include <rtems/confdefs.h>
+test-description: |
+  This resource benchmark is configured for exactly one processor, no clock
+  driver, no Newlib reentrancy support, and no file system.
+test-includes:
+- rtems.h
+test-local-includes: []
+test-target: mem-rtems-basic.c
+type: memory-benchmark
diff --git a/rtemsspec/tests/spec-membench/t1.yml b/rtemsspec/tests/spec-membench/t1.yml
index 1ea13629..b5683255 100644
--- a/rtemsspec/tests/spec-membench/t1.yml
+++ b/rtemsspec/tests/spec-membench/t1.yml
@@ -16,5 +16,5 @@ test-includes:
 - blue.h
 test-local-includes:
 - green.h
-test-target: t0.c
+test-target: t1.c
 type: memory-benchmark
diff --git a/rtemsspec/tests/spec-membench/t2.yml b/rtemsspec/tests/spec-membench/t2.yml
new file mode 100644
index 00000000..2fff64c4
--- /dev/null
+++ b/rtemsspec/tests/spec-membench/t2.yml
@@ -0,0 +1,20 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+code: |
+  /* Blue green code */
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH & Co. KG
+description: The Blue Green description.
+enabled-by: true
+links:
+- role: validation
+  uid: r2
+test-brief: The Blue Green brief description.
+test-code: |
+  /* Blue green code */
+test-description: The Blue Green description.
+test-includes:
+- blue.h
+test-local-includes:
+- green.h
+test-target: t2.c
+type: memory-benchmark
diff --git a/rtemsspec/tests/spec-membench/t3.yml b/rtemsspec/tests/spec-membench/t3.yml
new file mode 100644
index 00000000..cf20ed3f
--- /dev/null
+++ b/rtemsspec/tests/spec-membench/t3.yml
@@ -0,0 +1,20 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+code: |
+  /* Blue green code */
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH & Co. KG
+description: The Blue Green description.
+enabled-by: true
+links:
+- role: validation
+  uid: r3
+test-brief: The Blue Green brief description.
+test-code: |
+  /* Blue green code */
+test-description: The Blue Green description.
+test-includes:
+- blue.h
+test-local-includes:
+- green.h
+test-target: t3.c
+type: memory-benchmark
diff --git a/rtemsspec/tests/test_membench.py b/rtemsspec/tests/test_membench.py
index 76de9180..66ca8c7d 100644
--- a/rtemsspec/tests/test_membench.py
+++ b/rtemsspec/tests/test_membench.py
@@ -24,13 +24,26 @@
 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 # POSSIBILITY OF SUCH DAMAGE.
 
-from rtemsspec.membench import generate
+from rtemsspec.membench import gather_object_sizes, gather_sections, \
+    generate, generate_tables, generate_variants_table, MembenchVariant
 from rtemsspec.items import ItemCache, ItemMapper
 from rtemsspec.sphinxcontent import SphinxContent
 from rtemsspec.tests.util import create_item_cache_config_and_copy_spec
 
 
 def run_command(args, cwd=None, stdout=None):
+    if args[0] == "object-sizes":
+        if "Thread" in args[2]:
+            stdout.append("$1 = 42")
+            return 0
+        return 1
+    if args[0] == "gdb" and "t3" in args[-1]:
+        stdout.append("$1 = 133")
+        return 0
+    if "t2" in args[-1]:
+        return 1
+    if "t1" in args[-1] and "path-2" in args[-1]:
+        return 1
     stdout.extend([
         "  0 .start        00000708  00100000  00100000  00010000  2**2",
         "                  CONTENTS, ALLOC, LOAD, READONLY, CODE",
@@ -38,6 +51,11 @@ def run_command(args, cwd=None, stdout=None):
         "                  CONTENTS, ALLOC, LOAD, READONLY, CODE",
         " 22 .debug_aranges 000011d8  00000000  00000000  000202e8  2**3"
     ])
+    if "t1" in args[-1]:
+        stdout.extend([
+            "  3 .noinit       00001900  400809e0  400809e0  000909e0  2**3",
+            "                  ALLOC"
+        ])
     return 0
 
 
@@ -46,28 +64,55 @@ def test_membench(tmpdir, monkeypatch):
     item_cache_config = create_item_cache_config_and_copy_spec(
         tmpdir, "spec-membench", with_spec_types=True)
     item_cache = ItemCache(item_cache_config)
+    object_sizes = gather_object_sizes(item_cache, "path", "object-sizes")
+    assert len(object_sizes) == 2
+    assert object_sizes["/rtems/task/obj"] == 42
+    sections_by_uid = gather_sections(item_cache, "path", "objdump", "gdb")
+    sections_by_uid_2 = gather_sections(item_cache, "path-2", "objdump", "gdb")
     root = item_cache["/r0"]
     content = SphinxContent()
-    generate(content, root, ItemMapper(root), ["/r0"], "path")
+    generate(content, sections_by_uid, root, ["r0", "r4"], ItemMapper(root))
     assert str(content) == """.. _BenchmarksBasedOnSpecT0:
 
 Benchmarks Based on: spec:/t0
 =============================
 
 The following memory benchmarks are based on the memory benchmark defined by
-:ref:`spec:/t0 <MemBenchmarkT0>`.
+:ref:`spec:/t0 <MembenchT0>`.
 
 .. table::
     :class: longtable
 
-    =========================== =====
-    spec                        .text
-    =========================== =====
-    :ref:`/t0 <MemBenchmarkT0>` 38908
-    :ref:`/t1 <MemBenchmarkT1>` +0
-    =========================== =====
+    ======================= ===== ======= ===== ==== =======
+    Specification           .text .rodata .data .bss .noinit
+    ======================= ===== ======= ===== ==== =======
+    :ref:`/t0 <MembenchT0>` 38908 0       0     0    0
+    :ref:`/t1 <MembenchT1>` +0    +0      +0    +0   +6400
+    :ref:`/t2 <MembenchT2>` ?     ?       ?     ?    ?
+    :ref:`/t3 <MembenchT3>` +0    +0      +0    +0   +133
+    ======================= ===== ======= ===== ==== =======
 
-.. _MemBenchmarkT0:
+.. _BenchmarksBasedOnSpecT0:
+
+Benchmarks Based on: spec:/t0
+=============================
+
+The following memory benchmarks are based on the memory benchmark defined by
+:ref:`spec:/t0 <MembenchT0>`.
+
+.. table::
+    :class: longtable
+
+    ======================= ===== ======= ===== ==== =======
+    Specification           .text .rodata .data .bss .noinit
+    ======================= ===== ======= ===== ==== =======
+    :ref:`/t0 <MembenchT0>` 38908 0       0     0    0
+    :ref:`/t1 <MembenchT1>` +0    +0      +0    +0   +6400
+    :ref:`/t2 <MembenchT2>` ?     ?       ?     ?    ?
+    :ref:`/t3 <MembenchT3>` +0    +0      +0    +0   +133
+    ======================= ===== ======= ===== ==== =======
+
+.. _MembenchT0:
 
 Benchmark: spec:/t0
 ===================
@@ -76,7 +121,16 @@ The Blue Green brief description.
 
 The Blue Green description.
 
-.. _MemBenchmarkT1:
+.. table::
+    :class: longtable
+
+    ===== ======= ===== ==== =======
+    .text .rodata .data .bss .noinit
+    ===== ======= ===== ==== =======
+    38908 0       0     0    0
+    ===== ======= ===== ==== =======
+
+.. _MembenchT1:
 
 Benchmark: spec:/t1
 ===================
@@ -84,4 +138,122 @@ Benchmark: spec:/t1
 The Blue Green brief description.
 
 The Blue Green description.
+
+.. table::
+    :class: longtable
+
+    ===== ======= ===== ==== =======
+    .text .rodata .data .bss .noinit
+    ===== ======= ===== ==== =======
+    38908 0       0     0    6400
+    ===== ======= ===== ==== =======
+
+.. _MembenchT2:
+
+Benchmark: spec:/t2
+===================
+
+The Blue Green brief description.
+
+The Blue Green description.
+
+.. topic:: WARNING
+
+    There are no results available for this static memory usage benchmark.
+
+.. _MembenchT3:
+
+Benchmark: spec:/t3
+===================
+
+The Blue Green brief description.
+
+The Blue Green description.
+
+.. table::
+    :class: longtable
+
+    ===== ======= ===== ==== =======
+    .text .rodata .data .bss .noinit
+    ===== ======= ===== ==== =======
+    38908 0       0     0    133
+    ===== ======= ===== ==== =======
+"""
+    content = SphinxContent()
+    generate_tables(content, sections_by_uid, root, ["r0", "r4"])
+    assert str(content) == """.. _BenchmarksBasedOnSpecT0:
+
+Benchmarks Based on: spec:/t0
+=============================
+
+The following memory benchmarks are based on the memory benchmark defined by
+:ref:`spec:/t0 <MembenchT0>`.
+
+.. table::
+    :class: longtable
+
+    ======================= ===== ======= ===== ==== =======
+    Specification           .text .rodata .data .bss .noinit
+    ======================= ===== ======= ===== ==== =======
+    :ref:`/t0 <MembenchT0>` 38908 0       0     0    0
+    :ref:`/t1 <MembenchT1>` +0    +0      +0    +0   +6400
+    :ref:`/t2 <MembenchT2>` ?     ?       ?     ?    ?
+    :ref:`/t3 <MembenchT3>` +0    +0      +0    +0   +133
+    ======================= ===== ======= ===== ==== =======
+
+.. _BenchmarksBasedOnSpecT0:
+
+Benchmarks Based on: spec:/t0
+=============================
+
+The following memory benchmarks are based on the memory benchmark defined by
+:ref:`spec:/t0 <MembenchT0>`.
+
+.. table::
+    :class: longtable
+
+    ======================= ===== ======= ===== ==== =======
+    Specification           .text .rodata .data .bss .noinit
+    ======================= ===== ======= ===== ==== =======
+    :ref:`/t0 <MembenchT0>` 38908 0       0     0    0
+    :ref:`/t1 <MembenchT1>` +0    +0      +0    +0   +6400
+    :ref:`/t2 <MembenchT2>` ?     ?       ?     ?    ?
+    :ref:`/t3 <MembenchT3>` +0    +0      +0    +0   +133
+    ======================= ===== ======= ===== ==== =======
+"""
+    content = SphinxContent()
+    root_2 = item_cache["/r1"]
+    generate_variants_table(
+        content, {
+            "bla": {
+                "membench": sections_by_uid
+            },
+            "blb": {
+                "membench": sections_by_uid_2
+            }
+        }, root_2, [MembenchVariant("a", "bla"),
+                    MembenchVariant("b", "blb")])
+    assert str(content) == """.. raw:: latex
+
+    \\begin{scriptsize}
+
+.. table::
+    :class: longtable
+    :widths: 35,20,9,9,9,9,9
+
+    +-------------------------+---------+-------+---------+-------+------+---------+
+    | Specification           | Variant | .text | .rodata | .data | .bss | .noinit |
+    +=========================+=========+=======+=========+=======+======+=========+
+    | :ref:`/t0 <MembenchT0>` | a       | 38908 | 0       | 0     | 0    | 0       |
+    +                         +---------+-------+---------+-------+------+---------+
+    |                         | b       | +0    | +0      | +0    | +0   | +0      |
+    +-------------------------+---------+-------+---------+-------+------+---------+
+    | :ref:`/t1 <MembenchT1>` | a       | 38908 | 0       | 0     | 0    | 6400    |
+    +                         +---------+-------+---------+-------+------+---------+
+    |                         | b       | ?     | ?       | ?     | ?    | ?       |
+    +-------------------------+---------+-------+---------+-------+------+---------+
+
+.. raw:: latex
+
+    \\end{scriptsize}
 """



More information about the vc mailing list