Telemetry¶
Every build records its own metrics. Separate from the benchmark harness: telemetry runs on every build including real ones, the harness runs under controlled conditions.
telemetry/build.sh wraps a bake invocation and writes Prometheus
metrics for each target it produced, exiting with bake's own status — a
drop-in replacement for a bare bake. Every make build-* and
push-* already routes through it.
./telemetry/build.sh --builder snoc-think pytorch
make build-pytorch # the same thing
TELEMETRY=0 make build-pytorch # a bare bake
Both data sources already exist, so nothing is instrumented and no collector daemon runs: BuildKit's history API for duration, status and cached-step count, and sccache's own stats, printed at the end of the build step and scraped out of the replayed log — they cannot be queried afterwards, since the server dies with the build container.
Metrics¶
Eight per-target gauges, labelled {target="…",builder="…"}:
| Metric | Meaning |
|---|---|
rocm_build_duration_seconds |
BuildKit-reported duration |
rocm_build_steps_total |
Steps in the build |
rocm_build_steps_cached |
How many came from the layer cache |
rocm_build_layer_cache_hit_ratio |
Cached steps over total |
rocm_build_target_success |
1 if the target completed |
rocm_build_sccache_hits |
Compiler-cache hits for that build |
rocm_build_sccache_misses |
Compiler-cache misses |
rocm_build_sccache_hit_ratio |
Hits over hits plus misses |
Three more describe the invocation as a whole, labelled {builder="…"}:
rocm_build_invocation_duration_seconds,
rocm_build_invocation_success and
rocm_build_last_run_timestamp_seconds.
A HELP/TYPE block is emitted only when at least one sample exists for
that name, so a scrape never sees a declared-but-empty metric.
Where It Lands¶
Written to a temp path and renamed, so the collector never reads a
partial file, and picked up by the node_exporter already on :9100.
When that directory is not writable the wrapper falls back to
telemetry/out/ and says so.
Attributing sccache Stats To A Target¶
The subtle part. Replayed history records carry no bake-target prefix on their vertex headers, so the step cannot be identified the way the harness does it live. Each Dockerfile tees its stats to a known file instead, and the wrapper finds the vertex whose command contains that marker:
| Target | Marker |
|---|---|
pytorch |
/opt/pytorch/sccache-stats.txt |
triton-wheel |
/wheels-triton-sccache-stats.txt |
aiter-wheel |
/wheels-aiter-sccache-stats.txt |
torchvision-wheel |
/wheels-torchvision-sccache-stats.txt |
Having found the step number, it reads that step's stats lines. With no marker present it falls back to the last stats block in the log, which is a guess and is why the markers exist.
Reading It Back¶
make build-status # build records on $(BUILDER), with the node each ran on
make build-inspect # duration, steps, cached steps, VCS revision
make layer-summary # layer reuse per image, most recent build of each
make cache-summary # compiler-cache size and the delta since last call
layer-summary is keyed by image, not node: which node a target ran on
is an artefact of scheduling, so a per-node view answers the wrong
question. The node is still shown, because it explains a low number — a
target that moved nodes cannot use the previous node's local BuildKit
cache and must refetch from Zot.
When Numbers Are Missing¶
sccache counters are absent when a target was fully layer-cached: the build step never ran, so it printed no stats. That is correct, and it is exactly the distinction the harness uses to separate a hot run that compiled from one that skipped the compile.