Repository navigation
Description
arch-qemu-nightly has been red on riscv64 since 2026-10-27: five consecutive green runs up to 2026-10-25 08:30Z, six failures since.
The emulator is not the problem. "Install qemu user-mode emulators" succeeds on all five architectures, and arm, s390x, ppc64le and loong64 all pass. riscv64 alone fails, and the log says why:
FAIL github.com/go-embedded-ruby/ruby/internal/vm 3603.574s
panic: test timed out after 1h0m0s
running tests:
TestDefinedOperator (0s)
3603.574s against the -timeout=60m on line 122. Nothing is hung: the test running when the alarm fired had been running 0s, so the alarm simply landed on whichever test had just started. internal/vm accumulates the hour.
The wall is not what changed — the suite is
The workflow comment records the measurement that chose 60m, and that is what makes this diagnosable. Against tonight's run (41189897106):
| arch | the comment's number | 2026-10-31 | ratio |
|---|---|---|---|
| loong64 | 990s | 1484.9s | 1.50× |
| ppc64le | 1284s | 1590.9s | 1.24× |
| arm | — (lane added later) | 2032.5s | — |
| s390x | 1754s | 3469.5s | 1.98× |
| riscv64 | 1204s | ≥3603.6s (hit the wall) | ≥2.99× |
⛔ s390x now passes with 130 seconds to spare — 96.4% of the wall. The comment's own words were that "60m puts s390x at about half the wall, which is what generous headroom was meant to say". It is not half any more. On the next run where variance goes the wrong way, s390x joins riscv64.
Why raising the wall again is the wrong first move
This wall has already been widened once, 30m → 60m, for precisely this symptom — the comment documents that run dying at 1801.594s against a 1800s wall, a 2.6% margin. Widening it a second time would buy days and teach nothing: with s390x at 96%, the next number to choose is already in doubt.
And the slowdown is uneven, which is the part worth explaining. 1.24× on ppc64le against ≥2.99× on riscv64. If the suite had merely gained tests, every architecture would scale by roughly the same factor, because they all run the same tests. They do not, so there is plausibly a second effect that is disproportionately expensive on riscv64 and s390x — and that is a question about the VM, not about CI.
What landed in the regression window (5 commits, 2026-10-25 → 2026-10-27), two of which add work:
447ed1ccfix: build and test on 32-bit targets (386, arm) — fix: build and test on 32-bit targets (386, arm) #82822da5c67fix: compile on every GOOS/GOARCH Go supports except plan9 — fix: compile on every GOOS/GOARCH Go supports except plan9 #8290b8341e7ci: install qemu-user, not the virtual qemu-user-static (Ubuntu 26.04) — ci: install qemu-user, not the virtual qemu-user-static (Ubuntu 26.04) #830e6ca8c2dchore(deps): update dependency go to v1.27.3 — chore(deps): update dependency go to v1.27.3 #827e54df083ci: Go 1.27.3 in the arch-386 and every-target jobs — ci: Go 1.27.3 in the arch-386 and every-target jobs #834
e6ca8c2d/e54df083 move the toolchain to Go 1.27.3, which is a candidate for a uniform slowdown; #828/#829 are candidates for more tests. Neither obviously predicts a 2.4× spread between ppc64le and riscv64.
Suggested order
- Split
internal/vmso one package does not have to fit in one wall — it is the heaviest package on every architecture and is the only one at risk. - Separately, find out why riscv64 and s390x grew 2–3× while ppc64le grew 1.24×. Interleaved A/B of the suite at
22da5c67^and atmain, same arch, same runner class, would separate "more tests" from "slower per test". - Raise the wall only as a holding action, and say in the comment that it is one.
A note on tooling
gh run view <id> --log-failed returns zero lines for these runs, which is how this looked undiagnosable from the CLI. gh api repos/{o}/{r}/actions/jobs/{job}/logs returns all 3901 lines. Worth knowing before concluding a red run has no explanation.
🤖 Generated with Claude Code
Activity
tannevaled commentedon Nov 7, 2026
Today's figures: s390x is at 97.7% of the wall, and riscv64 has now failed 10 nights running
Measured from run 42363542913 (2026-11-07, scheduled,
0b8ea8ff), reading theTest under qemustep durations:armloong64ppc64les390xriscv64And the number this issue is actually about —
internal/vmalone on s390x, from the job log:3517.1 s = 58.62 min against a 60 min wall: 97.7%, so about 84 seconds of headroom. This issue records 96%; it has crept up since.
⛔ Two things that make these numbers easy to misread
go test -timeout=60mis per test BINARY, not per run (.github/workflows/arch-qemu-nightly.yml:122runs./...). That is why s390x's step can take 63 minutes and still pass — no single package exceeded the wall. A reader comparing the 63 against the 60 would conclude the timeout is not enforced; it is, per package.A job duration is not a test duration. My first reading of this run used job durations, which carry checkout,
setup-goand the qemu install. The step figures above are the comparable ones, and the 3517 s is the only figure directly against the wall.The lane has been red every night for at least 10 nights
Ten different commits, same outcome — so this is the known
internal/vmgrowth and not anything a particular change introduced. Worth saying explicitly, becausearch-qemu-nightlyis not one of the 26 checks a pull request runs: nothing merged in those ten days was ever judged by this lane, and a lane that has been red for a known reason is exactly where a second, newer cause would sit unnoticed. The four passing arches above are the evidence that there isn't one today.I came to this while checking
mainafter merging #904 and #906, nearly filed a duplicate, and found this issue already naming it.🤖 Generated with Claude Code
tannevaled commentedon Nov 7, 2026
internal/vmon REAL s390x: 5m 12s, against 58m 37s under QEMUMeasured on the LinuxONE host (
ssh linuxone— IBM z17, 8 vCPU, load 0.00, 39 days up), 2026-11-07 21:25–21:30:Against the same package in the nightly QEMU lane, from run 42363542913:
3517.1 s ÷ 311.9 s ≈ 11.3× slower under emulation.
⛔ This reframes the issue
The title reads "internal/vm outgrew the 60m wall". On the metal the package takes 5 minutes. It has not outgrown anything — QEMU is 11× slower than the hardware, and the 58.6 min that sits at 97.7% of the wall is almost entirely emulation overhead.
That matters because the two diagnoses call for opposite remedies. If the package were too big, the answers are splitting it, sharding the matrix, or raising the timeout. If the emulator is the cost, those are all beside the point, and the real options are:
ssh linuxoneworks non-interactively),user 8m3sagainstreal 5m12sis ~1.55 cores busy, so the 8 vCPUs are nowhere near saturated — the run is not fighting the host.What the figure includes, and what it does not
9 tests failed, every one of them in 0.00 s, and every one for want of repository files rather than anything architectural:
A compiled test binary does not carry its tree. At 0.00 s each they cannot move the 5m12s, so the comparison stands — but the figure is a lower bound by a few seconds, and those 9 tests were not exercised on big-endian. I am re-running with
go.mod,scripts/andinternal/copied alongside so the number needs no caveat; I will post it.⛔ No
-race: it needs CGO on s390x and the host has no Go toolchain. This run says nothing about data races.⭐ Everything that ran, ran correctly — on big-endian, for the first time
CI cross-compiles for
linux/s390xon every job and runs the tests on amd64; the nightly QEMU lane has been red for ten nights. So this is the first execution ofinternal/vmon big-endian hardware, and apart from the 9 path failures it is clean. The one real s390x defect this fleet has had wasbinary.NativeEndianon auxv in go-ndarray — the kind of thing only a big-endian run can find.⭐ Incidentally,
TestErrorMessagesUseStraightQuotesNotTheOldBacktickForm(added in #900) refused rather than reporting a clean sweep over an empty population: "only 0 source files scanned". A gate that counts its denominator caught a condition on hardware nobody had it in mind for.Method, for whoever repeats it
Write results to a file on the host: they outlive the local scratchpad.
🤖 Generated with Claude Code
tannevaled commentedon Nov 7, 2026
The clean rerun: 5m 14s, and the caveat was worth two seconds
The figure above carried a caveat — 9 tests failed for want of repository files, each in 0.00 s, so the 5m11.9s was a lower bound "by a few seconds". Copied
go.mod,go.sum,scripts/andinternal/to the host (18 MB) and ran the same binary fromrepo/internal/vmso those paths resolve:5m 14.0s against 5m 11.9s — the reasoning held, and now it is measured rather than argued. Against QEMU's 3517.1 s that is ≈11.2×.
Failures: 9 → 2, and the two left are the same shape as the seven that went away:
They shell out to
go build, and the host has no Go toolchain — which is why the binary is cross-compiled in the first place. Both refuse rather than skipping, so they cannot pass vacuously there.So, on big-endian hardware
Everything in
internal/vmruns clean except two tests that need a compiler on the host. That includes the seven that could not resolve their paths on the first run — the repository-wide quoting gate and everyShim*test — so they have now been exercised on big-endian too.⛔ Still not covered, and worth repeating so it is not read as a clean bill: no
-race(needs CGO on s390x), and these are the package's own tests, not the ruby/spec corpus.🤖 Generated with Claude Code