|  | #!/usr/bin/env python3 | 
|  | # Copyright lowRISC contributors. | 
|  | # Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | # SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | import logging | 
|  | from pathlib import Path, PurePath | 
|  | from pprint import pformat | 
|  | from typing import List | 
|  |  | 
|  | from device_profile_data import extract_profile_data | 
|  | from common import ( | 
|  | BAZEL, | 
|  | LLD_TARGET, | 
|  | BazelTestType, | 
|  | CoverageParams, | 
|  | run, | 
|  | ) | 
|  |  | 
|  | # Commands that this script uses | 
|  | LLVM_PROFDATA = "llvm-profdata" | 
|  |  | 
|  | # Bazel target for locally spliced test ROM bitstream | 
|  | BITSTREAM_TARGET = "//hw/bitstream:gcp_spliced_test_rom" | 
|  |  | 
|  |  | 
|  | def handle_libs(device_libs_all: List[str]) -> List[str]: | 
|  | """Filter device libraries that are not compatible with the target. | 
|  |  | 
|  | Args: | 
|  | device_libs_all: A list of device libraries. | 
|  |  | 
|  | Returns: | 
|  | `device_libs_all` with incompatible libraries filtered out. | 
|  | """ | 
|  | # Remove on_host libs | 
|  | device_libs_incompat = [lib for lib in device_libs_all if "on_host" in lib] | 
|  | logging.info(f"incompatible libraries: {pformat(device_libs_incompat)}") | 
|  | # TODO: may want to add the coverage runtime to avoid undefined symbol warnings | 
|  | return sorted(list(set(device_libs_all) - set(device_libs_incompat))) | 
|  |  | 
|  |  | 
|  | def handle_objs(merged_library: Path, obj_files: List[str]) -> None: | 
|  | """Create a library from the given object files. | 
|  |  | 
|  | Args: | 
|  | merged_library: Path where to save the merged library. | 
|  | obj_files: A list of object files. | 
|  | """ | 
|  | # Note: We allow unresolved symbols and multiple definitions because this library is | 
|  | # used only for generating a coverage report and we link only and all instrumented | 
|  | # object files. | 
|  | # TODO(#16761): Try to remove these flags. | 
|  | run(LLD_TARGET, "--warn-unresolved-symbols", "-zmuldefs", "-o", | 
|  | str(merged_library), *obj_files) | 
|  |  | 
|  |  | 
|  | def handle_test_targets(test_targets: List[str]) -> List[str]: | 
|  | """Choose cw310_test_rom tests from the given list of tests. | 
|  |  | 
|  | This function also | 
|  | - Programs the FPGA with the non-instrumented test ROM since the instrumented one | 
|  | overflows the ROM memory, and | 
|  | - Filters wycheproof tests since they take a long time to run. | 
|  |  | 
|  | Args: | 
|  | test_targets: A list of test targets. | 
|  |  | 
|  | Returns: | 
|  | cw310_test_rom tests without wycheproof tests. | 
|  | """ | 
|  | # Instrumented ROM overflows the space allocated for ROM. Program the fpga with the | 
|  | # non-instrumented test ROM and skip bitstream loading during tests. | 
|  | run(BAZEL, "build", BITSTREAM_TARGET) | 
|  | [workspace] = run(BAZEL, "info", "workspace") | 
|  | [bitstream] = run(BAZEL, "cquery", "--output=starlark", "--starlark:expr", | 
|  | "target.files.to_list()[0].path", BITSTREAM_TARGET) | 
|  | bitstream_path = PurePath(workspace) / PurePath(bitstream) | 
|  | run(BAZEL, "run", "//sw/host/opentitantool", "--", "fpga", | 
|  | "load-bitstream", str(bitstream_path)) | 
|  | return [ | 
|  | t for t in test_targets | 
|  | if "cw310_test_rom" in t and "wycheproof" not in t | 
|  | ] | 
|  |  | 
|  |  | 
|  | def handle_test_log_dirs(test_log_dirs: List[Path]) -> List[Path]: | 
|  | """Get coverage profiles. | 
|  |  | 
|  | This function processes the logs in the given list of test log directories to | 
|  | produce raw profiles and returns their paths. These profiles can then be indexed and | 
|  | merged to produce a single profile file. | 
|  |  | 
|  | Args: | 
|  | test_log_dirs: A list of test log directories. | 
|  |  | 
|  | Returns: | 
|  | Paths of individual raw coverage profiles. | 
|  |  | 
|  | """ | 
|  | raw_profiles = [] | 
|  | for d in test_log_dirs: | 
|  | with (Path(d) / "test.log").open("rb") as test_log, ( | 
|  | Path(d) / "prof.raw").open("wb") as raw_profile: | 
|  | raw_profile.write( | 
|  | extract_profile_data(test_log.read().decode("ascii", | 
|  | "ignore"))) | 
|  | raw_profiles += [Path(raw_profile.name)] | 
|  | logging.info(f"raw profiles: {pformat(raw_profiles)}") | 
|  | return raw_profiles | 
|  |  | 
|  |  | 
|  | PARAMS = CoverageParams( | 
|  | bazel_test_type=BazelTestType.SH_TEST, | 
|  | config="ot_coverage_on_target", | 
|  | libs_fn=handle_libs, | 
|  | objs_fn=handle_objs, | 
|  | test_targets_fn=handle_test_targets, | 
|  | test_log_dirs_fn=handle_test_log_dirs, | 
|  | report_title="OpenTitan Functional Test Coverage", | 
|  | ) |