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