CI: Show summary tables in 'pr: rtlmeter' results
This commit is contained in:
parent
680ef8dda9
commit
067cd6c9c6
|
|
@ -182,7 +182,7 @@ jobs:
|
|||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: repo/notification
|
||||
name: coverage-pr-notification
|
||||
name: pr-notification
|
||||
|
||||
# Create GitHub issue for failed scheduled jobs
|
||||
# This should always be the last job (we want an issue if anything breaks)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ on:
|
|||
paths: ["ci/**", ".github/workflows"]
|
||||
workflow_dispatch:
|
||||
workflow_run:
|
||||
workflows: ["Code coverage"]
|
||||
workflows: ["Code coverage", "RTLMeter"]
|
||||
types: [completed]
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||
|
|
@ -35,7 +35,7 @@ jobs:
|
|||
name: Build content
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
coverage-pr-run-ids: ${{ steps.build.outputs.coverage-pr-run-ids }}
|
||||
pr-run-ids: ${{ steps.build.outputs.pr-run-ids }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
|
@ -83,5 +83,5 @@ jobs:
|
|||
- name: Comment on PR
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||
COVERAGE_PR_RUN_IDS: ${{ needs.build.outputs.coverage-pr-run-ids }}
|
||||
PR_RUN_IDS: ${{ needs.build.outputs.pr-run-ids }}
|
||||
run: bash -x ./ci/ci-pages-notify.bash
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
---
|
||||
# DESCRIPTION: Github actions config
|
||||
# This name is key to badges in README.rst, so we use the name build
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
name: RTLMeter PR results
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [RTLMeter]
|
||||
types: [completed]
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: Publish
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }}
|
||||
permissions:
|
||||
actions: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Download report
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: rtlmeter-pr-results
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Download PR number
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: pr-number
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# Use the Verilator CI app to post the comment
|
||||
- name: Generate access token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@v3.2.0
|
||||
with:
|
||||
app-id: ${{ vars.VERILATOR_CI_ID }}
|
||||
private-key: ${{ secrets.VERILATOR_CI_KEY }}
|
||||
permission-pull-requests: write
|
||||
- name: Comment on PR
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||
run: |-
|
||||
ls -la
|
||||
cat report.txt
|
||||
gh pr --repo ${{ github.repository }} comment $(cat pr-number.txt) --body-file report.txt
|
||||
|
|
@ -286,106 +286,45 @@ jobs:
|
|||
with:
|
||||
repository: "verilator/rtlmeter"
|
||||
path: rtlmeter
|
||||
|
||||
- name: Setup RTLMeter venv
|
||||
working-directory: rtlmeter
|
||||
run: make venv
|
||||
- name: Download combined results
|
||||
uses: actions/download-artifact@v8
|
||||
|
||||
- name: Checkout Verilator
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
pattern: all-results-*
|
||||
path: all-results
|
||||
merge-multiple: true
|
||||
- name: Get scheduled run info
|
||||
id: scheduled-info
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
ID=$(gh run --repo ${{ github.repository }} list --workflow RTLMeter --event schedule --status success --limit 1 --json databaseId --jq ".[0].databaseId")
|
||||
echo "id=$ID" >> $GITHUB_OUTPUT
|
||||
URL=$(gh run --repo ${{ github.repository }} view $ID --json url --jq ".url")
|
||||
echo "url=$URL" >> $GITHUB_OUTPUT
|
||||
NUM=$(gh run --repo ${{ github.repository }} view $ID --json number --jq ".number")
|
||||
echo "num=$NUM" >> $GITHUB_OUTPUT
|
||||
DATE=$(gh run --repo ${{ github.repository }} view $ID --json createdAt --jq ".createdAt")
|
||||
echo "date=$DATE" >> $GITHUB_OUTPUT
|
||||
- name: Download scheduled run results
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: published-results
|
||||
path: nightly-results
|
||||
run-id: ${{ steps.scheduled-info.outputs.id }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Compare results
|
||||
working-directory: rtlmeter
|
||||
run: |
|
||||
for tag in gcc clang gcc-hier; do
|
||||
ADATA=../nightly-results/all-results-${tag}.json
|
||||
BDATA=../all-results/all-results-${tag}.json
|
||||
touch ../verilate-${tag}.txt
|
||||
touch ../execute-${tag}.txt
|
||||
touch ../cppbuild-${tag}.txt
|
||||
if [[ ! -e $ADATA ]]; then
|
||||
continue
|
||||
fi
|
||||
./rtlmeter compare --cases '* !Example:* !*:hello' --steps "verilate" --metrics "elapsed memory" $ADATA $BDATA > ../verilate-${tag}.txt
|
||||
cat ../verilate-${tag}.txt
|
||||
./rtlmeter compare --cases '* !Example:* !*:hello' --steps "execute" --metrics "speed memory elapsed" $ADATA $BDATA > ../execute-${tag}.txt
|
||||
cat ../execute-${tag}.txt
|
||||
./rtlmeter compare --cases '* !Example:* !*:hello' --steps "cppbuild" --metrics "elapsed memory cpu codeSize" $ADATA $BDATA > ../cppbuild-${tag}.txt
|
||||
cat ../cppbuild-${tag}.txt
|
||||
done
|
||||
path: verilator
|
||||
|
||||
- name: Create report
|
||||
working-directory: verilator
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -x
|
||||
NUM=$(gh run --repo ${{ github.repository }} view ${{ github.run_id }} --json number --jq ".number")
|
||||
URL=$(gh run --repo ${{ github.repository }} view ${{ github.run_id }} --json url --jq ".url")
|
||||
echo -n "Performance metrics for PR workflow [#$NUM]($URL) (B) compared to scheduled run" > report.txt
|
||||
echo -n " [#${{ steps.scheduled-info.outputs.num }}](${{ steps.scheduled-info.outputs.url }}) (A)" >> report.txt
|
||||
echo " from ${{ steps.scheduled-info.outputs.date }}" >> report.txt
|
||||
for tag in gcc clang gcc-hier; do
|
||||
echo "" >> report.txt
|
||||
if [[ $tag == "gcc" ]]; then
|
||||
echo "<details open>" >> report.txt
|
||||
else
|
||||
echo "<details>" >> report.txt
|
||||
fi
|
||||
echo -n "<summary><strong><em>" >> report.txt
|
||||
jq -rj ".[0].runName" all-results/all-results-${tag}.json >> report.txt
|
||||
echo "</em></strong></summary>" >> report.txt
|
||||
awk -v RS= -v tag=${tag} '{print > sprintf("frag-%02d-verilate-%s.txt",NR,tag)}' verilate-${tag}.txt
|
||||
awk -v RS= -v tag=${tag} '{print > sprintf("frag-%02d-execute-%s.txt" ,NR,tag)}' execute-${tag}.txt
|
||||
awk -v RS= -v tag=${tag} '{print > sprintf("frag-$02d-cppbuild-%s.txt",NR,tag)}' cppbuild-${tag}.txt
|
||||
for f in $(ls -1 frag-*-verilate-${tag}.txt | sort) $(ls -1 frag-*-execute-${tag}.txt | sort) $(ls -1 frag-*-cppbuild-${tag}.txt | sort); do
|
||||
if [[ $f == frag-01-verilate-${tag}.txt || $f == frag-01-execute-${tag}.txt ]]; then
|
||||
echo "<details open>" >> report.txt
|
||||
else
|
||||
echo "<details>" >> report.txt
|
||||
fi
|
||||
echo -n "<summary>" >> report.txt
|
||||
head -n 1 $f | tr -d '\n' >> report.txt
|
||||
echo "</summary>" >> report.txt
|
||||
echo '<pre>' >> report.txt
|
||||
tail -n +2 $f >> report.txt
|
||||
echo '</pre>' >> report.txt
|
||||
echo "</details>" >> report.txt
|
||||
done
|
||||
echo "</details>" >> report.txt
|
||||
done
|
||||
cat report.txt
|
||||
ln -s ../rtlmeter rtlmeter
|
||||
gh repo set-default ${{ github.repository }}
|
||||
# Compare to last successful scheduled run
|
||||
REF_ID=$(gh run list --workflow RTLMeter --event schedule --status success --limit 1 --json databaseId --jq ".[0].databaseId")
|
||||
NEW_ID=${{ github.run_id }}
|
||||
ci/ci-rtlmeter-pr-report.bash $REF_ID $NEW_ID gcc clang gcc-hier
|
||||
mv rtlmeter-pr-report/report.txt ../
|
||||
cat ../report.txt
|
||||
# Generate notification comment content
|
||||
mkdir -p ../notification
|
||||
mv rtlmeter-pr-report/notification.txt ../notification/body.txt
|
||||
echo ${{ github.event.number }} > ../notification/pr-number.txt
|
||||
|
||||
- name: Upload report
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: report.txt
|
||||
name: rtlmeter-pr-results
|
||||
- name: Save PR number
|
||||
run: echo ${{ github.event.number }} > pr-number.txt
|
||||
- name: Upload PR number
|
||||
name: rtlmeter-pr-report
|
||||
|
||||
- name: Upload notification
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: pr-number.txt
|
||||
name: pr-number
|
||||
path: notification
|
||||
name: pr-notification
|
||||
|
||||
# Create GitHub issue for failed scheduled jobs
|
||||
# This should always be the last job (we want an issue if anything breaks)
|
||||
|
|
|
|||
|
|
@ -535,6 +535,7 @@ PY_PROGRAMS = \
|
|||
# Python files, subject to format but not lint
|
||||
PY_FILES = \
|
||||
$(PY_PROGRAMS) \
|
||||
ci/*.py \
|
||||
test_regress/t/*.py \
|
||||
|
||||
# Python files, test_regress tests
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
# SPDX-FileCopyrightText: 2025 Geza Lore
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
# Notify PRs via comment that their coverage reports are available
|
||||
# Notify PRs via comment that their workflow reports are available
|
||||
|
||||
# Get the current repo URL - might differ on a fork
|
||||
readonly REPO_URL=$(gh repo view --json url --jq .url)
|
||||
|
|
@ -13,7 +13,7 @@ readonly REPO_URL=$(gh repo view --json url --jq .url)
|
|||
ARTIFACTS_ROOT=artifacts
|
||||
mkdir -p ${ARTIFACTS_ROOT}
|
||||
|
||||
for RUN_ID in ${COVERAGE_PR_RUN_IDS//,/ }; do
|
||||
for RUN_ID in ${PR_RUN_IDS//,/ }; do
|
||||
echo "@@@ Processing run ${RUN_ID}"
|
||||
|
||||
# Create workflow artifacts directory
|
||||
|
|
@ -21,7 +21,7 @@ for RUN_ID in ${COVERAGE_PR_RUN_IDS//,/ }; do
|
|||
mkdir -p ${ARTIFACTS_DIR}
|
||||
|
||||
# Download artifact of this run, if exists
|
||||
gh run download ${RUN_ID} --name coverage-pr-notification --dir ${ARTIFACTS_DIR} || true
|
||||
gh run download ${RUN_ID} --name pr-notification --dir ${ARTIFACTS_DIR} || true
|
||||
ls -lsha ${ARTIFACTS_DIR}
|
||||
|
||||
# Move on if no notification is required
|
||||
|
|
@ -35,7 +35,7 @@ for RUN_ID in ${COVERAGE_PR_RUN_IDS//,/ }; do
|
|||
gh pr comment $(cat ${ARTIFACTS_DIR}/pr-number.txt) --body-file ${ARTIFACTS_DIR}/body.txt
|
||||
|
||||
# Get the artifact ID
|
||||
ARTIFACT_ID=$(gh api "repos/{owner}/{repo}/actions/runs/${RUN_ID}/artifacts" --jq '.artifacts[] | select(.name == "coverage-pr-notification") | .id')
|
||||
ARTIFACT_ID=$(gh api "repos/{owner}/{repo}/actions/runs/${RUN_ID}/artifacts" --jq '.artifacts[] | select(.name == "pr-notification") | .id')
|
||||
|
||||
# Delete it, so we only notify once
|
||||
gh api --method DELETE "repos/{owner}/{repo}/actions/artifacts/${ARTIFACT_ID}"
|
||||
|
|
|
|||
168
ci/ci-pages.bash
168
ci/ci-pages.bash
|
|
@ -5,8 +5,7 @@
|
|||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
# This scipt build the content of the GitHub Pages for the repository.
|
||||
# Currently this only hosts code coverage reports, but it would be possible to
|
||||
# add any other contents to the page in parallel here.
|
||||
# Currently this hosts code coverage reports, and PR RTLMeter detailed reports.
|
||||
|
||||
# Developer note: You should be able to run this script in your local checkout
|
||||
# if you have GitHub CLI (command 'gh') setup, authenticated ('gh auth login'),
|
||||
|
|
@ -25,6 +24,9 @@ if [[ -z "$GITHUB_OUTPUT" ]]; then
|
|||
GITHUB_OUTPUT=github-output.txt
|
||||
fi
|
||||
|
||||
# Run IDs of PR jobs processed
|
||||
PR_RUN_IDS=""
|
||||
|
||||
# Populates ${PAGES_ROOT}/coverage-reports
|
||||
compile_coverage_reports() {
|
||||
# We will process all runs up to and including this date. This is chosen to be
|
||||
|
|
@ -53,9 +55,6 @@ compile_coverage_reports() {
|
|||
local CONTENTS=contents.tmp
|
||||
echo > ${CONTENTS}
|
||||
|
||||
# Run IDs of PR jobs processed
|
||||
local PR_RUN_IDS=""
|
||||
|
||||
# Iterate over all unique event types that triggered the workflows
|
||||
for EVENT in $(jq -r 'map(.event) | sort | unique | .[]' completedRuns.json); do
|
||||
echo "@@@ Processing '${EVENT}' runs"
|
||||
|
|
@ -73,11 +72,7 @@ compile_coverage_reports() {
|
|||
|
||||
# Record run ID of PR job
|
||||
if [[ $EVENT == "pull_request" ]]; then
|
||||
if [[ -z "$PR_RUN_IDS" ]]; then
|
||||
PR_RUN_IDS="$RUN_ID"
|
||||
else
|
||||
PR_RUN_IDS="$PR_RUN_IDS,$RUN_ID"
|
||||
fi
|
||||
PR_RUN_IDS="$PR_RUN_IDS $RUN_ID"
|
||||
fi
|
||||
|
||||
# Create workflow artifacts directory
|
||||
|
|
@ -148,7 +143,7 @@ CONTENTS_TEMPLATE
|
|||
|
||||
<body>
|
||||
$(cat ${CONTENTS})
|
||||
<h4>Assembled $(date --iso-8601=minutes --utc)</h1>
|
||||
<h4>Assembled $(date --iso-8601=minutes --utc)</h4>
|
||||
<body>
|
||||
|
||||
</html>
|
||||
|
|
@ -156,12 +151,159 @@ INDEX_TEMPLATE
|
|||
|
||||
# Report size
|
||||
du -shc ${COVERAGE_ROOT}/*
|
||||
}
|
||||
|
||||
# Set output
|
||||
echo "coverage-pr-run-ids=${PR_RUN_IDS}" >> $GITHUB_OUTPUT
|
||||
# Populates ${PAGES_ROOT}/rtlmeter-reports
|
||||
compile_rtlmeter_reports() {
|
||||
# We will process all runs up to and including this date. This is chosen to be
|
||||
# slightly less than the artifact retention period for simplicity.
|
||||
local OLDEST=$(date --date="28 days ago" --iso-8601=date)
|
||||
|
||||
# Gather all RTLMeter workflow runs within the time window
|
||||
gh run list -w rtlmeter.yml --limit 1000 --created ">=${OLDEST}" --json "databaseId,event,status,conclusion,createdAt,number" > recentRuns.json
|
||||
echo @@@ Recent runs:
|
||||
jq "." recentRuns.json
|
||||
|
||||
# Select completd runs that were not cancelled or skipped, sort by descending run number
|
||||
jq 'sort_by(-.number) | map(select(.status == "completed" and (.conclusion == "success" or .conclusion == "failure")))' recentRuns.json > completedRuns.json
|
||||
echo @@@ Completed with success or failure:
|
||||
jq "." completedRuns.json
|
||||
|
||||
# Create artifacts root directory
|
||||
local ARTIFACTS_ROOT=artifacts
|
||||
mkdir -p ${ARTIFACTS_ROOT}
|
||||
|
||||
# Create rtlmeter reports root directory
|
||||
local RTLMETER_ROOT=${PAGES_ROOT}/rtlmeter-reports
|
||||
mkdir -p ${RTLMETER_ROOT}
|
||||
|
||||
# Create index page contents fragment
|
||||
local CONTENTS=contents.tmp
|
||||
echo > ${CONTENTS}
|
||||
|
||||
# Iterate over all unique event types that triggered the workflows
|
||||
for EVENT in $(jq -r 'map(.event) | sort | unique | .[]' completedRuns.json); do
|
||||
echo "@@@ Processing '${EVENT}' runs"
|
||||
|
||||
# Emit section header if a report exists with this event type
|
||||
EMIT_SECTION_HEADER=1
|
||||
|
||||
# For each worfklow run that was triggered by this event type
|
||||
for RUN_ID in $(jq ".[] | select(.event == \"${EVENT}\") |.databaseId" completedRuns.json); do
|
||||
echo "@@@ Processing run ${RUN_ID}"
|
||||
|
||||
# Extract the info of this run
|
||||
jq ".[] | select(.databaseId == $RUN_ID)" completedRuns.json > workflow.json
|
||||
jq "." workflow.json
|
||||
|
||||
# Record run ID of PR job
|
||||
if [[ $EVENT == "pull_request" ]]; then
|
||||
PR_RUN_IDS="$PR_RUN_IDS $RUN_ID"
|
||||
fi
|
||||
|
||||
# Create workflow artifacts directory
|
||||
local ARTIFACTS_DIR=${ARTIFACTS_ROOT}/${RUN_ID}
|
||||
mkdir -p ${ARTIFACTS_DIR}
|
||||
|
||||
# Download artifacts of this run, if exists
|
||||
gh run download ${RUN_ID} --name rtlmeter-pr-report --dir ${ARTIFACTS_DIR} || true
|
||||
ls -lsha ${ARTIFACTS_DIR}
|
||||
|
||||
# Move on if no RTLMeter report is available
|
||||
if [ ! -f ${ARTIFACTS_DIR}/report.txt ]; then
|
||||
echo "No RTLMeter report found"
|
||||
continue
|
||||
fi
|
||||
echo "RTLMeter report found"
|
||||
|
||||
# Emit section header
|
||||
if [[ -n $EMIT_SECTION_HEADER ]]; then
|
||||
unset EMIT_SECTION_HEADER
|
||||
echo "<h4>RTLMeter reports for '${EVENT}' runs:</h4>" >> ${CONTENTS}
|
||||
fi
|
||||
|
||||
# Extract run metadata
|
||||
local WORKFLOW_CREATED=$(jq -r '.createdAt' workflow.json)
|
||||
local WOFKRLOW_NUMBER=$(jq -r '.number' workflow.json)
|
||||
|
||||
# Wrap the report into an HTML page. The report content is already HTML
|
||||
# (produced by ci-rtlmeter-pr-report.bash), so we just embed it in the
|
||||
# page body.
|
||||
cat > ${RTLMETER_ROOT}/${RUN_ID}.html <<REPORT_TEMPLATE
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Verilator RTLMeter report #${WOFKRLOW_NUMBER}</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: courier, serif;
|
||||
background-color: #f3f3f3;
|
||||
a {
|
||||
color: #008fd7;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
$(cat ${ARTIFACTS_DIR}/report.txt)
|
||||
</body>
|
||||
|
||||
</html>
|
||||
REPORT_TEMPLATE
|
||||
|
||||
# Add index page content
|
||||
cat >> ${CONTENTS} <<CONTENTS_TEMPLATE
|
||||
Run <a href="${RUN_ID}.html">#${WOFKRLOW_NUMBER}</a>
|
||||
| GitHub: <a href="${REPO_URL}/actions/runs/${RUN_ID}">${RUN_ID}</a>
|
||||
| started at: ${WORKFLOW_CREATED}
|
||||
CONTENTS_TEMPLATE
|
||||
echo "<br>" >> ${CONTENTS}
|
||||
done
|
||||
|
||||
# Section break
|
||||
if [[ -z "$EMIT_SECTION_HEADER" ]]; then
|
||||
echo "<hr>" >> ${CONTENTS}
|
||||
fi
|
||||
done
|
||||
|
||||
# Write rtlmeter report index.html
|
||||
cat > ${RTLMETER_ROOT}/index.html <<INDEX_TEMPLATE
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Verilator CI RTLMeter reports</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: courier, serif;
|
||||
background-color: #f3f3f3;
|
||||
a {
|
||||
color: #008fd7;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
$(cat ${CONTENTS})
|
||||
<h4>Assembled $(date --iso-8601=minutes --utc)</h4>
|
||||
<body>
|
||||
|
||||
</html>
|
||||
INDEX_TEMPLATE
|
||||
|
||||
# Report size
|
||||
du -shc ${RTLMETER_ROOT}/*
|
||||
}
|
||||
|
||||
# Compilie coverage reports
|
||||
compile_coverage_reports;
|
||||
|
||||
# Compile RTLMeter reports
|
||||
compile_rtlmeter_reports;
|
||||
|
||||
# You can build any other content here to be put under ${PAGES_ROOT}
|
||||
|
||||
# Set output to all affected unique PR run IDs (comma separated, no trailing comma)
|
||||
IDS=$(echo ${PR_RUN_IDS} | xargs -n1 | sort -u | paste -sd,)
|
||||
echo "pr-run-ids=${IDS}" >> $GITHUB_OUTPUT
|
||||
|
|
|
|||
|
|
@ -0,0 +1,135 @@
|
|||
#!/usr/bin/env bash
|
||||
# DESCRIPTION: Verilator: CI script for 'rtlmeter.yml' PR results
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
# This scipt builds the content of the response comment posten on PRs
|
||||
# at the end of RTLMeter runs.
|
||||
|
||||
# Developer note: You should be able to run this script in your local checkout
|
||||
# if you have GitHub CLI (command 'gh') setup, authenticated ('gh auth login'),
|
||||
# and have set a default repository ('gh repo set-default').
|
||||
|
||||
# Trace when running in the CI
|
||||
[ "$GITHUB_ACTIONS" != "true" ] || set -x
|
||||
|
||||
# Arguments:
|
||||
# 1. reference run ID
|
||||
# 2. new run ID
|
||||
# rest: run tags
|
||||
REF_ID=$1; shift
|
||||
NEW_ID=$1; shift
|
||||
RUNS="$@"
|
||||
|
||||
# $VERILATOR_CHECKOUT/ci directory
|
||||
SCRIPT_DIR=$(readlink -f $(dirname ${BASH_SOURCE[0]}))
|
||||
|
||||
# Move into a temporary directory
|
||||
rm -rf rtlmeter-pr-report
|
||||
mkdir rtlmeter-pr-report
|
||||
pushd rtlmeter-pr-report &> /dev/null
|
||||
TMP_DIR=$(readlink -f .)
|
||||
|
||||
# Artifacts to download
|
||||
DOWNLOAD_ARTIFACTS=""
|
||||
for r in $RUNS; do
|
||||
DOWNLOAD_ARTIFACTS="$DOWNLOAD_ARTIFACTS --name all-results-$r"
|
||||
done
|
||||
|
||||
# Download reference run results
|
||||
mkdir ref
|
||||
REF_DIR=$(readlink -f ref)
|
||||
gh run download ${REF_ID} $DOWNLOAD_ARTIFACTS --dir $REF_DIR
|
||||
mv $REF_DIR/*/*.json $REF_DIR/
|
||||
find $REF_DIR -mindepth 1 -type d -delete
|
||||
|
||||
# Download current run results
|
||||
mkdir new
|
||||
NEW_DIR=$(readlink -f new)
|
||||
gh run download ${NEW_ID} $DOWNLOAD_ARTIFACTS --dir $NEW_DIR
|
||||
mv $NEW_DIR/*/*.json $NEW_DIR/
|
||||
find $NEW_DIR -mindepth 1 -type d -delete
|
||||
|
||||
# Get Some metadata about the runs
|
||||
REF_URL=$(gh run view $REF_ID --json url --jq ".url")
|
||||
REF_NUM=$(gh run view $REF_ID --json number --jq ".number")
|
||||
REF_DATE=$(gh run view $REF_ID --json createdAt --jq ".createdAt")
|
||||
NEW_URL=$(gh run view $NEW_ID --json url --jq ".url")
|
||||
NEW_NUM=$(gh run view $NEW_ID --json number --jq ".number")
|
||||
|
||||
# Go back to root directory
|
||||
popd &> /dev/null
|
||||
# Go to RTLMeter directory
|
||||
cd rtlmeter
|
||||
|
||||
# Compare results
|
||||
SUMMARY_ARGS=()
|
||||
for r in $RUNS; do
|
||||
CMP_JSON=$TMP_DIR/cmp-$r.json
|
||||
# Gather args for summary script
|
||||
SUMMARY_ARGS+=($REF_DIR/all-results-$r.json $CMP_JSON)
|
||||
# Create JSON
|
||||
./rtlmeter compare --format json --steps "*" --metrics "*" \
|
||||
$REF_DIR/all-results-$r.json $NEW_DIR/all-results-$r.json > $CMP_JSON
|
||||
# Also create detailed tables
|
||||
./rtlmeter compare --format ascii --steps 'verilate' --metrics '* !system !user' $REF_DIR/all-results-$r.json $NEW_DIR/all-results-$r.json > $TMP_DIR/verilate-$r.txt
|
||||
./rtlmeter compare --format ascii --steps 'cppbuild' --metrics '* !system !user' $REF_DIR/all-results-$r.json $NEW_DIR/all-results-$r.json > $TMP_DIR/cppbuild-$r.txt
|
||||
./rtlmeter compare --format ascii --steps 'execute' --metrics '* !system !user' $REF_DIR/all-results-$r.json $NEW_DIR/all-results-$r.json > $TMP_DIR/execute-$r.txt
|
||||
# Chop them at new lines, into one table per file
|
||||
awk -v RS= -v prefix=$TMP_DIR/$r-frag '{print > sprintf("%s-verilate-%02d.txt",prefix,NR)}' $TMP_DIR/verilate-$r.txt
|
||||
awk -v RS= -v prefix=$TMP_DIR/$r-frag '{print > sprintf("%s-cppbuild-%02d.txt",prefix,NR)}' $TMP_DIR/cppbuild-$r.txt
|
||||
awk -v RS= -v prefix=$TMP_DIR/$r-frag '{print > sprintf("%s-execute-%02d.txt" ,prefix,NR)}' $TMP_DIR/execute-$r.txt
|
||||
done
|
||||
|
||||
# Create summary
|
||||
venv/bin/python3 $SCRIPT_DIR/ci-rtlmeter-pr-report.py ${SUMMARY_ARGS[@]} > $TMP_DIR/summary.txt
|
||||
# Print it
|
||||
cat $TMP_DIR/summary.txt
|
||||
|
||||
# Create notification comment content
|
||||
NOTIFICATION=$TMP_DIR/notification.txt
|
||||
cat > $NOTIFICATION <<NOTIFICATION_TEMPLATE
|
||||
Performance metrics for PR workflow [#$NEW_NUM]($NEW_URL) (B)
|
||||
compared to scheduled run [#$REF_NUM]($REF_URL) (A) from $REF_DATE
|
||||
<details open>
|
||||
<summary><strong>Summary of all runs</strong></summary>
|
||||
<pre>
|
||||
$(cat $TMP_DIR/summary.txt)
|
||||
</pre>
|
||||
</details>
|
||||
Blah Blah Blah
|
||||
NOTIFICATION_TEMPLATE
|
||||
|
||||
# Create detailed report
|
||||
REPORT=$TMP_DIR/report.txt
|
||||
cat > $REPORT <<SUMMARY_TEMPLATE
|
||||
Preamble Preamble
|
||||
<details open>
|
||||
<summary><strong>Summary of all runs</strong></summary>
|
||||
<pre>
|
||||
$(cat $TMP_DIR/summary.txt)
|
||||
</pre>
|
||||
</details>
|
||||
SUMMARY_TEMPLATE
|
||||
echo "<details>" >> $REPORT
|
||||
echo " <summary><strong>Detailed results</strong></summary>" >> $REPORT
|
||||
for r in $RUNS; do
|
||||
RUN_NAME=$(jq -rj ".[0].runName" $REF_DIR/all-results-$r.json)
|
||||
echo " <details>" >> $REPORT
|
||||
echo " <summary><strong><em>$RUN_NAME</em></strong></summary>" >> $REPORT
|
||||
for f in $(ls -1 $TMP_DIR/$r-frag-verilate-*.txt | sort) \
|
||||
$(ls -1 $TMP_DIR/$r-frag-cppbuild-*.txt | sort) \
|
||||
$(ls -1 $TMP_DIR/$r-frag-execute-*.txt | sort); do
|
||||
cat >> $REPORT <<DETAIL_TAMPLATE
|
||||
<details>
|
||||
<summary>$(head -n 1 $f | tr -d '\n')</summary>
|
||||
<pre>
|
||||
$(tail -n +2 $f)
|
||||
</pre>
|
||||
</details>
|
||||
DETAIL_TAMPLATE
|
||||
done
|
||||
echo " </details>" >> $REPORT
|
||||
done
|
||||
echo "</details>" >> $REPORT
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
# DESCRIPTION: Verilator: CI script for 'rtlmeter.yml' PR results
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
###############################################################################
|
||||
# This script runs with the venv of **RTLMeter**
|
||||
###############################################################################
|
||||
|
||||
import sys
|
||||
import json
|
||||
import tabulate
|
||||
from typing import Final, List
|
||||
|
||||
tabulate.PRESERVE_WHITESPACE = True
|
||||
tabulate.MIN_PADDING = 0
|
||||
|
||||
_ASCII_TABLE_FORMAT: Final = tabulate.TableFormat(
|
||||
lineabove=tabulate.Line("╒═", "═", "═╤═", "═╕"),
|
||||
linebelowheader=tabulate.Line("╞═", "═", "═╪═", "═╡"),
|
||||
linebelow=tabulate.Line("╘═", "═", "═╧═", "═╛"),
|
||||
headerrow=tabulate.DataRow("│ ", " │ ", " │"),
|
||||
datarow=tabulate.DataRow("│ ", " │ ", " │"),
|
||||
linebetweenrows=None,
|
||||
padding=0,
|
||||
with_header_hide=None,
|
||||
)
|
||||
|
||||
|
||||
def printTable(table: List[List[str]], **kwargs) -> None:
|
||||
print(tabulate.tabulate(table, tablefmt=_ASCII_TABLE_FORMAT, **kwargs))
|
||||
|
||||
|
||||
# fmt: off
|
||||
stepMetric = (
|
||||
("verilate", "elapsed"),
|
||||
("verilate", "memory"),
|
||||
("verilate", "cpu"),
|
||||
("cppbuild", "elapsed"),
|
||||
("cppbuild", "memory"),
|
||||
("cppbuild", "cpu"),
|
||||
("cppbuild", "codeSize"),
|
||||
("execute", "speed"),
|
||||
("execute", "clocks"),
|
||||
("execute", "memory"),
|
||||
("execute", "cpu"),
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
table = []
|
||||
|
||||
for ref, cmp in zip(sys.argv[1::2], sys.argv[2::2]):
|
||||
with open(ref, "r", encoding="utf-8") as f:
|
||||
ref_json = json.load(f)[0]
|
||||
with open(cmp, "r", encoding="utf-8") as f:
|
||||
cmp_json = json.load(f)
|
||||
if table:
|
||||
table.append(tabulate.SEPARATING_LINE)
|
||||
runName = ref_json["runName"]
|
||||
for step, metric in stepMetric:
|
||||
if step not in cmp_json:
|
||||
continue
|
||||
data = cmp_json[step]
|
||||
if metric not in data:
|
||||
continue
|
||||
data = data[metric]
|
||||
minGain = float("inf")
|
||||
maxGain = float("-inf")
|
||||
meanGain = 1
|
||||
count = 0
|
||||
for _, _, _, _, _, g, _ in data["table"]:
|
||||
minGain = min(minGain, g)
|
||||
maxGain = max(maxGain, g)
|
||||
meanGain *= g
|
||||
count += 1
|
||||
meanGain = meanGain**(1 / count)
|
||||
|
||||
if metric == "clocks":
|
||||
# Clock cycles should match exactly
|
||||
status = "❌" if minGain != 1 or maxGain != 1 else "✅"
|
||||
else:
|
||||
# Otherwise use some arbitrary brackets
|
||||
status = "❌"
|
||||
if (meanGain > 0.95):
|
||||
status = "⚠️"
|
||||
if (meanGain > 0.98):
|
||||
status = "✅"
|
||||
if (meanGain > 1.02):
|
||||
status = "💡"
|
||||
if (meanGain > 1.05):
|
||||
status = "⭐"
|
||||
|
||||
table.append([
|
||||
runName, step, ref_json["metrics"][metric]["header"], f"{meanGain:.2f}x {status} ",
|
||||
f"{minGain:.2f}x", f"{maxGain:.2f}x"
|
||||
])
|
||||
|
||||
printTable(
|
||||
table,
|
||||
headers=("Run", "Step", "Metric", "Improvement", "Min", "Max"),
|
||||
colalign=("left", "left", "left", "right", "right", "right"),
|
||||
disable_numparse=True,
|
||||
)
|
||||
|
|
@ -13,7 +13,7 @@ test.scenarios('dist')
|
|||
|
||||
Tabs_Exempt_Re = r'(\.out$)|(/fstcpp)|(Makefile)|(\.mk$)|(\.mk\.in$)|test_regress/t/t_preproc\.v|install-sh'
|
||||
|
||||
Unicode_Exempt_Re = r'(Changes$|CONTRIBUTORS$|LICENSES?|contributors.rst$|spelling.txt$)'
|
||||
Unicode_Exempt_Re = r'(Changes$|CONTRIBUTORS$|LICENSES?|contributors.rst$|spelling.txt$|ci-rtlmeter-pr-report.py)'
|
||||
|
||||
|
||||
def get_source_files():
|
||||
|
|
|
|||
Loading…
Reference in New Issue