github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/pkg/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  	"strings"
    13  	"testing"
    14  	"text/template"
    15  )
    16  
    17  // testEnv excludes GOGCTRACE from the environment
    18  // to prevent its output from breaking tests that
    19  // are trying to parse other command output.
    20  func testEnv(cmd *exec.Cmd) *exec.Cmd {
    21  	if cmd.Env != nil {
    22  		panic("environment already set")
    23  	}
    24  	for _, env := range os.Environ() {
    25  		if strings.HasPrefix(env, "GOGCTRACE=") {
    26  			continue
    27  		}
    28  		cmd.Env = append(cmd.Env, env)
    29  	}
    30  	return cmd
    31  }
    32  
    33  func executeTest(t *testing.T, templ string, data interface{}) string {
    34  	checkStaleRuntime(t)
    35  
    36  	st := template.Must(template.New("crashSource").Parse(templ))
    37  
    38  	dir, err := ioutil.TempDir("", "go-build")
    39  	if err != nil {
    40  		t.Fatalf("failed to create temp directory: %v", err)
    41  	}
    42  	defer os.RemoveAll(dir)
    43  
    44  	src := filepath.Join(dir, "main.go")
    45  	f, err := os.Create(src)
    46  	if err != nil {
    47  		t.Fatalf("failed to create %v: %v", src, err)
    48  	}
    49  	err = st.Execute(f, data)
    50  	if err != nil {
    51  		f.Close()
    52  		t.Fatalf("failed to execute template: %v", err)
    53  	}
    54  	f.Close()
    55  
    56  	got, _ := testEnv(exec.Command("go", "run", src)).CombinedOutput()
    57  	return string(got)
    58  }
    59  
    60  func checkStaleRuntime(t *testing.T) {
    61  	// 'go run' uses the installed copy of runtime.a, which may be out of date.
    62  	out, err := testEnv(exec.Command("go", "list", "-f", "{{.Stale}}", "runtime")).CombinedOutput()
    63  	if err != nil {
    64  		t.Fatalf("failed to execute 'go list': %v\n%v", err, string(out))
    65  	}
    66  	if string(out) != "false\n" {
    67  		t.Fatalf("Stale runtime.a. Run 'go install runtime'.")
    68  	}
    69  }
    70  
    71  func testCrashHandler(t *testing.T, cgo bool) {
    72  	type crashTest struct {
    73  		Cgo bool
    74  	}
    75  	got := executeTest(t, crashSource, &crashTest{Cgo: cgo})
    76  	want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n"
    77  	if got != want {
    78  		t.Fatalf("expected %q, but got %q", want, got)
    79  	}
    80  }
    81  
    82  func TestCrashHandler(t *testing.T) {
    83  	testCrashHandler(t, false)
    84  }
    85  
    86  func testDeadlock(t *testing.T, source string) {
    87  	got := executeTest(t, source, nil)
    88  	want := "fatal error: all goroutines are asleep - deadlock!\n"
    89  	if !strings.HasPrefix(got, want) {
    90  		t.Fatalf("expected %q, but got %q", want, got)
    91  	}
    92  }
    93  
    94  func TestSimpleDeadlock(t *testing.T) {
    95  	testDeadlock(t, simpleDeadlockSource)
    96  }
    97  
    98  func TestInitDeadlock(t *testing.T) {
    99  	testDeadlock(t, initDeadlockSource)
   100  }
   101  
   102  func TestLockedDeadlock(t *testing.T) {
   103  	testDeadlock(t, lockedDeadlockSource)
   104  }
   105  
   106  func TestLockedDeadlock2(t *testing.T) {
   107  	testDeadlock(t, lockedDeadlockSource2)
   108  }
   109  
   110  func TestGoexitDeadlock(t *testing.T) {
   111  	got := executeTest(t, goexitDeadlockSource, nil)
   112  	want := ""
   113  	if got != want {
   114  		t.Fatalf("expected %q, but got %q", want, got)
   115  	}
   116  }
   117  
   118  const crashSource = `
   119  package main
   120  
   121  import (
   122  	"fmt"
   123  	"runtime"
   124  )
   125  
   126  {{if .Cgo}}
   127  import "C"
   128  {{end}}
   129  
   130  func test(name string) {
   131  	defer func() {
   132  		if x := recover(); x != nil {
   133  			fmt.Printf(" recovered")
   134  		}
   135  		fmt.Printf(" done\n")
   136  	}()
   137  	fmt.Printf("%s:", name)
   138  	var s *string
   139  	_ = *s
   140  	fmt.Print("SHOULD NOT BE HERE")
   141  }
   142  
   143  func testInNewThread(name string) {
   144  	c := make(chan bool)
   145  	go func() {
   146  		runtime.LockOSThread()
   147  		test(name)
   148  		c <- true
   149  	}()
   150  	<-c
   151  }
   152  
   153  func main() {
   154  	runtime.LockOSThread()
   155  	test("main")
   156  	testInNewThread("new-thread")
   157  	testInNewThread("second-new-thread")
   158  	test("main-again")
   159  }
   160  `
   161  
   162  const simpleDeadlockSource = `
   163  package main
   164  func main() {
   165  	select {}
   166  }
   167  `
   168  
   169  const initDeadlockSource = `
   170  package main
   171  func init() {
   172  	select {}
   173  }
   174  func main() {
   175  }
   176  `
   177  
   178  const lockedDeadlockSource = `
   179  package main
   180  import "runtime"
   181  func main() {
   182  	runtime.LockOSThread()
   183  	select {}
   184  }
   185  `
   186  
   187  const lockedDeadlockSource2 = `
   188  package main
   189  import (
   190  	"runtime"
   191  	"time"
   192  )
   193  func main() {
   194  	go func() {
   195  		runtime.LockOSThread()
   196  		select {}
   197  	}()
   198  	time.Sleep(time.Millisecond)
   199  	select {}
   200  }
   201  `
   202  
   203  const goexitDeadlockSource = `
   204  package main
   205  import (
   206        "runtime"
   207  )
   208  
   209  func F() {
   210        for i := 0; i < 10; i++ {
   211        }
   212  }
   213  
   214  func main() {
   215        go F()
   216        go F()
   217        runtime.Goexit()
   218  }
   219  `