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