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