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/ppc64le":
238 default:
239 t.Skipf("not yet supported on %s", platform)
240 }
241 got := runTestProg(t, "testprogcgo", "CrashTraceback")
242 for i := 1; i <= 3; i++ {
243 if !strings.Contains(got, fmt.Sprintf("cgo symbolizer:%d", i)) {
244 t.Errorf("missing cgo symbolizer:%d", i)
245 }
246 }
247 }
248
249 func TestCgoCrashTracebackGo(t *testing.T) {
250 t.Parallel()
251 switch platform := runtime.GOOS + "/" + runtime.GOARCH; platform {
252 case "darwin/amd64":
253 case "linux/amd64":
254 case "linux/ppc64le":
255 default:
256 t.Skipf("not yet supported on %s", platform)
257 }
258 got := runTestProg(t, "testprogcgo", "CrashTracebackGo")
259 for i := 1; i <= 3; i++ {
260 want := fmt.Sprintf("main.h%d", i)
261 if !strings.Contains(got, want) {
262 t.Errorf("missing %s", want)
263 }
264 }
265 }
266
267 func TestCgoTracebackContext(t *testing.T) {
268 t.Parallel()
269 got := runTestProg(t, "testprogcgo", "TracebackContext")
270 want := "OK\n"
271 if got != want {
272 t.Errorf("expected %q got %v", want, got)
273 }
274 }
275
276 func TestCgoTracebackContextPreemption(t *testing.T) {
277 t.Parallel()
278 got := runTestProg(t, "testprogcgo", "TracebackContextPreemption")
279 want := "OK\n"
280 if got != want {
281 t.Errorf("expected %q got %v", want, got)
282 }
283 }
284
285 func testCgoPprof(t *testing.T, buildArg, runArg, top, bottom string) {
286 t.Parallel()
287 if runtime.GOOS != "linux" || (runtime.GOARCH != "amd64" && runtime.GOARCH != "ppc64le") {
288 t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
289 }
290 testenv.MustHaveGoRun(t)
291
292 exe, err := buildTestProg(t, "testprogcgo", buildArg)
293 if err != nil {
294 t.Fatal(err)
295 }
296
297 cmd := testenv.CleanCmdEnv(exec.Command(exe, runArg))
298 got, err := cmd.CombinedOutput()
299 if err != nil {
300 if testenv.Builder() == "linux-amd64-alpine" {
301
302 t.Skipf("Skipping failing test on Alpine (golang.org/issue/18243). Ignoring error: %v", err)
303 }
304 t.Fatalf("%s\n\n%v", got, err)
305 }
306 fn := strings.TrimSpace(string(got))
307 defer os.Remove(fn)
308
309 for try := 0; try < 2; try++ {
310 cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-tagignore=ignore", "-traces"))
311
312 if try == 0 {
313 cmd.Args = append(cmd.Args, exe, fn)
314 } else {
315 cmd.Args = append(cmd.Args, fn)
316 }
317
318 found := false
319 for i, e := range cmd.Env {
320 if strings.HasPrefix(e, "PPROF_TMPDIR=") {
321 cmd.Env[i] = "PPROF_TMPDIR=" + os.TempDir()
322 found = true
323 break
324 }
325 }
326 if !found {
327 cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
328 }
329
330 out, err := cmd.CombinedOutput()
331 t.Logf("%s:\n%s", cmd.Args, out)
332 if err != nil {
333 t.Error(err)
334 continue
335 }
336
337 trace := findTrace(string(out), top)
338 if len(trace) == 0 {
339 t.Errorf("%s traceback missing.", top)
340 continue
341 }
342 if trace[len(trace)-1] != bottom {
343 t.Errorf("invalid traceback origin: got=%v; want=[%s ... %s]", trace, top, bottom)
344 }
345 }
346 }
347
348 func TestCgoPprof(t *testing.T) {
349 testCgoPprof(t, "", "CgoPprof", "cpuHog", "runtime.main")
350 }
351
352 func TestCgoPprofPIE(t *testing.T) {
353 testCgoPprof(t, "-buildmode=pie", "CgoPprof", "cpuHog", "runtime.main")
354 }
355
356 func TestCgoPprofThread(t *testing.T) {
357 testCgoPprof(t, "", "CgoPprofThread", "cpuHogThread", "cpuHogThread2")
358 }
359
360 func TestCgoPprofThreadNoTraceback(t *testing.T) {
361 testCgoPprof(t, "", "CgoPprofThreadNoTraceback", "cpuHogThread", "runtime._ExternalCode")
362 }
363
364 func TestRaceProf(t *testing.T) {
365 if (runtime.GOOS != "linux" && runtime.GOOS != "freebsd") || runtime.GOARCH != "amd64" {
366 t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
367 }
368
369 testenv.MustHaveGoRun(t)
370
371
372
373 if testing.Short() {
374 t.Skip("skipping test in -short mode")
375 }
376
377 exe, err := buildTestProg(t, "testprogcgo", "-race")
378 if err != nil {
379 t.Fatal(err)
380 }
381
382 got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoRaceprof")).CombinedOutput()
383 if err != nil {
384 t.Fatal(err)
385 }
386 want := "OK\n"
387 if string(got) != want {
388 t.Errorf("expected %q got %s", want, got)
389 }
390 }
391
392 func TestRaceSignal(t *testing.T) {
393 t.Parallel()
394 if (runtime.GOOS != "linux" && runtime.GOOS != "freebsd") || runtime.GOARCH != "amd64" {
395 t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
396 }
397
398 testenv.MustHaveGoRun(t)
399
400
401
402 if testing.Short() {
403 t.Skip("skipping test in -short mode")
404 }
405
406 exe, err := buildTestProg(t, "testprogcgo", "-race")
407 if err != nil {
408 t.Fatal(err)
409 }
410
411 got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoRaceSignal")).CombinedOutput()
412 if err != nil {
413 t.Logf("%s\n", got)
414 t.Fatal(err)
415 }
416 want := "OK\n"
417 if string(got) != want {
418 t.Errorf("expected %q got %s", want, got)
419 }
420 }
421
422 func TestCgoNumGoroutine(t *testing.T) {
423 switch runtime.GOOS {
424 case "windows", "plan9":
425 t.Skipf("skipping numgoroutine test on %s", runtime.GOOS)
426 }
427 t.Parallel()
428 got := runTestProg(t, "testprogcgo", "NumGoroutine")
429 want := "OK\n"
430 if got != want {
431 t.Errorf("expected %q got %v", want, got)
432 }
433 }
434
435 func TestCatchPanic(t *testing.T) {
436 t.Parallel()
437 switch runtime.GOOS {
438 case "plan9", "windows":
439 t.Skipf("no signals on %s", runtime.GOOS)
440 case "darwin":
441 if runtime.GOARCH == "amd64" {
442 t.Skipf("crash() on darwin/amd64 doesn't raise SIGABRT")
443 }
444 }
445
446 testenv.MustHaveGoRun(t)
447
448 exe, err := buildTestProg(t, "testprogcgo")
449 if err != nil {
450 t.Fatal(err)
451 }
452
453 for _, early := range []bool{true, false} {
454 cmd := testenv.CleanCmdEnv(exec.Command(exe, "CgoCatchPanic"))
455
456 cmd.Env = append(cmd.Env, "GOTRACEBACK=crash")
457 if early {
458
459 cmd.Env = append(cmd.Env, "CGOCATCHPANIC_EARLY_HANDLER=1")
460 }
461 if out, err := cmd.CombinedOutput(); err != nil {
462 t.Errorf("testprogcgo CgoCatchPanic failed: %v\n%s", err, out)
463 }
464 }
465 }
466
467 func TestCgoLockOSThreadExit(t *testing.T) {
468 switch runtime.GOOS {
469 case "plan9", "windows":
470 t.Skipf("no pthreads on %s", runtime.GOOS)
471 }
472 t.Parallel()
473 testLockOSThreadExit(t, "testprogcgo")
474 }
475
476 func TestWindowsStackMemoryCgo(t *testing.T) {
477 if runtime.GOOS != "windows" {
478 t.Skip("skipping windows specific test")
479 }
480 testenv.SkipFlaky(t, 22575)
481 o := runTestProg(t, "testprogcgo", "StackMemory")
482 stackUsage, err := strconv.Atoi(o)
483 if err != nil {
484 t.Fatalf("Failed to read stack usage: %v", err)
485 }
486 if expected, got := 100<<10, stackUsage; got > expected {
487 t.Fatalf("expected < %d bytes of memory per thread, got %d", expected, got)
488 }
489 }
490
491 func TestSigStackSwapping(t *testing.T) {
492 switch runtime.GOOS {
493 case "plan9", "windows":
494 t.Skipf("no sigaltstack on %s", runtime.GOOS)
495 }
496 t.Parallel()
497 got := runTestProg(t, "testprogcgo", "SigStack")
498 want := "OK\n"
499 if got != want {
500 t.Errorf("expected %q got %v", want, got)
501 }
502 }
503
504 func TestCgoTracebackSigpanic(t *testing.T) {
505
506
507 if runtime.GOOS == "windows" {
508
509
510
511 t.Skip("no sigpanic in C on windows")
512 }
513 t.Parallel()
514 got := runTestProg(t, "testprogcgo", "TracebackSigpanic")
515 t.Log(got)
516 want := "runtime.sigpanic"
517 if !strings.Contains(got, want) {
518 t.Errorf("did not see %q in output", want)
519 }
520
521 nowant := "runtime: "
522 if strings.Contains(got, nowant) {
523 t.Errorf("unexpectedly saw %q in output", nowant)
524 }
525 }
526
527 func TestCgoPanicCallback(t *testing.T) {
528 t.Parallel()
529 got := runTestProg(t, "testprogcgo", "PanicCallback")
530 t.Log(got)
531 want := "panic: runtime error: invalid memory address or nil pointer dereference"
532 if !strings.Contains(got, want) {
533 t.Errorf("did not see %q in output", want)
534 }
535 want = "panic_callback"
536 if !strings.Contains(got, want) {
537 t.Errorf("did not see %q in output", want)
538 }
539 want = "PanicCallback"
540 if !strings.Contains(got, want) {
541 t.Errorf("did not see %q in output", want)
542 }
543
544 nowant := "runtime: "
545 if strings.Contains(got, nowant) {
546 t.Errorf("did not see %q in output", want)
547 }
548 }
549
550
551
552
553
554 func TestBigStackCallbackCgo(t *testing.T) {
555 if runtime.GOOS != "windows" {
556 t.Skip("skipping windows specific test")
557 }
558 t.Parallel()
559 got := runTestProg(t, "testprogcgo", "BigStack")
560 want := "OK\n"
561 if got != want {
562 t.Errorf("expected %q got %v", want, got)
563 }
564 }
565
566 func nextTrace(lines []string) ([]string, []string) {
567 var trace []string
568 for n, line := range lines {
569 if strings.HasPrefix(line, "---") {
570 return trace, lines[n+1:]
571 }
572 fields := strings.Fields(strings.TrimSpace(line))
573 if len(fields) == 0 {
574 continue
575 }
576
577 trace = append(trace, fields[len(fields)-1])
578 }
579 return nil, nil
580 }
581
582 func findTrace(text, top string) []string {
583 lines := strings.Split(text, "\n")
584 _, lines = nextTrace(lines)
585 for len(lines) > 0 {
586 var t []string
587 t, lines = nextTrace(lines)
588 if len(t) == 0 {
589 continue
590 }
591 if t[0] == top {
592 return t
593 }
594 }
595 return nil
596 }
597
598 func TestSegv(t *testing.T) {
599 switch runtime.GOOS {
600 case "plan9", "windows":
601 t.Skipf("no signals on %s", runtime.GOOS)
602 }
603
604 for _, test := range []string{"Segv", "SegvInCgo"} {
605 test := test
606 t.Run(test, func(t *testing.T) {
607 t.Parallel()
608 got := runTestProg(t, "testprogcgo", test)
609 t.Log(got)
610 want := "SIGSEGV"
611 if !strings.Contains(got, want) {
612 if runtime.GOOS == "darwin" && runtime.GOARCH == "amd64" && strings.Contains(got, "fatal: morestack on g0") {
613 testenv.SkipFlaky(t, 39457)
614 }
615 t.Errorf("did not see %q in output", want)
616 }
617
618
619 switch runtime.GOOS {
620 case "darwin", "illumos", "solaris":
621
622 testenv.SkipFlaky(t, 49182)
623 case "linux":
624 if runtime.GOARCH == "386" {
625
626
627 testenv.SkipFlaky(t, 50504)
628 }
629 }
630 if test == "SegvInCgo" && strings.Contains(got, "runtime: unknown pc") {
631 testenv.SkipFlaky(t, 50979)
632 }
633
634 nowant := "runtime: "
635 if strings.Contains(got, nowant) {
636 t.Errorf("unexpectedly saw %q in output", nowant)
637 }
638 })
639 }
640 }
641
642 func TestAbortInCgo(t *testing.T) {
643 switch runtime.GOOS {
644 case "plan9", "windows":
645
646
647 t.Skipf("no signals on %s", runtime.GOOS)
648 }
649
650 t.Parallel()
651 got := runTestProg(t, "testprogcgo", "Abort")
652 t.Log(got)
653 want := "SIGABRT"
654 if !strings.Contains(got, want) {
655 t.Errorf("did not see %q in output", want)
656 }
657
658 nowant := "runtime: "
659 if strings.Contains(got, nowant) {
660 t.Errorf("did not see %q in output", want)
661 }
662 }
663
664
665
666 func TestEINTR(t *testing.T) {
667 switch runtime.GOOS {
668 case "plan9", "windows":
669 t.Skipf("no EINTR on %s", runtime.GOOS)
670 case "linux":
671 if runtime.GOARCH == "386" {
672
673
674
675
676
677
678
679 t.Skip("skipping on linux-386; C sigaction does not preserve Go restorer")
680 }
681 }
682
683 t.Parallel()
684 output := runTestProg(t, "testprogcgo", "EINTR")
685 want := "OK\n"
686 if output != want {
687 t.Fatalf("want %s, got %s\n", want, output)
688 }
689 }
690
691
692 func TestNeedmDeadlock(t *testing.T) {
693 switch runtime.GOOS {
694 case "plan9", "windows":
695 t.Skipf("no signals on %s", runtime.GOOS)
696 }
697 output := runTestProg(t, "testprogcgo", "NeedmDeadlock")
698 want := "OK\n"
699 if output != want {
700 t.Fatalf("want %s, got %s\n", want, output)
701 }
702 }
703
704 func TestCgoTracebackGoroutineProfile(t *testing.T) {
705 output := runTestProg(t, "testprogcgo", "GoroutineProfile")
706 want := "OK\n"
707 if output != want {
708 t.Fatalf("want %s, got %s\n", want, output)
709 }
710 }
711
View as plain text