diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2026-03-06 11:36:44 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2026-03-09 12:29:48 +0000 |
| commit | 176bead5023749b301d573375d79dabee4d0d888 (patch) | |
| tree | d63c6e04860a64a12599c80d50f3b8a5ff27aa4b /pkg/instance/execprog.go | |
| parent | 5cb44a80412f4dcfba9693d7d0e4c2ae352acdaa (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/execprog.go')
| -rw-r--r-- | pkg/instance/execprog.go | 63 |
1 files changed, 57 insertions, 6 deletions
diff --git a/pkg/instance/execprog.go b/pkg/instance/execprog.go index 94a6ddbd5..a1dd9e8a5 100644 --- a/pkg/instance/execprog.go +++ b/pkg/instance/execprog.go @@ -4,9 +4,14 @@ package instance import ( + "bufio" + "bytes" "context" "fmt" "os" + "path/filepath" + "slices" + "strconv" "time" "github.com/google/syzkaller/pkg/csource" @@ -40,6 +45,7 @@ type RunResult struct { Output []byte Report *report.Report Duration time.Duration + Coverage [][]uint64 } const ( @@ -165,8 +171,9 @@ type ExecParams struct { CProg *prog.Prog SyzProg []byte - Opts csource.Options - Duration time.Duration + Opts csource.Options + Duration time.Duration + CollectCoverage bool // If ExitConditions is empty, RunSyzProg() will assume instance.SyzExitConditions. // RunCProg() always runs with binExitConditions. ExitConditions vm.ExitCondition @@ -193,14 +200,41 @@ func (inst *ExecProgInstance) RunCProgRaw(src []byte, target *prog.Target, } func (inst *ExecProgInstance) RunSyzProgFile(progFile string, duration time.Duration, - opts csource.Options, exitCondition vm.ExitCondition) (*RunResult, error) { + opts csource.Options, collectCoverage bool, exitCondition vm.ExitCondition) (*RunResult, error) { + coverFile := "" + if collectCoverage { + coverDir, err := os.MkdirTemp("", "syz-cover") + if err != nil { + return nil, err + } + defer osutil.RemoveAll(coverDir) + coverFile = filepath.Join(coverDir, "cover") + } vmProgFile, err := inst.VMInstance.Copy(progFile) if err != nil { return nil, &TestError{Title: fmt.Sprintf("failed to copy prog to VM: %v", err)} } command := ExecprogCmd(inst.execprogBin, inst.executorBin, inst.mgrCfg.TargetOS, inst.mgrCfg.TargetArch, - inst.mgrCfg.Type, opts, !inst.OldFlagsCompatMode, inst.mgrCfg.Timeouts.Slowdown, vmProgFile) - return inst.runCommand(command, duration, exitCondition) + inst.mgrCfg.Type, opts, !inst.OldFlagsCompatMode, inst.mgrCfg.Timeouts.Slowdown, coverFile, vmProgFile) + res, err := inst.runCommand(command, duration, exitCondition) + if err != nil { + return nil, err + } + if coverFile != "" { + files, err := filepath.Glob(coverFile + "*") + if err != nil { + return nil, fmt.Errorf("failed to glob cover files: %w", err) + } + slices.Sort(files) + for _, f := range files { + cover, err := parseCoverageFile(f) + if err != nil { + return nil, fmt.Errorf("failed to parse cover file: %w", err) + } + res.Coverage = append(res.Coverage, cover) + } + } + return res, nil } func (inst *ExecProgInstance) RunSyzProg(params ExecParams) (*RunResult, error) { @@ -213,5 +247,22 @@ func (inst *ExecProgInstance) RunSyzProg(params ExecParams) (*RunResult, error) if params.ExitConditions == 0 { params.ExitConditions = SyzExitConditions } - return inst.RunSyzProgFile(progFile, params.Duration, params.Opts, params.ExitConditions) + return inst.RunSyzProgFile(progFile, params.Duration, params.Opts, + params.CollectCoverage, params.ExitConditions) +} + +func parseCoverageFile(filename string) ([]uint64, error) { + data, err := os.ReadFile(filename) + if err != nil { + return nil, err + } + var res []uint64 + for s := bufio.NewScanner(bytes.NewReader(data)); s.Scan(); { + v, err := strconv.ParseUint(s.Text(), 16, 64) + if err != nil { + return nil, err + } + res = append(res, v) + } + return res, nil } |
