From af3aea58121bece04748239baf47dd6bb052228c Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Tue, 21 Nov 2023 11:13:16 +0100 Subject: 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(+) create mode 100644 rtemsspec/gcdaproducer.py create mode 100644 rtemsspec/tests/spec-packagebuild/qdp/build/gcda.yml create mode 100644 rtemsspec/tests/spec-packagebuild/qdp/steps/gcda-producer.yml create mode 100644 rtemsspec/tests/spec-packagebuild/qdp/test-logs/coverage.yml create mode 100644 rtemsspec/tests/test-files/pkg/build/bsp/f.gcda create mode 100644 rtemsspec/tests/test-files/pkg/build/bsp/f.gcno create mode 100644 rtemsspec/tests/test-files/pkg/test-log-coverage.json create mode 100644 spec-qdp/spec/qdp-gcda-producer.yml 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@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 -- cgit v1.2.3