[rtems-central commit] gcdaproducer: New

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


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

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

gcdaproducer: New

---

 rtemsspec/gcdaproducer.py                          |  95 ++++++++++++
 rtemsspec/packagebuildfactory.py                   |   2 +
 .../tests/spec-packagebuild/qdp/build/bsp.yml      |   2 +
 .../tests/spec-packagebuild/qdp/build/gcda.yml     |  13 ++
 .../tests/spec-packagebuild/qdp/package-build.yml  |   2 +
 .../spec-packagebuild/qdp/steps/gcda-producer.yml  |  23 +++
 .../spec-packagebuild/qdp/test-logs/coverage.yml   |  15 ++
 rtemsspec/tests/test-files/pkg/build/bsp/f.gcda    |   0
 rtemsspec/tests/test-files/pkg/build/bsp/f.gcno    |   0
 .../tests/test-files/pkg/test-log-coverage.json    | 167 +++++++++++++++++++++
 rtemsspec/tests/test_packagebuild.py               |  21 +++
 spec-qdp/spec/qdp-gcda-producer.yml                |  30 ++++
 12 files changed, 370 insertions(+)

diff --git a/rtemsspec/gcdaproducer.py b/rtemsspec/gcdaproducer.py
new file mode 100644
index 00000000..23215b0c
--- /dev/null
+++ b/rtemsspec/gcdaproducer.py
@@ -0,0 +1,95 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" This module provides a build step to produce gcda files. """
+
+# Copyright (C) 2022 embedded brains GmbH & Co. KG
+#
+# 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 base64
+import glob
+import json
+import logging
+import os
+import shutil
+from subprocess import run as subprocess_run
+
+from rtemsspec.directorystate import DirectoryState
+from rtemsspec.packagebuild import BuildItem
+
+
+class GCDAProducer(BuildItem):
+    """ Runs the gcov-tool to produce gcda files from a test log. """
+
+    def run(self):
+        build = self.input("build")
+        assert isinstance(build, DirectoryState)
+
+        destination = self.output("destination")
+        assert isinstance(destination, DirectoryState)
+
+        logging.info("%s: copy *.gcno files from '%s' to '%s'", self.uid,
+                     build.directory, destination.directory)
+        for file in build.files():
+            assert not file.endswith(".gcda")
+            if file.endswith(".gcno"):
+                file_dest = file.replace(build.directory,
+                                         destination.directory)
+                os.makedirs(os.path.dirname(file_dest), exist_ok=True)
+                shutil.copy2(file, file_dest)
+
+        gcda_pattern = f"{build.directory}/**/*.gcda"
+        for file in glob.glob(gcda_pattern, recursive=True):
+            logging.warning(
+                "%s: remove unexpected *.gcda file in build directory: '%s'",
+                self.uid, file)
+            os.remove(file)
+
+        log = self.input("log")
+        assert isinstance(log, DirectoryState)
+
+        gcov_tool = self["gcov-tool"]
+        cwd = self["working-directory"]
+
+        with open(log.file, "r", encoding="utf-8") as src:
+            data = json.load(src)
+
+        for report in data["reports"]:
+            if "line-end-of-test" not in report["info"]:
+                # Do not use coverage data of failed tests
+                continue
+            begin = report.get("line-gcov-info-base64-begin", -1)
+            end = report.get("line-gcov-info-base64-end", -1)
+            if begin >= 0 and end >= 0:
+                logging.debug("%s: process: %s", self.uid,
+                              report["executable"])
+                gcov_info = base64.b64decode("".join(report["output"][begin +
+                                                                      1:end]))
+                subprocess_run([gcov_tool, "merge-stream"],
+                               check=True,
+                               cwd=cwd,
+                               input=gcov_info)
+
+        logging.info("%s: move *.gcda files from '%s' to '%s'", self.uid,
+                     build.directory, destination.directory)
+        for file in glob.glob(gcda_pattern, recursive=True):
+            file_dest = file.replace(build.directory, destination.directory)
+            os.replace(file, file_dest)
diff --git a/rtemsspec/packagebuildfactory.py b/rtemsspec/packagebuildfactory.py
index 28e52fa3..c38faf60 100644
--- a/rtemsspec/packagebuildfactory.py
+++ b/rtemsspec/packagebuildfactory.py
@@ -26,6 +26,7 @@
 
 from rtemsspec.archiver import Archiver
 from rtemsspec.directorystate import DirectoryState
+from rtemsspec.gcdaproducer import GCDAProducer
 from rtemsspec.packagebuild import BuildItemFactory, PackageVariant
 from rtemsspec.reposubset import RepositorySubset
 from rtemsspec.rtems import RTEMSItemCache
@@ -39,6 +40,7 @@ def create_build_item_factory() -> BuildItemFactory:
     """ Creates the default build item factory. """
     factory = BuildItemFactory()
     factory.add_constructor("qdp/build-step/archive", Archiver)
+    factory.add_constructor("qdp/build-step/gcda-producer", GCDAProducer)
     factory.add_constructor("qdp/build-step/repository-subset",
                             RepositorySubset)
     factory.add_constructor("qdp/build-step/rtems-item-cache", RTEMSItemCache)
diff --git a/rtemsspec/tests/spec-packagebuild/qdp/build/bsp.yml b/rtemsspec/tests/spec-packagebuild/qdp/build/bsp.yml
index 109b8ed8..eba8a01b 100644
--- a/rtemsspec/tests/spec-packagebuild/qdp/build/bsp.yml
+++ b/rtemsspec/tests/spec-packagebuild/qdp/build/bsp.yml
@@ -12,6 +12,8 @@ files:
   hash: null
 - file: b.norun.exe
   hash: null
+- file: f.gcno
+  hash: null
 hash: null
 links: []
 patterns: []
diff --git a/rtemsspec/tests/spec-packagebuild/qdp/build/gcda.yml b/rtemsspec/tests/spec-packagebuild/qdp/build/gcda.yml
new file mode 100644
index 00000000..687663e3
--- /dev/null
+++ b/rtemsspec/tests/spec-packagebuild/qdp/build/gcda.yml
@@ -0,0 +1,13 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2023 embedded brains GmbH & Co. KG
+copyrights-by-license: {}
+directory: ${../variant:/build-directory}/gcda
+directory-state-type: generic
+enabled-by: true
+files: []
+hash: null
+links: []
+patterns: []
+qdp-type: directory-state
+type: qdp
diff --git a/rtemsspec/tests/spec-packagebuild/qdp/package-build.yml b/rtemsspec/tests/spec-packagebuild/qdp/package-build.yml
index b97ccd14..3b8c2419 100644
--- a/rtemsspec/tests/spec-packagebuild/qdp/package-build.yml
+++ b/rtemsspec/tests/spec-packagebuild/qdp/package-build.yml
@@ -17,6 +17,8 @@ links:
   uid: steps/run-actions
 - role: build-step
   uid: steps/run-tests
+- role: build-step
+  uid: steps/gcda-producer
 - role: build-step
   uid: steps/archive
 qdp-type: package-build
diff --git a/rtemsspec/tests/spec-packagebuild/qdp/steps/gcda-producer.yml b/rtemsspec/tests/spec-packagebuild/qdp/steps/gcda-producer.yml
new file mode 100644
index 00000000..957464f3
--- /dev/null
+++ b/rtemsspec/tests/spec-packagebuild/qdp/steps/gcda-producer.yml
@@ -0,0 +1,23 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+build-step-type: gcda-producer
+copyrights:
+- Copyright (C) 2023 embedded brains GmbH & Co. KG
+description: |
+  Produces gcda files.
+enabled-by: gcda-producer
+gcov-tool: foo
+links:
+- hash: null
+  name: build
+  role: input
+  uid: ../build/bsp
+- hash: null
+  name: log
+  role: input
+  uid: ../test-logs/coverage
+- name: destination
+  role: output
+  uid: ../build/gcda
+qdp-type: build-step
+type: qdp
+working-directory: ${../build/bsp:/directory}
diff --git a/rtemsspec/tests/spec-packagebuild/qdp/test-logs/coverage.yml b/rtemsspec/tests/spec-packagebuild/qdp/test-logs/coverage.yml
new file mode 100644
index 00000000..abc53ac2
--- /dev/null
+++ b/rtemsspec/tests/spec-packagebuild/qdp/test-logs/coverage.yml
@@ -0,0 +1,15 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2023 embedded brains GmbH & Co. KG
+copyrights-by-license: {}
+directory: ${../variant:/deployment-directory}
+directory-state-type: generic
+enabled-by: true
+files:
+- file: test-log-coverage.json
+  hash: null
+hash: null
+links: []
+patterns: []
+qdp-type: directory-state
+type: qdp
diff --git a/rtemsspec/tests/test-files/pkg/build/bsp/f.gcda b/rtemsspec/tests/test-files/pkg/build/bsp/f.gcda
new file mode 100644
index 00000000..e69de29b
diff --git a/rtemsspec/tests/test-files/pkg/build/bsp/f.gcno b/rtemsspec/tests/test-files/pkg/build/bsp/f.gcno
new file mode 100644
index 00000000..e69de29b
diff --git a/rtemsspec/tests/test-files/pkg/test-log-coverage.json b/rtemsspec/tests/test-files/pkg/test-log-coverage.json
new file mode 100644
index 00000000..ef729a6a
--- /dev/null
+++ b/rtemsspec/tests/test-files/pkg/test-log-coverage.json
@@ -0,0 +1,167 @@
+{
+  "duration": 272.82567146699876,
+  "end-time": "2023-07-11T09:52:01.875951",
+  "reports": [
+    {
+      "command-line": [
+        "sparc-rtems6-sis",
+        "-leon3",
+        "-extirq",
+        "12",
+        "-dumbio",
+        "-r",
+        "-m",
+        "2",
+        "ts-unit-no-clock-0.exe"
+      ],
+      "data-ranges": [
+        [
+          41,
+          2081
+        ]
+      ],
+      "duration": 11.234533725000801,
+      "executable": "ts-unit-no-clock-0.exe",
+      "executable-sha512": "605858485568eff4a0e4ebccbc979d113b226127a9e13bffd4d650255074a5cbeeb0ca671742038a365ded9f049ae750752b4ef3d757d13a3ee44f866f4e9271",
+      "info": {
+        "build": [
+          "RTEMS_SMP"
+        ],
+        "line-begin-of-test": 9,
+        "line-build": 12,
+        "line-end-of-test": 37,
+        "line-state": 11,
+        "line-tools": 13,
+        "line-version": 10,
+        "name": "TestsuitesUnitNoClock0",
+        "state": "EXPECTED_PASS",
+        "tools": "10.4.0 20220628 (RTEMS 6, RSB f8d79ee51187d98b88b8b7400ad77a991f53c8c0, Newlib b9898fc)",
+        "version": "6.0.0.1c46841ad34939632fee92894e8c73b4ea92f979"
+      },
+      "line-gcov-info-base64-begin": 40,
+      "line-gcov-info-base64-end": 42,
+      "output": [
+        "",
+        " SIS - SPARC/RISCV instruction simulator 2.30,  copyright Jiri Gaisler 2020",
+        " Bug-reports to jiri at gaisler.se",
+        "",
+        " LEON3 emulation enabled, 2 cpus online, delta 50 clocks",
+        "",
+        " Loaded ts-unit-no-clock-0.exe, entry 0x40000000",
+        "",
+        "",
+        "*** BEGIN OF TEST TestsuitesUnitNoClock0 ***",
+        "*** TEST VERSION: 6.0.0.1c46841ad34939632fee92894e8c73b4ea92f979",
+        "*** TEST STATE: EXPECTED_PASS",
+        "*** TEST BUILD: RTEMS_SMP",
+        "*** TEST TOOLS: 10.4.0 20220628 (RTEMS 6, RSB f8d79ee51187d98b88b8b7400ad77a991f53c8c0, Newlib b9898fc)",
+        "A:TestsuitesUnitNoClock0",
+        "S:Platform:RTEMS",
+        "S:Compiler:10.4.0 20220628 (RTEMS 6, RSB f8d79ee51187d98b88b8b7400ad77a991f53c8c0, Newlib b9898fc)",
+        "S:Version:6.0.0.1c46841ad34939632fee92894e8c73b4ea92f979",
+        "S:BSP:gr712rc",
+        "S:BuildLabel:sparc/gr712rc/smp/qual-only-coverage",
+        "S:TargetHash:SHA256:5zrudgccHYC6azWEU3SRYUnkVXCeq9MufBd5zmfMeEg=",
+        "S:RTEMS_DEBUG:0",
+        "S:RTEMS_MULTIPROCESSING:0",
+        "S:RTEMS_POSIX_API:0",
+        "S:RTEMS_PROFILING:0",
+        "S:RTEMS_SMP:1",
+        "B:ScoreRbtreeUnitRbtree",
+        "E:ScoreRbtreeUnitRbtree:N:495132:F:0:D:1.164048",
+        "B:ScoreMsgqUnitMsgq",
+        "E:ScoreMsgqUnitMsgq:N:41:F:0:D:0.010895",
+        "B:RtemsConfigUnitConfig",
+        "E:RtemsConfigUnitConfig:N:1:F:0:D:0.001789",
+        "B:MisalignedBuiltinMemcpy",
+        "E:MisalignedBuiltinMemcpy:N:1:F:0:D:0.001803",
+        "Z:TestsuitesUnitNoClock0:C:4:N:495175:F:0:D:1.185642",
+        "Y:ReportHash:SHA256:a8e6RdqXa7Pt_SGpe7s2ezaVCSSY-j5PFEj6JTjrB4A=",
+        "",
+        "*** END OF TEST TestsuitesUnitNoClock0 ***",
+        "",
+        "",
+        "*** BEGIN OF GCOV INFO BASE64 ***",
+        "Z2NmbkIwNFIAAACVL29wdC==",
+        "*** END OF GCOV INFO BASE64 ***",
+        "cpu 0 in error mode (tt = 0x80)",
+        " 804542950  4003a5a0:  91d02000   ta  0x0"
+      ],
+      "start-time": "2023-07-11T09:47:29.059658"
+    },
+    {
+      "command-line": [
+        "sparc-rtems6-sis",
+        "-leon3",
+        "-extirq",
+        "12",
+        "-dumbio",
+        "-r",
+        "-m",
+        "2",
+        "ts-fatal-sparc-leon3-cache-snooping-disabled-boot.exe"
+      ],
+      "data-ranges": [
+        [
+          35,
+          1559
+        ]
+      ],
+      "duration": 1.5510155570082134,
+      "executable": "ts-fatal-sparc-leon3-cache-snooping-disabled-boot.exe",
+      "executable-sha512": "69f7d8cabeb23bba8c5b43fd658803f479d19a83cc858485ffc54bb727a0810bddb8ae61493839b4ade11094990e27c064426e45beaac3eadba01643ef120366",
+      "info": {
+        "build": [
+          "RTEMS_SMP"
+        ],
+        "line-begin-of-test": 9,
+        "line-build": 12,
+        "line-state": 11,
+        "line-tools": 13,
+        "line-version": 10,
+        "name": "TestsuitesBspsFatalSparcLeon3CacheSnoopingDisabledBoot",
+        "state": "EXPECTED_PASS",
+        "tools": "10.4.0 20220628 (RTEMS 6, RSB f8d79ee51187d98b88b8b7400ad77a991f53c8c0, Newlib b9898fc)",
+        "version": "6.0.0.1c46841ad34939632fee92894e8c73b4ea92f979"
+      }
+    },
+    {
+      "command-line": [
+        "sparc-rtems6-sis",
+        "-leon3",
+        "-extirq",
+        "12",
+        "-dumbio",
+        "-r",
+        "-m",
+        "2",
+        "ts-fatal-sparc-leon3-cache-snooping-disabled-boot.exe"
+      ],
+      "data-ranges": [
+        [
+          35,
+          1559
+        ]
+      ],
+      "duration": 1.5510155570082134,
+      "executable": "ts-fatal-sparc-leon3-cache-snooping-disabled-boot.exe",
+      "executable-sha512": "69f7d8cabeb23bba8c5b43fd658803f479d19a83cc858485ffc54bb727a0810bddb8ae61493839b4ade11094990e27c064426e45beaac3eadba01643ef120366",
+      "info": {
+        "build": [
+          "RTEMS_SMP"
+        ],
+        "line-begin-of-test": 9,
+        "line-end-of-test": 14,
+        "line-build": 12,
+        "line-state": 11,
+        "line-tools": 13,
+        "line-version": 10,
+        "name": "TestsuitesBspsFatalSparcLeon3CacheSnoopingDisabledBoot",
+        "state": "EXPECTED_PASS",
+        "tools": "10.4.0 20220628 (RTEMS 6, RSB f8d79ee51187d98b88b8b7400ad77a991f53c8c0, Newlib b9898fc)",
+        "version": "6.0.0.1c46841ad34939632fee92894e8c73b4ea92f979"
+      }
+    }
+  ],
+  "start-time": "2023-07-11T09:47:29.050266"
+}
diff --git a/rtemsspec/tests/test_packagebuild.py b/rtemsspec/tests/test_packagebuild.py
index 6ff9c8e4..cf6d91c2 100644
--- a/rtemsspec/tests/test_packagebuild.py
+++ b/rtemsspec/tests/test_packagebuild.py
@@ -35,6 +35,7 @@ import tarfile
 from typing import List, NamedTuple
 
 from rtemsspec.items import EmptyItem, Item, ItemCache, ItemGetValueContext
+import rtemsspec.gcdaproducer
 from rtemsspec.packagebuild import BuildItem, BuildItemMapper, \
     build_item_input, PackageBuildDirector
 from rtemsspec.packagebuildfactory import create_build_item_factory
@@ -113,6 +114,13 @@ def _test_runner_subprocess(command, check, stdin, stdout, timeout):
     return _Subprocess(b"u\r\nv\nw\n")
 
 
+def _gcov_tool(command, check, cwd, input):
+    assert command == ["foo", "merge-stream"]
+    assert check
+    assert input == b"gcfnB04R\x00\x00\x00\x95/opt"
+    (Path(cwd) / "file.gcda").touch()
+
+
 def test_packagebuild(caplog, tmpdir, monkeypatch):
     tmp_dir = Path(tmpdir)
     item_cache = _create_item_cache(tmp_dir, Path("spec-packagebuild"))
@@ -403,3 +411,16 @@ def test_packagebuild(caplog, tmpdir, monkeypatch):
     director.build_package(None, ["/qdp/steps/run-tests"])
     log = get_and_clear_log(caplog)
     assert f"use previous report for: {build_bsp.directory}/a.exe"
+
+    # Test GCDAProducer
+    variant["enabled"] = ["gcda-producer"]
+    test_log_coverage = director["/qdp/test-logs/coverage"]
+    test_log_coverage.load()
+    monkeypatch.setattr(rtemsspec.gcdaproducer, "subprocess_run", _gcov_tool)
+    director.build_package(None, None)
+    monkeypatch.undo()
+    log = get_and_clear_log(caplog)
+    assert f"/qdp/steps/gcda-producer: copy *.gcno files from '{tmp_dir}/pkg/build/bsp' to '{tmp_dir}/pkg/build/gcda'" in log
+    assert f"/qdp/steps/gcda-producer: remove unexpected *.gcda file in build directory: '{tmp_dir}/pkg/build/bsp/f.gcda'" in log
+    assert f"/qdp/steps/gcda-producer: process: ts-unit-no-clock-0.exe" in log
+    assert f"/qdp/steps/gcda-producer: move *.gcda files from '{tmp_dir}/pkg/build/bsp' to '{tmp_dir}/pkg/build/gcda'" in log
diff --git a/spec-qdp/spec/qdp-gcda-producer.yml b/spec-qdp/spec/qdp-gcda-producer.yml
new file mode 100644
index 00000000..8b032598
--- /dev/null
+++ b/spec-qdp/spec/qdp-gcda-producer.yml
@@ -0,0 +1,30 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2022 embedded brains GmbH & Co. KG
+enabled-by: true
+links:
+- role: spec-member
+  uid: root
+- role: spec-refinement
+  spec-key: build-step-type
+  spec-value: gcda-producer
+  uid: qdp-build-step
+spec-description: null
+spec-example: null
+spec-info:
+  dict:
+    attributes:
+      gcov-tool:
+        description: |
+          It shall the path to the gcov-tool.
+        spec-type: str
+      working-directory:
+        description: |
+          It shall be the working directory to run the commands.
+        spec-type: str
+    description: |
+      This set of attributes specifies how to run the gcda producer.
+    mandatory-attributes: all
+spec-name: Gcda Producer Item Type
+spec-type: qdp-gcda-producer
+type: spec



More information about the vc mailing list