github.com/panjjo/go@v0.0.0-20161104043856-d62b31386338/src/runtime/crash_unix_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 // +build darwin dragonfly freebsd linux netbsd openbsd solaris 6 7 package runtime_test 8 9 import ( 10 "bytes" 11 "internal/testenv" 12 "io/ioutil" 13 "os" 14 "os/exec" 15 "path/filepath" 16 "runtime" 17 "strings" 18 "syscall" 19 "testing" 20 ) 21 22 // sigquit is the signal to send to kill a hanging testdata program. 23 // Send SIGQUIT to get a stack trace. 24 var sigquit = syscall.SIGQUIT 25 26 func TestCrashDumpsAllThreads(t *testing.T) { 27 switch runtime.GOOS { 28 case "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris": 29 default: 30 t.Skipf("skipping; not supported on %v", runtime.GOOS) 31 } 32 33 // We don't use executeTest because we need to kill the 34 // program while it is running. 35 36 testenv.MustHaveGoBuild(t) 37 38 checkStaleRuntime(t) 39 40 dir, err := ioutil.TempDir("", "go-build") 41 if err != nil { 42 t.Fatalf("failed to create temp directory: %v", err) 43 } 44 defer os.RemoveAll(dir) 45 46 if err := ioutil.WriteFile(filepath.Join(dir, "main.go"), []byte(crashDumpsAllThreadsSource), 0666); err != nil { 47 t.Fatalf("failed to create Go file: %v", err) 48 } 49 50 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe") 51 cmd.Dir = dir 52 out, err := testEnv(cmd).CombinedOutput() 53 if err != nil { 54 t.Fatalf("building source: %v\n%s", err, out) 55 } 56 57 cmd = exec.Command(filepath.Join(dir, "a.exe")) 58 cmd = testEnv(cmd) 59 cmd.Env = append(cmd.Env, "GOTRACEBACK=crash") 60 61 // Set GOGC=off. Because of golang.org/issue/10958, the tight 62 // loops in the test program are not preemptible. If GC kicks 63 // in, it may lock up and prevent main from saying it's ready. 64 newEnv := []string{} 65 for _, s := range cmd.Env { 66 if !strings.HasPrefix(s, "GOGC=") { 67 newEnv = append(newEnv, s) 68 } 69 } 70 cmd.Env = append(newEnv, "GOGC=off") 71 72 var outbuf bytes.Buffer 73 cmd.Stdout = &outbuf 74 cmd.Stderr = &outbuf 75 76 rp, wp, err := os.Pipe() 77 if err != nil { 78 t.Fatal(err) 79 } 80 cmd.ExtraFiles = []*os.File{wp} 81 82 if err := cmd.Start(); err != nil { 83 t.Fatalf("starting program: %v", err) 84 } 85 86 if err := wp.Close(); err != nil { 87 t.Logf("closing write pipe: %v", err) 88 } 89 if _, err := rp.Read(make([]byte, 1)); err != nil { 90 t.Fatalf("reading from pipe: %v", err) 91 } 92 93 if err := cmd.Process.Signal(syscall.SIGQUIT); err != nil { 94 t.Fatalf("signal: %v", err) 95 } 96 97 // No point in checking the error return from Wait--we expect 98 // it to fail. 99 cmd.Wait() 100 101 // We want to see a stack trace for each thread. 102 // Before https://golang.org/cl/2811 running threads would say 103 // "goroutine running on other thread; stack unavailable". 104 out = outbuf.Bytes() 105 n := bytes.Count(out, []byte("main.loop(")) 106 if n != 4 { 107 t.Errorf("found %d instances of main.loop; expected 4", n) 108 t.Logf("%s", out) 109 } 110 } 111 112 const crashDumpsAllThreadsSource = ` 113 package main 114 115 import ( 116 "fmt" 117 "os" 118 "runtime" 119 ) 120 121 func main() { 122 const count = 4 123 runtime.GOMAXPROCS(count + 1) 124 125 chans := make([]chan bool, count) 126 for i := range chans { 127 chans[i] = make(chan bool) 128 go loop(i, chans[i]) 129 } 130 131 // Wait for all the goroutines to start executing. 132 for _, c := range chans { 133 <-c 134 } 135 136 // Tell our parent that all the goroutines are executing. 137 if _, err := os.NewFile(3, "pipe").WriteString("x"); err != nil { 138 fmt.Fprintf(os.Stderr, "write to pipe failed: %v\n", err) 139 os.Exit(2) 140 } 141 142 select {} 143 } 144 145 func loop(i int, c chan bool) { 146 close(c) 147 for { 148 for j := 0; j < 0x7fffffff; j++ { 149 } 150 } 151 } 152 ` 153 154 func TestSignalExitStatus(t *testing.T) { 155 testenv.MustHaveGoBuild(t) 156 exe, err := buildTestProg(t, "testprog") 157 if err != nil { 158 t.Fatal(err) 159 } 160 err = testEnv(exec.Command(exe, "SignalExitStatus")).Run() 161 if err == nil { 162 t.Error("test program succeeded unexpectedly") 163 } else if ee, ok := err.(*exec.ExitError); !ok { 164 t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err) 165 } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok { 166 t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys()) 167 } else if !ws.Signaled() || ws.Signal() != syscall.SIGTERM { 168 t.Errorf("got %v; expected SIGTERM", ee) 169 } 170 } 171 172 func TestSignalIgnoreSIGTRAP(t *testing.T) { 173 output := runTestProg(t, "testprognet", "SignalIgnoreSIGTRAP") 174 want := "OK\n" 175 if output != want { 176 t.Fatalf("want %s, got %s\n", want, output) 177 } 178 }