diff options
Diffstat (limited to 'test/stress')
-rw-r--r-- | test/stress/maps.go | 113 | ||||
-rw-r--r-- | test/stress/parsego.go | 220 | ||||
-rw-r--r-- | test/stress/runstress.go | 168 |
3 files changed, 501 insertions, 0 deletions
diff --git a/test/stress/maps.go b/test/stress/maps.go new file mode 100644 index 0000000..8ada23a --- /dev/null +++ b/test/stress/maps.go @@ -0,0 +1,113 @@ +// 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. + +package main + +import ( + "math/rand" + "runtime" + "sync" +) + +func mapTypes() []MapType { + // TODO(bradfitz): bunch more map types of all different key and value types. + // Use reflect.MapOf and a program to generate lots of types & struct types. + // For now, just one: + return []MapType{intMapType{}} +} + +type MapType interface { + NewMap() Map +} + +type Map interface { + AddItem() + DelItem() + Len() int + GetItem() + RangeAll() +} + +func stressMapType(mt MapType, done func()) { + defer done() + m := mt.NewMap() + for m.Len() < 10000 { + Println("map at ", m.Len()) + if m.Len()%100 == 0 { + runtime.Gosched() + } + m.AddItem() + m.AddItem() + m.DelItem() + var wg sync.WaitGroup + const numGets = 10 + wg.Add(numGets) + for i := 0; i < numGets; i++ { + go func(i int) { + if i&1 == 0 { + m.GetItem() + } else { + m.RangeAll() + } + wg.Done() + }(i) + } + wg.Wait() + } + for m.Len() > 0 { + m.DelItem() + } +} + +type intMapType struct{} + +func (intMapType) NewMap() Map { + return make(intMap) +} + +var deadcafe = []byte("\xDE\xAD\xCA\xFE") + +type intMap map[int][]byte + +func (m intMap) AddItem() { + s0 := len(m) + for len(m) == s0 { + key := rand.Intn(s0 + 1) + m[key] = make([]byte, rand.Intn(64<<10)) + } +} + +func (m intMap) DelItem() { + for k := range m { + delete(m, k) + return + } +} + +func (m intMap) GetItem() { + key := rand.Intn(len(m)) + if s, ok := m[key]; ok { + copy(s, deadcafe) + } +} + +func (m intMap) Len() int { return len(m) } + +func (m intMap) RangeAll() { + for _ = range m { + } + for range m { + } +} + +func stressMaps() { + for { + var wg sync.WaitGroup + for _, mt := range mapTypes() { + wg.Add(1) + go stressMapType(mt, wg.Done) + } + wg.Wait() + } +} diff --git a/test/stress/parsego.go b/test/stress/parsego.go new file mode 100644 index 0000000..98c4d9a --- /dev/null +++ b/test/stress/parsego.go @@ -0,0 +1,220 @@ +// 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. + +package main + +import ( + "go/ast" + "go/parser" + "go/token" + "os" + "path" + "runtime" + "strings" +) + +func isGoFile(dir os.FileInfo) bool { + return !dir.IsDir() && + !strings.HasPrefix(dir.Name(), ".") && // ignore .files + path.Ext(dir.Name()) == ".go" +} + +func isPkgFile(dir os.FileInfo) bool { + return isGoFile(dir) && + !strings.HasSuffix(dir.Name(), "_test.go") // ignore test files +} + +func pkgName(filename string) string { + file, err := parser.ParseFile(token.NewFileSet(), filename, nil, parser.PackageClauseOnly) + if err != nil || file == nil { + return "" + } + return file.Name.Name +} + +func parseDir(dirpath string) map[string]*ast.Package { + // the package name is the directory name within its parent. + // (use dirname instead of path because dirname is clean; it + // has no trailing '/') + _, pkgname := path.Split(dirpath) + + // filter function to select the desired .go files + filter := func(d os.FileInfo) bool { + if isPkgFile(d) { + // Some directories contain main packages: Only accept + // files that belong to the expected package so that + // parser.ParsePackage doesn't return "multiple packages + // found" errors. + // Additionally, accept the special package name + // fakePkgName if we are looking at cmd documentation. + name := pkgName(dirpath + "/" + d.Name()) + return name == pkgname + } + return false + } + + // get package AST + pkgs, err := parser.ParseDir(token.NewFileSet(), dirpath, filter, parser.ParseComments) + if err != nil { + println("parse", dirpath, err.Error()) + panic("go ParseDir fail: " + err.Error()) + } + return pkgs +} + +func stressParseGo() { + pkgroot := runtime.GOROOT() + "/src/" + for { + m := make(map[string]map[string]*ast.Package) + for _, pkg := range packages { + m[pkg] = parseDir(pkgroot + pkg) + Println("parsed go package", pkg) + } + } +} + +// find . -type d -not -path "./exp" -not -path "./exp/*" -printf "\t\"%p\",\n" | sort | sed "s/\.\///" | grep -v testdata +var packages = []string{ + "archive", + "archive/tar", + "archive/zip", + "bufio", + "builtin", + "bytes", + "compress", + "compress/bzip2", + "compress/flate", + "compress/gzip", + "compress/lzw", + "compress/zlib", + "container", + "container/heap", + "container/list", + "container/ring", + "crypto", + "crypto/aes", + "crypto/cipher", + "crypto/des", + "crypto/dsa", + "crypto/ecdsa", + "crypto/elliptic", + "crypto/hmac", + "crypto/md5", + "crypto/rand", + "crypto/rc4", + "crypto/rsa", + "crypto/sha1", + "crypto/sha256", + "crypto/sha512", + "crypto/subtle", + "crypto/tls", + "crypto/x509", + "crypto/x509/pkix", + "database", + "database/sql", + "database/sql/driver", + "debug", + "debug/dwarf", + "debug/elf", + "debug/gosym", + "debug/macho", + "debug/pe", + "encoding", + "encoding/ascii85", + "encoding/asn1", + "encoding/base32", + "encoding/base64", + "encoding/binary", + "encoding/csv", + "encoding/gob", + "encoding/hex", + "encoding/json", + "encoding/pem", + "encoding/xml", + "errors", + "expvar", + "flag", + "fmt", + "go", + "go/ast", + "go/build", + "go/doc", + "go/format", + "go/parser", + "go/printer", + "go/scanner", + "go/token", + "hash", + "hash/adler32", + "hash/crc32", + "hash/crc64", + "hash/fnv", + "html", + "html/template", + "image", + "image/color", + "image/draw", + "image/gif", + "image/jpeg", + "image/png", + "index", + "index/suffixarray", + "io", + "io/ioutil", + "log", + "log/syslog", + "math", + "math/big", + "math/cmplx", + "math/rand", + "mime", + "mime/multipart", + "net", + "net/http", + "net/http/cgi", + "net/http/cookiejar", + "net/http/fcgi", + "net/http/httptest", + "net/http/httputil", + "net/http/pprof", + "net/mail", + "net/rpc", + "net/rpc/jsonrpc", + "net/smtp", + "net/textproto", + "net/url", + "os", + "os/exec", + "os/signal", + "os/user", + "path", + "path/filepath", + "reflect", + "regexp", + "regexp/syntax", + "runtime", + "runtime/cgo", + "runtime/debug", + "runtime/pprof", + "runtime/race", + "sort", + "strconv", + "strings", + "sync", + "sync/atomic", + "syscall", + "testing", + "testing/iotest", + "testing/quick", + "text", + "text/scanner", + "text/tabwriter", + "text/template", + "text/template/parse", + "time", + "unicode", + "unicode/utf16", + "unicode/utf8", + "unsafe", +} diff --git a/test/stress/runstress.go b/test/stress/runstress.go new file mode 100644 index 0000000..b752fa8 --- /dev/null +++ b/test/stress/runstress.go @@ -0,0 +1,168 @@ +// 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 {} +} |