A lane that runs no block is said once at start, at info

The start-up report warned about every CPU lane on every start: cevm's Go
entry passes no host, so a CPU lane declines the health battery, and a
build with no library linked runs nothing at all. Those lanes are what they
are, not something that went wrong, so they are named together in one info
line; a GPU lane that does not run the battery is still a warning of its
own. A loaded library of another ABI is one warning, and no lane is probed.

The report tests move out of backend_cgo_test.go, which the native build
failed: it expected ErrNotLinked from a build that links the library.
cevmreport_test.go runs in every build and holds the start-up report to
warn only about a GPU lane.

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
2026-09-23 11:28:01 -07:00
co-authored by Hanzo Dev
parent acd68484d7
commit 47028b9f41
3 changed files with 222 additions and 127 deletions
-116
View File
@@ -1,116 +0,0 @@
// Copyright (C) 2019-2026, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
//go:build cgo
package main
import (
"bytes"
"encoding/json"
"errors"
"strings"
"testing"
"github.com/luxfi/chains/evm/cevm"
"github.com/luxfi/log"
)
// A backend that answered the health battery is reported at info; one that did
// not is reported at warn, carrying the probe that failed and the reason.
//
// The level is the whole point: an operator's alerting reads it, and a backend
// that cannot execute a block reported at info is a chain that will not run
// with nothing raised about it.
func TestAnUnhealthyBackendIsReportedAtWarnWithItsReason(t *testing.T) {
var buf bytes.Buffer
logger := log.NewWriter(&buf)
report(logger, cevm.HealthReport{
Backend: cevm.GPUCUDA,
Name: "gpu-cuda",
OK: false,
Probe: "storage",
Err: errors.New("kernel did not launch"),
})
got := lines(t, &buf)
if len(got) != 1 {
t.Fatalf("one report produced %d log lines", len(got))
}
if lvl := strings.ToLower(str(got[0]["level"])); lvl != "warn" {
t.Errorf("an unhealthy backend was logged at %q, want warn", lvl)
}
for k, want := range map[string]string{"backend": "gpu-cuda", "probe": "storage"} {
if str(got[0][k]) != want {
t.Errorf("log field %q = %q, want %q", k, str(got[0][k]), want)
}
}
if !strings.Contains(str(got[0]["err"]), "kernel did not launch") {
t.Errorf("log does not carry the reason: %v", got[0]["err"])
}
}
func TestAHealthyBackendIsReportedAtInfo(t *testing.T) {
var buf bytes.Buffer
logger := log.NewWriter(&buf)
report(logger, cevm.HealthReport{
Backend: cevm.CPUParallel,
Name: "cpu-parallel",
OK: true,
ProbesRun: 5,
GasUsed: 105000,
})
got := lines(t, &buf)
if len(got) != 1 {
t.Fatalf("one report produced %d log lines", len(got))
}
if lvl := strings.ToLower(str(got[0]["level"])); lvl != "info" {
t.Errorf("a healthy backend was logged at %q, want info", lvl)
}
if str(got[0]["backend"]) != "cpu-parallel" {
t.Errorf("log names backend %q", got[0]["backend"])
}
}
// This build links no native library, so the one report it has says so — and
// says the right thing. It used to blame CGo, which is enabled here; an
// operator who followed that checked CGO_ENABLED and found it already 1.
func TestThisBuildReportsWhyItCannotExecute(t *testing.T) {
reports := cevm.Health()
if len(reports) == 0 {
t.Fatal("Health() said nothing at all")
}
for _, h := range reports {
if h.OK {
continue
}
if !errors.Is(h.Err, cevm.ErrNotLinked) {
t.Errorf("backend %q is not healthy and blames %v, want cevm.ErrNotLinked",
h.Name, h.Err)
}
}
}
func lines(t *testing.T, buf *bytes.Buffer) []map[string]any {
t.Helper()
var out []map[string]any
for _, l := range strings.Split(strings.TrimSpace(buf.String()), "\n") {
if l == "" {
continue
}
var m map[string]any
if err := json.Unmarshal([]byte(l), &m); err != nil {
t.Fatalf("log line %q is not JSON: %v", l, err)
}
out = append(out, m)
}
return out
}
func str(v any) string {
s, _ := v.(string)
return s
}
+47 -11
View File
@@ -8,26 +8,62 @@
package main
import (
"errors"
"github.com/luxfi/chains/evm/cevm"
"github.com/luxfi/log"
)
func reportCevm(logger log.Logger) {
logger.Info("cevm linked",
"abi", cevm.LibraryABIVersion(),
"backends", cevm.AvailableBackends(),
)
for _, h := range cevm.Health() {
abi := cevm.LibraryABIVersion()
logger.Info("cevm linked", "abi", abi, "backends", cevm.AvailableBackends())
if abi != cevm.ABIVersion {
// Every block is declined, so no lane has anything to report.
logger.Warn("cevm library speaks another ABI; the Go EVM runs every block",
"library", abi, "reads", cevm.ABIVersion)
return
}
reportHealth(logger, cevm.Health())
}
// reportHealth says what the backends answered to the health battery.
//
// A lane that runs no block in any build of this kind is said once, at info:
// a CPU lane, which cevm's Go entry passes no host and so runs nothing on,
// and the one lane of a build with no library. That is what the lane is, not
// something that went wrong, and a warning every start would bury the ones
// that did. Every other lane is reported on its own.
func reportHealth(logger log.Logger, reports []cevm.HealthReport) {
var idle []string
for _, h := range reports {
if runsNothing(h) {
idle = append(idle, h.Name)
continue
}
report(logger, h)
}
if len(idle) > 0 {
logger.Info("cevm runs no block on these backends; the Go EVM does", "backends", idle)
}
}
// report says what one cevm backend answered to the health battery.
//
// It is separate from the report above because it is the only part that
// depends on what the health check found: a build with no library linked has
// exactly one report and it is never healthy, so a check folded into the loop
// could only ever be read one way.
// runsNothing reports whether h is a lane that runs no block by what it is: a
// CPU lane that declined, or a lane with no library behind it.
func runsNothing(h cevm.HealthReport) bool {
if h.OK {
return false
}
if errors.Is(h.Err, cevm.ErrNotLinked) {
return true
}
cpu := h.Backend == cevm.CPUSequential || h.Backend == cevm.CPUParallel
return cpu && errors.Is(h.Err, cevm.ErrDeclined)
}
// report says what one cevm backend answered to the health battery: at info
// when it ran it, and at warn, with the probe and the reason, when it did not.
// A GPU lane that declines the battery's funded transfer has no device, or a
// device that does not work, and the Go EVM is running every block it would.
func report(logger log.Logger, h cevm.HealthReport) {
if h.OK {
logger.Info("cevm backend healthy",
+175
View File
@@ -0,0 +1,175 @@
// Copyright (C) 2019-2026, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package main
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"strings"
"testing"
"github.com/luxfi/chains/evm/cevm"
"github.com/luxfi/log"
)
// A backend that should have run the health battery and did not is reported
// at warn, carrying the probe that failed and the reason; one that ran it is
// reported at info.
//
// The level is the whole point: an operator's alerting reads it, and a GPU
// lane that cannot run a block reported at info is a device doing nothing with
// nothing raised about it.
func TestAnUnhealthyBackendIsReportedAtWarnWithItsReason(t *testing.T) {
var buf bytes.Buffer
logger := log.NewWriter(&buf)
report(logger, cevm.HealthReport{
Backend: cevm.GPUCUDA,
Name: "gpu-cuda",
OK: false,
Probe: "storage",
Err: errors.New("kernel did not launch"),
})
got := lines(t, &buf)
if len(got) != 1 {
t.Fatalf("one report produced %d log lines", len(got))
}
if lvl := level(got[0]); lvl != "warn" {
t.Errorf("an unhealthy backend was logged at %q, want warn", lvl)
}
for k, want := range map[string]string{"backend": "gpu-cuda", "probe": "storage"} {
if str(got[0][k]) != want {
t.Errorf("log field %q = %q, want %q", k, str(got[0][k]), want)
}
}
if !strings.Contains(str(got[0]["err"]), "kernel did not launch") {
t.Errorf("log does not carry the reason: %v", got[0]["err"])
}
}
func TestAHealthyBackendIsReportedAtInfo(t *testing.T) {
var buf bytes.Buffer
logger := log.NewWriter(&buf)
report(logger, cevm.HealthReport{
Backend: cevm.GPUMetal,
Name: "gpu-metal",
OK: true,
ProbesRun: 1,
GasUsed: 21000,
})
got := lines(t, &buf)
if len(got) != 1 {
t.Fatalf("one report produced %d log lines", len(got))
}
if lvl := level(got[0]); lvl != "info" {
t.Errorf("a healthy backend was logged at %q, want info", lvl)
}
if str(got[0]["backend"]) != "gpu-metal" {
t.Errorf("log names backend %q", got[0]["backend"])
}
}
// A CPU lane declines every block through cevm's Go entry, and a build with
// no library runs none: that is what those lanes are on every start, so they
// are said once, together, at info — never a warning per lane per start.
func TestALaneThatRunsNothingIsSaidOnceAtInfo(t *testing.T) {
declined := fmt.Errorf("probe %q: %w", "transfer", cevm.ErrDeclined)
for name, reports := range map[string][]cevm.HealthReport{
"cpu lanes": {
{Backend: cevm.CPUSequential, Name: "cpu-sequential", Err: declined},
{Backend: cevm.CPUParallel, Name: "cpu-parallel (Block-STM)", Err: declined},
},
"no library": {
{Backend: cevm.CPUSequential, Name: "cpu-sequential", Err: cevm.ErrNotLinked},
},
} {
t.Run(name, func(t *testing.T) {
var buf bytes.Buffer
reportHealth(log.NewWriter(&buf), reports)
got := lines(t, &buf)
if len(got) != 1 {
t.Fatalf("%d lanes that run nothing produced %d log lines, want 1", len(reports), len(got))
}
if lvl := level(got[0]); lvl != "info" {
t.Errorf("lanes that run nothing were logged at %q, want info", lvl)
}
named, _ := got[0]["backends"].([]any)
if len(named) != len(reports) {
t.Errorf("the line names %v, want every one of %d lanes", got[0]["backends"], len(reports))
}
})
}
}
// A GPU lane that declines the funded transfer is not what a GPU lane is: it
// is warned about on its own, beside the CPU lanes' one info line and the
// healthy lane's.
func TestAGPULaneThatDeclinesIsWarnedAboutAlone(t *testing.T) {
declined := fmt.Errorf("probe %q: %w", "transfer", cevm.ErrDeclined)
var buf bytes.Buffer
reportHealth(log.NewWriter(&buf), []cevm.HealthReport{
{Backend: cevm.CPUSequential, Name: "cpu-sequential", Err: declined},
{Backend: cevm.CPUParallel, Name: "cpu-parallel (Block-STM)", Err: declined},
{Backend: cevm.GPUMetal, Name: "gpu-metal", OK: true, ProbesRun: 1, GasUsed: 21000},
{Backend: cevm.GPUCUDA, Name: "gpu-cuda", Probe: "transfer", Err: declined},
})
var warns []string
for _, l := range lines(t, &buf) {
if level(l) == "warn" {
warns = append(warns, str(l["backend"]))
}
}
if len(warns) != 1 || warns[0] != "gpu-cuda" {
t.Fatalf("warned about %v, want the declining GPU lane alone", warns)
}
}
// What this build says at start: a lane that is not healthy says why, and
// nothing it is by construction — a CPU lane, or a build with no library — is
// a warning. Only a GPU lane that did not run the battery may warn.
func TestThisBuildWarnsOnlyAboutAGPULane(t *testing.T) {
for _, h := range cevm.Health() {
if !h.OK && h.Err == nil {
t.Errorf("backend %q is not healthy and does not say why", h.Name)
}
}
var buf bytes.Buffer
reportCevm(log.NewWriter(&buf))
for _, l := range lines(t, &buf) {
if level(l) != "warn" {
continue
}
if b := str(l["backend"]); !strings.HasPrefix(b, "gpu-") {
t.Errorf("start warned about %q: %v", b, l)
}
}
}
func lines(t *testing.T, buf *bytes.Buffer) []map[string]any {
t.Helper()
var out []map[string]any
for _, l := range strings.Split(strings.TrimSpace(buf.String()), "\n") {
if l == "" {
continue
}
var m map[string]any
if err := json.Unmarshal([]byte(l), &m); err != nil {
t.Fatalf("log line %q is not JSON: %v", l, err)
}
out = append(out, m)
}
return out
}
func level(m map[string]any) string { return strings.ToLower(str(m["level"])) }
func str(v any) string {
s, _ := v.(string)
return s
}