github.com/ActiveState/go@v0.0.0-20170614201249-0b81c023a722/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" 13 "io/ioutil" 14 "os" 15 "os/exec" 16 "path/filepath" 17 "runtime" 18 "strings" 19 "syscall" 20 "testing" 21 ) 22 23 // sigquit is the signal to send to kill a hanging testdata program. 24 // Send SIGQUIT to get a stack trace. 25 var sigquit = syscall.SIGQUIT 26 27 func TestCrashDumpsAllThreads(t *testing.T) { 28 switch runtime.GOOS { 29 case "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris": 30 default: 31 t.Skipf("skipping; not supported on %v", runtime.GOOS) 32 } 33 34 // We don't use executeTest because we need to kill the 35 // program while it is running. 36 37 testenv.MustHaveGoBuild(t) 38 39 checkStaleRuntime(t) 40 41 t.Parallel() 42 43 dir, err := ioutil.TempDir("", "go-build") 44 if err != nil { 45 t.Fatalf("failed to create temp directory: %v", err) 46 } 47 defer os.RemoveAll(dir) 48 49 if err := ioutil.WriteFile(filepath.Join(dir, "main.go"), []byte(crashDumpsAllThreadsSource), 0666); err != nil { 50 t.Fatalf("failed to create Go file: %v", err) 51 } 52 53 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe") 54 cmd.Dir = dir 55 out, err := testEnv(cmd).CombinedOutput() 56 if err != nil { 57 t.Fatalf("building source: %v\n%s", err, out) 58 } 59 60 cmd = exec.Command(filepath.Join(dir, "a.exe")) 61 cmd = testEnv(cmd) 62 cmd.Env = append(cmd.Env, "GOTRACEBACK=crash") 63 64 // Set GOGC=off. Because of golang.org/issue/10958, the tight 65 // loops in the test program are not preemptible. If GC kicks 66 // in, it may lock up and prevent main from saying it's ready. 67 newEnv := []string{} 68 for _, s := range cmd.Env { 69 if !strings.HasPrefix(s, "GOGC=") { 70 newEnv = append(newEnv, s) 71 } 72 } 73 cmd.Env = append(newEnv, "GOGC=off") 74 75 var outbuf bytes.Buffer 76 cmd.Stdout = &outbuf 77 cmd.Stderr = &outbuf 78 79 rp, wp, err := os.Pipe() 80 if err != nil { 81 t.Fatal(err) 82 } 83 cmd.ExtraFiles = []*os.File{wp} 84 85 if err := cmd.Start(); err != nil { 86 t.Fatalf("starting program: %v", err) 87 } 88 89 if err := wp.Close(); err != nil { 90 t.Logf("closing write pipe: %v", err) 91 } 92 if _, err := rp.Read(make([]byte, 1)); err != nil { 93 t.Fatalf("reading from pipe: %v", err) 94 } 95 96 if err := cmd.Process.Signal(syscall.SIGQUIT); err != nil { 97 t.Fatalf("signal: %v", err) 98 } 99 100 // No point in checking the error return from Wait--we expect 101 // it to fail. 102 cmd.Wait() 103 104 // We want to see a stack trace for each thread. 105 // Before https://golang.org/cl/2811 running threads would say 106 // "goroutine running on other thread; stack unavailable". 107 out = outbuf.Bytes() 108 n := bytes.Count(out, []byte("main.loop(")) 109 if n != 4 { 110 t.Errorf("found %d instances of main.loop; expected 4", n) 111 t.Logf("%s", out) 112 } 113 } 114 115 const crashDumpsAllThreadsSource = ` 116 package main 117 118 import ( 119 "fmt" 120 "os" 121 "runtime" 122 ) 123 124 func main() { 125 const count = 4 126 runtime.GOMAXPROCS(count + 1) 127 128 chans := make([]chan bool, count) 129 for i := range chans { 130 chans[i] = make(chan bool) 131 go loop(i, chans[i]) 132 } 133 134 // Wait for all the goroutines to start executing. 135 for _, c := range chans { 136 <-c 137 } 138 139 // Tell our parent that all the goroutines are executing. 140 if _, err := os.NewFile(3, "pipe").WriteString("x"); err != nil { 141 fmt.Fprintf(os.Stderr, "write to pipe failed: %v\n", err) 142 os.Exit(2) 143 } 144 145 select {} 146 } 147 148 func loop(i int, c chan bool) { 149 close(c) 150 for { 151 for j := 0; j < 0x7fffffff; j++ { 152 } 153 } 154 } 155 ` 156 157 func TestPanicSystemstack(t *testing.T) { 158 // Test that GOTRACEBACK=crash prints both the system and user 159 // stack of other threads. 160 161 // The GOTRACEBACK=crash handler takes 0.1 seconds even if 162 // it's not writing a core file and potentially much longer if 163 // it is. Skip in short mode. 164 if testing.Short() { 165 t.Skip("Skipping in short mode (GOTRACEBACK=crash is slow)") 166 } 167 168 t.Parallel() 169 cmd := exec.Command(os.Args[0], "testPanicSystemstackInternal") 170 cmd = testEnv(cmd) 171 cmd.Env = append(cmd.Env, "GOTRACEBACK=crash") 172 pr, pw, err := os.Pipe() 173 if err != nil { 174 t.Fatal("creating pipe: ", err) 175 } 176 cmd.Stderr = pw 177 if err := cmd.Start(); err != nil { 178 t.Fatal("starting command: ", err) 179 } 180 defer cmd.Process.Wait() 181 defer cmd.Process.Kill() 182 if err := pw.Close(); err != nil { 183 t.Log("closing write pipe: ", err) 184 } 185 defer pr.Close() 186 187 // Wait for "x\nx\n" to indicate readiness. 188 buf := make([]byte, 4) 189 _, err = io.ReadFull(pr, buf) 190 if err != nil || string(buf) != "x\nx\n" { 191 t.Fatal("subprocess failed; output:\n", string(buf)) 192 } 193 194 // Send SIGQUIT. 195 if err := cmd.Process.Signal(syscall.SIGQUIT); err != nil { 196 t.Fatal("signaling subprocess: ", err) 197 } 198 199 // Get traceback. 200 tb, err := ioutil.ReadAll(pr) 201 if err != nil { 202 t.Fatal("reading traceback from pipe: ", err) 203 } 204 205 // Traceback should have two testPanicSystemstackInternal's 206 // and two blockOnSystemStackInternal's. 207 if bytes.Count(tb, []byte("testPanicSystemstackInternal")) != 2 { 208 t.Fatal("traceback missing user stack:\n", string(tb)) 209 } else if bytes.Count(tb, []byte("blockOnSystemStackInternal")) != 2 { 210 t.Fatal("traceback missing system stack:\n", string(tb)) 211 } 212 } 213 214 func init() { 215 if len(os.Args) >= 2 && os.Args[1] == "testPanicSystemstackInternal" { 216 // Get two threads running on the system stack with 217 // something recognizable in the stack trace. 218 runtime.GOMAXPROCS(2) 219 go testPanicSystemstackInternal() 220 testPanicSystemstackInternal() 221 } 222 } 223 224 func testPanicSystemstackInternal() { 225 runtime.BlockOnSystemStack() 226 os.Exit(1) // Should be unreachable. 227 } 228 229 func TestSignalExitStatus(t *testing.T) { 230 testenv.MustHaveGoBuild(t) 231 exe, err := buildTestProg(t, "testprog") 232 if err != nil { 233 t.Fatal(err) 234 } 235 err = testEnv(exec.Command(exe, "SignalExitStatus")).Run() 236 if err == nil { 237 t.Error("test program succeeded unexpectedly") 238 } else if ee, ok := err.(*exec.ExitError); !ok { 239 t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err) 240 } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok { 241 t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys()) 242 } else if !ws.Signaled() || ws.Signal() != syscall.SIGTERM { 243 t.Errorf("got %v; expected SIGTERM", ee) 244 } 245 } 246 247 func TestSignalIgnoreSIGTRAP(t *testing.T) { 248 output := runTestProg(t, "testprognet", "SignalIgnoreSIGTRAP") 249 want := "OK\n" 250 if output != want { 251 t.Fatalf("want %s, got %s\n", want, output) 252 } 253 } 254 255 func TestSignalDuringExec(t *testing.T) { 256 switch runtime.GOOS { 257 case "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd": 258 default: 259 t.Skipf("skipping test on %s", runtime.GOOS) 260 } 261 output := runTestProg(t, "testprognet", "SignalDuringExec") 262 want := "OK\n" 263 if output != want { 264 t.Fatalf("want %s, got %s\n", want, output) 265 } 266 }