diff --git a/.github/workflows/energy-hardware.yml b/.github/workflows/energy-hardware.yml new file mode 100644 index 000000000..00118e5f3 --- /dev/null +++ b/.github/workflows/energy-hardware.yml @@ -0,0 +1,123 @@ +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. + +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/**' + - 'proof/proof-rs/crates/proof-cascade/**' + - 'jouleclaw/jouleclaw-rs/crates/jouleclaw-energy/**' + - '.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/**' + - 'proof/proof-rs/crates/proof-cascade/**' + - 'jouleclaw/jouleclaw-rs/crates/jouleclaw-energy/**' + - '.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; } + + - 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; }