134 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			134 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			Go
		
	
	
	
// Copyright 2015 The Go Authors. All rights reserved.
 | 
						|
// Use of this source code is governed by a BSD-style
 | 
						|
// license that can be found in the LICENSE file.
 | 
						|
 | 
						|
// +build !plan9
 | 
						|
 | 
						|
// The stress utility is intended for catching sporadic failures.
 | 
						|
// It runs a given process in parallel in a loop and collects any failures.
 | 
						|
// Usage:
 | 
						|
// 	$ stress ./fmt.test -test.run=TestSometing -test.cpu=10
 | 
						|
// You can also specify a number of parallel processes with -p flag;
 | 
						|
// instruct the utility to not kill hanged processes for gdb attach;
 | 
						|
// or specify the failure output you are looking for (if you want to
 | 
						|
// ignore some other sporadic failures).
 | 
						|
package main
 | 
						|
 | 
						|
import (
 | 
						|
	"flag"
 | 
						|
	"fmt"
 | 
						|
	"io/ioutil"
 | 
						|
	"os"
 | 
						|
	"os/exec"
 | 
						|
	"regexp"
 | 
						|
	"runtime"
 | 
						|
	"syscall"
 | 
						|
	"time"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	flagP       = flag.Int("p", runtime.NumCPU(), "run `N` processes in parallel")
 | 
						|
	flagTimeout = flag.Duration("timeout", 10*time.Minute, "timeout each process after `duration`")
 | 
						|
	flagKill    = flag.Bool("kill", true, "kill timed out processes if true, otherwise just print pid (to attach with gdb)")
 | 
						|
	flagFailure = flag.String("failure", "", "fail only if output matches `regexp`")
 | 
						|
	flagIgnore  = flag.String("ignore", "", "ignore failure if output matches `regexp`")
 | 
						|
)
 | 
						|
 | 
						|
func init() {
 | 
						|
	flag.Usage = func() {
 | 
						|
		os.Stderr.WriteString(`The stress utility is intended for catching sporadic failures.
 | 
						|
It runs a given process in parallel in a loop and collects any failures.
 | 
						|
Usage:
 | 
						|
 | 
						|
	$ stress ./fmt.test -test.run=TestSometing -test.cpu=10
 | 
						|
 | 
						|
`)
 | 
						|
		flag.PrintDefaults()
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func main() {
 | 
						|
	flag.Parse()
 | 
						|
	if *flagP <= 0 || *flagTimeout <= 0 || len(flag.Args()) == 0 {
 | 
						|
		flag.Usage()
 | 
						|
		os.Exit(1)
 | 
						|
	}
 | 
						|
	var failureRe, ignoreRe *regexp.Regexp
 | 
						|
	if *flagFailure != "" {
 | 
						|
		var err error
 | 
						|
		if failureRe, err = regexp.Compile(*flagFailure); err != nil {
 | 
						|
			fmt.Println("bad failure regexp:", err)
 | 
						|
			os.Exit(1)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if *flagIgnore != "" {
 | 
						|
		var err error
 | 
						|
		if ignoreRe, err = regexp.Compile(*flagIgnore); err != nil {
 | 
						|
			fmt.Println("bad ignore regexp:", err)
 | 
						|
			os.Exit(1)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	res := make(chan []byte)
 | 
						|
	for i := 0; i < *flagP; i++ {
 | 
						|
		go func() {
 | 
						|
			for {
 | 
						|
				cmd := exec.Command(flag.Args()[0], flag.Args()[1:]...)
 | 
						|
				done := make(chan bool)
 | 
						|
				if *flagTimeout > 0 {
 | 
						|
					go func() {
 | 
						|
						select {
 | 
						|
						case <-done:
 | 
						|
							return
 | 
						|
						case <-time.After(*flagTimeout):
 | 
						|
						}
 | 
						|
						if !*flagKill {
 | 
						|
							fmt.Printf("process %v timed out\n", cmd.Process.Pid)
 | 
						|
							return
 | 
						|
						}
 | 
						|
						cmd.Process.Signal(syscall.SIGABRT)
 | 
						|
						select {
 | 
						|
						case <-done:
 | 
						|
							return
 | 
						|
						case <-time.After(10 * time.Second):
 | 
						|
						}
 | 
						|
						cmd.Process.Kill()
 | 
						|
					}()
 | 
						|
				}
 | 
						|
				out, err := cmd.CombinedOutput()
 | 
						|
				close(done)
 | 
						|
				if err != nil && (failureRe == nil || failureRe.Match(out)) && (ignoreRe == nil || !ignoreRe.Match(out)) {
 | 
						|
					out = append(out, fmt.Sprintf("\n\nERROR: %v\n", err)...)
 | 
						|
				} else {
 | 
						|
					out = []byte{}
 | 
						|
				}
 | 
						|
				res <- out
 | 
						|
			}
 | 
						|
		}()
 | 
						|
	}
 | 
						|
	runs, fails := 0, 0
 | 
						|
	ticker := time.NewTicker(5 * time.Second).C
 | 
						|
	for {
 | 
						|
		select {
 | 
						|
		case out := <-res:
 | 
						|
			runs++
 | 
						|
			if len(out) == 0 {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			fails++
 | 
						|
			f, err := ioutil.TempFile("", "go-stress")
 | 
						|
			if err != nil {
 | 
						|
				fmt.Printf("failed to create temp file: %v\n", err)
 | 
						|
				os.Exit(1)
 | 
						|
			}
 | 
						|
			f.Write(out)
 | 
						|
			f.Close()
 | 
						|
			if len(out) > 2<<10 {
 | 
						|
				out = out[:2<<10]
 | 
						|
			}
 | 
						|
			fmt.Printf("\n%s\n%s\n", f.Name(), out)
 | 
						|
		case <-ticker:
 | 
						|
			fmt.Printf("%v runs so far, %v failures\n", runs, fails)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 |