244 lines
12 KiB
YAML
244 lines
12 KiB
YAML
name: energy meters on real hardware
|
|
|
|
# The Linux RAPL and NVML meters, run against real counters.
|
|
#
|
|
# Every other job runs in a container, where Docker masks
|
|
# /sys/devices/virtual/powercap and there is no GPU, so the energy crates'
|
|
# hardware tests only ever take their "unavailable, and the oracle agrees"
|
|
# branch there. This job runs on `linux-hw`: a host executor on jetson-hub
|
|
# (Intel i9-13900H: package-0, a core sub-zone, psys, an mmio package; NVIDIA
|
|
# RTX 4050 Laptop GPU), as an unprivileged user that can read
|
|
# /sys/class/powercap and open /dev/nvidia*. Here the live branch runs, and each
|
|
# step fails unless it did:
|
|
#
|
|
# - RAPL: the test printed the oracle's package-only bracket ("package-only
|
|
# inner ... outer ..."), which only the live path prints, after asserting the
|
|
# meter read inside it, below package+psys and below package+core.
|
|
# - NVML: the test saw the GPU ("nvml hardware: 1 GPU"). On this GPU the
|
|
# counter runs past the board while it is polled, so the test asserts the
|
|
# meter calls it Implausible and never grades it as hardware.
|
|
#
|
|
# The host has no Node, so there are no JavaScript actions: the source is
|
|
# fetched with plain git, and each workspace keeps its target directory in the
|
|
# runner's home between runs.
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
on:
|
|
pull_request:
|
|
paths:
|
|
- 'jcp/jcp-rs/crates/jcp-energy/**'
|
|
- 'eoc/eoc-rs/crates/eoc-meter/**'
|
|
- 'sandbox/sandbox-rs/crates/sandbox-meter/**'
|
|
- 'sandbox/sandbox-rs/crates/sandbox-exec-microvm/**'
|
|
- 'sandbox/sandbox-rs/crates/sandbox-proof/**'
|
|
- 'proof/proof-rs/crates/proof-cascade/**'
|
|
- 'jouleclaw/jouleclaw-rs/crates/jouleclaw-energy/**'
|
|
# The sink's readers and its table procedure (wai/extensions/sink-energy.md §4.4).
|
|
- 'wai/wai-rs/src/sink_meter.rs'
|
|
- 'wai/wai-rs/src/sink_energy.rs'
|
|
- 'wai/wai-rs/src/sink_energy/**'
|
|
- 'wai/wai-rs/src/bin/wai_sink_table.rs'
|
|
- 'wai/live-conformance/sink_energy_verify.py'
|
|
- '.github/workflows/energy-hardware.yml'
|
|
push:
|
|
branches:
|
|
- main
|
|
paths:
|
|
- 'jcp/jcp-rs/crates/jcp-energy/**'
|
|
- 'eoc/eoc-rs/crates/eoc-meter/**'
|
|
- 'sandbox/sandbox-rs/crates/sandbox-meter/**'
|
|
- 'sandbox/sandbox-rs/crates/sandbox-exec-microvm/**'
|
|
- 'sandbox/sandbox-rs/crates/sandbox-proof/**'
|
|
- 'proof/proof-rs/crates/proof-cascade/**'
|
|
- 'jouleclaw/jouleclaw-rs/crates/jouleclaw-energy/**'
|
|
# The sink's readers and its table procedure (wai/extensions/sink-energy.md §4.4).
|
|
- 'wai/wai-rs/src/sink_meter.rs'
|
|
- 'wai/wai-rs/src/sink_energy.rs'
|
|
- 'wai/wai-rs/src/sink_energy/**'
|
|
- 'wai/wai-rs/src/bin/wai_sink_table.rs'
|
|
- 'wai/live-conformance/sink_energy_verify.py'
|
|
- '.github/workflows/energy-hardware.yml'
|
|
|
|
jobs:
|
|
hardware:
|
|
name: RAPL and NVML on jetson-hub
|
|
runs-on: linux-hw
|
|
timeout-minutes: 150
|
|
env:
|
|
CARGO_PROFILE_DEV_DEBUG: '0'
|
|
CARGO_BUILD_JOBS: '8'
|
|
steps:
|
|
- name: Fetch the source
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
ref="${{ github.event_name == 'pull_request' && format('refs/pull/{0}/head', github.event.pull_request.number) || github.ref }}"
|
|
want="${{ github.event.pull_request.head.sha || github.sha }}"
|
|
rm -rf src && mkdir src && cd src
|
|
git init -q
|
|
git fetch -q --depth 1 http://100.64.224.0:3400/Transaction-Science/open-standards.git "$ref"
|
|
git checkout -q FETCH_HEAD
|
|
got="$(git rev-parse HEAD)"
|
|
echo "fetched $ref at $got"
|
|
if [ "$got" != "$want" ]; then
|
|
echo "::error::$ref is at $got, not $want: it moved after this run started, and a newer run tests it"
|
|
exit 1
|
|
fi
|
|
|
|
- name: jcp-energy (RAPL, NVML)
|
|
shell: bash
|
|
working-directory: src/jcp/jcp-rs
|
|
run: |
|
|
set -euo pipefail
|
|
log=$(mktemp)
|
|
CARGO_TARGET_DIR="$HOME/hw-target/jcp-rs" nice -n 10 cargo test -p jcp-energy --features nvml --test rapl_hardware --test nvml_hardware -- --nocapture --test-threads=1 2>&1 | tee "$log"
|
|
grep -q 'package-only inner' "$log" || { echo "::error::the RAPL live path never ran"; exit 1; }
|
|
grep -q 'nvml hardware: 1 GPU' "$log" || { echo "::error::NVML saw no GPU"; exit 1; }
|
|
|
|
- name: eoc-meter (RAPL, NVML)
|
|
shell: bash
|
|
working-directory: src/eoc/eoc-rs
|
|
run: |
|
|
set -euo pipefail
|
|
log=$(mktemp)
|
|
CARGO_TARGET_DIR="$HOME/hw-target/eoc-rs" nice -n 10 cargo test -p eoc-meter --features cuda --test rapl_hardware --test nvml_hardware -- --nocapture --test-threads=1 2>&1 | tee "$log"
|
|
grep -q 'package-only inner' "$log" || { echo "::error::the RAPL live path never ran"; exit 1; }
|
|
grep -q 'nvml hardware: 1 GPU' "$log" || { echo "::error::NVML saw no GPU"; exit 1; }
|
|
|
|
- name: sandbox-meter (RAPL, NVML) and sandbox-exec-microvm (RAPL)
|
|
shell: bash
|
|
working-directory: src/sandbox/sandbox-rs
|
|
run: |
|
|
set -euo pipefail
|
|
log=$(mktemp)
|
|
CARGO_TARGET_DIR="$HOME/hw-target/sandbox-rs" nice -n 10 cargo test -p sandbox-meter --test rapl_hardware --test nvml_hardware -- --nocapture --test-threads=1 2>&1 | tee "$log"
|
|
grep -q 'package-only inner' "$log" || { echo "::error::sandbox-meter: the RAPL live path never ran"; exit 1; }
|
|
grep -q 'nvml hardware: 1 GPU' "$log" || { echo "::error::sandbox-meter: NVML saw no GPU"; exit 1; }
|
|
log=$(mktemp)
|
|
CARGO_TARGET_DIR="$HOME/hw-target/sandbox-rs" nice -n 10 cargo test -p sandbox-exec-microvm --test rapl_hardware -- --nocapture --test-threads=1 2>&1 | tee "$log"
|
|
grep -q 'package-only inner' "$log" || { echo "::error::sandbox-exec-microvm: the RAPL live path never ran"; exit 1; }
|
|
# sandbox-proof's accelerator proof: the span over a real workload's
|
|
# run, its RAPL figure against the package zones read around it.
|
|
log=$(mktemp)
|
|
CARGO_TARGET_DIR="$HOME/hw-target/sandbox-rs" nice -n 10 cargo test -p sandbox-proof --lib accelerator -- --nocapture --test-threads=1 2>&1 | tee "$log"
|
|
grep -q 'RAPL package .* oracle around the call' "$log" || { echo "::error::sandbox-proof: the accelerator span's RAPL path never ran"; exit 1; }
|
|
|
|
- name: proof-cascade (RAPL)
|
|
shell: bash
|
|
working-directory: src/proof/proof-rs
|
|
run: |
|
|
set -euo pipefail
|
|
log=$(mktemp)
|
|
CARGO_TARGET_DIR="$HOME/hw-target/proof-rs" nice -n 10 cargo test -p proof-cascade --test rapl_hardware -- --nocapture --test-threads=1 2>&1 | tee "$log"
|
|
grep -q 'rapl hardware:.*oracle Live' "$log" || { echo "::error::the RAPL counter was not live to the oracle"; exit 1; }
|
|
|
|
- name: jouleclaw-energy (RAPL, NVML)
|
|
shell: bash
|
|
working-directory: src/jouleclaw/jouleclaw-rs
|
|
run: |
|
|
set -euo pipefail
|
|
log=$(mktemp)
|
|
CARGO_TARGET_DIR="$HOME/hw-target/jouleclaw-rs" nice -n 10 cargo test -p jouleclaw-energy --features rapl,nvml --test rapl_hardware --test nvml_hardware -- --nocapture --test-threads=1 2>&1 | tee "$log"
|
|
grep -q 'package-only inner' "$log" || { echo "::error::the RAPL live path never ran"; exit 1; }
|
|
grep -q 'nvml hardware: 1 GPU' "$log" || { echo "::error::NVML saw no GPU"; exit 1; }
|
|
|
|
# The sink's decode table (wai/extensions/sink-energy.md §4.4) on the two platforms whose
|
|
# counters energy-measurement §9 profiles. Each job surveys its runner's counters first.
|
|
# Where no sink profile is live there, the measurement step is skipped — shown as skipped,
|
|
# with the survey in the run's summary — and never reported as passed. Where one is, the
|
|
# procedure takes seven rounds of one work item (the committed int_motion vector) and
|
|
# writes a signed table with its readings and procedure, which the reference checks as it
|
|
# writes it and the independent verifier checks again. No figure is asserted: a busy
|
|
# runner gives an unmetered row with its reason, which is the procedure working, not
|
|
# failing (feedback on energy tests and host load: assert what the meter reports).
|
|
sink-table-linux:
|
|
name: sink decode table · RAPL on jetson-hub (NVML surveyed)
|
|
runs-on: linux-hw
|
|
timeout-minutes: 90
|
|
env:
|
|
CARGO_PROFILE_RELEASE_DEBUG: '0'
|
|
CARGO_BUILD_JOBS: '8'
|
|
steps:
|
|
- name: Fetch the source
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
ref="${{ github.event_name == 'pull_request' && format('refs/pull/{0}/head', github.event.pull_request.number) || github.ref }}"
|
|
want="${{ github.event.pull_request.head.sha || github.sha }}"
|
|
rm -rf src && mkdir src && cd src
|
|
git init -q
|
|
git fetch -q --depth 1 http://100.64.224.0:3400/Transaction-Science/open-standards.git "$ref"
|
|
git checkout -q FETCH_HEAD
|
|
got="$(git rev-parse HEAD)"
|
|
echo "fetched $ref at $got"
|
|
if [ "$got" != "$want" ]; then
|
|
echo "::error::$ref is at $got, not $want: it moved after this run started, and a newer run tests it"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Survey the sink profiles
|
|
id: survey
|
|
shell: bash
|
|
working-directory: src/wai/wai-rs
|
|
run: |
|
|
set -euo pipefail
|
|
CARGO_TARGET_DIR="$HOME/hw-target/wai-rs" nice -n 10 cargo build --locked --release --no-default-features --features sink_energy,video_int --bin wai_sink_table
|
|
"$HOME/hw-target/wai-rs/release/wai_sink_table" survey | tee "$RUNNER_TEMP/survey.txt"
|
|
if grep -qE '^linux-rapl +live' "$RUNNER_TEMP/survey.txt"; then
|
|
echo "live=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "live=false" >> "$GITHUB_OUTPUT"
|
|
{ echo "### sink decode table: SKIPPED (not passed)"; echo; echo "No sink profile is live on this runner:"; echo '```'; cat "$RUNNER_TEMP/survey.txt"; echo '```'; } >> "$GITHUB_STEP_SUMMARY"
|
|
echo "::warning::sink decode table SKIPPED: no live counter on this runner"
|
|
fi
|
|
|
|
- name: Measure one work item and verify the table
|
|
if: steps.survey.outputs.live == 'true'
|
|
shell: bash
|
|
working-directory: src/wai/wai-rs
|
|
run: |
|
|
set -euo pipefail
|
|
out="$RUNNER_TEMP/sink-table"
|
|
rm -rf "$out"
|
|
nice -n 10 "$HOME/hw-target/wai-rs/release/wai_sink_table" run --out "$out" --rounds 7 --filter int_motion
|
|
python3 -I ../live-conformance/sink_energy_verify.py --table "$out"
|
|
|
|
sink-table-macos:
|
|
name: sink decode table · per-task energy and the GPU rail on macOS
|
|
runs-on: macos
|
|
timeout-minutes: 90
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
|
|
- name: Survey the sink profiles
|
|
id: survey
|
|
shell: bash
|
|
working-directory: wai/wai-rs
|
|
run: |
|
|
set -euo pipefail
|
|
export PATH="$HOME/.cargo/bin:$PATH"
|
|
cargo build --locked --release --no-default-features --features sink_energy,video_int --bin wai_sink_table
|
|
bin="$(cargo metadata --format-version 1 --no-deps | python3 -c 'import json,sys; print(json.load(sys.stdin)["target_directory"])')/release/wai_sink_table"
|
|
echo "bin=$bin" >> "$GITHUB_OUTPUT"
|
|
"$bin" survey | tee "$RUNNER_TEMP/survey.txt"
|
|
if grep -qE '^macos-task +live' "$RUNNER_TEMP/survey.txt"; then
|
|
echo "live=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "live=false" >> "$GITHUB_OUTPUT"
|
|
{ echo "### sink decode table: SKIPPED (not passed)"; echo; echo "No sink profile is live on this runner:"; echo '```'; cat "$RUNNER_TEMP/survey.txt"; echo '```'; } >> "$GITHUB_STEP_SUMMARY"
|
|
echo "::warning::sink decode table SKIPPED: no live counter on this runner"
|
|
fi
|
|
|
|
- name: Measure one work item and verify the table
|
|
if: steps.survey.outputs.live == 'true'
|
|
shell: bash
|
|
working-directory: wai/wai-rs
|
|
run: |
|
|
set -euo pipefail
|
|
out="$RUNNER_TEMP/sink-table"
|
|
rm -rf "$out"
|
|
"${{ steps.survey.outputs.bin }}" run --out "$out" --rounds 7 --filter int_motion --platform-decoder /nonexistent
|
|
python3 -I ../live-conformance/sink_energy_verify.py --table "$out"
|