* builtins.def (BUILT_IN_SETJMP): Revert latest change.
[official-gcc.git] / libgo / go / runtime / crash_test.go
blob1cde6bf7997752b37edf57f157318897ad55bf21
1 // Copyright 2012 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 package runtime_test
7 import (
8 "bytes"
9 "flag"
10 "fmt"
11 "internal/testenv"
12 "io/ioutil"
13 "os"
14 "os/exec"
15 "path/filepath"
16 "regexp"
17 "runtime"
18 "strconv"
19 "strings"
20 "sync"
21 "testing"
22 "time"
25 var toRemove []string
27 func TestMain(m *testing.M) {
28 status := m.Run()
29 for _, file := range toRemove {
30 os.RemoveAll(file)
32 os.Exit(status)
35 func testEnv(cmd *exec.Cmd) *exec.Cmd {
36 if cmd.Env != nil {
37 panic("environment already set")
39 for _, env := range os.Environ() {
40 // Exclude GODEBUG from the environment to prevent its output
41 // from breaking tests that are trying to parse other command output.
42 if strings.HasPrefix(env, "GODEBUG=") {
43 continue
45 // Exclude GOTRACEBACK for the same reason.
46 if strings.HasPrefix(env, "GOTRACEBACK=") {
47 continue
49 cmd.Env = append(cmd.Env, env)
51 return cmd
54 var testprog struct {
55 sync.Mutex
56 dir string
57 target map[string]buildexe
60 type buildexe struct {
61 exe string
62 err error
65 func runTestProg(t *testing.T, binary, name string) string {
66 testenv.MustHaveGoBuild(t)
68 exe, err := buildTestProg(t, binary)
69 if err != nil {
70 t.Fatal(err)
73 cmd := testEnv(exec.Command(exe, name))
74 var b bytes.Buffer
75 cmd.Stdout = &b
76 cmd.Stderr = &b
77 if err := cmd.Start(); err != nil {
78 t.Fatalf("starting %s %s: %v", binary, name, err)
81 // If the process doesn't complete within 1 minute,
82 // assume it is hanging and kill it to get a stack trace.
83 p := cmd.Process
84 done := make(chan bool)
85 go func() {
86 scale := 1
87 // This GOARCH/GOOS test is copied from cmd/dist/test.go.
88 // TODO(iant): Have cmd/dist update the environment variable.
89 if runtime.GOARCH == "arm" || runtime.GOOS == "windows" {
90 scale = 2
92 if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" {
93 if sc, err := strconv.Atoi(s); err == nil {
94 scale = sc
98 select {
99 case <-done:
100 case <-time.After(time.Duration(scale) * time.Minute):
101 p.Signal(sigquit)
105 if err := cmd.Wait(); err != nil {
106 t.Logf("%s %s exit status: %v", binary, name, err)
108 close(done)
110 return b.String()
113 func buildTestProg(t *testing.T, binary string, flags ...string) (string, error) {
114 checkStaleRuntime(t)
116 testprog.Lock()
117 defer testprog.Unlock()
118 if testprog.dir == "" {
119 dir, err := ioutil.TempDir("", "go-build")
120 if err != nil {
121 t.Fatalf("failed to create temp directory: %v", err)
123 testprog.dir = dir
124 toRemove = append(toRemove, dir)
127 if testprog.target == nil {
128 testprog.target = make(map[string]buildexe)
130 name := binary
131 if len(flags) > 0 {
132 name += "_" + strings.Join(flags, "_")
134 target, ok := testprog.target[name]
135 if ok {
136 return target.exe, target.err
139 exe := filepath.Join(testprog.dir, name+".exe")
140 cmd := exec.Command(testenv.GoToolPath(t), append([]string{"build", "-o", exe}, flags...)...)
141 cmd.Dir = "testdata/" + binary
142 out, err := testEnv(cmd).CombinedOutput()
143 if err != nil {
144 target.err = fmt.Errorf("building %s %v: %v\n%s", binary, flags, err, out)
145 testprog.target[name] = target
146 return "", target.err
148 target.exe = exe
149 testprog.target[name] = target
150 return exe, nil
153 var (
154 staleRuntimeOnce sync.Once // guards init of staleRuntimeErr
155 staleRuntimeErr error
158 func checkStaleRuntime(t *testing.T) {
159 staleRuntimeOnce.Do(func() {
160 // 'go run' uses the installed copy of runtime.a, which may be out of date.
161 out, err := testEnv(exec.Command(testenv.GoToolPath(t), "list", "-f", "{{.Stale}}", "runtime")).CombinedOutput()
162 if err != nil {
163 staleRuntimeErr = fmt.Errorf("failed to execute 'go list': %v\n%v", err, string(out))
164 return
166 if string(out) != "false\n" {
167 t.Logf("go list -f {{.Stale}} runtime:\n%s", out)
168 out, err := testEnv(exec.Command(testenv.GoToolPath(t), "list", "-f", "{{.StaleReason}}", "runtime")).CombinedOutput()
169 if err != nil {
170 t.Logf("go list -f {{.StaleReason}} failed: %v", err)
172 t.Logf("go list -f {{.StaleReason}} runtime:\n%s", out)
173 staleRuntimeErr = fmt.Errorf("Stale runtime.a. Run 'go install runtime'.")
176 if staleRuntimeErr != nil {
177 t.Fatal(staleRuntimeErr)
181 func testCrashHandler(t *testing.T, cgo bool) {
182 type crashTest struct {
183 Cgo bool
185 var output string
186 if cgo {
187 output = runTestProg(t, "testprogcgo", "Crash")
188 } else {
189 output = runTestProg(t, "testprog", "Crash")
191 want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n"
192 if output != want {
193 t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
197 func TestCrashHandler(t *testing.T) {
198 testCrashHandler(t, false)
201 func testDeadlock(t *testing.T, name string) {
202 output := runTestProg(t, "testprog", name)
203 want := "fatal error: all goroutines are asleep - deadlock!\n"
204 if !strings.HasPrefix(output, want) {
205 t.Fatalf("output does not start with %q:\n%s", want, output)
209 func TestSimpleDeadlock(t *testing.T) {
210 testDeadlock(t, "SimpleDeadlock")
213 func TestInitDeadlock(t *testing.T) {
214 testDeadlock(t, "InitDeadlock")
217 func TestLockedDeadlock(t *testing.T) {
218 testDeadlock(t, "LockedDeadlock")
221 func TestLockedDeadlock2(t *testing.T) {
222 testDeadlock(t, "LockedDeadlock2")
225 func TestGoexitDeadlock(t *testing.T) {
226 output := runTestProg(t, "testprog", "GoexitDeadlock")
227 want := "no goroutines (main called runtime.Goexit) - deadlock!"
228 if !strings.Contains(output, want) {
229 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
233 func TestStackOverflow(t *testing.T) {
234 if runtime.Compiler == "gccgo" {
235 t.Skip("gccgo does not do stack overflow checking")
237 output := runTestProg(t, "testprog", "StackOverflow")
238 want := "runtime: goroutine stack exceeds 1474560-byte limit\nfatal error: stack overflow"
239 if !strings.HasPrefix(output, want) {
240 t.Fatalf("output does not start with %q:\n%s", want, output)
244 func TestThreadExhaustion(t *testing.T) {
245 output := runTestProg(t, "testprog", "ThreadExhaustion")
246 want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion"
247 if !strings.HasPrefix(output, want) {
248 t.Fatalf("output does not start with %q:\n%s", want, output)
252 func TestRecursivePanic(t *testing.T) {
253 output := runTestProg(t, "testprog", "RecursivePanic")
254 want := `wrap: bad
255 panic: again
258 if !strings.HasPrefix(output, want) {
259 t.Fatalf("output does not start with %q:\n%s", want, output)
264 func TestGoexitCrash(t *testing.T) {
265 output := runTestProg(t, "testprog", "GoexitExit")
266 want := "no goroutines (main called runtime.Goexit) - deadlock!"
267 if !strings.Contains(output, want) {
268 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
272 func TestGoexitDefer(t *testing.T) {
273 c := make(chan struct{})
274 go func() {
275 defer func() {
276 r := recover()
277 if r != nil {
278 t.Errorf("non-nil recover during Goexit")
280 c <- struct{}{}
282 runtime.Goexit()
284 // Note: if the defer fails to run, we will get a deadlock here
288 func TestGoNil(t *testing.T) {
289 output := runTestProg(t, "testprog", "GoNil")
290 want := "go of nil func value"
291 if !strings.Contains(output, want) {
292 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
296 func TestMainGoroutineID(t *testing.T) {
297 output := runTestProg(t, "testprog", "MainGoroutineID")
298 want := "panic: test\n\ngoroutine 1 [running]:\n"
299 if !strings.HasPrefix(output, want) {
300 t.Fatalf("output does not start with %q:\n%s", want, output)
304 func TestNoHelperGoroutines(t *testing.T) {
305 output := runTestProg(t, "testprog", "NoHelperGoroutines")
306 matches := regexp.MustCompile(`goroutine [0-9]+ \[`).FindAllStringSubmatch(output, -1)
307 if len(matches) != 1 || matches[0][0] != "goroutine 1 [" {
308 t.Fatalf("want to see only goroutine 1, see:\n%s", output)
312 func TestBreakpoint(t *testing.T) {
313 output := runTestProg(t, "testprog", "Breakpoint")
314 // If runtime.Breakpoint() is inlined, then the stack trace prints
315 // "runtime.Breakpoint(...)" instead of "runtime.Breakpoint()".
316 // For gccgo, no parens.
317 want := "runtime.Breakpoint"
318 if !strings.Contains(output, want) {
319 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
323 func TestGoexitInPanic(t *testing.T) {
324 // see issue 8774: this code used to trigger an infinite recursion
325 output := runTestProg(t, "testprog", "GoexitInPanic")
326 want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
327 if !strings.HasPrefix(output, want) {
328 t.Fatalf("output does not start with %q:\n%s", want, output)
332 // Issue 14965: Runtime panics should be of type runtime.Error
333 func TestRuntimePanicWithRuntimeError(t *testing.T) {
334 testCases := [...]func(){
335 0: func() {
336 var m map[uint64]bool
337 m[1234] = true
339 1: func() {
340 ch := make(chan struct{})
341 close(ch)
342 close(ch)
344 2: func() {
345 var ch = make(chan struct{})
346 close(ch)
347 ch <- struct{}{}
349 3: func() {
350 var s = make([]int, 2)
351 _ = s[2]
353 4: func() {
354 n := -1
355 _ = make(chan bool, n)
357 5: func() {
358 close((chan bool)(nil))
362 for i, fn := range testCases {
363 got := panicValue(fn)
364 if _, ok := got.(runtime.Error); !ok {
365 t.Errorf("test #%d: recovered value %v(type %T) does not implement runtime.Error", i, got, got)
370 func panicValue(fn func()) (recovered interface{}) {
371 defer func() {
372 recovered = recover()
374 fn()
375 return
378 func TestPanicAfterGoexit(t *testing.T) {
379 // an uncaught panic should still work after goexit
380 output := runTestProg(t, "testprog", "PanicAfterGoexit")
381 want := "panic: hello"
382 if !strings.HasPrefix(output, want) {
383 t.Fatalf("output does not start with %q:\n%s", want, output)
387 func TestRecoveredPanicAfterGoexit(t *testing.T) {
388 output := runTestProg(t, "testprog", "RecoveredPanicAfterGoexit")
389 want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
390 if !strings.HasPrefix(output, want) {
391 t.Fatalf("output does not start with %q:\n%s", want, output)
395 func TestRecoverBeforePanicAfterGoexit(t *testing.T) {
396 // 1. defer a function that recovers
397 // 2. defer a function that panics
398 // 3. call goexit
399 // Goexit should run the #2 defer. Its panic
400 // should be caught by the #1 defer, and execution
401 // should resume in the caller. Like the Goexit
402 // never happened!
403 defer func() {
404 r := recover()
405 if r == nil {
406 panic("bad recover")
409 defer func() {
410 panic("hello")
412 runtime.Goexit()
415 func TestNetpollDeadlock(t *testing.T) {
416 t.Parallel()
417 output := runTestProg(t, "testprognet", "NetpollDeadlock")
418 want := "done\n"
419 if !strings.HasSuffix(output, want) {
420 t.Fatalf("output does not start with %q:\n%s", want, output)
424 func TestPanicTraceback(t *testing.T) {
425 t.Parallel()
426 output := runTestProg(t, "testprog", "PanicTraceback")
427 want := "panic: hello"
428 if !strings.HasPrefix(output, want) {
429 t.Fatalf("output does not start with %q:\n%s", want, output)
432 // Check functions in the traceback.
433 fns := []string{"main.pt1.func1", "panic", "main.pt2.func1", "panic", "main.pt2", "main.pt1"}
434 if runtime.Compiler == "gccgo" {
435 fns = []string{"main.$nested", "panic", "main.$nested", "panic", "main.pt2", "main.pt1"}
437 for _, fn := range fns {
438 var re *regexp.Regexp
439 if runtime.Compiler != "gccgo" {
440 re = regexp.MustCompile(`(?m)^` + regexp.QuoteMeta(fn) + `\(.*\n`)
441 } else {
442 re = regexp.MustCompile(`(?m)^` + regexp.QuoteMeta(fn) + `.*\n`)
444 idx := re.FindStringIndex(output)
445 if idx == nil {
446 t.Fatalf("expected %q function in traceback:\n%s", fn, output)
448 output = output[idx[1]:]
452 func testPanicDeadlock(t *testing.T, name string, want string) {
453 // test issue 14432
454 output := runTestProg(t, "testprog", name)
455 if !strings.HasPrefix(output, want) {
456 t.Fatalf("output does not start with %q:\n%s", want, output)
460 func TestPanicDeadlockGosched(t *testing.T) {
461 testPanicDeadlock(t, "GoschedInPanic", "panic: errorThatGosched\n\n")
464 func TestPanicDeadlockSyscall(t *testing.T) {
465 testPanicDeadlock(t, "SyscallInPanic", "1\n2\npanic: 3\n\n")
468 func TestPanicLoop(t *testing.T) {
469 output := runTestProg(t, "testprog", "PanicLoop")
470 if want := "panic while printing panic value"; !strings.Contains(output, want) {
471 t.Errorf("output does not contain %q:\n%s", want, output)
475 func TestMemPprof(t *testing.T) {
476 testenv.MustHaveGoRun(t)
477 if runtime.Compiler == "gccgo" {
478 t.Skip("gccgo may not have the pprof tool")
481 exe, err := buildTestProg(t, "testprog")
482 if err != nil {
483 t.Fatal(err)
486 got, err := testEnv(exec.Command(exe, "MemProf")).CombinedOutput()
487 if err != nil {
488 t.Fatal(err)
490 fn := strings.TrimSpace(string(got))
491 defer os.Remove(fn)
493 for try := 0; try < 2; try++ {
494 cmd := testEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-alloc_space", "-top"))
495 // Check that pprof works both with and without explicit executable on command line.
496 if try == 0 {
497 cmd.Args = append(cmd.Args, exe, fn)
498 } else {
499 cmd.Args = append(cmd.Args, fn)
501 found := false
502 for i, e := range cmd.Env {
503 if strings.HasPrefix(e, "PPROF_TMPDIR=") {
504 cmd.Env[i] = "PPROF_TMPDIR=" + os.TempDir()
505 found = true
506 break
509 if !found {
510 cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
513 top, err := cmd.CombinedOutput()
514 t.Logf("%s:\n%s", cmd.Args, top)
515 if err != nil {
516 t.Error(err)
517 } else if !bytes.Contains(top, []byte("MemProf")) {
518 t.Error("missing MemProf in pprof output")
523 var concurrentMapTest = flag.Bool("run_concurrent_map_tests", false, "also run flaky concurrent map tests")
525 func TestConcurrentMapWrites(t *testing.T) {
526 if !*concurrentMapTest {
527 t.Skip("skipping without -run_concurrent_map_tests")
529 testenv.MustHaveGoRun(t)
530 output := runTestProg(t, "testprog", "concurrentMapWrites")
531 want := "fatal error: concurrent map writes"
532 if !strings.HasPrefix(output, want) {
533 t.Fatalf("output does not start with %q:\n%s", want, output)
536 func TestConcurrentMapReadWrite(t *testing.T) {
537 if !*concurrentMapTest {
538 t.Skip("skipping without -run_concurrent_map_tests")
540 testenv.MustHaveGoRun(t)
541 output := runTestProg(t, "testprog", "concurrentMapReadWrite")
542 want := "fatal error: concurrent map read and map write"
543 if !strings.HasPrefix(output, want) {
544 t.Fatalf("output does not start with %q:\n%s", want, output)
547 func TestConcurrentMapIterateWrite(t *testing.T) {
548 if !*concurrentMapTest {
549 t.Skip("skipping without -run_concurrent_map_tests")
551 testenv.MustHaveGoRun(t)
552 output := runTestProg(t, "testprog", "concurrentMapIterateWrite")
553 want := "fatal error: concurrent map iteration and map write"
554 if !strings.HasPrefix(output, want) {
555 t.Fatalf("output does not start with %q:\n%s", want, output)
559 type point struct {
560 x, y *int
563 func (p *point) negate() {
564 *p.x = *p.x * -1
565 *p.y = *p.y * -1
568 // Test for issue #10152.
569 func TestPanicInlined(t *testing.T) {
570 defer func() {
571 r := recover()
572 if r == nil {
573 t.Fatalf("recover failed")
575 buf := make([]byte, 2048)
576 n := runtime.Stack(buf, false)
577 buf = buf[:n]
578 want := []byte("(*point).negate(")
579 if runtime.Compiler == "gccgo" {
580 want = []byte("negate.pN18_runtime_test.point")
582 if !bytes.Contains(buf, want) {
583 t.Logf("%s", buf)
584 t.Fatalf("expecting stack trace to contain call to %s", want)
588 pt := new(point)
589 pt.negate()
592 // Test for issues #3934 and #20018.
593 // We want to delay exiting until a panic print is complete.
594 func TestPanicRace(t *testing.T) {
595 testenv.MustHaveGoRun(t)
597 exe, err := buildTestProg(t, "testprog")
598 if err != nil {
599 t.Fatal(err)
602 // The test is intentionally racy, and in my testing does not
603 // produce the expected output about 0.05% of the time.
604 // So run the program in a loop and only fail the test if we
605 // get the wrong output ten times in a row.
606 const tries = 10
607 retry:
608 for i := 0; i < tries; i++ {
609 got, err := testEnv(exec.Command(exe, "PanicRace")).CombinedOutput()
610 if err == nil {
611 t.Logf("try %d: program exited successfully, should have failed", i+1)
612 continue
615 if i > 0 {
616 t.Logf("try %d:\n", i+1)
618 t.Logf("%s\n", got)
620 wants := []string{
621 "panic: crash",
622 "PanicRace",
623 "created by ",
625 if runtime.Compiler == "gccgo" {
626 // gccgo will dump a function name like main.$nested30.
627 // Match on the file name instead.
628 wants[1] = "panicrace"
630 for _, want := range wants {
631 if !bytes.Contains(got, []byte(want)) {
632 t.Logf("did not find expected string %q", want)
633 continue retry
637 // Test generated expected output.
638 return
640 t.Errorf("test ran %d times without producing expected output", tries)