feat(debug): label coverage report for headless runs
This commit is contained in:
@@ -37,6 +37,7 @@ REPL commands:
|
||||
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")
|
||||
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")
|
||||
fs.Parse(args)
|
||||
|
||||
@@ -179,6 +180,62 @@ REPL commands:
|
||||
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
|
||||
// exit. The watchdog armed above covers launch, continue and step.
|
||||
var in io.Reader = os.Stdin
|
||||
|
||||
Reference in New Issue
Block a user