blob: 3f16fc9fb3fa87e8eb6e4eb6b34dad6e2d2c83db [file] [log] [blame]
Dan Willemsen38f2dba2016-07-08 14:54:35 -07001// Copyright 2013 The Go Authors. All rights reserved.
Brent Austinba3052e2015-04-21 16:08:23 -07002// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// The runstress tool stresses the runtime.
6//
7// It runs forever and should never fail. It tries to stress the garbage collector,
8// maps, channels, the network, and everything else provided by the runtime.
9package main
10
11import (
12 "flag"
13 "fmt"
14 "io"
15 "io/ioutil"
16 "log"
17 "math/rand"
18 "net"
19 "net/http"
20 "net/http/httptest"
21 "os/exec"
22 "strconv"
23 "time"
24)
25
26var (
27 v = flag.Bool("v", false, "verbose")
28 doMaps = flag.Bool("maps", true, "stress maps")
29 doExec = flag.Bool("exec", true, "stress exec")
30 doChan = flag.Bool("chan", true, "stress channels")
31 doNet = flag.Bool("net", true, "stress networking")
32 doParseGo = flag.Bool("parsego", true, "stress parsing Go (generates garbage)")
33)
34
35func Println(a ...interface{}) {
36 if *v {
37 log.Println(a...)
38 }
39}
40
41func dialStress(a net.Addr) {
42 for {
43 d := net.Dialer{Timeout: time.Duration(rand.Intn(1e9))}
44 c, err := d.Dial("tcp", a.String())
45 if err == nil {
46 Println("did dial")
47 go func() {
48 time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)
49 c.Close()
50 Println("closed dial")
51 }()
52 }
53 // Don't run out of ephermeral ports too quickly:
54 time.Sleep(250 * time.Millisecond)
55 }
56}
57
58func stressNet() {
59 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
60 size, _ := strconv.Atoi(r.FormValue("size"))
61 w.Write(make([]byte, size))
62 }))
63 go dialStress(ts.Listener.Addr())
64 for {
65 size := rand.Intn(128 << 10)
66 res, err := http.Get(fmt.Sprintf("%s/?size=%d", ts.URL, size))
67 if err != nil {
68 log.Fatalf("stressNet: http Get error: %v", err)
69 }
70 if res.StatusCode != 200 {
71 log.Fatalf("stressNet: Status code = %d", res.StatusCode)
72 }
73 n, err := io.Copy(ioutil.Discard, res.Body)
74 if err != nil {
75 log.Fatalf("stressNet: io.Copy: %v", err)
76 }
77 if n != int64(size) {
78 log.Fatalf("stressNet: copied = %d; want %d", n, size)
79 }
80 res.Body.Close()
81 Println("did http", size)
82 }
83}
84
85func doAnExec() {
86 exit := rand.Intn(2)
87 wantOutput := fmt.Sprintf("output-%d", rand.Intn(1e9))
88 cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("echo %s; exit %d", wantOutput, exit))
89 out, err := cmd.CombinedOutput()
90 if exit == 1 {
91 if err == nil {
92 log.Fatal("stressExec: unexpected exec success")
93 }
94 return
95 }
96 if err != nil {
97 log.Fatalf("stressExec: exec failure: %v: %s", err, out)
98 }
99 wantOutput += "\n"
100 if string(out) != wantOutput {
101 log.Fatalf("stressExec: exec output = %q; want %q", out, wantOutput)
102 }
103 Println("did exec")
104}
105
106func stressExec() {
107 gate := make(chan bool, 10) // max execs at once
108 for {
109 gate <- true
110 go func() {
111 doAnExec()
112 <-gate
113 }()
114 }
115}
116
117func ringf(in <-chan int, out chan<- int, donec chan bool) {
118 for {
119 var n int
120 select {
121 case <-donec:
122 return
123 case n = <-in:
124 }
125 if n == 0 {
126 close(donec)
127 return
128 }
129 out <- n - 1
130 }
131}
132
133func threadRing(bufsize int) {
134 const N = 100
135 donec := make(chan bool)
136 one := make(chan int, bufsize) // will be input to thread 1
137 var in, out chan int = nil, one
138 for i := 1; i <= N-1; i++ {
139 in, out = out, make(chan int, bufsize)
140 go ringf(in, out, donec)
141 }
142 go ringf(out, one, donec)
143 one <- N
144 <-donec
145 Println("did threadring of", bufsize)
146}
147
148func stressChannels() {
149 for {
150 threadRing(0)
151 threadRing(1)
152 }
153}
154
155func main() {
156 flag.Parse()
157 for want, f := range map[*bool]func(){
158 doMaps: stressMaps,
159 doNet: stressNet,
160 doExec: stressExec,
161 doChan: stressChannels,
162 doParseGo: stressParseGo,
163 } {
164 if *want {
165 go f()
166 }
167 }
168 select {}
169}