diff options
Diffstat (limited to 'libgo/go/runtime/crash_test.go')
-rw-r--r-- | libgo/go/runtime/crash_test.go | 432 |
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") -} -` |