Source file
src/runtime/crash_unix_test.go
Documentation: runtime
1
2
3
4
5
6
7
8 package runtime_test
9
10 import (
11 "bytes"
12 "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
26
27 var sigquit = syscall.SIGQUIT
28
29 func init() {
30 if runtime.Sigisblocked(int(syscall.SIGQUIT)) {
31
32
33
34 sigquit = syscall.SIGKILL
35 }
36 }
37
38 func TestBadOpen(t *testing.T) {
39
40
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
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
93
94
95 "GOGC=off",
96
97
98
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
131
132 cmd.Wait()
133
134
135
136
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
147
148
149
150
151
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
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
187
188
189
190
191 time.Sleep(100 * time.Millisecond)
192
193
194 if err := cmd.Process.Signal(syscall.SIGQUIT); err != nil {
195 t.Fatal("signaling subprocess: ", err)
196 }
197
198
199 tb, err := io.ReadAll(pr)
200 if err != nil {
201 t.Fatal("reading traceback from pipe: ", err)
202 }
203
204
205
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
216
217 runtime.GOMAXPROCS(2)
218 go testPanicSystemstackInternal()
219 testPanicSystemstackInternal()
220 }
221 }
222
223 func testPanicSystemstackInternal() {
224 runtime.BlockOnSystemStack()
225 os.Exit(1)
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
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 }
316
View as plain text