aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/instance/instance.go
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2026-03-06 11:36:44 +0100
committerDmitry Vyukov <dvyukov@google.com>2026-03-09 12:29:48 +0000
commit176bead5023749b301d573375d79dabee4d0d888 (patch)
treed63c6e04860a64a12599c80d50f3b8a5ff27aa4b /pkg/instance/instance.go
parent5cb44a80412f4dcfba9693d7d0e4c2ae352acdaa (diff)
pkg/aflow/action/crash: collect test coverage
Collect code coverage for test programs. This is likley to be needed for #6878 and seed generation workflow. For now it's not wired into any workflow/tool and is not tested. But this should provide most of the plumbing to wire it up.
Diffstat (limited to 'pkg/instance/instance.go')
-rw-r--r--pkg/instance/instance.go86
1 files changed, 47 insertions, 39 deletions
diff --git a/pkg/instance/instance.go b/pkg/instance/instance.go
index d8b3a8f71..e63dd7808 100644
--- a/pkg/instance/instance.go
+++ b/pkg/instance/instance.go
@@ -34,7 +34,7 @@ type Env interface {
BuildSyzkaller(string, string) (string, error)
CleanKernel(*BuildKernelConfig) error
BuildKernel(*BuildKernelConfig) (string, build.ImageDetails, error)
- Test(numVMs int, reproSyz, reproOpts, reproC []byte) ([]EnvTestResult, error)
+ Test(numVMs int, reproSyz, reproOpts, reproC []byte, collectCoverage bool) ([]EnvTestResult, error)
}
type env struct {
@@ -258,7 +258,7 @@ func (err *CrashError) Error() string {
// Test boots numVMs VMs, tests basic kernel operation, and optionally tests the provided reproducer.
// *TestError is returned if there is a problem with kernel/image (crash, reboot loop, etc).
// *CrashError is returned if the reproducer crashes kernel.
-func (env *env) Test(numVMs int, reproSyz, reproOpts, reproC []byte) ([]EnvTestResult, error) {
+func (env *env) Test(numVMs int, reproSyz, reproOpts, reproC []byte, collectCoverage bool) ([]EnvTestResult, error) {
if env.testSem != nil {
env.testSem.Wait()
defer env.testSem.Signal()
@@ -279,14 +279,15 @@ func (env *env) Test(numVMs int, reproSyz, reproOpts, reproC []byte) ([]EnvTestR
res := make(chan EnvTestResult, numVMs)
for i := 0; i < numVMs; i++ {
inst := &inst{
- cfg: env.cfg,
- optionalFlags: env.optionalFlags,
- reporter: reporter,
- vmPool: vmPool,
- vmIndex: i,
- reproSyz: reproSyz,
- reproOpts: reproOpts,
- reproC: reproC,
+ cfg: env.cfg,
+ optionalFlags: env.optionalFlags,
+ reporter: reporter,
+ vmPool: vmPool,
+ vmIndex: i,
+ reproSyz: reproSyz,
+ reproOpts: reproOpts,
+ reproC: reproC,
+ collectCoverage: collectCoverage,
}
go func() { res <- inst.test() }()
}
@@ -298,20 +299,22 @@ func (env *env) Test(numVMs int, reproSyz, reproOpts, reproC []byte) ([]EnvTestR
}
type inst struct {
- cfg *mgrconfig.Config
- optionalFlags bool
- reporter *report.Reporter
- vmPool *vm.Pool
- vm *vm.Instance
- vmIndex int
- reproSyz []byte
- reproOpts []byte
- reproC []byte
+ cfg *mgrconfig.Config
+ optionalFlags bool
+ reporter *report.Reporter
+ vmPool *vm.Pool
+ vm *vm.Instance
+ vmIndex int
+ reproSyz []byte
+ reproOpts []byte
+ reproC []byte
+ collectCoverage bool
}
type EnvTestResult struct {
Error error
RawOutput []byte
+ Coverage [][]uint64
}
func (inst *inst) test() EnvTestResult {
@@ -362,7 +365,7 @@ func (inst *inst) test() EnvTestResult {
return ret
}
if len(inst.reproSyz) != 0 || len(inst.reproC) != 0 {
- ret.RawOutput, ret.Error = inst.testRepro()
+ ret.RawOutput, ret.Coverage, ret.Error = inst.testRepro()
}
return ret
}
@@ -403,44 +406,45 @@ func (inst *inst) testInstance() error {
return nil
}
-func (inst *inst) testRepro() ([]byte, error) {
+func (inst *inst) testRepro() ([]byte, [][]uint64, error) {
execProg, err := SetupExecProg(inst.vm, inst.cfg, inst.reporter, &OptionalConfig{
OldFlagsCompatMode: !inst.optionalFlags,
})
if err != nil {
- return nil, err
+ return nil, nil, err
}
- transformError := func(res *RunResult, err error) ([]byte, error) {
+ transformError := func(res *RunResult, err error) ([]byte, [][]uint64, error) {
if err != nil {
- return nil, err
+ return nil, nil, err
}
- if res != nil && res.Report != nil {
- return res.Output, &CrashError{Report: res.Report}
+ if res.Report != nil {
+ err = &CrashError{Report: res.Report}
}
- return res.Output, nil
+ return res.Output, res.Coverage, err
}
- out := []byte{}
+ out, coverage := []byte{}, [][]uint64{}
if len(inst.reproSyz) > 0 {
opts, err := inst.csourceOptions()
if err != nil {
- return nil, err
+ return nil, nil, err
}
- out, err = transformError(execProg.RunSyzProg(ExecParams{
- SyzProg: inst.reproSyz,
- Duration: inst.cfg.Timeouts.NoOutputRunningTime,
- Opts: opts,
+ out, coverage, err = transformError(execProg.RunSyzProg(ExecParams{
+ SyzProg: inst.reproSyz,
+ Duration: inst.cfg.Timeouts.NoOutputRunningTime,
+ Opts: opts,
+ CollectCoverage: inst.collectCoverage,
}))
if err != nil {
- return out, err
+ return out, coverage, err
}
}
if len(inst.reproC) > 0 {
// We should test for more than full "no output" timeout, but the problem is that C reproducers
// don't print anything, so we will get a false "no output" crash.
- out, err = transformError(execProg.RunCProgRaw(inst.reproC, inst.cfg.Target,
+ out, _, err = transformError(execProg.RunCProgRaw(inst.reproC, inst.cfg.Target,
inst.cfg.Timeouts.NoOutput/2))
}
- return out, err
+ return out, coverage, err
}
func (inst *inst) csourceOptions() (csource.Options, error) {
@@ -462,7 +466,7 @@ func (inst *inst) csourceOptions() (csource.Options, error) {
// nolint:revive
func ExecprogCmd(execprog, executor, OS, arch, vmType string, opts csource.Options,
- optionalFlags bool, slowdown int, progFile string) string {
+ optionalFlags bool, slowdown int, coverFile, progFile string) string {
repeatCount := 1
if opts.Repeat {
repeatCount = 0
@@ -488,11 +492,15 @@ func ExecprogCmd(execprog, executor, OS, arch, vmType string, opts csource.Optio
{Name: "type", Value: fmt.Sprint(vmType)},
})
}
+ coverArg := ""
+ if coverFile != "" {
+ coverArg = " -cover=%v -coverfile=" + coverFile
+ }
return fmt.Sprintf("%v -executor=%v -arch=%v%v -sandbox=%v"+
- " -procs=%v -repeat=%v -threaded=%v -collide=%v -cover=0%v %v",
+ " -procs=%v -repeat=%v -threaded=%v -collide=%v%v%v %v",
execprog, executor, arch, osArg, sandbox,
opts.Procs, repeatCount, opts.Threaded, opts.Collide,
- optionalArg, progFile)
+ coverArg, optionalArg, progFile)
}
var MakeBin = func() string {