| // 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" |
| "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(io.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 {} |
| } |