aboutsummaryrefslogtreecommitdiffstats
path: root/tools/execprog/execprog.go
diff options
context:
space:
mode:
Diffstat (limited to 'tools/execprog/execprog.go')
-rw-r--r--tools/execprog/execprog.go136
1 files changed, 99 insertions, 37 deletions
diff --git a/tools/execprog/execprog.go b/tools/execprog/execprog.go
index eb915af87..02cf5df00 100644
--- a/tools/execprog/execprog.go
+++ b/tools/execprog/execprog.go
@@ -1,17 +1,21 @@
// 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.
-// execprog executes a single program passed via a flag
-// and prints information about execution.
+// execprog executes a single program or a set of programs
+// and optinally prints information about execution.
package main
import (
+ "bufio"
"bytes"
"encoding/binary"
"flag"
"fmt"
"io/ioutil"
+ "log"
"os"
+ "sync"
+ "sync/atomic"
"time"
"github.com/google/syzkaller/cover"
@@ -21,29 +25,35 @@ import (
var (
flagExecutor = flag.String("executor", "", "path to executor binary")
- flagProg = flag.String("prog", "", "file with a program to execute")
- flagThreaded = flag.Bool("threaded", false, "use threaded mode in executor")
- flagCollide = flag.Bool("collide", false, "collide syscalls to provoke data races")
- flagDebug = flag.Bool("debug", true, "debug output from executor")
+ flagThreaded = flag.Bool("threaded", true, "use threaded mode in executor")
+ flagCollide = flag.Bool("collide", true, "collide syscalls to provoke data races")
+ flagDebug = flag.Bool("debug", false, "debug output from executor")
flagStrace = flag.Bool("strace", false, "run executor under strace")
flagCover = flag.String("cover", "", "collect coverage and write to the file")
flagNobody = flag.Bool("nobody", true, "impersonate into nobody")
flagDedup = flag.Bool("dedup", false, "deduplicate coverage in executor")
+ flagLoop = flag.Bool("loop", false, "execute programs in a loop")
+ flagProcs = flag.Int("procs", 1, "number of parallel processes to execute programs")
flagTimeout = flag.Duration("timeout", 5*time.Second, "execution timeout")
)
func main() {
flag.Parse()
- data, err := ioutil.ReadFile(*flagProg)
- if err != nil {
- fmt.Fprintf(os.Stderr, "failed to read prog file: %v\n", err)
+ if len(flag.Args()) == 0 {
+ fmt.Fprintf(os.Stderr, "usage: execprog [flags] file-with-programs*\n")
+ flag.PrintDefaults()
os.Exit(1)
}
- p, err := prog.Deserialize(data)
- if err != nil {
- fmt.Fprintf(os.Stderr, "failed to deserialize the program: %v\n", err)
- os.Exit(1)
+
+ var progs []*prog.Prog
+ for _, fn := range flag.Args() {
+ progs = append(progs, parseFile(fn)...)
}
+ log.Printf("parsed %v programs", len(progs))
+ if len(progs) == 0 {
+ return
+ }
+
var flags uint64
if *flagThreaded {
flags |= ipc.FlagThreaded
@@ -66,34 +76,86 @@ func main() {
if *flagNobody {
flags |= ipc.FlagDropPrivs
}
- env, err := ipc.MakeEnv(*flagExecutor, *flagTimeout, flags)
- if err != nil {
- fmt.Fprintf(os.Stderr, "failed to create execution environment: %v\n", err)
- os.Exit(1)
+
+ var wg sync.WaitGroup
+ wg.Add(*flagProcs)
+ var pos uint32
+ for p := 0; p < *flagProcs; p++ {
+ go func() {
+ env, err := ipc.MakeEnv(*flagExecutor, *flagTimeout, flags)
+ if err != nil {
+ log.Fatalf("failed to create ipc env: %v", err)
+ }
+ for {
+ idx := int(atomic.AddUint32(&pos, 1) - 1)
+ if idx%len(progs) == 0{
+ log.Printf("executed %v programs\n", idx)
+ }
+ if !*flagLoop && idx >= len(progs) {
+ env.Close()
+ wg.Done()
+ return
+ }
+ p := progs[idx%len(progs)]
+ output, strace, cov, failed, hanged, err := env.Exec(p)
+ if *flagDebug {
+ fmt.Printf("result: failed=%v hanged=%v err=%v\n\n%s", failed, hanged, err, output)
+ }
+ if *flagStrace {
+ fmt.Printf("strace output:\n%s", strace)
+ }
+ // Coverage is dumped in sanitizer format.
+ // github.com/google/sanitizers/tools/sancov command can be used to dump PCs,
+ // then they can be piped via addr2line to symbolize.
+ for i, c := range cov {
+ fmt.Printf("call #%v: coverage %v\n", i, len(c))
+ if len(c) == 0 {
+ continue
+ }
+ buf := new(bytes.Buffer)
+ binary.Write(buf, binary.LittleEndian, uint64(0xC0BFFFFFFFFFFF64))
+ for _, pc := range c {
+ binary.Write(buf, binary.LittleEndian, cover.RestorePC(pc))
+ }
+ err := ioutil.WriteFile(fmt.Sprintf("%v.%v", *flagCover, i), buf.Bytes(), 0660)
+ if err != nil {
+ log.Fatalf("failed to write coverage file: %v", err)
+ }
+ }
+ }
+ }()
}
- defer env.Close()
- output, strace, cov, failed, hanged, err := env.Exec(p)
- fmt.Printf("result: failed=%v hanged=%v err=%v\n\n%s", failed, hanged, err, output)
- if *flagStrace {
- fmt.Printf("strace output:\n%s", strace)
+ wg.Wait()
+}
+
+func parseFile(fn string) []*prog.Prog {
+ logf, err := os.Open(fn)
+ if err != nil {
+ log.Fatalf("failed to open log file: %v", err)
}
- // Coverage is dumped in sanitizer format.
- // github.com/google/sanitizers/tools/sancov command can be used to dump PCs,
- // then they can be piped via addr2line to symbolize.
- for i, c := range cov {
- fmt.Printf("call #%v: coverage %v\n", i, len(c))
- if len(c) == 0 {
+ log.Printf("parsing log %v", fn)
+ s := bufio.NewScanner(logf)
+ var cur []byte
+ var last *prog.Prog
+ var progs []*prog.Prog
+ for s.Scan() {
+ ln := s.Text()
+ tmp := append(cur, ln...)
+ tmp = append(tmp, '\n')
+ p, err := prog.Deserialize(tmp)
+ if err == nil {
+ cur = tmp
+ last = p
continue
}
- buf := new(bytes.Buffer)
- binary.Write(buf, binary.LittleEndian, uint64(0xC0BFFFFFFFFFFF64))
- for _, pc := range c {
- binary.Write(buf, binary.LittleEndian, cover.RestorePC(pc))
- }
- err := ioutil.WriteFile(fmt.Sprintf("%v.%v", *flagCover, i), buf.Bytes(), 0660)
- if err != nil {
- fmt.Fprintf(os.Stderr, "failed to write coverage file: %v\n", err)
- os.Exit(1)
+ if last != nil {
+ progs = append(progs, last)
+ last = nil
+ cur = cur[:0]
}
}
+ if last != nil {
+ progs = append(progs, last)
+ }
+ return progs
}