libgo: update to Go1.10rc1
[official-gcc.git] / libgo / go / runtime / crash_test.go
blob7a099beb765eff5a0add8edeca9f6b110accc503
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 var testprog struct {
36 sync.Mutex
37 dir string
38 target map[string]buildexe
41 type buildexe struct {
42 exe string
43 err error
46 func runTestProg(t *testing.T, binary, name string, env ...string) string {
47 if *flagQuick {
48 t.Skip("-quick")
51 testenv.MustHaveGoBuild(t)
53 exe, err := buildTestProg(t, binary)
54 if err != nil {
55 t.Fatal(err)
58 cmd := testenv.CleanCmdEnv(exec.Command(exe, name))
59 cmd.Env = append(cmd.Env, env...)
60 if testing.Short() {
61 cmd.Env = append(cmd.Env, "RUNTIME_TEST_SHORT=1")
63 var b bytes.Buffer
64 cmd.Stdout = &b
65 cmd.Stderr = &b
66 if err := cmd.Start(); err != nil {
67 t.Fatalf("starting %s %s: %v", binary, name, err)
70 // If the process doesn't complete within 1 minute,
71 // assume it is hanging and kill it to get a stack trace.
72 p := cmd.Process
73 done := make(chan bool)
74 go func() {
75 scale := 1
76 // This GOARCH/GOOS test is copied from cmd/dist/test.go.
77 // TODO(iant): Have cmd/dist update the environment variable.
78 if runtime.GOARCH == "arm" || runtime.GOOS == "windows" {
79 scale = 2
81 if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" {
82 if sc, err := strconv.Atoi(s); err == nil {
83 scale = sc
87 select {
88 case <-done:
89 case <-time.After(time.Duration(scale) * time.Minute):
90 p.Signal(sigquit)
92 }()
94 if err := cmd.Wait(); err != nil {
95 t.Logf("%s %s exit status: %v", binary, name, err)
97 close(done)
99 return b.String()
102 func buildTestProg(t *testing.T, binary string, flags ...string) (string, error) {
103 if *flagQuick {
104 t.Skip("-quick")
107 checkStaleRuntime(t)
109 testprog.Lock()
110 defer testprog.Unlock()
111 if testprog.dir == "" {
112 dir, err := ioutil.TempDir("", "go-build")
113 if err != nil {
114 t.Fatalf("failed to create temp directory: %v", err)
116 testprog.dir = dir
117 toRemove = append(toRemove, dir)
120 if testprog.target == nil {
121 testprog.target = make(map[string]buildexe)
123 name := binary
124 if len(flags) > 0 {
125 name += "_" + strings.Join(flags, "_")
127 target, ok := testprog.target[name]
128 if ok {
129 return target.exe, target.err
132 exe := filepath.Join(testprog.dir, name+".exe")
133 cmd := exec.Command(testenv.GoToolPath(t), append([]string{"build", "-o", exe}, flags...)...)
134 cmd.Dir = "testdata/" + binary
135 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
136 if err != nil {
137 target.err = fmt.Errorf("building %s %v: %v\n%s", binary, flags, err, out)
138 testprog.target[name] = target
139 return "", target.err
141 target.exe = exe
142 testprog.target[name] = target
143 return exe, nil
146 var (
147 staleRuntimeOnce sync.Once // guards init of staleRuntimeErr
148 staleRuntimeErr error
151 func checkStaleRuntime(t *testing.T) {
152 staleRuntimeOnce.Do(func() {
153 // 'go run' uses the installed copy of runtime.a, which may be out of date.
154 out, err := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "list", "-gcflags=all="+os.Getenv("GO_GCFLAGS"), "-f", "{{.Stale}}", "runtime")).CombinedOutput()
155 if err != nil {
156 staleRuntimeErr = fmt.Errorf("failed to execute 'go list': %v\n%v", err, string(out))
157 return
159 if string(out) != "false\n" {
160 t.Logf("go list -f {{.Stale}} runtime:\n%s", out)
161 out, err := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "list", "-gcflags=all="+os.Getenv("GO_GCFLAGS"), "-f", "{{.StaleReason}}", "runtime")).CombinedOutput()
162 if err != nil {
163 t.Logf("go list -f {{.StaleReason}} failed: %v", err)
165 t.Logf("go list -f {{.StaleReason}} runtime:\n%s", out)
166 staleRuntimeErr = fmt.Errorf("Stale runtime.a. Run 'go install runtime'.")
169 if staleRuntimeErr != nil {
170 t.Fatal(staleRuntimeErr)
174 func testCrashHandler(t *testing.T, cgo bool) {
175 type crashTest struct {
176 Cgo bool
178 var output string
179 if cgo {
180 output = runTestProg(t, "testprogcgo", "Crash")
181 } else {
182 output = runTestProg(t, "testprog", "Crash")
184 want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n"
185 if output != want {
186 t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
190 func TestCrashHandler(t *testing.T) {
191 testCrashHandler(t, false)
194 func testDeadlock(t *testing.T, name string) {
195 output := runTestProg(t, "testprog", name)
196 want := "fatal error: all goroutines are asleep - deadlock!\n"
197 if !strings.HasPrefix(output, want) {
198 t.Fatalf("output does not start with %q:\n%s", want, output)
202 func TestSimpleDeadlock(t *testing.T) {
203 testDeadlock(t, "SimpleDeadlock")
206 func TestInitDeadlock(t *testing.T) {
207 testDeadlock(t, "InitDeadlock")
210 func TestLockedDeadlock(t *testing.T) {
211 testDeadlock(t, "LockedDeadlock")
214 func TestLockedDeadlock2(t *testing.T) {
215 testDeadlock(t, "LockedDeadlock2")
218 func TestGoexitDeadlock(t *testing.T) {
219 output := runTestProg(t, "testprog", "GoexitDeadlock")
220 want := "no goroutines (main called runtime.Goexit) - deadlock!"
221 if !strings.Contains(output, want) {
222 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
226 func TestStackOverflow(t *testing.T) {
227 if runtime.Compiler == "gccgo" {
228 t.Skip("gccgo does not do stack overflow checking")
230 output := runTestProg(t, "testprog", "StackOverflow")
231 want := "runtime: goroutine stack exceeds 1474560-byte limit\nfatal error: stack overflow"
232 if !strings.HasPrefix(output, want) {
233 t.Fatalf("output does not start with %q:\n%s", want, output)
237 func TestThreadExhaustion(t *testing.T) {
238 output := runTestProg(t, "testprog", "ThreadExhaustion")
239 want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion"
240 if !strings.HasPrefix(output, want) {
241 t.Fatalf("output does not start with %q:\n%s", want, output)
245 func TestRecursivePanic(t *testing.T) {
246 output := runTestProg(t, "testprog", "RecursivePanic")
247 want := `wrap: bad
248 panic: again
251 if !strings.HasPrefix(output, want) {
252 t.Fatalf("output does not start with %q:\n%s", want, output)
257 func TestGoexitCrash(t *testing.T) {
258 output := runTestProg(t, "testprog", "GoexitExit")
259 want := "no goroutines (main called runtime.Goexit) - deadlock!"
260 if !strings.Contains(output, want) {
261 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
265 func TestGoexitDefer(t *testing.T) {
266 c := make(chan struct{})
267 go func() {
268 defer func() {
269 r := recover()
270 if r != nil {
271 t.Errorf("non-nil recover during Goexit")
273 c <- struct{}{}
275 runtime.Goexit()
277 // Note: if the defer fails to run, we will get a deadlock here
281 func TestGoNil(t *testing.T) {
282 output := runTestProg(t, "testprog", "GoNil")
283 want := "go of nil func value"
284 if !strings.Contains(output, want) {
285 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
289 func TestMainGoroutineID(t *testing.T) {
290 output := runTestProg(t, "testprog", "MainGoroutineID")
291 want := "panic: test\n\ngoroutine 1 [running]:\n"
292 if !strings.HasPrefix(output, want) {
293 t.Fatalf("output does not start with %q:\n%s", want, output)
297 func TestNoHelperGoroutines(t *testing.T) {
298 output := runTestProg(t, "testprog", "NoHelperGoroutines")
299 matches := regexp.MustCompile(`goroutine [0-9]+ \[`).FindAllStringSubmatch(output, -1)
300 if len(matches) != 1 || matches[0][0] != "goroutine 1 [" {
301 t.Fatalf("want to see only goroutine 1, see:\n%s", output)
305 func TestBreakpoint(t *testing.T) {
306 output := runTestProg(t, "testprog", "Breakpoint")
307 // If runtime.Breakpoint() is inlined, then the stack trace prints
308 // "runtime.Breakpoint(...)" instead of "runtime.Breakpoint()".
309 // For gccgo, no parens.
310 want := "runtime.Breakpoint"
311 if !strings.Contains(output, want) {
312 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
316 func TestGoexitInPanic(t *testing.T) {
317 // see issue 8774: this code used to trigger an infinite recursion
318 output := runTestProg(t, "testprog", "GoexitInPanic")
319 want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
320 if !strings.HasPrefix(output, want) {
321 t.Fatalf("output does not start with %q:\n%s", want, output)
325 // Issue 14965: Runtime panics should be of type runtime.Error
326 func TestRuntimePanicWithRuntimeError(t *testing.T) {
327 testCases := [...]func(){
328 0: func() {
329 var m map[uint64]bool
330 m[1234] = true
332 1: func() {
333 ch := make(chan struct{})
334 close(ch)
335 close(ch)
337 2: func() {
338 var ch = make(chan struct{})
339 close(ch)
340 ch <- struct{}{}
342 3: func() {
343 var s = make([]int, 2)
344 _ = s[2]
346 4: func() {
347 n := -1
348 _ = make(chan bool, n)
350 5: func() {
351 close((chan bool)(nil))
355 for i, fn := range testCases {
356 got := panicValue(fn)
357 if _, ok := got.(runtime.Error); !ok {
358 t.Errorf("test #%d: recovered value %v(type %T) does not implement runtime.Error", i, got, got)
363 func panicValue(fn func()) (recovered interface{}) {
364 defer func() {
365 recovered = recover()
367 fn()
368 return
371 func TestPanicAfterGoexit(t *testing.T) {
372 // an uncaught panic should still work after goexit
373 output := runTestProg(t, "testprog", "PanicAfterGoexit")
374 want := "panic: hello"
375 if !strings.HasPrefix(output, want) {
376 t.Fatalf("output does not start with %q:\n%s", want, output)
380 func TestRecoveredPanicAfterGoexit(t *testing.T) {
381 output := runTestProg(t, "testprog", "RecoveredPanicAfterGoexit")
382 want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
383 if !strings.HasPrefix(output, want) {
384 t.Fatalf("output does not start with %q:\n%s", want, output)
388 func TestRecoverBeforePanicAfterGoexit(t *testing.T) {
389 // 1. defer a function that recovers
390 // 2. defer a function that panics
391 // 3. call goexit
392 // Goexit should run the #2 defer. Its panic
393 // should be caught by the #1 defer, and execution
394 // should resume in the caller. Like the Goexit
395 // never happened!
396 defer func() {
397 r := recover()
398 if r == nil {
399 panic("bad recover")
402 defer func() {
403 panic("hello")
405 runtime.Goexit()
408 func TestNetpollDeadlock(t *testing.T) {
409 t.Parallel()
410 output := runTestProg(t, "testprognet", "NetpollDeadlock")
411 want := "done\n"
412 if !strings.HasSuffix(output, want) {
413 t.Fatalf("output does not start with %q:\n%s", want, output)
417 func TestPanicTraceback(t *testing.T) {
418 t.Parallel()
419 output := runTestProg(t, "testprog", "PanicTraceback")
420 want := "panic: hello"
421 if !strings.HasPrefix(output, want) {
422 t.Fatalf("output does not start with %q:\n%s", want, output)
425 // Check functions in the traceback.
426 fns := []string{"main.pt1.func1", "panic", "main.pt2.func1", "panic", "main.pt2", "main.pt1"}
427 if runtime.Compiler == "gccgo" {
428 fns = []string{"main.pt1..func1", "panic", "main.pt2..func1", "panic", "main.pt2", "main.pt1"}
430 for _, fn := range fns {
431 var re *regexp.Regexp
432 if runtime.Compiler != "gccgo" {
433 re = regexp.MustCompile(`(?m)^` + regexp.QuoteMeta(fn) + `\(.*\n`)
434 } else {
435 re = regexp.MustCompile(`(?m)^` + regexp.QuoteMeta(fn) + `.*\n`)
437 idx := re.FindStringIndex(output)
438 if idx == nil {
439 t.Fatalf("expected %q function in traceback:\n%s", fn, output)
441 output = output[idx[1]:]
445 func testPanicDeadlock(t *testing.T, name string, want string) {
446 // test issue 14432
447 output := runTestProg(t, "testprog", name)
448 if !strings.HasPrefix(output, want) {
449 t.Fatalf("output does not start with %q:\n%s", want, output)
453 func TestPanicDeadlockGosched(t *testing.T) {
454 testPanicDeadlock(t, "GoschedInPanic", "panic: errorThatGosched\n\n")
457 func TestPanicDeadlockSyscall(t *testing.T) {
458 testPanicDeadlock(t, "SyscallInPanic", "1\n2\npanic: 3\n\n")
461 func TestPanicLoop(t *testing.T) {
462 output := runTestProg(t, "testprog", "PanicLoop")
463 if want := "panic while printing panic value"; !strings.Contains(output, want) {
464 t.Errorf("output does not contain %q:\n%s", want, output)
468 func TestMemPprof(t *testing.T) {
469 testenv.MustHaveGoRun(t)
470 if runtime.Compiler == "gccgo" {
471 t.Skip("gccgo may not have the pprof tool")
474 exe, err := buildTestProg(t, "testprog")
475 if err != nil {
476 t.Fatal(err)
479 got, err := testenv.CleanCmdEnv(exec.Command(exe, "MemProf")).CombinedOutput()
480 if err != nil {
481 t.Fatal(err)
483 fn := strings.TrimSpace(string(got))
484 defer os.Remove(fn)
486 for try := 0; try < 2; try++ {
487 cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-alloc_space", "-top"))
488 // Check that pprof works both with and without explicit executable on command line.
489 if try == 0 {
490 cmd.Args = append(cmd.Args, exe, fn)
491 } else {
492 cmd.Args = append(cmd.Args, fn)
494 found := false
495 for i, e := range cmd.Env {
496 if strings.HasPrefix(e, "PPROF_TMPDIR=") {
497 cmd.Env[i] = "PPROF_TMPDIR=" + os.TempDir()
498 found = true
499 break
502 if !found {
503 cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
506 top, err := cmd.CombinedOutput()
507 t.Logf("%s:\n%s", cmd.Args, top)
508 if err != nil {
509 t.Error(err)
510 } else if !bytes.Contains(top, []byte("MemProf")) {
511 t.Error("missing MemProf in pprof output")
516 var concurrentMapTest = flag.Bool("run_concurrent_map_tests", false, "also run flaky concurrent map tests")
518 func TestConcurrentMapWrites(t *testing.T) {
519 if !*concurrentMapTest {
520 t.Skip("skipping without -run_concurrent_map_tests")
522 testenv.MustHaveGoRun(t)
523 output := runTestProg(t, "testprog", "concurrentMapWrites")
524 want := "fatal error: concurrent map writes"
525 if !strings.HasPrefix(output, want) {
526 t.Fatalf("output does not start with %q:\n%s", want, output)
529 func TestConcurrentMapReadWrite(t *testing.T) {
530 if !*concurrentMapTest {
531 t.Skip("skipping without -run_concurrent_map_tests")
533 testenv.MustHaveGoRun(t)
534 output := runTestProg(t, "testprog", "concurrentMapReadWrite")
535 want := "fatal error: concurrent map read and map write"
536 if !strings.HasPrefix(output, want) {
537 t.Fatalf("output does not start with %q:\n%s", want, output)
540 func TestConcurrentMapIterateWrite(t *testing.T) {
541 if !*concurrentMapTest {
542 t.Skip("skipping without -run_concurrent_map_tests")
544 testenv.MustHaveGoRun(t)
545 output := runTestProg(t, "testprog", "concurrentMapIterateWrite")
546 want := "fatal error: concurrent map iteration and map write"
547 if !strings.HasPrefix(output, want) {
548 t.Fatalf("output does not start with %q:\n%s", want, output)
552 type point struct {
553 x, y *int
556 func (p *point) negate() {
557 *p.x = *p.x * -1
558 *p.y = *p.y * -1
561 // Test for issue #10152.
562 func TestPanicInlined(t *testing.T) {
563 defer func() {
564 r := recover()
565 if r == nil {
566 t.Fatalf("recover failed")
568 buf := make([]byte, 2048)
569 n := runtime.Stack(buf, false)
570 buf = buf[:n]
571 want := []byte("(*point).negate(")
572 if runtime.Compiler == "gccgo" {
573 want = []byte("point.negate")
575 if !bytes.Contains(buf, want) {
576 t.Logf("%s", buf)
577 t.Fatalf("expecting stack trace to contain call to %s", want)
581 pt := new(point)
582 pt.negate()
585 // Test for issues #3934 and #20018.
586 // We want to delay exiting until a panic print is complete.
587 func TestPanicRace(t *testing.T) {
588 testenv.MustHaveGoRun(t)
590 exe, err := buildTestProg(t, "testprog")
591 if err != nil {
592 t.Fatal(err)
595 // The test is intentionally racy, and in my testing does not
596 // produce the expected output about 0.05% of the time.
597 // So run the program in a loop and only fail the test if we
598 // get the wrong output ten times in a row.
599 const tries = 10
600 retry:
601 for i := 0; i < tries; i++ {
602 got, err := testenv.CleanCmdEnv(exec.Command(exe, "PanicRace")).CombinedOutput()
603 if err == nil {
604 t.Logf("try %d: program exited successfully, should have failed", i+1)
605 continue
608 if i > 0 {
609 t.Logf("try %d:\n", i+1)
611 t.Logf("%s\n", got)
613 wants := []string{
614 "panic: crash",
615 "PanicRace",
616 "created by ",
618 if runtime.Compiler == "gccgo" {
619 // gccgo will dump a function name like main.$nested30.
620 // Match on the file name instead.
621 wants[1] = "panicrace"
623 for _, want := range wants {
624 if !bytes.Contains(got, []byte(want)) {
625 t.Logf("did not find expected string %q", want)
626 continue retry
630 // Test generated expected output.
631 return
633 t.Errorf("test ran %d times without producing expected output", tries)
636 func TestBadTraceback(t *testing.T) {
637 if runtime.Compiler == "gccgo" {
638 t.Skip("gccgo does not do a hex dump")
640 output := runTestProg(t, "testprog", "BadTraceback")
641 for _, want := range []string{
642 "runtime: unexpected return pc",
643 "called from 0xbad",
644 "00000bad", // Smashed LR in hex dump
645 "<main.badLR", // Symbolization in hex dump (badLR1 or badLR2)
647 if !strings.Contains(output, want) {
648 t.Errorf("output does not contain %q:\n%s", want, output)