1// Copyright 2012 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5//go:build cgo
6
7package runtime_test
8
9import (
10	"fmt"
11	"internal/goos"
12	"internal/platform"
13	"internal/testenv"
14	"os"
15	"os/exec"
16	"runtime"
17	"strconv"
18	"strings"
19	"testing"
20	"time"
21)
22
23func TestCgoCrashHandler(t *testing.T) {
24	t.Parallel()
25	testCrashHandler(t, true)
26}
27
28func TestCgoSignalDeadlock(t *testing.T) {
29	// Don't call t.Parallel, since too much work going on at the
30	// same time can cause the testprogcgo code to overrun its
31	// timeouts (issue #18598).
32
33	if testing.Short() && runtime.GOOS == "windows" {
34		t.Skip("Skipping in short mode") // takes up to 64 seconds
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
43func 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
52func 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	if testenv.Builder() == "darwin-amd64-10_14" {
69		// TODO(#23011): When the 10.14 builders are gone, remove this skip.
70		t.Skip("skipping due to platform bug on macOS 10.14; see https://golang.org/issue/43926")
71	}
72	got := runTestProg(t, "testprogcgo", "CgoCallbackGC")
73	want := "OK\n"
74	if got != want {
75		t.Fatalf("expected %q, but got:\n%s", want, got)
76	}
77}
78
79func TestCgoExternalThreadPanic(t *testing.T) {
80	t.Parallel()
81	if runtime.GOOS == "plan9" {
82		t.Skipf("no pthreads on %s", runtime.GOOS)
83	}
84	got := runTestProg(t, "testprogcgo", "CgoExternalThreadPanic")
85	want := "panic: BOOM"
86	if !strings.Contains(got, want) {
87		t.Fatalf("want failure containing %q. output:\n%s\n", want, got)
88	}
89}
90
91func TestCgoExternalThreadSIGPROF(t *testing.T) {
92	t.Parallel()
93	// issue 9456.
94	switch runtime.GOOS {
95	case "plan9", "windows":
96		t.Skipf("no pthreads on %s", runtime.GOOS)
97	}
98
99	got := runTestProg(t, "testprogcgo", "CgoExternalThreadSIGPROF", "GO_START_SIGPROF_THREAD=1")
100	if want := "OK\n"; got != want {
101		t.Fatalf("expected %q, but got:\n%s", want, got)
102	}
103}
104
105func TestCgoExternalThreadSignal(t *testing.T) {
106	t.Parallel()
107	// issue 10139
108	switch runtime.GOOS {
109	case "plan9", "windows":
110		t.Skipf("no pthreads on %s", runtime.GOOS)
111	}
112
113	got := runTestProg(t, "testprogcgo", "CgoExternalThreadSignal")
114	if want := "OK\n"; got != want {
115		if runtime.GOOS == "ios" && strings.Contains(got, "C signal did not crash as expected") {
116			testenv.SkipFlaky(t, 59913)
117		}
118		t.Fatalf("expected %q, but got:\n%s", want, got)
119	}
120}
121
122func TestCgoDLLImports(t *testing.T) {
123	// test issue 9356
124	if runtime.GOOS != "windows" {
125		t.Skip("skipping windows specific test")
126	}
127	got := runTestProg(t, "testprogcgo", "CgoDLLImportsMain")
128	want := "OK\n"
129	if got != want {
130		t.Fatalf("expected %q, but got %v", want, got)
131	}
132}
133
134func TestCgoExecSignalMask(t *testing.T) {
135	t.Parallel()
136	// Test issue 13164.
137	switch runtime.GOOS {
138	case "windows", "plan9":
139		t.Skipf("skipping signal mask test on %s", runtime.GOOS)
140	}
141	got := runTestProg(t, "testprogcgo", "CgoExecSignalMask", "GOTRACEBACK=system")
142	want := "OK\n"
143	if got != want {
144		t.Errorf("expected %q, got %v", want, got)
145	}
146}
147
148func TestEnsureDropM(t *testing.T) {
149	t.Parallel()
150	// Test for issue 13881.
151	switch runtime.GOOS {
152	case "windows", "plan9":
153		t.Skipf("skipping dropm test on %s", runtime.GOOS)
154	}
155	got := runTestProg(t, "testprogcgo", "EnsureDropM")
156	want := "OK\n"
157	if got != want {
158		t.Errorf("expected %q, got %v", want, got)
159	}
160}
161
162// Test for issue 14387.
163// Test that the program that doesn't need any cgo pointer checking
164// takes about the same amount of time with it as without it.
165func TestCgoCheckBytes(t *testing.T) {
166	t.Parallel()
167	// Make sure we don't count the build time as part of the run time.
168	testenv.MustHaveGoBuild(t)
169	exe, err := buildTestProg(t, "testprogcgo")
170	if err != nil {
171		t.Fatal(err)
172	}
173
174	// Try it 10 times to avoid flakiness.
175	const tries = 10
176	var tot1, tot2 time.Duration
177	for i := 0; i < tries; i++ {
178		cmd := testenv.CleanCmdEnv(exec.Command(exe, "CgoCheckBytes"))
179		cmd.Env = append(cmd.Env, "GODEBUG=cgocheck=0", fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i))
180
181		start := time.Now()
182		cmd.Run()
183		d1 := time.Since(start)
184
185		cmd = testenv.CleanCmdEnv(exec.Command(exe, "CgoCheckBytes"))
186		cmd.Env = append(cmd.Env, fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i))
187
188		start = time.Now()
189		cmd.Run()
190		d2 := time.Since(start)
191
192		if d1*20 > d2 {
193			// The slow version (d2) was less than 20 times
194			// slower than the fast version (d1), so OK.
195			return
196		}
197
198		tot1 += d1
199		tot2 += d2
200	}
201
202	t.Errorf("cgo check too slow: got %v, expected at most %v", tot2/tries, (tot1/tries)*20)
203}
204
205func TestCgoPanicDeadlock(t *testing.T) {
206	t.Parallel()
207	// test issue 14432
208	got := runTestProg(t, "testprogcgo", "CgoPanicDeadlock")
209	want := "panic: cgo error\n\n"
210	if !strings.HasPrefix(got, want) {
211		t.Fatalf("output does not start with %q:\n%s", want, got)
212	}
213}
214
215func TestCgoCCodeSIGPROF(t *testing.T) {
216	t.Parallel()
217	got := runTestProg(t, "testprogcgo", "CgoCCodeSIGPROF")
218	want := "OK\n"
219	if got != want {
220		t.Errorf("expected %q got %v", want, got)
221	}
222}
223
224func TestCgoPprofCallback(t *testing.T) {
225	if testing.Short() {
226		t.Skip("skipping in short mode") // takes a full second
227	}
228	switch runtime.GOOS {
229	case "windows", "plan9":
230		t.Skipf("skipping cgo pprof callback test on %s", runtime.GOOS)
231	}
232	got := runTestProg(t, "testprogcgo", "CgoPprofCallback")
233	want := "OK\n"
234	if got != want {
235		t.Errorf("expected %q got %v", want, got)
236	}
237}
238
239func TestCgoCrashTraceback(t *testing.T) {
240	t.Parallel()
241	switch platform := runtime.GOOS + "/" + runtime.GOARCH; platform {
242	case "darwin/amd64":
243	case "linux/amd64":
244	case "linux/arm64":
245	case "linux/ppc64le":
246	default:
247		t.Skipf("not yet supported on %s", platform)
248	}
249	got := runTestProg(t, "testprogcgo", "CrashTraceback")
250	for i := 1; i <= 3; i++ {
251		if !strings.Contains(got, fmt.Sprintf("cgo symbolizer:%d", i)) {
252			t.Errorf("missing cgo symbolizer:%d", i)
253		}
254	}
255}
256
257func TestCgoCrashTracebackGo(t *testing.T) {
258	t.Parallel()
259	switch platform := runtime.GOOS + "/" + runtime.GOARCH; platform {
260	case "darwin/amd64":
261	case "linux/amd64":
262	case "linux/arm64":
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
276func 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
285func 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
294func testCgoPprof(t *testing.T, buildArg, runArg, top, bottom string) {
295	t.Parallel()
296	if runtime.GOOS != "linux" || (runtime.GOARCH != "amd64" && runtime.GOARCH != "ppc64le" && runtime.GOARCH != "arm64") {
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	cmd := testenv.CleanCmdEnv(exec.Command(exe, runArg))
307	got, err := cmd.CombinedOutput()
308	if err != nil {
309		if testenv.Builder() == "linux-amd64-alpine" {
310			// See Issue 18243 and Issue 19938.
311			t.Skipf("Skipping failing test on Alpine (golang.org/issue/18243). Ignoring error: %v", err)
312		}
313		t.Fatalf("%s\n\n%v", got, err)
314	}
315	fn := strings.TrimSpace(string(got))
316	defer os.Remove(fn)
317
318	for try := 0; try < 2; try++ {
319		cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-tagignore=ignore", "-traces"))
320		// Check that pprof works both with and without explicit executable on command line.
321		if try == 0 {
322			cmd.Args = append(cmd.Args, exe, fn)
323		} else {
324			cmd.Args = append(cmd.Args, fn)
325		}
326
327		found := false
328		for i, e := range cmd.Env {
329			if strings.HasPrefix(e, "PPROF_TMPDIR=") {
330				cmd.Env[i] = "PPROF_TMPDIR=" + os.TempDir()
331				found = true
332				break
333			}
334		}
335		if !found {
336			cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
337		}
338
339		out, err := cmd.CombinedOutput()
340		t.Logf("%s:\n%s", cmd.Args, out)
341		if err != nil {
342			t.Error(err)
343			continue
344		}
345
346		trace := findTrace(string(out), top)
347		if len(trace) == 0 {
348			t.Errorf("%s traceback missing.", top)
349			continue
350		}
351		if trace[len(trace)-1] != bottom {
352			t.Errorf("invalid traceback origin: got=%v; want=[%s ... %s]", trace, top, bottom)
353		}
354	}
355}
356
357func TestCgoPprof(t *testing.T) {
358	testCgoPprof(t, "", "CgoPprof", "cpuHog", "runtime.main")
359}
360
361func TestCgoPprofPIE(t *testing.T) {
362	testCgoPprof(t, "-buildmode=pie", "CgoPprof", "cpuHog", "runtime.main")
363}
364
365func TestCgoPprofThread(t *testing.T) {
366	testCgoPprof(t, "", "CgoPprofThread", "cpuHogThread", "cpuHogThread2")
367}
368
369func TestCgoPprofThreadNoTraceback(t *testing.T) {
370	testCgoPprof(t, "", "CgoPprofThreadNoTraceback", "cpuHogThread", "runtime._ExternalCode")
371}
372
373func TestRaceProf(t *testing.T) {
374	if !platform.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH) {
375		t.Skipf("skipping on %s/%s because race detector not supported", runtime.GOOS, runtime.GOARCH)
376	}
377	if runtime.GOOS == "windows" {
378		t.Skipf("skipping: test requires pthread support")
379		// TODO: Can this test be rewritten to use the C11 thread API instead?
380	}
381
382	testenv.MustHaveGoRun(t)
383
384	// This test requires building various packages with -race, so
385	// it's somewhat slow.
386	if testing.Short() {
387		t.Skip("skipping test in -short mode")
388	}
389
390	exe, err := buildTestProg(t, "testprogcgo", "-race")
391	if err != nil {
392		t.Fatal(err)
393	}
394
395	got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoRaceprof")).CombinedOutput()
396	if err != nil {
397		t.Fatal(err)
398	}
399	want := "OK\n"
400	if string(got) != want {
401		t.Errorf("expected %q got %s", want, got)
402	}
403}
404
405func TestRaceSignal(t *testing.T) {
406	if !platform.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH) {
407		t.Skipf("skipping on %s/%s because race detector not supported", runtime.GOOS, runtime.GOARCH)
408	}
409	if runtime.GOOS == "windows" {
410		t.Skipf("skipping: test requires pthread support")
411		// TODO: Can this test be rewritten to use the C11 thread API instead?
412	}
413	if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
414		testenv.SkipFlaky(t, 60316)
415	}
416
417	t.Parallel()
418
419	testenv.MustHaveGoRun(t)
420
421	// This test requires building various packages with -race, so
422	// it's somewhat slow.
423	if testing.Short() {
424		t.Skip("skipping test in -short mode")
425	}
426
427	exe, err := buildTestProg(t, "testprogcgo", "-race")
428	if err != nil {
429		t.Fatal(err)
430	}
431
432	got, err := testenv.CleanCmdEnv(testenv.Command(t, exe, "CgoRaceSignal")).CombinedOutput()
433	if err != nil {
434		t.Logf("%s\n", got)
435		t.Fatal(err)
436	}
437	want := "OK\n"
438	if string(got) != want {
439		t.Errorf("expected %q got %s", want, got)
440	}
441}
442
443func TestCgoNumGoroutine(t *testing.T) {
444	switch runtime.GOOS {
445	case "windows", "plan9":
446		t.Skipf("skipping numgoroutine test on %s", runtime.GOOS)
447	}
448	t.Parallel()
449	got := runTestProg(t, "testprogcgo", "NumGoroutine")
450	want := "OK\n"
451	if got != want {
452		t.Errorf("expected %q got %v", want, got)
453	}
454}
455
456func TestCatchPanic(t *testing.T) {
457	t.Parallel()
458	switch runtime.GOOS {
459	case "plan9", "windows":
460		t.Skipf("no signals on %s", runtime.GOOS)
461	case "darwin":
462		if runtime.GOARCH == "amd64" {
463			t.Skipf("crash() on darwin/amd64 doesn't raise SIGABRT")
464		}
465	}
466
467	testenv.MustHaveGoRun(t)
468
469	exe, err := buildTestProg(t, "testprogcgo")
470	if err != nil {
471		t.Fatal(err)
472	}
473
474	for _, early := range []bool{true, false} {
475		cmd := testenv.CleanCmdEnv(exec.Command(exe, "CgoCatchPanic"))
476		// Make sure a panic results in a crash.
477		cmd.Env = append(cmd.Env, "GOTRACEBACK=crash")
478		if early {
479			// Tell testprogcgo to install an early signal handler for SIGABRT
480			cmd.Env = append(cmd.Env, "CGOCATCHPANIC_EARLY_HANDLER=1")
481		}
482		if out, err := cmd.CombinedOutput(); err != nil {
483			t.Errorf("testprogcgo CgoCatchPanic failed: %v\n%s", err, out)
484		}
485	}
486}
487
488func TestCgoLockOSThreadExit(t *testing.T) {
489	switch runtime.GOOS {
490	case "plan9", "windows":
491		t.Skipf("no pthreads on %s", runtime.GOOS)
492	}
493	t.Parallel()
494	testLockOSThreadExit(t, "testprogcgo")
495}
496
497func TestWindowsStackMemoryCgo(t *testing.T) {
498	if runtime.GOOS != "windows" {
499		t.Skip("skipping windows specific test")
500	}
501	testenv.SkipFlaky(t, 22575)
502	o := runTestProg(t, "testprogcgo", "StackMemory")
503	stackUsage, err := strconv.Atoi(o)
504	if err != nil {
505		t.Fatalf("Failed to read stack usage: %v", err)
506	}
507	if expected, got := 100<<10, stackUsage; got > expected {
508		t.Fatalf("expected < %d bytes of memory per thread, got %d", expected, got)
509	}
510}
511
512func TestSigStackSwapping(t *testing.T) {
513	switch runtime.GOOS {
514	case "plan9", "windows":
515		t.Skipf("no sigaltstack on %s", runtime.GOOS)
516	}
517	t.Parallel()
518	got := runTestProg(t, "testprogcgo", "SigStack")
519	want := "OK\n"
520	if got != want {
521		t.Errorf("expected %q got %v", want, got)
522	}
523}
524
525func TestCgoTracebackSigpanic(t *testing.T) {
526	// Test unwinding over a sigpanic in C code without a C
527	// symbolizer. See issue #23576.
528	if runtime.GOOS == "windows" {
529		// On Windows if we get an exception in C code, we let
530		// the Windows exception handler unwind it, rather
531		// than injecting a sigpanic.
532		t.Skip("no sigpanic in C on windows")
533	}
534	if runtime.GOOS == "ios" {
535		testenv.SkipFlaky(t, 59912)
536	}
537	t.Parallel()
538	got := runTestProg(t, "testprogcgo", "TracebackSigpanic")
539	t.Log(got)
540	// We should see the function that calls the C function.
541	want := "main.TracebackSigpanic"
542	if !strings.Contains(got, want) {
543		if runtime.GOOS == "android" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
544			testenv.SkipFlaky(t, 58794)
545		}
546		t.Errorf("did not see %q in output", want)
547	}
548	// We shouldn't inject a sigpanic call. (see issue 57698)
549	nowant := "runtime.sigpanic"
550	if strings.Contains(got, nowant) {
551		t.Errorf("unexpectedly saw %q in output", nowant)
552	}
553	// No runtime errors like "runtime: unexpected return pc".
554	nowant = "runtime: "
555	if strings.Contains(got, nowant) {
556		t.Errorf("unexpectedly saw %q in output", nowant)
557	}
558}
559
560func TestCgoPanicCallback(t *testing.T) {
561	t.Parallel()
562	got := runTestProg(t, "testprogcgo", "PanicCallback")
563	t.Log(got)
564	want := "panic: runtime error: invalid memory address or nil pointer dereference"
565	if !strings.Contains(got, want) {
566		t.Errorf("did not see %q in output", want)
567	}
568	want = "panic_callback"
569	if !strings.Contains(got, want) {
570		t.Errorf("did not see %q in output", want)
571	}
572	want = "PanicCallback"
573	if !strings.Contains(got, want) {
574		t.Errorf("did not see %q in output", want)
575	}
576	// No runtime errors like "runtime: unexpected return pc".
577	nowant := "runtime: "
578	if strings.Contains(got, nowant) {
579		t.Errorf("did not see %q in output", want)
580	}
581}
582
583// Test that C code called via cgo can use large Windows thread stacks
584// and call back in to Go without crashing. See issue #20975.
585//
586// See also TestBigStackCallbackSyscall.
587func TestBigStackCallbackCgo(t *testing.T) {
588	if runtime.GOOS != "windows" {
589		t.Skip("skipping windows specific test")
590	}
591	t.Parallel()
592	got := runTestProg(t, "testprogcgo", "BigStack")
593	want := "OK\n"
594	if got != want {
595		t.Errorf("expected %q got %v", want, got)
596	}
597}
598
599func nextTrace(lines []string) ([]string, []string) {
600	var trace []string
601	for n, line := range lines {
602		if strings.HasPrefix(line, "---") {
603			return trace, lines[n+1:]
604		}
605		fields := strings.Fields(strings.TrimSpace(line))
606		if len(fields) == 0 {
607			continue
608		}
609		// Last field contains the function name.
610		trace = append(trace, fields[len(fields)-1])
611	}
612	return nil, nil
613}
614
615func findTrace(text, top string) []string {
616	lines := strings.Split(text, "\n")
617	_, lines = nextTrace(lines) // Skip the header.
618	for len(lines) > 0 {
619		var t []string
620		t, lines = nextTrace(lines)
621		if len(t) == 0 {
622			continue
623		}
624		if t[0] == top {
625			return t
626		}
627	}
628	return nil
629}
630
631func TestSegv(t *testing.T) {
632	switch runtime.GOOS {
633	case "plan9", "windows":
634		t.Skipf("no signals on %s", runtime.GOOS)
635	}
636
637	for _, test := range []string{"Segv", "SegvInCgo", "TgkillSegv", "TgkillSegvInCgo"} {
638		test := test
639
640		// The tgkill variants only run on Linux.
641		if runtime.GOOS != "linux" && strings.HasPrefix(test, "Tgkill") {
642			continue
643		}
644
645		t.Run(test, func(t *testing.T) {
646			if test == "SegvInCgo" && runtime.GOOS == "ios" {
647				testenv.SkipFlaky(t, 59947) // Don't even try, in case it times out.
648			}
649
650			t.Parallel()
651			prog := "testprog"
652			if strings.HasSuffix(test, "InCgo") {
653				prog = "testprogcgo"
654			}
655			got := runTestProg(t, prog, test)
656			t.Log(got)
657			want := "SIGSEGV"
658			if !strings.Contains(got, want) {
659				if runtime.GOOS == "darwin" && runtime.GOARCH == "amd64" && strings.Contains(got, "fatal: morestack on g0") {
660					testenv.SkipFlaky(t, 39457)
661				}
662				t.Errorf("did not see %q in output", want)
663			}
664
665			// No runtime errors like "runtime: unknown pc".
666			switch runtime.GOOS {
667			case "darwin", "ios", "illumos", "solaris":
668				// Runtime sometimes throws when generating the traceback.
669				testenv.SkipFlaky(t, 49182)
670			case "linux":
671				if runtime.GOARCH == "386" {
672					// Runtime throws when generating a traceback from
673					// a VDSO call via asmcgocall.
674					testenv.SkipFlaky(t, 50504)
675				}
676			}
677			if test == "SegvInCgo" && strings.Contains(got, "unknown pc") {
678				testenv.SkipFlaky(t, 50979)
679			}
680
681			for _, nowant := range []string{"fatal error: ", "runtime: "} {
682				if strings.Contains(got, nowant) {
683					if runtime.GOOS == "darwin" && strings.Contains(got, "0xb01dfacedebac1e") {
684						// See the comment in signal_darwin_amd64.go.
685						t.Skip("skipping due to Darwin handling of malformed addresses")
686					}
687					t.Errorf("unexpectedly saw %q in output", nowant)
688				}
689			}
690		})
691	}
692}
693
694func TestAbortInCgo(t *testing.T) {
695	switch runtime.GOOS {
696	case "plan9", "windows":
697		// N.B. On Windows, C abort() causes the program to exit
698		// without going through the runtime at all.
699		t.Skipf("no signals on %s", runtime.GOOS)
700	}
701
702	t.Parallel()
703	got := runTestProg(t, "testprogcgo", "Abort")
704	t.Log(got)
705	want := "SIGABRT"
706	if !strings.Contains(got, want) {
707		t.Errorf("did not see %q in output", want)
708	}
709	// No runtime errors like "runtime: unknown pc".
710	nowant := "runtime: "
711	if strings.Contains(got, nowant) {
712		t.Errorf("did not see %q in output", want)
713	}
714}
715
716// TestEINTR tests that we handle EINTR correctly.
717// See issue #20400 and friends.
718func TestEINTR(t *testing.T) {
719	switch runtime.GOOS {
720	case "plan9", "windows":
721		t.Skipf("no EINTR on %s", runtime.GOOS)
722	case "linux":
723		if runtime.GOARCH == "386" {
724			// On linux-386 the Go signal handler sets
725			// a restorer function that is not preserved
726			// by the C sigaction call in the test,
727			// causing the signal handler to crash when
728			// returning the normal code. The test is not
729			// architecture-specific, so just skip on 386
730			// rather than doing a complicated workaround.
731			t.Skip("skipping on linux-386; C sigaction does not preserve Go restorer")
732		}
733	}
734
735	t.Parallel()
736	output := runTestProg(t, "testprogcgo", "EINTR")
737	want := "OK\n"
738	if output != want {
739		t.Fatalf("want %s, got %s\n", want, output)
740	}
741}
742
743// Issue #42207.
744func TestNeedmDeadlock(t *testing.T) {
745	switch runtime.GOOS {
746	case "plan9", "windows":
747		t.Skipf("no signals on %s", runtime.GOOS)
748	}
749	output := runTestProg(t, "testprogcgo", "NeedmDeadlock")
750	want := "OK\n"
751	if output != want {
752		t.Fatalf("want %s, got %s\n", want, output)
753	}
754}
755
756func TestCgoNoCallback(t *testing.T) {
757	t.Skip("TODO(#56378): enable in Go 1.23")
758	got := runTestProg(t, "testprogcgo", "CgoNoCallback")
759	want := "function marked with #cgo nocallback called back into Go"
760	if !strings.Contains(got, want) {
761		t.Fatalf("did not see %q in output:\n%s", want, got)
762	}
763}
764
765func TestCgoNoEscape(t *testing.T) {
766	t.Skip("TODO(#56378): enable in Go 1.23")
767	got := runTestProg(t, "testprogcgo", "CgoNoEscape")
768	want := "OK\n"
769	if got != want {
770		t.Fatalf("want %s, got %s\n", want, got)
771	}
772}
773
774func TestCgoTracebackGoroutineProfile(t *testing.T) {
775	output := runTestProg(t, "testprogcgo", "GoroutineProfile")
776	want := "OK\n"
777	if output != want {
778		t.Fatalf("want %s, got %s\n", want, output)
779	}
780}
781
782func TestCgoSigfwd(t *testing.T) {
783	t.Parallel()
784	if !goos.IsUnix {
785		t.Skipf("no signals on %s", runtime.GOOS)
786	}
787
788	got := runTestProg(t, "testprogcgo", "CgoSigfwd", "GO_TEST_CGOSIGFWD=1")
789	if want := "OK\n"; got != want {
790		t.Fatalf("expected %q, but got:\n%s", want, got)
791	}
792}
793
794func TestDestructorCallback(t *testing.T) {
795	t.Parallel()
796	got := runTestProg(t, "testprogcgo", "DestructorCallback")
797	if want := "OK\n"; got != want {
798		t.Errorf("expected %q, but got:\n%s", want, got)
799	}
800}
801
802func TestDestructorCallbackRace(t *testing.T) {
803	// This test requires building with -race,
804	// so it's somewhat slow.
805	if testing.Short() {
806		t.Skip("skipping test in -short mode")
807	}
808
809	if !platform.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH) {
810		t.Skipf("skipping on %s/%s because race detector not supported", runtime.GOOS, runtime.GOARCH)
811	}
812
813	t.Parallel()
814
815	exe, err := buildTestProg(t, "testprogcgo", "-race")
816	if err != nil {
817		t.Fatal(err)
818	}
819
820	got, err := testenv.CleanCmdEnv(exec.Command(exe, "DestructorCallback")).CombinedOutput()
821	if err != nil {
822		t.Fatal(err)
823	}
824
825	if want := "OK\n"; string(got) != want {
826		t.Errorf("expected %q, but got:\n%s", want, got)
827	}
828}
829
830func TestEnsureBindM(t *testing.T) {
831	t.Parallel()
832	switch runtime.GOOS {
833	case "windows", "plan9":
834		t.Skipf("skipping bindm test on %s", runtime.GOOS)
835	}
836	got := runTestProg(t, "testprogcgo", "EnsureBindM")
837	want := "OK\n"
838	if got != want {
839		t.Errorf("expected %q, got %v", want, got)
840	}
841}
842
843func TestStackSwitchCallback(t *testing.T) {
844	t.Parallel()
845	switch runtime.GOOS {
846	case "windows", "plan9", "android", "ios", "openbsd": // no getcontext
847		t.Skipf("skipping test on %s", runtime.GOOS)
848	}
849	got := runTestProg(t, "testprogcgo", "StackSwitchCallback")
850	skip := "SKIP\n"
851	if got == skip {
852		t.Skip("skipping on musl/bionic libc")
853	}
854	want := "OK\n"
855	if got != want {
856		t.Errorf("expected %q, got %v", want, got)
857	}
858}
859