GoPLS Viewer

Home|gopls/cmd/stress/stress.go
1// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5//go:build !plan9
6// +build !plan9
7
8// The stress utility is intended for catching sporadic failures.
9// It runs a given process in parallel in a loop and collects any failures.
10// Usage:
11//
12//    $ stress ./fmt.test -test.run=TestSometing -test.cpu=10
13//
14// You can also specify a number of parallel processes with -p flag;
15// instruct the utility to not kill hanged processes for gdb attach;
16// or specify the failure output you are looking for (if you want to
17// ignore some other sporadic failures).
18package main
19
20import (
21    "flag"
22    "fmt"
23    exec "golang.org/x/sys/execabs"
24    "io/ioutil"
25    "os"
26    "path/filepath"
27    "regexp"
28    "runtime"
29    "syscall"
30    "time"
31)
32
33var (
34    flagP       = flag.Int("p"runtime.NumCPU(), "run `N` processes in parallel")
35    flagTimeout = flag.Duration("timeout"10*time.Minute"timeout each process after `duration`")
36    flagKill    = flag.Bool("kill"true"kill timed out processes if true, otherwise just print pid (to attach with gdb)")
37    flagFailure = flag.String("failure""""fail only if output matches `regexp`")
38    flagIgnore  = flag.String("ignore""""ignore failure if output matches `regexp`")
39    flagOutput  = flag.String("o"defaultPrefix(), "output failure logs to `path` plus a unique suffix")
40)
41
42func init() {
43    flag.Usage = func() {
44        os.Stderr.WriteString(`The stress utility is intended for catching sporadic failures.
45It runs a given process in parallel in a loop and collects any failures.
46Usage:
47
48    $ stress ./fmt.test -test.run=TestSometing -test.cpu=10
49
50`)
51        flag.PrintDefaults()
52    }
53}
54
55func defaultPrefix() string {
56    date := time.Now().Format("go-stress-20060102T150405-")
57    return filepath.Join(os.TempDir(), date)
58}
59
60func main() {
61    flag.Parse()
62    if *flagP <= 0 || *flagTimeout <= 0 || len(flag.Args()) == 0 {
63        flag.Usage()
64        os.Exit(1)
65    }
66    var failureReignoreRe *regexp.Regexp
67    if *flagFailure != "" {
68        var err error
69        if failureReerr = regexp.Compile(*flagFailure); err != nil {
70            fmt.Println("bad failure regexp:"err)
71            os.Exit(1)
72        }
73    }
74    if *flagIgnore != "" {
75        var err error
76        if ignoreReerr = regexp.Compile(*flagIgnore); err != nil {
77            fmt.Println("bad ignore regexp:"err)
78            os.Exit(1)
79        }
80    }
81    res := make(chan []byte)
82    for i := 0i < *flagPi++ {
83        go func() {
84            for {
85                cmd := exec.Command(flag.Args()[0], flag.Args()[1:]...)
86                done := make(chan bool)
87                if *flagTimeout > 0 {
88                    go func() {
89                        select {
90                        case <-done:
91                            return
92                        case <-time.After(*flagTimeout):
93                        }
94                        if !*flagKill {
95                            fmt.Printf("process %v timed out\n"cmd.Process.Pid)
96                            return
97                        }
98                        cmd.Process.Signal(syscall.SIGABRT)
99                        select {
100                        case <-done:
101                            return
102                        case <-time.After(10 * time.Second):
103                        }
104                        cmd.Process.Kill()
105                    }()
106                }
107                outerr := cmd.CombinedOutput()
108                close(done)
109                if err != nil && (failureRe == nil || failureRe.Match(out)) && (ignoreRe == nil || !ignoreRe.Match(out)) {
110                    out = append(outfmt.Sprintf("\n\nERROR: %v\n"err)...)
111                } else {
112                    out = []byte{}
113                }
114                res <- out
115            }
116        }()
117    }
118    runsfails := 00
119    start := time.Now()
120    ticker := time.NewTicker(5 * time.Second).C
121    for {
122        select {
123        case out := <-res:
124            runs++
125            if len(out) == 0 {
126                continue
127            }
128            fails++
129            dirpath := filepath.Split(*flagOutput)
130            ferr := ioutil.TempFile(dirpath)
131            if err != nil {
132                fmt.Printf("failed to create temp file: %v\n"err)
133                os.Exit(1)
134            }
135            f.Write(out)
136            f.Close()
137            if len(out) > 2<<10 {
138                out := out[:2<<10]
139                fmt.Printf("\n%s\n%s\n…\n"f.Name(), out)
140            } else {
141                fmt.Printf("\n%s\n%s\n"f.Name(), out)
142            }
143        case <-ticker:
144            elapsed := time.Since(start).Truncate(time.Second)
145            var pct string
146            if fails > 0 {
147                pct = fmt.Sprintf(" (%0.2f%%)"100.0*float64(fails)/float64(runs))
148            }
149            fmt.Printf("%v: %v runs so far, %v failures%s\n"elapsedrunsfailspct)
150        }
151    }
152}
153
MembersX
main.res
main.BlockStmt.BlockStmt.elapsed
main.BlockStmt.BlockStmt.out
main.BlockStmt.BlockStmt.f
main.BlockStmt.BlockStmt.pct
init
main.ignoreRe
main.runs
main.BlockStmt.BlockStmt.dir
ioutil
os
defaultPrefix.date
main
main.BlockStmt.err
main.start
flag
exec
main.BlockStmt.BlockStmt.BlockStmt.cmd
main.fails
main.BlockStmt.BlockStmt.err
syscall
time
main.BlockStmt.BlockStmt.BlockStmt.out
main.BlockStmt.BlockStmt.BlockStmt.err
regexp
main.BlockStmt.BlockStmt.BlockStmt.done
main.i
main.ticker
runtime
defaultPrefix
main.failureRe
main.BlockStmt.BlockStmt.path
fmt
filepath
Members
X