aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2015-12-16 17:10:52 +0100
committerDmitry Vyukov <dvyukov@google.com>2015-12-17 14:38:46 +0100
commitd665e11e9df6cd99160817aad775bacdbbd2e26f (patch)
tree0d9092bced32d534e4023de269d7a281d0990abc
parentce0bb4c05d45db0bba56c5ab11d2b1d7c17e002a (diff)
move Gate type to ipc package and use it in stress tool
This allows to print what programs stress executes.
-rw-r--r--fuzzer/fuzzer.go57
-rw-r--r--ipc/gate.go54
-rw-r--r--tools/stress/stress.go30
3 files changed, 86 insertions, 55 deletions
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)