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