SIGN IN SIGN UP

feat(ci): drive the Level Zero backend against a stub loader (#382)

feat(ci): drive the Level Zero backend against a stub loader

The loader check from #365 stops at `dlopen` plus symbol resolution, and
deliberately so: a runner with no Intel GPU has no driver for the loader to
return, so `zeInit` failing there is correct rather than a defect. The
consequence is that everything past `try_load_library` is unexercised by any
runner this project has. Device enumeration, the count-then-buffer idiom and
its clamp, the BDF mapping, the delta arithmetic, and the metric refresh all
ship on the strength of review alone.

The struct layouts are the sharpest edge. The coverage that exists compares
our `#[repr(C)]` struct against our own transcription of the spec, which
catches a mis-sized field and did once: `zes_pci_properties_t` declared three
`ze_bool_t` fields as `u32`, inflating it from 56 to 64 bytes. It cannot
catch two same-typed fields in the wrong order, a wrong offset that preserves
the total, or a spec value transcribed wrong in both the struct and its
assertion.

## The stub

`tests/fixtures/level_zero/stub_ze_loader.c` plays the driver's part,
exporting all 23 symbols the backend resolves: the four mandatory ones, plus
`zesInit` so initialisation takes the modern Sysman route, plus the engine,
power, temperature, memory, frequency, and fan families.

It is compiled against the **vendor headers**, not against our transcription.
That is the whole point: the vendor's own struct is filled on the C side and
read back through our type on the Rust side, so a value arriving intact is
evidence the two layouts agree field by field. A hand-written stub using our
definitions would only prove our definitions agree with themselves.

Values are chosen to be individually identifiable. `zes_freq_state_t` carries
five `f64` fields; the stub gives each a different number, so `actual` read
one field early reports 1200, 2200, or 2300 rather than a plausible 2100.
Counters advance by a fixed step per call, no clock reads and no randomness,
so the second refresh is exactly 25.00% compute, 10.00% render, and 45.00 W,
asserted as constants rather than ranges.

## How it binds, and why nothing shipped can reach it

`LIBZE_PATHS[0]` is the bare SONAME `libze_loader.so.1`, and `dlopen` on a
bare SONAME searches `LD_LIBRARY_PATH` before the default paths. A stub built
under that name in a directory placed first therefore wins over the real
`libze1` the same job installs, with no new branch in the loader.

The rejected alternative was an `ALL_SMI_LEVEL_ZERO_LIBRARY` path override,
which would have put a "load this arbitrary shared object" switch into every
released binary in exchange for nothing this route does not provide. So:
`LIBZE_PATHS` is unchanged, no environment override exists in production
code, and the stub is neither compiled into the `all-smi` binary nor into any
release artifact. `LD_LIBRARY_PATH` is set on one workflow step, so every
other step still sees the real loader.

## The tests

`tests/level_zero_stub.rs` is its own integration binary, therefore its own
process, because `LZ_RUNTIME` is a process-wide `OnceCell` and the library's
unit tests already latch it. Whoever ran first would otherwise decide which
loader the entire binary sees.

Five tests, driving only the public API: sorted BDF enumeration end to end;
the exact delta-derived percentages and watts; the point-in-time families
landing in a real `GpuInfo`; a device whose enumerator reports 4096 handles
and then fills one, exercising both the `MAX_L0_HANDLES` clamp and the
post-fill truncate; and an unknown BDF binding to nothing.

They fail loudly rather than skipping, in the same three-part shape as #365:
an env key arms the assertions, a marker prints only after they pass, and the
CI step greps for it.

## Verification

- The C stub cannot be compiled from this macOS host: it needs the vendor
  headers, which have no macOS package. CI is the compile check, and the
  build step also asserts with `nm` that all 23 symbols are exported.
- Rust side verified locally: the integration binary compiles both with the
  backend on (through the scratch probe) and off (macOS, where the file
  compiles away), and runs green unarmed.
- `cargo fmt --check` and `cargo clippy --all-targets -- -D warnings` clean.

Reachability proof and the full CI evidence follow in a PR comment once the
first run reports.

Closes #379
J
Jeongkyu Shin committed
89cd907bbb5ee2be0426c3a2f733283a6a1760af
Parent: ba82d92
Committed by GitHub <noreply@github.com> on 8/23/2026, 5:11:05 PM