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")
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user