aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/repro
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2020-11-25 09:17:50 +0100
committerDmitry Vyukov <dvyukov@google.com>2020-12-28 14:22:41 +0100
commitcbd0445ec3b0b184db66966d8a47e6b37d13692e (patch)
tree14ed47723c325ef1b388e4e732a70c0fed4fa101 /pkg/repro
parent2242f77fdc5a6c50bd8fa1021d2abc8b83e09e8d (diff)
all: make timeouts configurable
Add sys/targets.Timeouts struct that parametrizes timeouts throughout the system. The struct allows to control syscall/program/no output timeouts for OS/arch/VM/etc. See comment on the struct for more details.
Diffstat (limited to 'pkg/repro')
-rw-r--r--pkg/repro/repro.go33
1 files changed, 18 insertions, 15 deletions
diff --git a/pkg/repro/repro.go b/pkg/repro/repro.go
index ad0d7c4df..aca892cb9 100644
--- a/pkg/repro/repro.go
+++ b/pkg/repro/repro.go
@@ -49,10 +49,11 @@ type context struct {
crashType report.Type
instances chan *instance
bootRequests chan int
- timeouts []time.Duration
+ testTimeouts []time.Duration
startOpts csource.Options
stats *Stats
report *report.Report
+ timeouts targets.Timeouts
}
type instance struct {
@@ -78,10 +79,11 @@ func Run(crashLog []byte, cfg *mgrconfig.Config, features *host.Features, report
crashTitle = rep.Title
crashType = rep.Type
}
- // The shortest duration is 10 seconds to detect simple crashes (i.e. no races and no hangs).
- // The longest duration is 6 minutes to catch races and hangs.
- noOutputTimeout := vm.NoOutputTimeout + time.Minute
- timeouts := []time.Duration{15 * time.Second, time.Minute, noOutputTimeout}
+ testTimeouts := []time.Duration{
+ 3 * cfg.Timeouts.Program, // to catch simpler crashes (i.e. no races and no hangs)
+ 20 * cfg.Timeouts.Program,
+ cfg.Timeouts.NoOutputRunningTime, // to catch "no output", races and hangs
+ }
switch {
case crashTitle == "":
crashTitle = "no output/lost connection"
@@ -89,12 +91,12 @@ func Run(crashLog []byte, cfg *mgrconfig.Config, features *host.Features, report
// but theoretically if it's caused by a race it may need the largest timeout.
// No output can only be reproduced with the max timeout.
// As a compromise we use the smallest and the largest timeouts.
- timeouts = []time.Duration{15 * time.Second, noOutputTimeout}
+ testTimeouts = []time.Duration{testTimeouts[0], testTimeouts[2]}
case crashType == report.MemoryLeak:
// Memory leaks can't be detected quickly because of expensive setup and scanning.
- timeouts = []time.Duration{time.Minute, noOutputTimeout}
+ testTimeouts = testTimeouts[1:]
case crashType == report.Hang:
- timeouts = []time.Duration{noOutputTimeout}
+ testTimeouts = testTimeouts[2:]
}
ctx := &context{
target: cfg.SysTarget,
@@ -103,11 +105,12 @@ func Run(crashLog []byte, cfg *mgrconfig.Config, features *host.Features, report
crashType: crashType,
instances: make(chan *instance, len(vmIndexes)),
bootRequests: make(chan int, len(vmIndexes)),
- timeouts: timeouts,
+ testTimeouts: testTimeouts,
startOpts: createStartOptions(cfg, features, crashType),
stats: new(Stats),
+ timeouts: cfg.Timeouts,
}
- ctx.reproLogf(0, "%v programs, %v VMs, timeouts %v", len(entries), len(vmIndexes), timeouts)
+ ctx.reproLogf(0, "%v programs, %v VMs, timeouts %v", len(entries), len(vmIndexes), testTimeouts)
var wg sync.WaitGroup
wg.Add(len(vmIndexes))
for _, vmIndex := range vmIndexes {
@@ -308,7 +311,7 @@ func (ctx *context) extractProg(entries []*prog.LogEntry) (*Result, error) {
for i := len(indices) - 1; i >= 0; i-- {
lastEntries = append(lastEntries, entries[indices[i]])
}
- for _, timeout := range ctx.timeouts {
+ for _, timeout := range ctx.testTimeouts {
// Execute each program separately to detect simple crashes caused by a single program.
// Programs are executed in reverse order, usually the last program is the guilty one.
res, err := ctx.extractProgSingle(lastEntries, timeout)
@@ -484,7 +487,7 @@ func (ctx *context) simplifyProg(res *Result) (*Result, error) {
for _, simplify := range progSimplifies {
opts := res.Opts
- if !simplify(&opts) || !checkOpts(&opts, res.Duration) {
+ if !simplify(&opts) || !checkOpts(&opts, ctx.timeouts, res.Duration) {
continue
}
crashed, err := ctx.testProg(res.Prog, res.Duration, opts)
@@ -534,7 +537,7 @@ func (ctx *context) simplifyC(res *Result) (*Result, error) {
for _, simplify := range cSimplifies {
opts := res.Opts
- if !simplify(&opts) || !checkOpts(&opts, res.Duration) {
+ if !simplify(&opts) || !checkOpts(&opts, ctx.timeouts, res.Duration) {
continue
}
crashed, err := ctx.testCProg(res.Prog, res.Duration, opts)
@@ -549,7 +552,7 @@ func (ctx *context) simplifyC(res *Result) (*Result, error) {
return res, nil
}
-func checkOpts(opts *csource.Options, timeout time.Duration) bool {
+func checkOpts(opts *csource.Options, timeouts targets.Timeouts, timeout time.Duration) bool {
if !opts.Repeat && timeout >= time.Minute {
// If we have a non-repeating C reproducer with timeout > vm.NoOutputTimeout and it hangs
// (the reproducer itself does not terminate on its own, note: it does not have builtin timeout),
@@ -621,7 +624,7 @@ func (ctx *context) testProgs(entries []*prog.LogEntry, duration time.Duration,
command := instancePkg.ExecprogCmd(inst.execprogBin, inst.executorBin,
ctx.target.OS, ctx.target.Arch, opts.Sandbox, opts.Repeat,
- opts.Threaded, opts.Collide, opts.Procs, -1, -1, vmProgFile)
+ opts.Threaded, opts.Collide, opts.Procs, -1, -1, true, ctx.timeouts.Slowdown, vmProgFile)
ctx.reproLogf(2, "testing program (duration=%v, %+v): %s", duration, opts, program)
ctx.reproLogf(3, "detailed listing:\n%s", pstr)
return ctx.testImpl(inst.Instance, command, duration)