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