github.com/AESNooper/go/src@v0.0.0-20220218095104-b56a4ab1bbbb/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 //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris 6 7 package runtime_test 8 9 import ( 10 "bytes" 11 "internal/testenv" 12 "io" 13 "os" 14 "os/exec" 15 "runtime" 16 "runtime/debug" 17 "sync" 18 "syscall" 19 "testing" 20 "time" 21 "unsafe" 22 ) 23 24 // sigquit is the signal to send to kill a hanging testdata program. 25 // Send SIGQUIT to get a stack trace. 26 var sigquit = syscall.SIGQUIT 27 28 func init() { 29 if runtime.Sigisblocked(int(syscall.SIGQUIT)) { 30 // We can't use SIGQUIT to kill subprocesses because 31 // it's blocked. Use SIGKILL instead. See issue 32 // #19196 for an example of when this happens. 33 sigquit = syscall.SIGKILL 34 } 35 } 36 37 func TestBadOpen(t *testing.T) { 38 // make sure we get the correct error code if open fails. Same for 39 // read/write/close on the resulting -1 fd. See issue 10052. 40 nonfile := []byte("/notreallyafile") 41 fd := runtime.Open(&nonfile[0], 0, 0) 42 if fd != -1 { 43 t.Errorf("open(%q)=%d, want -1", nonfile, fd) 44 } 45 var buf [32]byte 46 r := runtime.Read(-1, unsafe.Pointer(&buf[0]), int32(len(buf))) 47 if got, want := r, -int32(syscall.EBADF); got != want { 48 t.Errorf("read()=%d, want %d", got, want) 49 } 50 w := runtime.Write(^uintptr(0), unsafe.Pointer(&buf[0]), int32(len(buf))) 51 if got, want := w, -int32(syscall.EBADF); got != want { 52 t.Errorf("write()=%d, want %d", got, want) 53 } 54 c := runtime.Close(-1) 55 if c != -1 { 56 t.Errorf("close()=%d, want -1", c) 57 } 58 } 59 60 func TestCrashDumpsAllThreads(t *testing.T) { 61 if *flagQuick { 62 t.Skip("-quick") 63 } 64 65 switch runtime.GOOS { 66 case "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "illumos", "solaris": 67 default: 68 t.Skipf("skipping; not supported on %v", runtime.GOOS) 69 } 70 71 if runtime.GOOS == "openbsd" && (runtime.GOARCH == "arm" || runtime.GOARCH == "mips64") { 72 // This may be ncpu < 2 related... 73 t.Skipf("skipping; test fails on %s/%s - see issue #42464", runtime.GOOS, runtime.GOARCH) 74 } 75 76 if runtime.Sigisblocked(int(syscall.SIGQUIT)) { 77 t.Skip("skipping; SIGQUIT is blocked, see golang.org/issue/19196") 78 } 79 80 testenv.MustHaveGoBuild(t) 81 82 exe, err := buildTestProg(t, "testprog") 83 if err != nil { 84 t.Fatal(err) 85 } 86 87 cmd := exec.Command(exe, "CrashDumpsAllThreads") 88 cmd = testenv.CleanCmdEnv(cmd) 89 cmd.Env = append(cmd.Env, 90 "GOTRACEBACK=crash", 91 // Set GOGC=off. Because of golang.org/issue/10958, the tight 92 // loops in the test program are not preemptible. If GC kicks 93 // in, it may lock up and prevent main from saying it's ready. 94 "GOGC=off", 95 // Set GODEBUG=asyncpreemptoff=1. If a thread is preempted 96 // when it receives SIGQUIT, it won't show the expected 97 // stack trace. See issue 35356. 98 "GODEBUG=asyncpreemptoff=1", 99 ) 100 101 var outbuf bytes.Buffer 102 cmd.Stdout = &outbuf 103 cmd.Stderr = &outbuf 104 105 rp, wp, err := os.Pipe() 106 if err != nil { 107 t.Fatal(err) 108 } 109 defer rp.Close() 110 111 cmd.ExtraFiles = []*os.File{wp} 112 113 if err := cmd.Start(); err != nil { 114 wp.Close() 115 t.Fatalf("starting program: %v", err) 116 } 117 118 if err := wp.Close(); err != nil { 119 t.Logf("closing write pipe: %v", err) 120 } 121 if _, err := rp.Read(make([]byte, 1)); err != nil { 122 t.Fatalf("reading from pipe: %v", err) 123 } 124 125 if err := cmd.Process.Signal(syscall.SIGQUIT); err != nil { 126 t.Fatalf("signal: %v", err) 127 } 128 129 // No point in checking the error return from Wait--we expect 130 // it to fail. 131 cmd.Wait() 132 133 // We want to see a stack trace for each thread. 134 // Before https://golang.org/cl/2811 running threads would say 135 // "goroutine running on other thread; stack unavailable". 136 out := outbuf.Bytes() 137 n := bytes.Count(out, []byte("main.crashDumpsAllThreadsLoop(")) 138 if n != 4 { 139 t.Errorf("found %d instances of main.loop; expected 4", n) 140 t.Logf("%s", out) 141 } 142 } 143 144 func TestPanicSystemstack(t *testing.T) { 145 // Test that GOTRACEBACK=crash prints both the system and user 146 // stack of other threads. 147 148 // The GOTRACEBACK=crash handler takes 0.1 seconds even if 149 // it's not writing a core file and potentially much longer if 150 // it is. Skip in short mode. 151 if testing.Short() { 152 t.Skip("Skipping in short mode (GOTRACEBACK=crash is slow)") 153 } 154 155 if runtime.Sigisblocked(int(syscall.SIGQUIT)) { 156 t.Skip("skipping; SIGQUIT is blocked, see golang.org/issue/19196") 157 } 158 159 t.Parallel() 160 cmd := exec.Command(os.Args[0], "testPanicSystemstackInternal") 161 cmd = testenv.CleanCmdEnv(cmd) 162 cmd.Env = append(cmd.Env, "GOTRACEBACK=crash") 163 pr, pw, err := os.Pipe() 164 if err != nil { 165 t.Fatal("creating pipe: ", err) 166 } 167 cmd.Stderr = pw 168 if err := cmd.Start(); err != nil { 169 t.Fatal("starting command: ", err) 170 } 171 defer cmd.Process.Wait() 172 defer cmd.Process.Kill() 173 if err := pw.Close(); err != nil { 174 t.Log("closing write pipe: ", err) 175 } 176 defer pr.Close() 177 178 // Wait for "x\nx\n" to indicate almost-readiness. 179 buf := make([]byte, 4) 180 _, err = io.ReadFull(pr, buf) 181 if err != nil || string(buf) != "x\nx\n" { 182 t.Fatal("subprocess failed; output:\n", string(buf)) 183 } 184 185 // The child blockers print "x\n" and then block on a lock. Receiving 186 // those bytes only indicates that the child is _about to block_. Since 187 // we don't have a way to know when it is fully blocked, sleep a bit to 188 // make us less likely to lose the race and signal before the child 189 // blocks. 190 time.Sleep(100 * time.Millisecond) 191 192 // Send SIGQUIT. 193 if err := cmd.Process.Signal(syscall.SIGQUIT); err != nil { 194 t.Fatal("signaling subprocess: ", err) 195 } 196 197 // Get traceback. 198 tb, err := io.ReadAll(pr) 199 if err != nil { 200 t.Fatal("reading traceback from pipe: ", err) 201 } 202 203 // Traceback should have two testPanicSystemstackInternal's 204 // and two blockOnSystemStackInternal's. 205 if bytes.Count(tb, []byte("testPanicSystemstackInternal")) != 2 { 206 t.Fatal("traceback missing user stack:\n", string(tb)) 207 } else if bytes.Count(tb, []byte("blockOnSystemStackInternal")) != 2 { 208 t.Fatal("traceback missing system stack:\n", string(tb)) 209 } 210 } 211 212 func init() { 213 if len(os.Args) >= 2 && os.Args[1] == "testPanicSystemstackInternal" { 214 // Complete any in-flight GCs and disable future ones. We're going to 215 // block goroutines on runtime locks, which aren't ever preemptible for the 216 // GC to scan them. 217 runtime.GC() 218 debug.SetGCPercent(-1) 219 // Get two threads running on the system stack with 220 // something recognizable in the stack trace. 221 runtime.GOMAXPROCS(2) 222 go testPanicSystemstackInternal() 223 testPanicSystemstackInternal() 224 } 225 } 226 227 func testPanicSystemstackInternal() { 228 runtime.BlockOnSystemStack() 229 os.Exit(1) // Should be unreachable. 230 } 231 232 func TestSignalExitStatus(t *testing.T) { 233 testenv.MustHaveGoBuild(t) 234 exe, err := buildTestProg(t, "testprog") 235 if err != nil { 236 t.Fatal(err) 237 } 238 err = testenv.CleanCmdEnv(exec.Command(exe, "SignalExitStatus")).Run() 239 if err == nil { 240 t.Error("test program succeeded unexpectedly") 241 } else if ee, ok := err.(*exec.ExitError); !ok { 242 t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err) 243 } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok { 244 t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys()) 245 } else if !ws.Signaled() || ws.Signal() != syscall.SIGTERM { 246 t.Errorf("got %v; expected SIGTERM", ee) 247 } 248 } 249 250 func TestSignalIgnoreSIGTRAP(t *testing.T) { 251 if runtime.GOOS == "openbsd" { 252 testenv.SkipFlaky(t, 49725) 253 } 254 255 output := runTestProg(t, "testprognet", "SignalIgnoreSIGTRAP") 256 want := "OK\n" 257 if output != want { 258 t.Fatalf("want %s, got %s\n", want, output) 259 } 260 } 261 262 func TestSignalDuringExec(t *testing.T) { 263 switch runtime.GOOS { 264 case "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd": 265 default: 266 t.Skipf("skipping test on %s", runtime.GOOS) 267 } 268 output := runTestProg(t, "testprognet", "SignalDuringExec") 269 want := "OK\n" 270 if output != want { 271 t.Fatalf("want %s, got %s\n", want, output) 272 } 273 } 274 275 func TestSignalM(t *testing.T) { 276 r, w, errno := runtime.Pipe() 277 if errno != 0 { 278 t.Fatal(syscall.Errno(errno)) 279 } 280 defer func() { 281 runtime.Close(r) 282 runtime.Close(w) 283 }() 284 runtime.Closeonexec(r) 285 runtime.Closeonexec(w) 286 287 var want, got int64 288 var wg sync.WaitGroup 289 ready := make(chan *runtime.M) 290 wg.Add(1) 291 go func() { 292 runtime.LockOSThread() 293 want, got = runtime.WaitForSigusr1(r, w, func(mp *runtime.M) { 294 ready <- mp 295 }) 296 runtime.UnlockOSThread() 297 wg.Done() 298 }() 299 waitingM := <-ready 300 runtime.SendSigusr1(waitingM) 301 302 timer := time.AfterFunc(time.Second, func() { 303 // Write 1 to tell WaitForSigusr1 that we timed out. 304 bw := byte(1) 305 if n := runtime.Write(uintptr(w), unsafe.Pointer(&bw), 1); n != 1 { 306 t.Errorf("pipe write failed: %d", n) 307 } 308 }) 309 defer timer.Stop() 310 311 wg.Wait() 312 if got == -1 { 313 t.Fatal("signalM signal not received") 314 } else if want != got { 315 t.Fatalf("signal sent to M %d, but received on M %d", want, got) 316 } 317 }