github.com/tcnksm/go@v0.0.0-20141208075154-439b32936367/src/runtime/crash_test.go (about)

     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.
     4  
     5  package runtime_test
     6  
     7  import (
     8  	"io/ioutil"
     9  	"os"
    10  	"os/exec"
    11  	"path/filepath"
    12  	"runtime"
    13  	"strings"
    14  	"testing"
    15  	"text/template"
    16  )
    17  
    18  // testEnv excludes GODEBUG from the environment
    19  // to prevent its output from breaking tests that
    20  // are trying to parse other command output.
    21  func testEnv(cmd *exec.Cmd) *exec.Cmd {
    22  	if cmd.Env != nil {
    23  		panic("environment already set")
    24  	}
    25  	for _, env := range os.Environ() {
    26  		if strings.HasPrefix(env, "GODEBUG=") {
    27  			continue
    28  		}
    29  		cmd.Env = append(cmd.Env, env)
    30  	}
    31  	return cmd
    32  }
    33  
    34  func executeTest(t *testing.T, templ string, data interface{}, extra ...string) string {
    35  	switch runtime.GOOS {
    36  	case "android", "nacl":
    37  		t.Skipf("skipping on %s", runtime.GOOS)
    38  	}
    39  
    40  	checkStaleRuntime(t)
    41  
    42  	st := template.Must(template.New("crashSource").Parse(templ))
    43  
    44  	dir, err := ioutil.TempDir("", "go-build")
    45  	if err != nil {
    46  		t.Fatalf("failed to create temp directory: %v", err)
    47  	}
    48  	defer os.RemoveAll(dir)
    49  
    50  	src := filepath.Join(dir, "main.go")
    51  	f, err := os.Create(src)
    52  	if err != nil {
    53  		t.Fatalf("failed to create file: %v", err)
    54  	}
    55  	err = st.Execute(f, data)
    56  	if err != nil {
    57  		f.Close()
    58  		t.Fatalf("failed to execute template: %v", err)
    59  	}
    60  	if err := f.Close(); err != nil {
    61  		t.Fatalf("failed to close file: %v", err)
    62  	}
    63  
    64  	for i := 0; i < len(extra); i += 2 {
    65  		if err := ioutil.WriteFile(filepath.Join(dir, extra[i]), []byte(extra[i+1]), 0666); err != nil {
    66  			t.Fatal(err)
    67  		}
    68  	}
    69  
    70  	cmd := exec.Command("go", "build", "-o", "a.exe")
    71  	cmd.Dir = dir
    72  	out, err := testEnv(cmd).CombinedOutput()
    73  	if err != nil {
    74  		t.Fatalf("building source: %v\n%s", err, out)
    75  	}
    76  
    77  	got, _ := testEnv(exec.Command(filepath.Join(dir, "a.exe"))).CombinedOutput()
    78  	return string(got)
    79  }
    80  
    81  func checkStaleRuntime(t *testing.T) {
    82  	// 'go run' uses the installed copy of runtime.a, which may be out of date.
    83  	out, err := testEnv(exec.Command("go", "list", "-f", "{{.Stale}}", "runtime")).CombinedOutput()
    84  	if err != nil {
    85  		t.Fatalf("failed to execute 'go list': %v\n%v", err, string(out))
    86  	}
    87  	if string(out) != "false\n" {
    88  		t.Fatalf("Stale runtime.a. Run 'go install runtime'.")
    89  	}
    90  }
    91  
    92  func testCrashHandler(t *testing.T, cgo bool) {
    93  	type crashTest struct {
    94  		Cgo bool
    95  	}
    96  	output := executeTest(t, crashSource, &crashTest{Cgo: cgo})
    97  	want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n"
    98  	if output != want {
    99  		t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
   100  	}
   101  }
   102  
   103  func TestCrashHandler(t *testing.T) {
   104  	testCrashHandler(t, false)
   105  }
   106  
   107  func testDeadlock(t *testing.T, source string) {
   108  	output := executeTest(t, source, nil)
   109  	want := "fatal error: all goroutines are asleep - deadlock!\n"
   110  	if !strings.HasPrefix(output, want) {
   111  		t.Fatalf("output does not start with %q:\n%s", want, output)
   112  	}
   113  }
   114  
   115  func TestSimpleDeadlock(t *testing.T) {
   116  	testDeadlock(t, simpleDeadlockSource)
   117  }
   118  
   119  func TestInitDeadlock(t *testing.T) {
   120  	testDeadlock(t, initDeadlockSource)
   121  }
   122  
   123  func TestLockedDeadlock(t *testing.T) {
   124  	testDeadlock(t, lockedDeadlockSource)
   125  }
   126  
   127  func TestLockedDeadlock2(t *testing.T) {
   128  	testDeadlock(t, lockedDeadlockSource2)
   129  }
   130  
   131  func TestGoexitDeadlock(t *testing.T) {
   132  	output := executeTest(t, goexitDeadlockSource, nil)
   133  	want := "no goroutines (main called runtime.Goexit) - deadlock!"
   134  	if !strings.Contains(output, want) {
   135  		t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
   136  	}
   137  }
   138  
   139  func TestStackOverflow(t *testing.T) {
   140  	output := executeTest(t, stackOverflowSource, nil)
   141  	want := "runtime: goroutine stack exceeds 4194304-byte limit\nfatal error: stack overflow"
   142  	if !strings.HasPrefix(output, want) {
   143  		t.Fatalf("output does not start with %q:\n%s", want, output)
   144  	}
   145  }
   146  
   147  func TestThreadExhaustion(t *testing.T) {
   148  	output := executeTest(t, threadExhaustionSource, nil)
   149  	want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion"
   150  	if !strings.HasPrefix(output, want) {
   151  		t.Fatalf("output does not start with %q:\n%s", want, output)
   152  	}
   153  }
   154  
   155  func TestRecursivePanic(t *testing.T) {
   156  	output := executeTest(t, recursivePanicSource, nil)
   157  	want := `wrap: bad
   158  panic: again
   159  
   160  `
   161  	if !strings.HasPrefix(output, want) {
   162  		t.Fatalf("output does not start with %q:\n%s", want, output)
   163  	}
   164  
   165  }
   166  
   167  func TestGoexitCrash(t *testing.T) {
   168  	output := executeTest(t, goexitExitSource, nil)
   169  	want := "no goroutines (main called runtime.Goexit) - deadlock!"
   170  	if !strings.Contains(output, want) {
   171  		t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
   172  	}
   173  }
   174  
   175  func TestGoexitDefer(t *testing.T) {
   176  	c := make(chan struct{})
   177  	go func() {
   178  		defer func() {
   179  			r := recover()
   180  			if r != nil {
   181  				t.Errorf("non-nil recover during Goexit")
   182  			}
   183  			c <- struct{}{}
   184  		}()
   185  		runtime.Goexit()
   186  	}()
   187  	// Note: if the defer fails to run, we will get a deadlock here
   188  	<-c
   189  }
   190  
   191  func TestGoNil(t *testing.T) {
   192  	output := executeTest(t, goNilSource, nil)
   193  	want := "go of nil func value"
   194  	if !strings.Contains(output, want) {
   195  		t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
   196  	}
   197  }
   198  
   199  func TestMainGoroutineId(t *testing.T) {
   200  	output := executeTest(t, mainGoroutineIdSource, nil)
   201  	want := "panic: test\n\ngoroutine 1 [running]:\n"
   202  	if !strings.HasPrefix(output, want) {
   203  		t.Fatalf("output does not start with %q:\n%s", want, output)
   204  	}
   205  }
   206  
   207  func TestBreakpoint(t *testing.T) {
   208  	output := executeTest(t, breakpointSource, nil)
   209  	want := "runtime.Breakpoint()"
   210  	if !strings.Contains(output, want) {
   211  		t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
   212  	}
   213  }
   214  
   215  const crashSource = `
   216  package main
   217  
   218  import (
   219  	"fmt"
   220  	"runtime"
   221  )
   222  
   223  {{if .Cgo}}
   224  import "C"
   225  {{end}}
   226  
   227  func test(name string) {
   228  	defer func() {
   229  		if x := recover(); x != nil {
   230  			fmt.Printf(" recovered")
   231  		}
   232  		fmt.Printf(" done\n")
   233  	}()
   234  	fmt.Printf("%s:", name)
   235  	var s *string
   236  	_ = *s
   237  	fmt.Print("SHOULD NOT BE HERE")
   238  }
   239  
   240  func testInNewThread(name string) {
   241  	c := make(chan bool)
   242  	go func() {
   243  		runtime.LockOSThread()
   244  		test(name)
   245  		c <- true
   246  	}()
   247  	<-c
   248  }
   249  
   250  func main() {
   251  	runtime.LockOSThread()
   252  	test("main")
   253  	testInNewThread("new-thread")
   254  	testInNewThread("second-new-thread")
   255  	test("main-again")
   256  }
   257  `
   258  
   259  const simpleDeadlockSource = `
   260  package main
   261  func main() {
   262  	select {}
   263  }
   264  `
   265  
   266  const initDeadlockSource = `
   267  package main
   268  func init() {
   269  	select {}
   270  }
   271  func main() {
   272  }
   273  `
   274  
   275  const lockedDeadlockSource = `
   276  package main
   277  import "runtime"
   278  func main() {
   279  	runtime.LockOSThread()
   280  	select {}
   281  }
   282  `
   283  
   284  const lockedDeadlockSource2 = `
   285  package main
   286  import (
   287  	"runtime"
   288  	"time"
   289  )
   290  func main() {
   291  	go func() {
   292  		runtime.LockOSThread()
   293  		select {}
   294  	}()
   295  	time.Sleep(time.Millisecond)
   296  	select {}
   297  }
   298  `
   299  
   300  const goexitDeadlockSource = `
   301  package main
   302  import (
   303        "runtime"
   304  )
   305  
   306  func F() {
   307        for i := 0; i < 10; i++ {
   308        }
   309  }
   310  
   311  func main() {
   312        go F()
   313        go F()
   314        runtime.Goexit()
   315  }
   316  `
   317  
   318  const stackOverflowSource = `
   319  package main
   320  
   321  import "runtime/debug"
   322  
   323  func main() {
   324  	debug.SetMaxStack(4<<20)
   325  	f(make([]byte, 10))
   326  }
   327  
   328  func f(x []byte) byte {
   329  	var buf [64<<10]byte
   330  	return x[0] + f(buf[:])
   331  }
   332  `
   333  
   334  const threadExhaustionSource = `
   335  package main
   336  
   337  import (
   338  	"runtime"
   339  	"runtime/debug"
   340  )
   341  
   342  func main() {
   343  	debug.SetMaxThreads(10)
   344  	c := make(chan int)
   345  	for i := 0; i < 100; i++ {
   346  		go func() {
   347  			runtime.LockOSThread()
   348  			c <- 0
   349  			select{}
   350  		}()
   351  		<-c
   352  	}
   353  }
   354  `
   355  
   356  const recursivePanicSource = `
   357  package main
   358  
   359  import (
   360  	"fmt"
   361  )
   362  
   363  func main() {
   364  	func() {
   365  		defer func() {
   366  			fmt.Println(recover())
   367  		}()
   368  		var x [8192]byte
   369  		func(x [8192]byte) {
   370  			defer func() {
   371  				if err := recover(); err != nil {
   372  					panic("wrap: " + err.(string))
   373  				}
   374  			}()
   375  			panic("bad")
   376  		}(x)
   377  	}()
   378  	panic("again")
   379  }
   380  `
   381  
   382  const goexitExitSource = `
   383  package main
   384  
   385  import (
   386  	"runtime"
   387  	"time"
   388  )
   389  
   390  func main() {
   391  	go func() {
   392  		time.Sleep(time.Millisecond)
   393  	}()
   394  	i := 0
   395  	runtime.SetFinalizer(&i, func(p *int) {})
   396  	runtime.GC()
   397  	runtime.Goexit()
   398  }
   399  `
   400  
   401  const goNilSource = `
   402  package main
   403  
   404  func main() {
   405  	defer func() {
   406  		recover()
   407  	}()
   408  	var f func()
   409  	go f()
   410  	select{}
   411  }
   412  `
   413  
   414  const mainGoroutineIdSource = `
   415  package main
   416  func main() {
   417  	panic("test")
   418  }
   419  `
   420  
   421  const breakpointSource = `
   422  package main
   423  import "runtime"
   424  func main() {
   425  	runtime.Breakpoint()
   426  }
   427  `
   428  
   429  func TestGoexitInPanic(t *testing.T) {
   430  	// see issue 8774: this code used to trigger an infinite recursion
   431  	output := executeTest(t, goexitInPanicSource, nil)
   432  	want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
   433  	if !strings.HasPrefix(output, want) {
   434  		t.Fatalf("output does not start with %q:\n%s", want, output)
   435  	}
   436  }
   437  
   438  const goexitInPanicSource = `
   439  package main
   440  import "runtime"
   441  func main() {
   442  	go func() {
   443  		defer func() {
   444  			runtime.Goexit()
   445  		}()
   446  		panic("hello")
   447  	}()
   448  	runtime.Goexit()
   449  }
   450  `
   451  
   452  func TestPanicAfterGoexit(t *testing.T) {
   453  	// an uncaught panic should still work after goexit
   454  	output := executeTest(t, panicAfterGoexitSource, nil)
   455  	want := "panic: hello"
   456  	if !strings.HasPrefix(output, want) {
   457  		t.Fatalf("output does not start with %q:\n%s", want, output)
   458  	}
   459  }
   460  
   461  const panicAfterGoexitSource = `
   462  package main
   463  import "runtime"
   464  func main() {
   465  	defer func() {
   466  		panic("hello")
   467  	}()
   468  	runtime.Goexit()
   469  }
   470  `
   471  
   472  func TestRecoveredPanicAfterGoexit(t *testing.T) {
   473  	output := executeTest(t, recoveredPanicAfterGoexitSource, nil)
   474  	want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
   475  	if !strings.HasPrefix(output, want) {
   476  		t.Fatalf("output does not start with %q:\n%s", want, output)
   477  	}
   478  }
   479  
   480  const recoveredPanicAfterGoexitSource = `
   481  package main
   482  import "runtime"
   483  func main() {
   484  	defer func() {
   485  		defer func() {
   486  			r := recover()
   487  			if r == nil {
   488  				panic("bad recover")
   489  			}
   490  		}()
   491  		panic("hello")
   492  	}()
   493  	runtime.Goexit()
   494  }
   495  `
   496  
   497  func TestRecoverBeforePanicAfterGoexit(t *testing.T) {
   498  	// 1. defer a function that recovers
   499  	// 2. defer a function that panics
   500  	// 3. call goexit
   501  	// Goexit should run the #2 defer.  Its panic
   502  	// should be caught by the #1 defer, and execution
   503  	// should resume in the caller.  Like the Goexit
   504  	// never happened!
   505  	defer func() {
   506  		r := recover()
   507  		if r == nil {
   508  			panic("bad recover")
   509  		}
   510  	}()
   511  	defer func() {
   512  		panic("hello")
   513  	}()
   514  	runtime.Goexit()
   515  }