mirror of
https://github.com/golang/go
synced 2024-09-23 17:20:13 -06:00
test/stress: start of a runtime stress program
Runs forever, stressing the runtime in various ways. It should never terminate. R=golang-dev, r, minux.ma CC=golang-dev https://golang.org/cl/8583047
This commit is contained in:
parent
9ca4a25f4b
commit
b3809cae5e
111
test/stress/maps.go
Normal file
111
test/stress/maps.go
Normal file
@ -0,0 +1,111 @@
|
||||
// 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 {
|
||||
}
|
||||
}
|
||||
|
||||
func stressMaps() {
|
||||
for {
|
||||
var wg sync.WaitGroup
|
||||
for _, mt := range mapTypes() {
|
||||
wg.Add(1)
|
||||
go stressMapType(mt, wg.Done)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
}
|
220
test/stress/parsego.go
Normal file
220
test/stress/parsego.go
Normal file
@ -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/pkg/"
|
||||
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",
|
||||
}
|
164
test/stress/runstress.go
Normal file
164
test/stress/runstress.go
Normal file
@ -0,0 +1,164 @@
|
||||
// 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 {
|
||||
n := <-in
|
||||
if n == 0 {
|
||||
donec <- true
|
||||
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 {}
|
||||
}
|
Loading…
Reference in New Issue
Block a user