Source file
src/runtime/crash_cgo_test.go
Documentation: runtime
1
2
3
4
5
6
7
8 package runtime_test
9
10 import (
11 "bytes"
12 "fmt"
13 "internal/testenv"
14 "os"
15 "os/exec"
16 "runtime"
17 "strconv"
18 "strings"
19 "testing"
20 "time"
21 )
22
23 func TestCgoCrashHandler(t *testing.T) {
24 t.Parallel()
25 testCrashHandler(t, true)
26 }
27
28 func TestCgoSignalDeadlock(t *testing.T) {
29
30
31
32
33 if testing.Short() && runtime.GOOS == "windows" {
34 t.Skip("Skipping in short mode")
35 }
36 got := runTestProg(t, "testprogcgo", "CgoSignalDeadlock")
37 want := "OK\n"
38 if got != want {
39 t.Fatalf("expected %q, but got:\n%s", want, got)
40 }
41 }
42
43 func TestCgoTraceback(t *testing.T) {
44 t.Parallel()
45 got := runTestProg(t, "testprogcgo", "CgoTraceback")
46 want := "OK\n"
47 if got != want {
48 t.Fatalf("expected %q, but got:\n%s", want, got)
49 }
50 }
51
52 func TestCgoCallbackGC(t *testing.T) {
53 t.Parallel()
54 switch runtime.GOOS {
55 case "plan9", "windows":
56 t.Skipf("no pthreads on %s", runtime.GOOS)
57 }
58 if testing.Short() {
59 switch {
60 case runtime.GOOS == "dragonfly":
61 t.Skip("see golang.org/issue/11990")
62 case runtime.GOOS == "linux" && runtime.GOARCH == "arm":
63 t.Skip("too slow for arm builders")
64 case runtime.GOOS == "linux" && (runtime.GOARCH == "mips64" || runtime.GOARCH == "mips64le"):
65 t.Skip("too slow for mips64x builders")
66 }
67 }
68 got := runTestProg(t, "testprogcgo", "CgoCallbackGC")
69 want := "OK\n"
70 if got != want {
71 t.Fatalf("expected %q, but got:\n%s", want, got)
72 }
73 }
74
75 func TestCgoExternalThreadPanic(t *testing.T) {
76 t.Parallel()
77 if runtime.GOOS == "plan9" {
78 t.Skipf("no pthreads on %s", runtime.GOOS)
79 }
80 got := runTestProg(t, "testprogcgo", "CgoExternalThreadPanic")
81 want := "panic: BOOM"
82 if !strings.Contains(got, want) {
83 t.Fatalf("want failure containing %q. output:\n%s\n", want, got)
84 }
85 }
86
87 func TestCgoExternalThreadSIGPROF(t *testing.T) {
88 t.Parallel()
89
90 switch runtime.GOOS {
91 case "plan9", "windows":
92 t.Skipf("no pthreads on %s", runtime.GOOS)
93 }
94 if runtime.GOARCH == "ppc64" && runtime.GOOS == "linux" {
95
96
97 t.Skipf("no external linking on ppc64")
98 }
99
100 exe, err := buildTestProg(t, "testprogcgo", "-tags=threadprof")
101 if err != nil {
102 t.Fatal(err)
103 }
104
105 got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoExternalThreadSIGPROF")).CombinedOutput()
106 if err != nil {
107 t.Fatalf("exit status: %v\n%s", err, got)
108 }
109
110 if want := "OK\n"; string(got) != want {
111 t.Fatalf("expected %q, but got:\n%s", want, got)
112 }
113 }
114
115 func TestCgoExternalThreadSignal(t *testing.T) {
116 t.Parallel()
117
118 switch runtime.GOOS {
119 case "plan9", "windows":
120 t.Skipf("no pthreads on %s", runtime.GOOS)
121 }
122
123 exe, err := buildTestProg(t, "testprogcgo", "-tags=threadprof")
124 if err != nil {
125 t.Fatal(err)
126 }
127
128 got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoExternalThreadSIGPROF")).CombinedOutput()
129 if err != nil {
130 t.Fatalf("exit status: %v\n%s", err, got)
131 }
132
133 want := []byte("OK\n")
134 if !bytes.Equal(got, want) {
135 t.Fatalf("expected %q, but got:\n%s", want, got)
136 }
137 }
138
139 func TestCgoDLLImports(t *testing.T) {
140
141 if runtime.GOOS != "windows" {
142 t.Skip("skipping windows specific test")
143 }
144 got := runTestProg(t, "testprogcgo", "CgoDLLImportsMain")
145 want := "OK\n"
146 if got != want {
147 t.Fatalf("expected %q, but got %v", want, got)
148 }
149 }
150
151 func TestCgoExecSignalMask(t *testing.T) {
152 t.Parallel()
153
154 switch runtime.GOOS {
155 case "windows", "plan9":
156 t.Skipf("skipping signal mask test on %s", runtime.GOOS)
157 }
158 got := runTestProg(t, "testprogcgo", "CgoExecSignalMask", "GOTRACEBACK=system")
159 want := "OK\n"
160 if got != want {
161 t.Errorf("expected %q, got %v", want, got)
162 }
163 }
164
165 func TestEnsureDropM(t *testing.T) {
166 t.Parallel()
167
168 switch runtime.GOOS {
169 case "windows", "plan9":
170 t.Skipf("skipping dropm test on %s", runtime.GOOS)
171 }
172 got := runTestProg(t, "testprogcgo", "EnsureDropM")
173 want := "OK\n"
174 if got != want {
175 t.Errorf("expected %q, got %v", want, got)
176 }
177 }
178
179
180
181
182 func TestCgoCheckBytes(t *testing.T) {
183 t.Parallel()
184
185 testenv.MustHaveGoBuild(t)
186 exe, err := buildTestProg(t, "testprogcgo")
187 if err != nil {
188 t.Fatal(err)
189 }
190
191
192 const tries = 10
193 var tot1, tot2 time.Duration
194 for i := 0; i < tries; i++ {
195 cmd := testenv.CleanCmdEnv(exec.Command(exe, "CgoCheckBytes"))
196 cmd.Env = append(cmd.Env, "GODEBUG=cgocheck=0", fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i))
197
198 start := time.Now()
199 cmd.Run()
200 d1 := time.Since(start)
201
202 cmd = testenv.CleanCmdEnv(exec.Command(exe, "CgoCheckBytes"))
203 cmd.Env = append(cmd.Env, fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i))
204
205 start = time.Now()
206 cmd.Run()
207 d2 := time.Since(start)
208
209 if d1*20 > d2 {
210
211
212 return
213 }
214
215 tot1 += d1
216 tot2 += d2
217 }
218
219 t.Errorf("cgo check too slow: got %v, expected at most %v", tot2/tries, (tot1/tries)*20)
220 }
221
222 func TestCgoPanicDeadlock(t *testing.T) {
223 t.Parallel()
224
225 got := runTestProg(t, "testprogcgo", "CgoPanicDeadlock")
226 want := "panic: cgo error\n\n"
227 if !strings.HasPrefix(got, want) {
228 t.Fatalf("output does not start with %q:\n%s", want, got)
229 }
230 }
231
232 func TestCgoCCodeSIGPROF(t *testing.T) {
233 t.Parallel()
234 got := runTestProg(t, "testprogcgo", "CgoCCodeSIGPROF")
235 want := "OK\n"
236 if got != want {
237 t.Errorf("expected %q got %v", want, got)
238 }
239 }
240
241 func TestCgoCrashTraceback(t *testing.T) {
242 t.Parallel()
243 switch platform := runtime.GOOS + "/" + runtime.GOARCH; platform {
244 case "darwin/amd64":
245 case "linux/amd64":
246 case "linux/ppc64le":
247 default:
248 t.Skipf("not yet supported on %s", platform)
249 }
250 got := runTestProg(t, "testprogcgo", "CrashTraceback")
251 for i := 1; i <= 3; i++ {
252 if !strings.Contains(got, fmt.Sprintf("cgo symbolizer:%d", i)) {
253 t.Errorf("missing cgo symbolizer:%d", i)
254 }
255 }
256 }
257
258 func TestCgoCrashTracebackGo(t *testing.T) {
259 t.Parallel()
260 switch platform := runtime.GOOS + "/" + runtime.GOARCH; platform {
261 case "darwin/amd64":
262 case "linux/amd64":
263 case "linux/ppc64le":
264 default:
265 t.Skipf("not yet supported on %s", platform)
266 }
267 got := runTestProg(t, "testprogcgo", "CrashTracebackGo")
268 for i := 1; i <= 3; i++ {
269 want := fmt.Sprintf("main.h%d", i)
270 if !strings.Contains(got, want) {
271 t.Errorf("missing %s", want)
272 }
273 }
274 }
275
276 func TestCgoTracebackContext(t *testing.T) {
277 t.Parallel()
278 got := runTestProg(t, "testprogcgo", "TracebackContext")
279 want := "OK\n"
280 if got != want {
281 t.Errorf("expected %q got %v", want, got)
282 }
283 }
284
285 func TestCgoTracebackContextPreemption(t *testing.T) {
286 t.Parallel()
287 got := runTestProg(t, "testprogcgo", "TracebackContextPreemption")
288 want := "OK\n"
289 if got != want {
290 t.Errorf("expected %q got %v", want, got)
291 }
292 }
293
294 func testCgoPprof(t *testing.T, buildArg, runArg, top, bottom string) {
295 t.Parallel()
296 if runtime.GOOS != "linux" || (runtime.GOARCH != "amd64" && runtime.GOARCH != "ppc64le") {
297 t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
298 }
299 testenv.MustHaveGoRun(t)
300
301 exe, err := buildTestProg(t, "testprogcgo", buildArg)
302 if err != nil {
303 t.Fatal(err)
304 }
305
306
307
308
309 cmd := testenv.CleanCmdEnv(exec.Command(exe, runArg))
310 cmd.Env = append(cmd.Env, "GODEBUG=asyncpreemptoff=1")
311
312 got, err := cmd.CombinedOutput()
313 if err != nil {
314 if testenv.Builder() == "linux-amd64-alpine" {
315
316 t.Skipf("Skipping failing test on Alpine (golang.org/issue/18243). Ignoring error: %v", err)
317 }
318 t.Fatalf("%s\n\n%v", got, err)
319 }
320 fn := strings.TrimSpace(string(got))
321 defer os.Remove(fn)
322
323 for try := 0; try < 2; try++ {
324 cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-traces"))
325
326 if try == 0 {
327 cmd.Args = append(cmd.Args, exe, fn)
328 } else {
329 cmd.Args = append(cmd.Args, fn)
330 }
331
332 found := false
333 for i, e := range cmd.Env {
334 if strings.HasPrefix(e, "PPROF_TMPDIR=") {
335 cmd.Env[i] = "PPROF_TMPDIR=" + os.TempDir()
336 found = true
337 break
338 }
339 }
340 if !found {
341 cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
342 }
343
344 out, err := cmd.CombinedOutput()
345 t.Logf("%s:\n%s", cmd.Args, out)
346 if err != nil {
347 t.Error(err)
348 continue
349 }
350
351 trace := findTrace(string(out), top)
352 if len(trace) == 0 {
353 t.Errorf("%s traceback missing.", top)
354 continue
355 }
356 if trace[len(trace)-1] != bottom {
357 t.Errorf("invalid traceback origin: got=%v; want=[%s ... %s]", trace, top, bottom)
358 }
359 }
360 }
361
362 func TestCgoPprof(t *testing.T) {
363 testCgoPprof(t, "", "CgoPprof", "cpuHog", "runtime.main")
364 }
365
366 func TestCgoPprofPIE(t *testing.T) {
367 testCgoPprof(t, "-buildmode=pie", "CgoPprof", "cpuHog", "runtime.main")
368 }
369
370 func TestCgoPprofThread(t *testing.T) {
371 testCgoPprof(t, "", "CgoPprofThread", "cpuHogThread", "cpuHogThread2")
372 }
373
374 func TestCgoPprofThreadNoTraceback(t *testing.T) {
375 testCgoPprof(t, "", "CgoPprofThreadNoTraceback", "cpuHogThread", "runtime._ExternalCode")
376 }
377
378 func TestRaceProf(t *testing.T) {
379 if (runtime.GOOS != "linux" && runtime.GOOS != "freebsd") || runtime.GOARCH != "amd64" {
380 t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
381 }
382
383 testenv.MustHaveGoRun(t)
384
385
386
387 if testing.Short() {
388 t.Skip("skipping test in -short mode")
389 }
390
391 exe, err := buildTestProg(t, "testprogcgo", "-race")
392 if err != nil {
393 t.Fatal(err)
394 }
395
396 got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoRaceprof")).CombinedOutput()
397 if err != nil {
398 t.Fatal(err)
399 }
400 want := "OK\n"
401 if string(got) != want {
402 t.Errorf("expected %q got %s", want, got)
403 }
404 }
405
406 func TestRaceSignal(t *testing.T) {
407 t.Parallel()
408 if (runtime.GOOS != "linux" && runtime.GOOS != "freebsd") || runtime.GOARCH != "amd64" {
409 t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
410 }
411
412 testenv.MustHaveGoRun(t)
413
414
415
416 if testing.Short() {
417 t.Skip("skipping test in -short mode")
418 }
419
420 exe, err := buildTestProg(t, "testprogcgo", "-race")
421 if err != nil {
422 t.Fatal(err)
423 }
424
425 got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoRaceSignal")).CombinedOutput()
426 if err != nil {
427 t.Logf("%s\n", got)
428 t.Fatal(err)
429 }
430 want := "OK\n"
431 if string(got) != want {
432 t.Errorf("expected %q got %s", want, got)
433 }
434 }
435
436 func TestCgoNumGoroutine(t *testing.T) {
437 switch runtime.GOOS {
438 case "windows", "plan9":
439 t.Skipf("skipping numgoroutine test on %s", runtime.GOOS)
440 }
441 t.Parallel()
442 got := runTestProg(t, "testprogcgo", "NumGoroutine")
443 want := "OK\n"
444 if got != want {
445 t.Errorf("expected %q got %v", want, got)
446 }
447 }
448
449 func TestCatchPanic(t *testing.T) {
450 t.Parallel()
451 switch runtime.GOOS {
452 case "plan9", "windows":
453 t.Skipf("no signals on %s", runtime.GOOS)
454 case "darwin":
455 if runtime.GOARCH == "amd64" {
456 t.Skipf("crash() on darwin/amd64 doesn't raise SIGABRT")
457 }
458 }
459
460 testenv.MustHaveGoRun(t)
461
462 exe, err := buildTestProg(t, "testprogcgo")
463 if err != nil {
464 t.Fatal(err)
465 }
466
467 for _, early := range []bool{true, false} {
468 cmd := testenv.CleanCmdEnv(exec.Command(exe, "CgoCatchPanic"))
469
470 cmd.Env = append(cmd.Env, "GOTRACEBACK=crash")
471 if early {
472
473 cmd.Env = append(cmd.Env, "CGOCATCHPANIC_EARLY_HANDLER=1")
474 }
475 if out, err := cmd.CombinedOutput(); err != nil {
476 t.Errorf("testprogcgo CgoCatchPanic failed: %v\n%s", err, out)
477 }
478 }
479 }
480
481 func TestCgoLockOSThreadExit(t *testing.T) {
482 switch runtime.GOOS {
483 case "plan9", "windows":
484 t.Skipf("no pthreads on %s", runtime.GOOS)
485 }
486 t.Parallel()
487 testLockOSThreadExit(t, "testprogcgo")
488 }
489
490 func TestWindowsStackMemoryCgo(t *testing.T) {
491 if runtime.GOOS != "windows" {
492 t.Skip("skipping windows specific test")
493 }
494 testenv.SkipFlaky(t, 22575)
495 o := runTestProg(t, "testprogcgo", "StackMemory")
496 stackUsage, err := strconv.Atoi(o)
497 if err != nil {
498 t.Fatalf("Failed to read stack usage: %v", err)
499 }
500 if expected, got := 100<<10, stackUsage; got > expected {
501 t.Fatalf("expected < %d bytes of memory per thread, got %d", expected, got)
502 }
503 }
504
505 func TestSigStackSwapping(t *testing.T) {
506 switch runtime.GOOS {
507 case "plan9", "windows":
508 t.Skipf("no sigaltstack on %s", runtime.GOOS)
509 }
510 t.Parallel()
511 got := runTestProg(t, "testprogcgo", "SigStack")
512 want := "OK\n"
513 if got != want {
514 t.Errorf("expected %q got %v", want, got)
515 }
516 }
517
518 func TestCgoTracebackSigpanic(t *testing.T) {
519
520
521 if runtime.GOOS == "windows" {
522
523
524
525 t.Skip("no sigpanic in C on windows")
526 }
527 t.Parallel()
528 got := runTestProg(t, "testprogcgo", "TracebackSigpanic")
529 want := "runtime.sigpanic"
530 if !strings.Contains(got, want) {
531 t.Fatalf("want failure containing %q. output:\n%s\n", want, got)
532 }
533 nowant := "unexpected return pc"
534 if strings.Contains(got, nowant) {
535 t.Fatalf("failure incorrectly contains %q. output:\n%s\n", nowant, got)
536 }
537 }
538
539
540
541
542
543 func TestBigStackCallbackCgo(t *testing.T) {
544 if runtime.GOOS != "windows" {
545 t.Skip("skipping windows specific test")
546 }
547 t.Parallel()
548 got := runTestProg(t, "testprogcgo", "BigStack")
549 want := "OK\n"
550 if got != want {
551 t.Errorf("expected %q got %v", want, got)
552 }
553 }
554
555 func nextTrace(lines []string) ([]string, []string) {
556 var trace []string
557 for n, line := range lines {
558 if strings.HasPrefix(line, "---") {
559 return trace, lines[n+1:]
560 }
561 fields := strings.Fields(strings.TrimSpace(line))
562 if len(fields) == 0 {
563 continue
564 }
565
566 trace = append(trace, fields[len(fields)-1])
567 }
568 return nil, nil
569 }
570
571 func findTrace(text, top string) []string {
572 lines := strings.Split(text, "\n")
573 _, lines = nextTrace(lines)
574 for len(lines) > 0 {
575 var t []string
576 t, lines = nextTrace(lines)
577 if len(t) == 0 {
578 continue
579 }
580 if t[0] == top {
581 return t
582 }
583 }
584 return nil
585 }
586
587 func TestSegv(t *testing.T) {
588 switch runtime.GOOS {
589 case "plan9", "windows":
590 t.Skipf("no signals on %s", runtime.GOOS)
591 }
592
593 for _, test := range []string{"Segv", "SegvInCgo"} {
594 t.Run(test, func(t *testing.T) {
595 t.Parallel()
596 got := runTestProg(t, "testprogcgo", test)
597 t.Log(got)
598 if !strings.Contains(got, "SIGSEGV") {
599 t.Errorf("expected crash from signal")
600 }
601 })
602 }
603 }
604
605
606
607 func TestEINTR(t *testing.T) {
608 switch runtime.GOOS {
609 case "plan9", "windows":
610 t.Skipf("no EINTR on %s", runtime.GOOS)
611 case "linux":
612 if runtime.GOARCH == "386" {
613
614
615
616
617
618
619
620 t.Skip("skipping on linux-386; C sigaction does not preserve Go restorer")
621 }
622 }
623
624 t.Parallel()
625 output := runTestProg(t, "testprogcgo", "EINTR")
626 want := "OK\n"
627 if output != want {
628 t.Fatalf("want %s, got %s\n", want, output)
629 }
630 }
631
632
633 func TestNeedmDeadlock(t *testing.T) {
634 switch runtime.GOOS {
635 case "plan9", "windows":
636 t.Skipf("no signals on %s", runtime.GOOS)
637 }
638 output := runTestProg(t, "testprogcgo", "NeedmDeadlock")
639 want := "OK\n"
640 if output != want {
641 t.Fatalf("want %s, got %s\n", want, output)
642 }
643 }
644
View as plain text