From f5fcaf9fa6d0791fcea6d07cb129865ba8c2ed88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Balv=C3=ADn?= Date: Fri, 21 Aug 2026 01:10:14 +0200 Subject: [PATCH] perf(verify): parallelize smoke and ABI checks Assisted-by: MiMo V2.5 Pro --- cmd/gasm/main.go | 108 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 78 insertions(+), 30 deletions(-) diff --git a/cmd/gasm/main.go b/cmd/gasm/main.go index 927d40e..073e4be 100644 --- a/cmd/gasm/main.go +++ b/cmd/gasm/main.go @@ -16,9 +16,11 @@ import ( "os" "os/exec" "path/filepath" + "runtime" "sort" "strconv" "strings" + "sync" "syscall" "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) } + // Print function info and profile. for _, name := range names { fl, _ := k.Func(name) flags := "" @@ -1228,7 +1231,6 @@ decoders) that crash on random input but should succeed on valid data. flags = " NOSPLIT" } fmt.Printf(" %s: %d bytes, args=%d, frame=%d%s\n", name, fl.Size, fl.Args, fl.Frame, flags) - if *profile { blocks, err := k.Blocks(name) 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)) } } + } - if *smoke && fl.NoSplit { - args := make([]byte, fl.Args) - _, err := k.CallFunc(name, args) - if err != nil { - fmt.Printf(" smoke: FAIL — %v\n", err) - rc = 1 - } else { - fmt.Printf(" smoke: OK\n") - } + // Run smoke and ABI checks in parallel. + if *smoke || *abi { + type checkResult struct { + name string + msg string + fail bool } + var mu sync.Mutex + var results []checkResult + sem := make(chan struct{}, runtime.NumCPU()) + var wg sync.WaitGroup - if *abi && fl.NoSplit { - // Try varied-input ABI fuzzing first. - if src, err := readSource(path); err == nil { - result := k.FuzzFuncCheckedByName(name, src, *abiN, int64(*abiN)) - if result.Mismatches > 0 { - fmt.Printf(" abi: %s\n", result) - rc = 1 - } else { - fmt.Printf(" abi: clean (%d varied inputs)\n", result.Matches) + for _, name := range names { + fl, _ := k.Func(name) + if !fl.NoSplit { + continue + } + wg.Add(1) + go func(name string, fl asm.FuncLayout) { + defer wg.Done() + 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. - args := make([]byte, fl.Args) - _, report, err := k.CallFuncChecked(name, args) - if err != nil { - fmt.Printf(" abi: FAIL — %v\n", err) + + if *abi { + if src, err := readSource(path); err == nil { + result := k.FuzzFuncCheckedByName(name, src, *abiN, int64(*abiN)) + if result.Mismatches > 0 { + 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 - } else if !report.OK() { - fmt.Printf(" abi: %s\n", report) - rc = 1 - } else { - fmt.Printf(" abi: clean\n") } } }