summaryrefslogtreecommitdiff
path: root/libgo/go/runtime/crash_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/runtime/crash_test.go')
-rw-r--r--libgo/go/runtime/crash_test.go432
1 files changed, 81 insertions, 351 deletions
diff --git a/libgo/go/runtime/crash_test.go b/libgo/go/runtime/crash_test.go
index 8efce4da2d4..b622eb4526a 100644
--- a/libgo/go/runtime/crash_test.go
+++ b/libgo/go/runtime/crash_test.go
@@ -16,9 +16,18 @@ import (
"strings"
"sync"
"testing"
- "text/template"
)
+var toRemove []string
+
+func TestMain(m *testing.M) {
+ status := m.Run()
+ for _, file := range toRemove {
+ os.RemoveAll(file)
+ }
+ os.Exit(status)
+}
+
func testEnv(cmd *exec.Cmd) *exec.Cmd {
if cmd.Env != nil {
panic("environment already set")
@@ -38,55 +47,63 @@ func testEnv(cmd *exec.Cmd) *exec.Cmd {
return cmd
}
-func executeTest(t *testing.T, templ string, data interface{}, extra ...string) string {
- testenv.MustHaveGoBuild(t)
+var testprog struct {
+ sync.Mutex
+ dir string
+ target map[string]buildexe
+}
- checkStaleRuntime(t)
+type buildexe struct {
+ exe string
+ err error
+}
- st := template.Must(template.New("crashSource").Parse(templ))
+func runTestProg(t *testing.T, binary, name string) string {
+ testenv.MustHaveGoBuild(t)
- dir, err := ioutil.TempDir("", "go-build")
+ exe, err := buildTestProg(t, binary)
if err != nil {
- t.Fatalf("failed to create temp directory: %v", err)
+ t.Fatal(err)
}
- defer os.RemoveAll(dir)
+ got, _ := testEnv(exec.Command(exe, name)).CombinedOutput()
+ return string(got)
+}
- src := filepath.Join(dir, "main.go")
- f, err := os.Create(src)
- if err != nil {
- t.Fatalf("failed to create file: %v", err)
- }
- err = st.Execute(f, data)
- if err != nil {
- f.Close()
- t.Fatalf("failed to execute template: %v", err)
- }
- if err := f.Close(); err != nil {
- t.Fatalf("failed to close file: %v", err)
- }
+func buildTestProg(t *testing.T, binary string) (string, error) {
+ checkStaleRuntime(t)
- for i := 0; i < len(extra); i += 2 {
- fname := extra[i]
- contents := extra[i+1]
- if d, _ := filepath.Split(fname); d != "" {
- if err := os.Mkdir(filepath.Join(dir, d), 0755); err != nil {
- t.Fatal(err)
- }
- }
- if err := ioutil.WriteFile(filepath.Join(dir, fname), []byte(contents), 0666); err != nil {
- t.Fatal(err)
+ testprog.Lock()
+ defer testprog.Unlock()
+ if testprog.dir == "" {
+ dir, err := ioutil.TempDir("", "go-build")
+ if err != nil {
+ t.Fatalf("failed to create temp directory: %v", err)
}
+ testprog.dir = dir
+ toRemove = append(toRemove, dir)
+ }
+
+ if testprog.target == nil {
+ testprog.target = make(map[string]buildexe)
+ }
+ target, ok := testprog.target[binary]
+ if ok {
+ return target.exe, target.err
}
- cmd := exec.Command("go", "build", "-o", "a.exe")
- cmd.Dir = dir
+ exe := filepath.Join(testprog.dir, binary+".exe")
+ cmd := exec.Command("go", "build", "-o", exe)
+ cmd.Dir = "testdata/" + binary
out, err := testEnv(cmd).CombinedOutput()
if err != nil {
- t.Fatalf("building source: %v\n%s", err, out)
+ exe = ""
+ target.err = fmt.Errorf("building %s: %v\n%s", binary, err, out)
+ testprog.target[binary] = target
+ return "", target.err
}
-
- got, _ := testEnv(exec.Command(filepath.Join(dir, "a.exe"))).CombinedOutput()
- return string(got)
+ target.exe = exe
+ testprog.target[binary] = target
+ return exe, nil
}
var (
@@ -115,7 +132,12 @@ func testCrashHandler(t *testing.T, cgo bool) {
type crashTest struct {
Cgo bool
}
- output := executeTest(t, crashSource, &crashTest{Cgo: cgo})
+ var output string
+ if cgo {
+ output = runTestProg(t, "testprogcgo", "Crash")
+ } else {
+ output = runTestProg(t, "testprog", "Crash")
+ }
want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n"
if output != want {
t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
@@ -126,8 +148,8 @@ func TestCrashHandler(t *testing.T) {
testCrashHandler(t, false)
}
-func testDeadlock(t *testing.T, source string) {
- output := executeTest(t, source, nil)
+func testDeadlock(t *testing.T, name string) {
+ output := runTestProg(t, "testprog", name)
want := "fatal error: all goroutines are asleep - deadlock!\n"
if !strings.HasPrefix(output, want) {
t.Fatalf("output does not start with %q:\n%s", want, output)
@@ -135,23 +157,23 @@ func testDeadlock(t *testing.T, source string) {
}
func TestSimpleDeadlock(t *testing.T) {
- testDeadlock(t, simpleDeadlockSource)
+ testDeadlock(t, "SimpleDeadlock")
}
func TestInitDeadlock(t *testing.T) {
- testDeadlock(t, initDeadlockSource)
+ testDeadlock(t, "InitDeadlock")
}
func TestLockedDeadlock(t *testing.T) {
- testDeadlock(t, lockedDeadlockSource)
+ testDeadlock(t, "LockedDeadlock")
}
func TestLockedDeadlock2(t *testing.T) {
- testDeadlock(t, lockedDeadlockSource2)
+ testDeadlock(t, "LockedDeadlock2")
}
func TestGoexitDeadlock(t *testing.T) {
- output := executeTest(t, goexitDeadlockSource, nil)
+ output := runTestProg(t, "testprog", "GoexitDeadlock")
want := "no goroutines (main called runtime.Goexit) - deadlock!"
if !strings.Contains(output, want) {
t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
@@ -159,15 +181,15 @@ func TestGoexitDeadlock(t *testing.T) {
}
func TestStackOverflow(t *testing.T) {
- output := executeTest(t, stackOverflowSource, nil)
- want := "runtime: goroutine stack exceeds 4194304-byte limit\nfatal error: stack overflow"
+ output := runTestProg(t, "testprog", "StackOverflow")
+ want := "runtime: goroutine stack exceeds 1474560-byte limit\nfatal error: stack overflow"
if !strings.HasPrefix(output, want) {
t.Fatalf("output does not start with %q:\n%s", want, output)
}
}
func TestThreadExhaustion(t *testing.T) {
- output := executeTest(t, threadExhaustionSource, nil)
+ output := runTestProg(t, "testprog", "ThreadExhaustion")
want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion"
if !strings.HasPrefix(output, want) {
t.Fatalf("output does not start with %q:\n%s", want, output)
@@ -175,7 +197,7 @@ func TestThreadExhaustion(t *testing.T) {
}
func TestRecursivePanic(t *testing.T) {
- output := executeTest(t, recursivePanicSource, nil)
+ output := runTestProg(t, "testprog", "RecursivePanic")
want := `wrap: bad
panic: again
@@ -187,7 +209,7 @@ panic: again
}
func TestGoexitCrash(t *testing.T) {
- output := executeTest(t, goexitExitSource, nil)
+ output := runTestProg(t, "testprog", "GoexitExit")
want := "no goroutines (main called runtime.Goexit) - deadlock!"
if !strings.Contains(output, want) {
t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
@@ -211,15 +233,15 @@ func TestGoexitDefer(t *testing.T) {
}
func TestGoNil(t *testing.T) {
- output := executeTest(t, goNilSource, nil)
+ output := runTestProg(t, "testprog", "GoNil")
want := "go of nil func value"
if !strings.Contains(output, want) {
t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
}
}
-func TestMainGoroutineId(t *testing.T) {
- output := executeTest(t, mainGoroutineIdSource, nil)
+func TestMainGoroutineID(t *testing.T) {
+ output := runTestProg(t, "testprog", "MainGoroutineID")
want := "panic: test\n\ngoroutine 1 [running]:\n"
if !strings.HasPrefix(output, want) {
t.Fatalf("output does not start with %q:\n%s", want, output)
@@ -227,7 +249,7 @@ func TestMainGoroutineId(t *testing.T) {
}
func TestNoHelperGoroutines(t *testing.T) {
- output := executeTest(t, noHelperGoroutinesSource, nil)
+ output := runTestProg(t, "testprog", "NoHelperGoroutines")
matches := regexp.MustCompile(`goroutine [0-9]+ \[`).FindAllStringSubmatch(output, -1)
if len(matches) != 1 || matches[0][0] != "goroutine 1 [" {
t.Fatalf("want to see only goroutine 1, see:\n%s", output)
@@ -235,311 +257,39 @@ func TestNoHelperGoroutines(t *testing.T) {
}
func TestBreakpoint(t *testing.T) {
- output := executeTest(t, breakpointSource, nil)
+ output := runTestProg(t, "testprog", "Breakpoint")
want := "runtime.Breakpoint()"
if !strings.Contains(output, want) {
t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
}
}
-const crashSource = `
-package main
-
-import (
- "fmt"
- "runtime"
-)
-
-{{if .Cgo}}
-import "C"
-{{end}}
-
-func test(name string) {
- defer func() {
- if x := recover(); x != nil {
- fmt.Printf(" recovered")
- }
- fmt.Printf(" done\n")
- }()
- fmt.Printf("%s:", name)
- var s *string
- _ = *s
- fmt.Print("SHOULD NOT BE HERE")
-}
-
-func testInNewThread(name string) {
- c := make(chan bool)
- go func() {
- runtime.LockOSThread()
- test(name)
- c <- true
- }()
- <-c
-}
-
-func main() {
- runtime.LockOSThread()
- test("main")
- testInNewThread("new-thread")
- testInNewThread("second-new-thread")
- test("main-again")
-}
-`
-
-const simpleDeadlockSource = `
-package main
-func main() {
- select {}
-}
-`
-
-const initDeadlockSource = `
-package main
-func init() {
- select {}
-}
-func main() {
-}
-`
-
-const lockedDeadlockSource = `
-package main
-import "runtime"
-func main() {
- runtime.LockOSThread()
- select {}
-}
-`
-
-const lockedDeadlockSource2 = `
-package main
-import (
- "runtime"
- "time"
-)
-func main() {
- go func() {
- runtime.LockOSThread()
- select {}
- }()
- time.Sleep(time.Millisecond)
- select {}
-}
-`
-
-const goexitDeadlockSource = `
-package main
-import (
- "runtime"
-)
-
-func F() {
- for i := 0; i < 10; i++ {
- }
-}
-
-func main() {
- go F()
- go F()
- runtime.Goexit()
-}
-`
-
-const stackOverflowSource = `
-package main
-
-import "runtime/debug"
-
-func main() {
- debug.SetMaxStack(4<<20)
- f(make([]byte, 10))
-}
-
-func f(x []byte) byte {
- var buf [64<<10]byte
- return x[0] + f(buf[:])
-}
-`
-
-const threadExhaustionSource = `
-package main
-
-import (
- "runtime"
- "runtime/debug"
-)
-
-func main() {
- debug.SetMaxThreads(10)
- c := make(chan int)
- for i := 0; i < 100; i++ {
- go func() {
- runtime.LockOSThread()
- c <- 0
- select{}
- }()
- <-c
- }
-}
-`
-
-const recursivePanicSource = `
-package main
-
-import (
- "fmt"
-)
-
-func main() {
- func() {
- defer func() {
- fmt.Println(recover())
- }()
- var x [8192]byte
- func(x [8192]byte) {
- defer func() {
- if err := recover(); err != nil {
- panic("wrap: " + err.(string))
- }
- }()
- panic("bad")
- }(x)
- }()
- panic("again")
-}
-`
-
-const goexitExitSource = `
-package main
-
-import (
- "runtime"
- "time"
-)
-
-func main() {
- go func() {
- time.Sleep(time.Millisecond)
- }()
- i := 0
- runtime.SetFinalizer(&i, func(p *int) {})
- runtime.GC()
- runtime.Goexit()
-}
-`
-
-const goNilSource = `
-package main
-
-func main() {
- defer func() {
- recover()
- }()
- var f func()
- go f()
- select{}
-}
-`
-
-const mainGoroutineIdSource = `
-package main
-func main() {
- panic("test")
-}
-`
-
-const noHelperGoroutinesSource = `
-package main
-import (
- "runtime"
- "time"
-)
-func init() {
- i := 0
- runtime.SetFinalizer(&i, func(p *int) {})
- time.AfterFunc(time.Hour, func() {})
- panic("oops")
-}
-func main() {
-}
-`
-
-const breakpointSource = `
-package main
-import "runtime"
-func main() {
- runtime.Breakpoint()
-}
-`
-
func TestGoexitInPanic(t *testing.T) {
// see issue 8774: this code used to trigger an infinite recursion
- output := executeTest(t, goexitInPanicSource, nil)
+ output := runTestProg(t, "testprog", "GoexitInPanic")
want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
if !strings.HasPrefix(output, want) {
t.Fatalf("output does not start with %q:\n%s", want, output)
}
}
-const goexitInPanicSource = `
-package main
-import "runtime"
-func main() {
- go func() {
- defer func() {
- runtime.Goexit()
- }()
- panic("hello")
- }()
- runtime.Goexit()
-}
-`
-
func TestPanicAfterGoexit(t *testing.T) {
// an uncaught panic should still work after goexit
- output := executeTest(t, panicAfterGoexitSource, nil)
+ output := runTestProg(t, "testprog", "PanicAfterGoexit")
want := "panic: hello"
if !strings.HasPrefix(output, want) {
t.Fatalf("output does not start with %q:\n%s", want, output)
}
}
-const panicAfterGoexitSource = `
-package main
-import "runtime"
-func main() {
- defer func() {
- panic("hello")
- }()
- runtime.Goexit()
-}
-`
-
func TestRecoveredPanicAfterGoexit(t *testing.T) {
- output := executeTest(t, recoveredPanicAfterGoexitSource, nil)
+ output := runTestProg(t, "testprog", "RecoveredPanicAfterGoexit")
want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
if !strings.HasPrefix(output, want) {
t.Fatalf("output does not start with %q:\n%s", want, output)
}
}
-const recoveredPanicAfterGoexitSource = `
-package main
-import "runtime"
-func main() {
- defer func() {
- defer func() {
- r := recover()
- if r == nil {
- panic("bad recover")
- }
- }()
- panic("hello")
- }()
- runtime.Goexit()
-}
-`
-
func TestRecoverBeforePanicAfterGoexit(t *testing.T) {
// 1. defer a function that recovers
// 2. defer a function that panics
@@ -561,29 +311,9 @@ func TestRecoverBeforePanicAfterGoexit(t *testing.T) {
}
func TestNetpollDeadlock(t *testing.T) {
- output := executeTest(t, netpollDeadlockSource, nil)
+ output := runTestProg(t, "testprognet", "NetpollDeadlock")
want := "done\n"
if !strings.HasSuffix(output, want) {
t.Fatalf("output does not start with %q:\n%s", want, output)
}
}
-
-const netpollDeadlockSource = `
-package main
-import (
- "fmt"
- "net"
-)
-func init() {
- fmt.Println("dialing")
- c, err := net.Dial("tcp", "localhost:14356")
- if err == nil {
- c.Close()
- } else {
- fmt.Println("error: ", err)
- }
-}
-func main() {
- fmt.Println("done")
-}
-`