Source file
src/runtime/crash_unix_test.go
Documentation: runtime
1
2
3
4
5
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
28
29
30 testenv.Sigquit = syscall.SIGKILL
31 }
32 }
33
34 func TestBadOpen(t *testing.T) {
35
36
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
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
81
82
83
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()
95 cmd.Env = append(cmd.Env,
96 "GOTRACEBACK=crash",
97
98
99
100 "GOGC=off",
101
102
103
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
136
137 cmd.Wait()
138
139
140
141
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
152
153
154
155
156
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()
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
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
193
194
195
196
197 time.Sleep(100 * time.Millisecond)
198
199
200 if err := cmd.Process.Signal(syscall.SIGQUIT); err != nil {
201 t.Fatal("signaling subprocess: ", err)
202 }
203
204
205 tb, err := io.ReadAll(pr)
206 if err != nil {
207 t.Fatal("reading traceback from pipe: ", err)
208 }
209
210
211
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
221
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
230
231
232 runtime.GC()
233 debug.SetGCPercent(-1)
234
235
236 runtime.GOMAXPROCS(2)
237 go testPanicSystemstackInternal()
238 testPanicSystemstackInternal()
239 }
240 }
241
242 func testPanicSystemstackInternal() {
243 runtime.BlockOnSystemStack()
244 os.Exit(1)
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
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 }
333
View as plain text