Source file
src/runtime/crash_unix_test.go
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 "sync"
18 "syscall"
19 "testing"
20 "time"
21 "unsafe"
22 )
23
24 func init() {
25 if runtime.Sigisblocked(int(syscall.SIGQUIT)) {
26
27
28
29 testenv.Sigquit = syscall.SIGKILL
30 }
31 }
32
33 func TestBadOpen(t *testing.T) {
34
35
36 nonfile := []byte("/notreallyafile")
37 fd := runtime.Open(&nonfile[0], 0, 0)
38 if fd != -1 {
39 t.Errorf("open(%q)=%d, want -1", nonfile, fd)
40 }
41 var buf [32]byte
42 r := runtime.Read(-1, unsafe.Pointer(&buf[0]), int32(len(buf)))
43 if got, want := r, -int32(syscall.EBADF); got != want {
44 t.Errorf("read()=%d, want %d", got, want)
45 }
46 w := runtime.Write(^uintptr(0), unsafe.Pointer(&buf[0]), int32(len(buf)))
47 if got, want := w, -int32(syscall.EBADF); got != want {
48 t.Errorf("write()=%d, want %d", got, want)
49 }
50 c := runtime.Close(-1)
51 if c != -1 {
52 t.Errorf("close()=%d, want -1", c)
53 }
54 }
55
56 func TestCrashDumpsAllThreads(t *testing.T) {
57 if *flagQuick {
58 t.Skip("-quick")
59 }
60
61 switch runtime.GOOS {
62 case "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "illumos", "solaris":
63 default:
64 t.Skipf("skipping; not supported on %v", runtime.GOOS)
65 }
66
67 if runtime.GOOS == "openbsd" && (runtime.GOARCH == "arm" || runtime.GOARCH == "mips64") {
68
69 t.Skipf("skipping; test fails on %s/%s - see issue #42464", runtime.GOOS, runtime.GOARCH)
70 }
71
72 if runtime.Sigisblocked(int(syscall.SIGQUIT)) {
73 t.Skip("skipping; SIGQUIT is blocked, see golang.org/issue/19196")
74 }
75
76 testenv.MustHaveGoBuild(t)
77
78 exe, err := buildTestProg(t, "testprog")
79 if err != nil {
80 t.Fatal(err)
81 }
82
83 cmd := exec.Command(exe, "CrashDumpsAllThreads")
84 cmd = testenv.CleanCmdEnv(cmd)
85 cmd.Env = append(cmd.Env,
86 "GOTRACEBACK=crash",
87
88
89
90 "GOGC=off",
91
92
93
94 "GODEBUG=asyncpreemptoff=1",
95 )
96
97 var outbuf bytes.Buffer
98 cmd.Stdout = &outbuf
99 cmd.Stderr = &outbuf
100
101 rp, wp, err := os.Pipe()
102 if err != nil {
103 t.Fatal(err)
104 }
105 defer rp.Close()
106
107 cmd.ExtraFiles = []*os.File{wp}
108
109 if err := cmd.Start(); err != nil {
110 wp.Close()
111 t.Fatalf("starting program: %v", err)
112 }
113
114 if err := wp.Close(); err != nil {
115 t.Logf("closing write pipe: %v", err)
116 }
117 if _, err := rp.Read(make([]byte, 1)); err != nil {
118 t.Fatalf("reading from pipe: %v", err)
119 }
120
121 if err := cmd.Process.Signal(syscall.SIGQUIT); err != nil {
122 t.Fatalf("signal: %v", err)
123 }
124
125
126
127 cmd.Wait()
128
129
130
131
132 out := outbuf.Bytes()
133 n := bytes.Count(out, []byte("main.crashDumpsAllThreadsLoop("))
134 if n != 4 {
135 t.Errorf("found %d instances of main.crashDumpsAllThreadsLoop; expected 4", n)
136 t.Logf("%s", out)
137 }
138 }
139
140 func TestPanicSystemstack(t *testing.T) {
141
142
143
144
145
146
147 if testing.Short() {
148 t.Skip("Skipping in short mode (GOTRACEBACK=crash is slow)")
149 }
150
151 if runtime.Sigisblocked(int(syscall.SIGQUIT)) {
152 t.Skip("skipping; SIGQUIT is blocked, see golang.org/issue/19196")
153 }
154
155 t.Parallel()
156 cmd := exec.Command(os.Args[0], "testPanicSystemstackInternal")
157 cmd = testenv.CleanCmdEnv(cmd)
158 cmd.Env = append(cmd.Env, "GOTRACEBACK=crash")
159 pr, pw, err := os.Pipe()
160 if err != nil {
161 t.Fatal("creating pipe: ", err)
162 }
163 cmd.Stderr = pw
164 if err := cmd.Start(); err != nil {
165 t.Fatal("starting command: ", err)
166 }
167 defer cmd.Process.Wait()
168 defer cmd.Process.Kill()
169 if err := pw.Close(); err != nil {
170 t.Log("closing write pipe: ", err)
171 }
172 defer pr.Close()
173
174
175 buf := make([]byte, 4)
176 _, err = io.ReadFull(pr, buf)
177 if err != nil || string(buf) != "x\nx\n" {
178 t.Fatal("subprocess failed; output:\n", string(buf))
179 }
180
181
182
183
184
185
186 time.Sleep(100 * time.Millisecond)
187
188
189 if err := cmd.Process.Signal(syscall.SIGQUIT); err != nil {
190 t.Fatal("signaling subprocess: ", err)
191 }
192
193
194 tb, err := io.ReadAll(pr)
195 if err != nil {
196 t.Fatal("reading traceback from pipe: ", err)
197 }
198
199
200
201 if bytes.Count(tb, []byte("testPanicSystemstackInternal")) != 2 {
202 t.Fatal("traceback missing user stack:\n", string(tb))
203 } else if bytes.Count(tb, []byte("blockOnSystemStackInternal")) != 2 {
204 t.Fatal("traceback missing system stack:\n", string(tb))
205 }
206 }
207
208 func init() {
209 if len(os.Args) >= 2 && os.Args[1] == "testPanicSystemstackInternal" {
210
211
212
213 runtime.GC()
214 debug.SetGCPercent(-1)
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 testenv.SkipFlaky(t, 49725)
249 }
250
251 output := runTestProg(t, "testprognet", "SignalIgnoreSIGTRAP")
252 want := "OK\n"
253 if output != want {
254 t.Fatalf("want %s, got %s\n", want, output)
255 }
256 }
257
258 func TestSignalDuringExec(t *testing.T) {
259 switch runtime.GOOS {
260 case "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd":
261 default:
262 t.Skipf("skipping test on %s", runtime.GOOS)
263 }
264 output := runTestProg(t, "testprognet", "SignalDuringExec")
265 want := "OK\n"
266 if output != want {
267 t.Fatalf("want %s, got %s\n", want, output)
268 }
269 }
270
271 func TestSignalM(t *testing.T) {
272 r, w, errno := runtime.Pipe()
273 if errno != 0 {
274 t.Fatal(syscall.Errno(errno))
275 }
276 defer func() {
277 runtime.Close(r)
278 runtime.Close(w)
279 }()
280 runtime.Closeonexec(r)
281 runtime.Closeonexec(w)
282
283 var want, got int64
284 var wg sync.WaitGroup
285 ready := make(chan *runtime.M)
286 wg.Add(1)
287 go func() {
288 runtime.LockOSThread()
289 want, got = runtime.WaitForSigusr1(r, w, func(mp *runtime.M) {
290 ready <- mp
291 })
292 runtime.UnlockOSThread()
293 wg.Done()
294 }()
295 waitingM := <-ready
296 runtime.SendSigusr1(waitingM)
297
298 timer := time.AfterFunc(time.Second, func() {
299
300 bw := byte(1)
301 if n := runtime.Write(uintptr(w), unsafe.Pointer(&bw), 1); n != 1 {
302 t.Errorf("pipe write failed: %d", n)
303 }
304 })
305 defer timer.Stop()
306
307 wg.Wait()
308 if got == -1 {
309 t.Fatal("signalM signal not received")
310 } else if want != got {
311 t.Fatalf("signal sent to M %d, but received on M %d", want, got)
312 }
313 }
314
View as plain text