blob: 76ab2a8b4faf918ccf6474e88be145c760b594e7 [file] [log] [blame]
// Copyright 2013 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.
// The runstress tool stresses the runtime.
//
// It runs forever and should never fail. It tries to stress the garbage collector,
// maps, channels, the network, and everything else provided by the runtime.
package main
import (
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"math/rand"
"net"
"net/http"
"net/http/httptest"
"os/exec"
"strconv"
"time"
)
var (
v = flag.Bool("v", false, "verbose")
doMaps = flag.Bool("maps", true, "stress maps")
doExec = flag.Bool("exec", true, "stress exec")
doChan = flag.Bool("chan", true, "stress channels")
doNet = flag.Bool("net", true, "stress networking")
doParseGo = flag.Bool("parsego", true, "stress parsing Go (generates garbage)")
)
func Println(a ...interface{}) {
if *v {
log.Println(a...)
}
}
func dialStress(a net.Addr) {
for {
d := net.Dialer{Timeout: time.Duration(rand.Intn(1e9))}
c, err := d.Dial("tcp", a.String())
if err == nil {
Println("did dial")
go func() {
time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)
c.Close()
Println("closed dial")
}()
}
// Don't run out of ephermeral ports too quickly:
time.Sleep(250 * time.Millisecond)
}
}
func stressNet() {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
size, _ := strconv.Atoi(r.FormValue("size"))
w.Write(make([]byte, size))
}))
go dialStress(ts.Listener.Addr())
for {
size := rand.Intn(128 << 10)
res, err := http.Get(fmt.Sprintf("%s/?size=%d", ts.URL, size))
if err != nil {
log.Fatalf("stressNet: http Get error: %v", err)
}
if res.StatusCode != 200 {
log.Fatalf("stressNet: Status code = %d", res.StatusCode)
}
n, err := io.Copy(ioutil.Discard, res.Body)
if err != nil {
log.Fatalf("stressNet: io.Copy: %v", err)
}
if n != int64(size) {
log.Fatalf("stressNet: copied = %d; want %d", n, size)
}
res.Body.Close()
Println("did http", size)
}
}
func doAnExec() {
exit := rand.Intn(2)
wantOutput := fmt.Sprintf("output-%d", rand.Intn(1e9))
cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("echo %s; exit %d", wantOutput, exit))
out, err := cmd.CombinedOutput()
if exit == 1 {
if err == nil {
log.Fatal("stressExec: unexpected exec success")
}
return
}
if err != nil {
log.Fatalf("stressExec: exec failure: %v: %s", err, out)
}
wantOutput += "\n"
if string(out) != wantOutput {
log.Fatalf("stressExec: exec output = %q; want %q", out, wantOutput)
}
Println("did exec")
}
func stressExec() {
gate := make(chan bool, 10) // max execs at once
for {
gate <- true
go func() {
doAnExec()
<-gate
}()
}
}
func ringf(in <-chan int, out chan<- int, donec chan bool) {
for {
var n int
select {
case <-donec:
return
case n = <-in:
}
if n == 0 {
close(donec)
return
}
out <- n - 1
}
}
func threadRing(bufsize int) {
const N = 100
donec := make(chan bool)
one := make(chan int, bufsize) // will be input to thread 1
var in, out chan int = nil, one
for i := 1; i <= N-1; i++ {
in, out = out, make(chan int, bufsize)
go ringf(in, out, donec)
}
go ringf(out, one, donec)
one <- N
<-donec
Println("did threadring of", bufsize)
}
func stressChannels() {
for {
threadRing(0)
threadRing(1)
}
}
func main() {
flag.Parse()
for want, f := range map[*bool]func(){
doMaps: stressMaps,
doNet: stressNet,
doExec: stressExec,
doChan: stressChannels,
doParseGo: stressParseGo,
} {
if *want {
go f()
}
}
select {}
}