feat(debug): label coverage report for headless runs

This commit is contained in:
2026-08-29 14:17:26 +02:00
parent cb398d9498
commit 7a30e902fc
+57
View File
@@ -37,6 +37,7 @@ REPL commands:
argsFile := fs.String("args", "", "file containing the ABI0 argument block") argsFile := fs.String("args", "", "file containing the ABI0 argument block")
bufSpec := fs.String("buf", "", "buffer specification: name:size:pattern[,name:size:pattern...] where pattern is zero, ones, seq, or hex") bufSpec := fs.String("buf", "", "buffer specification: name:size:pattern[,name:size:pattern...] where pattern is zero, ones, seq, or hex")
script := fs.String("script", "", "run REPL commands from a file (one per line) and exit; '-' reads stdin") script := fs.String("script", "", "run REPL commands from a file (one per line) and exit; '-' reads stdin")
cover := fs.Bool("cover", false, "run to completion with breakpoints on every label and report which blocks executed")
timeout := fs.Duration("timeout", 0, "kill the debuggee after this duration (e.g. 30s); for headless --script runs") timeout := fs.Duration("timeout", 0, "kill the debuggee after this duration (e.g. 30s); for headless --script runs")
fs.Parse(args) fs.Parse(args)
@@ -179,6 +180,62 @@ REPL commands:
srcLines = append(srcLines, debug.SourceLine{Offset: le.Offset, Line: le.Line}) srcLines = append(srcLines, debug.SourceLine{Offset: le.Offset, Line: le.Line})
} }
// Coverage mode: pre-register a breakpoint on every label (the basic
// blocks of a hand-written kernel) and let the kernel run to completion.
// Each hit removes its breakpoint, so the final report lists exactly the
// labels execution reached — the fast answer to "did my test ever enter
// the tail loop / the negative branch?".
if *cover {
covered := map[string]bool{}
for _, l := range labels {
addr := sess.CodeBase() + uint64(fl.Offset) + uint64(l.Offset)
name := l.Name
if _, err := bm.SetWithCond(addr, name, nil); err != nil {
fmt.Fprintf(os.Stderr, "gasm debug: cover: %v\n", err)
return 1
}
}
fmt.Printf("gasm debug: coverage run over %d labels\n", len(labels))
for {
for _, bp := range bm.All() {
bm.Reinsert(bp.Addr)
}
// Remove the breakpoint the run is parked on, then continue.
regs, rerr := sess.GetRegs()
if rerr == nil {
if bp := bm.At(regs.GetPC()); bp != nil {
bm.Clear(bp.Addr)
covered[bp.Label] = true
}
}
if err := sess.Continue(); err != nil {
break // debuggee finished or died
}
if sess.Exited() {
break
}
}
var hit []string
var missed []string
for _, l := range labels {
if covered[l.Name] {
hit = append(hit, l.Name)
} else {
missed = append(missed, l.Name)
}
}
sort.Strings(hit)
sort.Strings(missed)
fmt.Printf("coverage: %d/%d labels reached\n", len(hit), len(labels))
for _, l := range hit {
fmt.Printf(" covered %s\n", l)
}
for _, l := range missed {
fmt.Printf(" MISSED %s\n", l)
}
return 0
}
// Headless mode: run the script through the normal command loop and // Headless mode: run the script through the normal command loop and
// exit. The watchdog armed above covers launch, continue and step. // exit. The watchdog armed above covers launch, continue and step.
var in io.Reader = os.Stdin var in io.Reader = os.Stdin