[rocprofiler-compute] added ctest coverage and cdash submission (#366)

* added cdash automatic CI upload

* added cdash automatic CI upload

* tweaked wording

* changed nightly to continuous

* removed unnecessary dry-run arg

* updated README.md

* edited workflow description

* update coverage

* formatted cmakelists.txt

* ruff formatting and update coverage
このコミットが含まれているのは:
jamessiddeley-amd
2025-09-02 11:21:40 -04:00
committed by GitHub
コミット f3a2bb07a4
6個のファイルの変更5864行の追加1行の削除
+89
ファイルの表示
@@ -0,0 +1,89 @@
name: rocprofiler-compute CDash Coverage Upload
on:
push:
branches: [ develop ]
paths:
- 'projects/rocprofiler-compute/coverage/coverage-latest.xml'
workflow_dispatch:
inputs:
coverage_file:
description: 'Path to coverage XML file'
required: false
default: 'projects/rocprofiler-compute/coverage/coverage-latest.xml'
concurrency:
group: rocprofiler-compute-cdash-${{ github.ref }}
cancel-in-progress: true
jobs:
upload-coverage:
runs-on: ubuntu-latest
name: Upload Coverage to CDash
steps:
- name: Checkout
uses: actions/checkout@v4
with:
sparse-checkout: projects/rocprofiler-compute
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.8'
- name: Check Coverage File Exists
working-directory: projects/rocprofiler-compute
run: |
COVERAGE_FILE="${{ github.event.inputs.coverage_file || 'coverage/coverage-latest.xml' }}"
if [[ "$COVERAGE_FILE" == projects/rocprofiler-compute/* ]]; then
COVERAGE_FILE="${COVERAGE_FILE#projects/rocprofiler-compute/}"
fi
if [ ! -f "$COVERAGE_FILE" ]; then
echo "ERROR: Coverage file not found at $COVERAGE_FILE!"
echo "Please run manual coverage generation"
echo "Use: cd projects/rocprofiler-compute && ./utils/update_coverage.sh"
exit 1
fi
COVERAGE_INFO=$(python3 -c "
import xml.etree.ElementTree as ET
try:
tree = ET.parse('$COVERAGE_FILE')
root = tree.getroot()
line_rate = float(root.get('line-rate', 0)) * 100
print(f'{line_rate:.1f}%')
except Exception as e:
print(f'Error parsing coverage: {e}')
exit(1)
")
echo "Line Coverage: $COVERAGE_INFO"
echo "COVERAGE_INFO=$COVERAGE_INFO" >> $GITHUB_ENV
echo "COVERAGE_FILE=$COVERAGE_FILE" >> $GITHUB_ENV
- name: Upload to CDash
working-directory: projects/rocprofiler-compute
run: |
GIT_SHA_SHORT=$(git rev-parse --short HEAD)
BRANCH_NAME=${GITHUB_REF#refs/heads/}
python3 utils/run-ci.py \
--coverage-file "${{ env.COVERAGE_FILE }}" \
--build-name "${BRANCH_NAME}-${GIT_SHA_SHORT}-$(date +%Y%m%d)" \
--mode "Continuous" \
--site "Monorepo-Upload" \
--project-name "rocprofiler-compute" \
--source-dir "." \
--binary-dir "build"
- name: Summary
run: |
echo "## CDash Upload Complete!" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Branch**: ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
echo "- **Line Coverage**: ${{ env.COVERAGE_INFO }}" >> $GITHUB_STEP_SUMMARY
echo "- **Commit**: $(git rev-parse --short HEAD)" >> $GITHUB_STEP_SUMMARY
echo "- **CDash Project**: rocprofiler-compute" >> $GITHUB_STEP_SUMMARY
echo "- **Coverage File**: ${{ env.COVERAGE_FILE }}" >> $GITHUB_STEP_SUMMARY
+41 -1
ファイルの表示
@@ -170,13 +170,17 @@ endif()
message(STATUS "Enable tests compilation: ${ENABLE_TESTS}")
enable_testing()
include(CTest)
option(ENABLE_COVERAGE "Enable code coverage" OFF)
set(COV_OPTION "")
if(${ENABLE_COVERAGE})
set(COV_OPTION "--cov=src" "--cov-append" "--cov-report=term-missing"
"--cov-report=lcov:tests/coverage.info")
# "--cov-report=term-missing" "--cov-report=xml:tests/coverage.xml")
set(CTEST_COVERAGE_COMMAND "python3")
set(CTEST_COVERAGE_EXTRA_FLAGS "-m" "coverage" "xml" "-o"
"${CMAKE_BINARY_DIR}/coverage.xml")
endif()
message(STATUS "Code coverage: ${ENABLE_COVERAGE}")
@@ -351,6 +355,42 @@ add_test(
${COV_OPTION} ${PROJECT_SOURCE_DIR}/tests/test_autogen_config.py
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
# -----------------------------------
# Generate coverage tests
# -----------------------------------
if(${ENABLE_COVERAGE})
set(ALL_TEST_NAMES
test_profile_kernel_execution
test_profile_dispatch
test_profile_mem
test_profile_join
test_profile_sort
test_profile_misc
test_profile_section
test_profile_sets_func
test_analyze_commands
test_analyze_workloads
test_L1_cache_counters
test_num_xcds_spec_class
test_num_xcds_cli_output
test_db_connector
test_utils
test_autogen_config)
string(REPLACE ";" ";" ALL_TESTS_STRING "${ALL_TEST_NAMES}")
add_test(
NAME generate_coverage_report
COMMAND ${Python3_EXECUTABLE} -m coverage xml -o ${CMAKE_BINARY_DIR}/coverage.xml
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
set_tests_properties(
generate_coverage_report
PROPERTIES DEPENDS "${ALL_TESTS_STRING}" LABELS "coverage" ENVIRONMENT
"COVERAGE_FILE=${PROJECT_SOURCE_DIR}/.coverage")
endif()
# ---------
# Install
# ---------
+77
ファイルの表示
@@ -0,0 +1,77 @@
# Coverage Workflow for rocprofiler-compute in Monorepo
## Overview
This document describes the coverage generation and CDash upload workflow for `rocprofiler-compute` within the super-repo structure.
## Directory Structure
```
monorepo/
├── projects/
│ └── rocprofiler-compute/
│ ├── CMakeLists.txt
│ ├── coverage/
│ │ └── coverage-latest.xml # committed coverage file
│ ├── utils/
│ │ ├── update_coverage.sh # coverage generation/update script
│ │ └── run-ci.py # CDash upload script
│ └── ...
└── .github/
└── workflows/
└── rocprofiler-compute-cdash.yml # gitHub Action for auto-upload
```
## Workflows
### 1. Manual Coverage Generation (Periodic Update)
Run this periodically to update the coverage baseline:
```bash
# From monorepo root
cd projects/rocprofiler-compute
./utils/update_coverage.sh
# This will:
# - Build with coverage enabled
# - Run all tests
# - Generate coverage.xml
# - Copy/overwrite to coverage/coverage-latest.xml
# - Show instructions for committing
```
### 2. Commit Coverage to Repository
```bash
# From monorepo root
git add projects/rocprofiler-compute/coverage/coverage-latest.xml
git commit -m "rocprofiler-compute: Update coverage to XX.X% line coverage"
git push origin develop
```
### 3. Automatic CDash Upload
When `coverage/coverage-latest.xml` is pushed to `develop` branch:
- GitHub Action triggers automatically
- Uploads coverage to CDash
- Posts summary to GitHub Actions
## CDash Project
View results at: https://my.cdash.org/index.php?project=rocprofiler-compute
## Maintenance
## Troubleshooting
### Coverage Generation Fails
```bash
#check Python dependencies
pip install coverage pytest pytest-cov
#verify tests can run
cd projects/rocprofiler-compute/build
ctest --verbose
```
ファイル差分が大きすぎるため省略します 差分を読み込み
+257
ファイルの表示
@@ -0,0 +1,257 @@
#!/usr/bin/env python3
##############################################################################
# MIT License
#
# Copyright (c) 2025 Advanced Micro Devices, Inc. All Rights Reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
##############################################################################
"""
CI script to upload coverage XML files to CDash
"""
import argparse
import os
import subprocess
import sys
import tempfile
from pathlib import Path
def detect_repo_structure():
"""Detect if in monorepo or standalone structure"""
cwd = Path.cwd()
if "projects/rocprofiler-compute" in str(cwd):
parts = cwd.parts
idx = parts.index("rocprofiler-compute")
if idx > 0 and parts[idx - 1] == "projects":
monorepo_root = Path(*parts[: idx - 1])
project_root = monorepo_root / "projects" / "rocprofiler-compute"
return True, monorepo_root, project_root
if (cwd / "CMakeLists.txt").exists():
with open(cwd / "CMakeLists.txt", "r") as f:
content = f.read()
if (
"project(rocprofiler-compute" in content
or "project(\n rocprofiler-compute" in content
):
return False, None, cwd
return False, None, cwd
def create_ctest_script(args):
"""Generate a CTest script for uploading coverage data"""
script_content = f"""
cmake_minimum_required(VERSION 3.19)
set(CTEST_PROJECT_NAME "{args.project_name}")
set(CTEST_NIGHTLY_START_TIME "01:00:00 UTC")
set(CTEST_DROP_SITE_CDASH TRUE)
if(CMAKE_VERSION VERSION_GREATER 3.14)
set(CTEST_SUBMIT_URL "{args.submit_url}")
else()
set(CTEST_DROP_METHOD "https")
set(CTEST_DROP_SITE "{args.cdash_host}")
set(CTEST_DROP_LOCATION "{args.submit_path}")
endif()
set(CTEST_SITE "{args.site}")
set(CTEST_BUILD_NAME "{args.build_name}")
set(CTEST_SOURCE_DIRECTORY "{args.source_dir}")
set(CTEST_BINARY_DIRECTORY "{args.binary_dir}")
ctest_start({args.mode})
set(CTEST_COVERAGE_COMMAND "python3")
set(CTEST_COVERAGE_EXTRA_FLAGS "-m" "coverage" "xml")
file(COPY "{args.coverage_file}" DESTINATION "${{CTEST_BINARY_DIRECTORY}}")
ctest_coverage()
ctest_submit(PARTS Coverage RETRY_COUNT 3 RETRY_DELAY 5)
"""
return script_content
def main():
if not os.getenv("CI"):
print("WARNING: CDash upload should normally only be done from CI/CD")
response = input("Continue anyway? (y/N): ")
if response.lower() != "y":
print("Aborted.")
return 1
parser = argparse.ArgumentParser(
description="Upload coverage XML to CDash for rocprofiler-compute"
)
# required
parser.add_argument(
"--coverage-file", required=True, help="Path to coverage XML file"
)
parser.add_argument("--build-name", required=True, help="Build name for CDash")
# optional
parser.add_argument(
"--project-name", default="rocprofiler-compute", help="CDash project name"
)
parser.add_argument(
"--submit-url",
default="https://my.cdash.org/submit.php?project=rocprofiler-compute",
help="CDash submission URL",
)
parser.add_argument(
"--cdash-host", default="my.cdash.org", help="CDash host (for older CMake)"
)
parser.add_argument(
"--submit-path",
default="/submit.php?project=rocprofiler-compute",
help="CDash submission path (for older CMake)",
)
parser.add_argument(
"--site",
default=None,
help="Site name for CDash (auto-detected if not provided)",
)
parser.add_argument(
"--source-dir",
default=None,
help="Source directory path (auto-detected if not provided)",
)
parser.add_argument(
"--binary-dir",
default=None,
help="Binary directory path (defaults to source-dir/build)",
)
parser.add_argument(
"--mode",
default="Experimental",
choices=["Experimental", "Nightly", "Continuous"],
help="CTest dashboard mode",
)
args = parser.parse_args()
is_monorepo, monorepo_root, project_root = detect_repo_structure()
if not args.source_dir:
args.source_dir = str(project_root)
if not args.binary_dir:
args.binary_dir = os.path.join(args.source_dir, "build")
if not args.site:
if is_monorepo:
args.site = f"Monorepo-{os.uname().nodename}"
else:
args.site = os.uname().nodename
coverage_path = Path(args.coverage_file)
if not coverage_path.is_absolute():
if not coverage_path.exists():
coverage_path = project_root / args.coverage_file
if not coverage_path.exists():
print(f"ERROR: Coverage file not found: {coverage_path}", file=sys.stderr)
print(f" Searched in: {Path(args.coverage_file).absolute()}")
print(f" And in: {project_root / args.coverage_file}")
return 1
args.coverage_file = str(coverage_path.absolute())
args.source_dir = str(Path(args.source_dir).absolute())
args.binary_dir = str(Path(args.binary_dir).absolute())
print(f"Repository type: {'Monorepo' if is_monorepo else 'Standalone'}")
print(f"Project root: {project_root}")
print(f"Uploading coverage file: {args.coverage_file}")
print(f"Build name: {args.build_name}")
print(f"Project: {args.project_name}")
print(f"Site: {args.site}")
print(f"Submit URL: {args.submit_url}")
try:
import xml.etree.ElementTree as ET
tree = ET.parse(args.coverage_file)
root = tree.getroot()
line_rate = float(root.get("line-rate", 0)) * 100
print(f"Line Coverage: {line_rate:.1f}%")
except Exception as e:
print(f"Warning: Could not parse coverage percentage: {e}")
script_content = create_ctest_script(args)
try:
with tempfile.NamedTemporaryFile(mode="w", suffix=".cmake", delete=False) as f:
f.write(script_content)
script_path = f.name
print(f"\nScript written to: {script_path}")
original_dir = os.getcwd()
os.chdir(args.source_dir)
cmd = ["ctest", "-S", script_path, "-V"]
print(f"Executing: {' '.join(cmd)}")
print(f"Working directory: {os.getcwd()}")
result = subprocess.run(cmd, capture_output=True, text=True)
if result.stdout:
print("\nSTDOUT:")
print(result.stdout)
if result.stderr:
print("\nSTDERR:")
print(result.stderr)
os.chdir(original_dir)
if result.returncode != 0:
print(f"\nCTest failed with return code: {result.returncode}")
return result.returncode
print("\n✅ Coverage successfully uploaded to CDash!")
print(
f"View results at: {
args.submit_url.replace('/submit.php?project=', '/index.php?project=')
}"
)
return 0
except Exception as e:
print(f"Error executing CTest script: {e}", file=sys.stderr)
import traceback
traceback.print_exc()
return 1
finally:
if "script_path" in locals():
try:
os.unlink(script_path)
except Exception:
pass
if __name__ == "__main__":
sys.exit(main())
+102
ファイルの表示
@@ -0,0 +1,102 @@
#!/bin/bash
##############################################################################
# MIT License
#
# Copyright (c) 2025 Advanced Micro Devices, Inc. All Rights Reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
##############################################################################
# generate coverage for develop branch - stores file in repo
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [[ "$SCRIPT_DIR" == */projects/rocprofiler-compute/* ]]; then
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
MONOREPO_ROOT="$(cd "$PROJECT_ROOT/../.." && pwd)"
IS_MONOREPO=true
else
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
MONOREPO_ROOT=""
IS_MONOREPO=false
fi
BUILD_DIR="$PROJECT_ROOT/build"
COVERAGE_FILE="$PROJECT_ROOT/coverage/coverage-latest.xml"
echo "=== Generating Coverage for rocprofiler-compute ==="
echo "Project root: $PROJECT_ROOT"
if [ "$IS_MONOREPO" = true ]; then
echo "Monorepo root: $MONOREPO_ROOT"
fi
echo "Setting up clean build..."
rm -rf "$BUILD_DIR"
mkdir -p "$BUILD_DIR"
cd "$BUILD_DIR"
echo "Configuring with coverage enabled..."
cmake -DENABLE_COVERAGE=ON -DENABLE_TESTS=ON -DPYTEST_NUMPROCS=8 ..
echo "Building..."
make -j$(nproc)
echo "Running ALL tests to generate coverage (this may take a while)..."
ctest --verbose --output-on-failure --parallel 1 || {
echo "WARNING: Some tests failed, but continuing with coverage generation"
}
echo "Generating coverage report..."
if ctest -R generate_coverage_report --output-on-failure --parallel 2; then
echo "Coverage report generated via ctest"
else
echo "Trying alternative coverage generation..."
cd "$PROJECT_ROOT"
python3 -m coverage combine || true
python3 -m coverage xml -o "$BUILD_DIR/coverage.xml"
cd "$BUILD_DIR"
fi
if [ ! -f "coverage.xml" ]; then
echo "ERROR: coverage.xml not generated"
exit 1
fi
COVERAGE_INFO=$(python3 -c "
import xml.etree.ElementTree as ET
tree = ET.parse('coverage.xml')
root = tree.getroot()
line_rate = float(root.get('line-rate', 0)) * 100
print(f'{line_rate:.1f}%')
")
mkdir -p "$PROJECT_ROOT/coverage"
cp coverage.xml "$COVERAGE_FILE"
echo ""
echo "=== Coverage Generated Successfully ==="
echo "Line Coverage: $COVERAGE_INFO"
echo "File: $COVERAGE_FILE"
echo ""
echo "Coverage file generated: coverage/coverage-latest.xml"
echo "To update official coverage:"
echo " 1. Commit this file to develop branch"
echo " 2. GitHub Actions will automatically upload to CDash"