Home | History | Annotate | Download | only in stress
      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.
      9 package main
     10 
     11 import (
     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 
     26 var (
     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 
     35 func Println(a ...interface{}) {
     36 	if *v {
     37 		log.Println(a...)
     38 	}
     39 }
     40 
     41 func 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 
     58 func 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 
     85 func 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 
    106 func 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 
    117 func 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 
    133 func 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 
    148 func stressChannels() {
    149 	for {
    150 		threadRing(0)
    151 		threadRing(1)
    152 	}
    153 }
    154 
    155 func 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 }
    170