From d665e11e9df6cd99160817aad775bacdbbd2e26f Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Wed, 16 Dec 2015 17:10:52 +0100 Subject: move Gate type to ipc package and use it in stress tool This allows to print what programs stress executes. --- fuzzer/fuzzer.go | 57 +++++++++----------------------------------------- ipc/gate.go | 54 +++++++++++++++++++++++++++++++++++++++++++++++ tools/stress/stress.go | 30 +++++++++++++++++++------- 3 files changed, 86 insertions(+), 55 deletions(-) create mode 100644 ipc/gate.go diff --git a/fuzzer/fuzzer.go b/fuzzer/fuzzer.go index f85a59a64..34d99499f 100644 --- a/fuzzer/fuzzer.go +++ b/fuzzer/fuzzer.go @@ -76,7 +76,7 @@ var ( triage []Input candidates []*prog.Prog - gate *Gate + gate *ipc.Gate statExecGen uint64 statExecFuzz uint64 @@ -136,7 +136,7 @@ func main() { *flagProcs = 1 } - gate = newGate(2 * *flagProcs) + gate = ipc.NewGate(2 * *flagProcs) envs := make([]*ipc.Env, *flagProcs) for pid := 0; pid < *flagProcs; pid++ { env, err := ipc.MakeEnv(*flagExecutor, 10*time.Second, flags) @@ -418,8 +418,15 @@ func execute1(pid int, env *ipc.Env, p *prog.Prog, stat *uint64) []cover.Cover { triageMu.Unlock() } + // Limit concurrency window and do leak checking once in a while. idx := gate.Enter() - defer gate.Leave(idx) + defer gate.Leave(idx, func() { + if idx == 0 && *flagLeak && atomic.LoadUint32(&allTriaged) != 0 { + // Scan for leaks once in a while (it is damn slow). + kmemleakScan(true) + } + }) + if *flagSaveProg { f, err := os.Create(fmt.Sprintf("%v-%v.prog", *flagName, pid)) if err == nil { @@ -471,50 +478,6 @@ func logf(v int, msg string, args ...interface{}) { } } -type Gate struct { - cv *sync.Cond - busy []bool - pos int -} - -func newGate(c int) *Gate { - return &Gate{ - cv: sync.NewCond(new(sync.Mutex)), - busy: make([]bool, c), - } -} - -func (g *Gate) Enter() int { - g.cv.L.Lock() - for g.busy[g.pos] { - g.cv.Wait() - } - idx := g.pos - g.pos++ - if g.pos >= len(g.busy) { - g.pos = 0 - } - g.busy[idx] = true - g.cv.L.Unlock() - return idx -} - -func (g *Gate) Leave(idx int) { - g.cv.L.Lock() - if !g.busy[idx] { - panic("broken gate") - } - if idx == 0 && *flagLeak && atomic.LoadUint32(&allTriaged) != 0 { - // Scan for leaks once in a while (it is damn slow). - kmemleakScan(true) - } - g.busy[idx] = false - if idx == g.pos { - g.cv.Broadcast() - } - g.cv.L.Unlock() -} - func kmemleakInit() { fd, err := syscall.Open("/sys/kernel/debug/kmemleak", syscall.O_RDWR, 0) if err != nil { diff --git a/ipc/gate.go b/ipc/gate.go new file mode 100644 index 000000000..941dc9f2a --- /dev/null +++ b/ipc/gate.go @@ -0,0 +1,54 @@ +// Copyright 2015 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +package ipc + +import ( + "sync" +) + +// Gate limits concurrency level and window to the given value. +// Limitation of concurrency window means that if a very old activity is still +// running it will not let new activities to start even if concurrency level is low. +type Gate struct { + cv *sync.Cond + busy []bool + pos int +} + +func NewGate(c int) *Gate { + return &Gate{ + cv: sync.NewCond(new(sync.Mutex)), + busy: make([]bool, c), + } +} + +func (g *Gate) Enter() int { + g.cv.L.Lock() + for g.busy[g.pos] { + g.cv.Wait() + } + idx := g.pos + g.pos++ + if g.pos >= len(g.busy) { + g.pos = 0 + } + g.busy[idx] = true + g.cv.L.Unlock() + return idx +} + +func (g *Gate) Leave(idx int, f func()) { + g.cv.L.Lock() + if !g.busy[idx] { + panic("broken gate") + } + if f != nil { + f() + } + g.busy[idx] = false + if idx == g.pos { + g.cv.Broadcast() + } + g.cv.L.Unlock() +} diff --git a/tools/stress/stress.go b/tools/stress/stress.go index 69cf4e052..e4f1a506c 100644 --- a/tools/stress/stress.go +++ b/tools/stress/stress.go @@ -13,6 +13,7 @@ import ( "os" "regexp" "runtime" + "sync" "sync/atomic" "time" @@ -30,10 +31,12 @@ var ( flagCollide = flag.Bool("collide", true, "collide syscalls to provoke data races") flagNobody = flag.Bool("nobody", true, "impersonate into nobody") flagTimeout = flag.Duration("timeout", 10*time.Second, "executor timeout") + flagLogProg = flag.Bool("logprog", false, "print programs before execution") failedRe = regexp.MustCompile("runtime error: |panic: |Panic: ") statExec uint64 + gate *ipc.Gate ) const programLength = 30 @@ -56,28 +59,29 @@ func main() { flags |= ipc.FlagDebug } - for p := 0; p < *flagProcs; p++ { - p := p + gate = ipc.NewGate(2 * *flagProcs) + for pid := 0; pid < *flagProcs; pid++ { + pid := pid go func() { env, err := ipc.MakeEnv(*flagExecutor, *flagTimeout, flags) if err != nil { failf("failed to create execution environment: %v", err) } - rs := rand.NewSource(time.Now().UnixNano() + int64(p)*1e12) + rs := rand.NewSource(time.Now().UnixNano() + int64(pid)*1e12) rnd := rand.New(rs) for i := 0; ; i++ { var p *prog.Prog if len(corpus) == 0 || i%2 != 0 { p = prog.Generate(rs, programLength, nil) - execute(env, p) + execute(pid, env, p) p.Mutate(rs, programLength, nil) - execute(env, p) + execute(pid, env, p) } else { p = corpus[rnd.Intn(len(corpus))].Clone() p.Mutate(rs, programLength, nil) - execute(env, p) + execute(pid, env, p) p.Mutate(rs, programLength, nil) - execute(env, p) + execute(pid, env, p) } } }() @@ -87,11 +91,21 @@ func main() { } } -func execute(env *ipc.Env, p *prog.Prog) { +var outMu sync.Mutex + +func execute(pid int, env *ipc.Env, p *prog.Prog) { if *flagExecutor == "" { return } atomic.AddUint64(&statExec, 1) + if *flagLogProg { + ticket := gate.Enter() + defer gate.Leave(ticket, nil) + outMu.Lock() + fmt.Printf("executing program %v\n%s\n", pid, p.Serialize()) + outMu.Unlock() + } + output, _, _, _, _, err := env.Exec(p) if err != nil { fmt.Printf("failed to execute executor: %v\n", err) -- cgit mrf-deployment