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