perf(verify): parallelize smoke and ABI checks

Assisted-by: MiMo V2.5 Pro
This commit is contained in:
2026-08-21 01:20:31 +02:00
parent 94b98468bb
commit f5fcaf9fa6
+78 -30
View File
@@ -16,9 +16,11 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"runtime"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
"sync"
"syscall" "syscall"
"sourcedock.dev/petrbalvin/gasm-devkit/arch" "sourcedock.dev/petrbalvin/gasm-devkit/arch"
@@ -1221,6 +1223,7 @@ decoders) that crash on random input but should succeed on valid data.
} }
fmt.Printf("fuzz: %d functions tested, %d iterations each\n", fuzzed, *fuzzN) fmt.Printf("fuzz: %d functions tested, %d iterations each\n", fuzzed, *fuzzN)
} }
// Print function info and profile.
for _, name := range names { for _, name := range names {
fl, _ := k.Func(name) fl, _ := k.Func(name)
flags := "" flags := ""
@@ -1228,7 +1231,6 @@ decoders) that crash on random input but should succeed on valid data.
flags = " NOSPLIT" flags = " NOSPLIT"
} }
fmt.Printf(" %s: %d bytes, args=%d, frame=%d%s\n", name, fl.Size, fl.Args, fl.Frame, flags) fmt.Printf(" %s: %d bytes, args=%d, frame=%d%s\n", name, fl.Size, fl.Args, fl.Frame, flags)
if *profile { if *profile {
blocks, err := k.Blocks(name) blocks, err := k.Blocks(name)
if err != nil { if err != nil {
@@ -1237,40 +1239,86 @@ decoders) that crash on random input but should succeed on valid data.
fmt.Printf(" blocks: %d\n", len(blocks)) fmt.Printf(" blocks: %d\n", len(blocks))
} }
} }
}
if *smoke && fl.NoSplit { // Run smoke and ABI checks in parallel.
args := make([]byte, fl.Args) if *smoke || *abi {
_, err := k.CallFunc(name, args) type checkResult struct {
if err != nil { name string
fmt.Printf(" smoke: FAIL — %v\n", err) msg string
rc = 1 fail bool
} else {
fmt.Printf(" smoke: OK\n")
}
} }
var mu sync.Mutex
var results []checkResult
sem := make(chan struct{}, runtime.NumCPU())
var wg sync.WaitGroup
if *abi && fl.NoSplit { for _, name := range names {
// Try varied-input ABI fuzzing first. fl, _ := k.Func(name)
if src, err := readSource(path); err == nil { if !fl.NoSplit {
result := k.FuzzFuncCheckedByName(name, src, *abiN, int64(*abiN)) continue
if result.Mismatches > 0 { }
fmt.Printf(" abi: %s\n", result) wg.Add(1)
rc = 1 go func(name string, fl asm.FuncLayout) {
} else { defer wg.Done()
fmt.Printf(" abi: clean (%d varied inputs)\n", result.Matches) sem <- struct{}{}
defer func() { <-sem }()
var msgs []string
failed := false
if *smoke {
args := make([]byte, fl.Args)
_, err := k.CallFunc(name, args)
if err != nil {
msgs = append(msgs, fmt.Sprintf(" smoke: FAIL — %v", err))
failed = true
} else {
msgs = append(msgs, " smoke: OK")
}
} }
} else {
// Fallback: single zeroed-arg call. if *abi {
args := make([]byte, fl.Args) if src, err := readSource(path); err == nil {
_, report, err := k.CallFuncChecked(name, args) result := k.FuzzFuncCheckedByName(name, src, *abiN, int64(*abiN))
if err != nil { if result.Mismatches > 0 {
fmt.Printf(" abi: FAIL — %v\n", err) msgs = append(msgs, fmt.Sprintf(" abi: %s", result))
failed = true
} else {
msgs = append(msgs, fmt.Sprintf(" abi: clean (%d varied inputs)", result.Matches))
}
} else {
args := make([]byte, fl.Args)
_, report, err := k.CallFuncChecked(name, args)
if err != nil {
msgs = append(msgs, fmt.Sprintf(" abi: FAIL — %v", err))
failed = true
} else if !report.OK() {
msgs = append(msgs, fmt.Sprintf(" abi: %s", report))
failed = true
} else {
msgs = append(msgs, " abi: clean")
}
}
}
mu.Lock()
results = append(results, checkResult{name: name, msg: strings.Join(msgs, "\n"), fail: failed})
mu.Unlock()
}(name, fl)
}
wg.Wait()
// Print results in source order.
resultMap := make(map[string]checkResult, len(results))
for _, r := range results {
resultMap[r.name] = r
}
for _, name := range names {
if r, ok := resultMap[name]; ok {
fmt.Println(r.msg)
if r.fail {
rc = 1 rc = 1
} else if !report.OK() {
fmt.Printf(" abi: %s\n", report)
rc = 1
} else {
fmt.Printf(" abi: clean\n")
} }
} }
} }