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