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