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// +build cgo
7
8package runtime_test
9
10import (
11	"bytes"
12	"fmt"
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	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
75func 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
87func TestCgoExternalThreadSIGPROF(t *testing.T) {
88	t.Parallel()
89	// issue 9456.
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		// TODO(austin) External linking not implemented on
96		// linux/ppc64 (issue #8912)
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
115func TestCgoExternalThreadSignal(t *testing.T) {
116	t.Parallel()
117	// issue 10139
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
139func TestCgoDLLImports(t *testing.T) {
140	// test issue 9356
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
151func TestCgoExecSignalMask(t *testing.T) {
152	t.Parallel()
153	// Test issue 13164.
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
165func TestEnsureDropM(t *testing.T) {
166	t.Parallel()
167	// Test for issue 13881.
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// Test for issue 14387.
180// Test that the program that doesn't need any cgo pointer checking
181// takes about the same amount of time with it as without it.
182func TestCgoCheckBytes(t *testing.T) {
183	t.Parallel()
184	// Make sure we don't count the build time as part of the run time.
185	testenv.MustHaveGoBuild(t)
186	exe, err := buildTestProg(t, "testprogcgo")
187	if err != nil {
188		t.Fatal(err)
189	}
190
191	// Try it 10 times to avoid flakiness.
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			// The slow version (d2) was less than 20 times
211			// slower than the fast version (d1), so OK.
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
222func TestCgoPanicDeadlock(t *testing.T) {
223	t.Parallel()
224	// test issue 14432
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
232func 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
241func 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	if runtime.Compiler == "gccgo" {
251		t.Skip("gccgo does not have SetCgoTraceback")
252	}
253	got := runTestProg(t, "testprogcgo", "CrashTraceback")
254	for i := 1; i <= 3; i++ {
255		if !strings.Contains(got, fmt.Sprintf("cgo symbolizer:%d", i)) {
256			t.Errorf("missing cgo symbolizer:%d", i)
257		}
258	}
259}
260
261func TestCgoCrashTracebackGo(t *testing.T) {
262	t.Parallel()
263	switch platform := runtime.GOOS + "/" + runtime.GOARCH; platform {
264	case "darwin/amd64":
265	case "linux/amd64":
266	case "linux/ppc64le":
267	default:
268		t.Skipf("not yet supported on %s", platform)
269	}
270	if runtime.Compiler == "gccgo" {
271		t.Skip("gccgo does not have SetCgoTraceback")
272	}
273	got := runTestProg(t, "testprogcgo", "CrashTracebackGo")
274	for i := 1; i <= 3; i++ {
275		want := fmt.Sprintf("main.h%d", i)
276		if !strings.Contains(got, want) {
277			t.Errorf("missing %s", want)
278		}
279	}
280}
281
282func TestCgoTracebackContext(t *testing.T) {
283	t.Parallel()
284	if runtime.Compiler == "gccgo" {
285		t.Skip("gccgo does not have SetCgoTraceback")
286	}
287	got := runTestProg(t, "testprogcgo", "TracebackContext")
288	want := "OK\n"
289	if got != want {
290		t.Errorf("expected %q got %v", want, got)
291	}
292}
293
294func TestCgoTracebackContextPreemption(t *testing.T) {
295	t.Parallel()
296	if runtime.Compiler == "gccgo" {
297		t.Skip("gccgo does not have SetCgoTraceback")
298	}
299	got := runTestProg(t, "testprogcgo", "TracebackContextPreemption")
300	want := "OK\n"
301	if got != want {
302		t.Errorf("expected %q got %v", want, got)
303	}
304}
305
306func testCgoPprof(t *testing.T, buildArg, runArg, top, bottom string) {
307	t.Parallel()
308	if runtime.GOOS != "linux" || (runtime.GOARCH != "amd64" && runtime.GOARCH != "ppc64le") {
309		t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
310	}
311	if runtime.Compiler == "gccgo" {
312		t.Skip("gccgo does not have SetCgoTraceback")
313	}
314	testenv.MustHaveGoRun(t)
315
316	exe, err := buildTestProg(t, "testprogcgo", buildArg)
317	if err != nil {
318		t.Fatal(err)
319	}
320
321	// pprofCgoTraceback is called whenever CGO code is executing and a signal
322	// is received. Disable signal preemption to increase the likelihood at
323	// least one SIGPROF signal fired to capture a sample. See issue #37201.
324	cmd := testenv.CleanCmdEnv(exec.Command(exe, runArg))
325	cmd.Env = append(cmd.Env, "GODEBUG=asyncpreemptoff=1")
326
327	got, err := cmd.CombinedOutput()
328	if err != nil {
329		if testenv.Builder() == "linux-amd64-alpine" {
330			// See Issue 18243 and Issue 19938.
331			t.Skipf("Skipping failing test on Alpine (golang.org/issue/18243). Ignoring error: %v", err)
332		}
333		t.Fatalf("%s\n\n%v", got, err)
334	}
335	fn := strings.TrimSpace(string(got))
336	defer os.Remove(fn)
337
338	for try := 0; try < 2; try++ {
339		cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-traces"))
340		// Check that pprof works both with and without explicit executable on command line.
341		if try == 0 {
342			cmd.Args = append(cmd.Args, exe, fn)
343		} else {
344			cmd.Args = append(cmd.Args, fn)
345		}
346
347		found := false
348		for i, e := range cmd.Env {
349			if strings.HasPrefix(e, "PPROF_TMPDIR=") {
350				cmd.Env[i] = "PPROF_TMPDIR=" + os.TempDir()
351				found = true
352				break
353			}
354		}
355		if !found {
356			cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
357		}
358
359		out, err := cmd.CombinedOutput()
360		t.Logf("%s:\n%s", cmd.Args, out)
361		if err != nil {
362			t.Error(err)
363			continue
364		}
365
366		trace := findTrace(string(out), top)
367		if len(trace) == 0 {
368			t.Errorf("%s traceback missing.", top)
369			continue
370		}
371		if trace[len(trace)-1] != bottom {
372			t.Errorf("invalid traceback origin: got=%v; want=[%s ... %s]", trace, top, bottom)
373		}
374	}
375}
376
377func TestCgoPprof(t *testing.T) {
378	testCgoPprof(t, "", "CgoPprof", "cpuHog", "runtime.main")
379}
380
381func TestCgoPprofPIE(t *testing.T) {
382	testCgoPprof(t, "-buildmode=pie", "CgoPprof", "cpuHog", "runtime.main")
383}
384
385func TestCgoPprofThread(t *testing.T) {
386	testCgoPprof(t, "", "CgoPprofThread", "cpuHogThread", "cpuHogThread2")
387}
388
389func TestCgoPprofThreadNoTraceback(t *testing.T) {
390	testCgoPprof(t, "", "CgoPprofThreadNoTraceback", "cpuHogThread", "runtime._ExternalCode")
391}
392
393func TestRaceProf(t *testing.T) {
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	if runtime.Compiler == "gccgo" {
398		t.Skip("gccgo does not have SetCgoTraceback")
399	}
400
401	testenv.MustHaveGoRun(t)
402
403	// This test requires building various packages with -race, so
404	// it's somewhat slow.
405	if testing.Short() {
406		t.Skip("skipping test in -short mode")
407	}
408
409	exe, err := buildTestProg(t, "testprogcgo", "-race")
410	if err != nil {
411		t.Fatal(err)
412	}
413
414	got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoRaceprof")).CombinedOutput()
415	if err != nil {
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
424func TestRaceSignal(t *testing.T) {
425	t.Parallel()
426	if (runtime.GOOS != "linux" && runtime.GOOS != "freebsd") || runtime.GOARCH != "amd64" {
427		t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
428	}
429
430	testenv.MustHaveGoRun(t)
431
432	// This test requires building various packages with -race, so
433	// it's somewhat slow.
434	if testing.Short() {
435		t.Skip("skipping test in -short mode")
436	}
437
438	exe, err := buildTestProg(t, "testprogcgo", "-race")
439	if err != nil {
440		t.Fatal(err)
441	}
442
443	got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoRaceSignal")).CombinedOutput()
444	if err != nil {
445		t.Logf("%s\n", got)
446		t.Fatal(err)
447	}
448	want := "OK\n"
449	if string(got) != want {
450		t.Errorf("expected %q got %s", want, got)
451	}
452}
453
454func TestCgoNumGoroutine(t *testing.T) {
455	switch runtime.GOOS {
456	case "windows", "plan9":
457		t.Skipf("skipping numgoroutine test on %s", runtime.GOOS)
458	}
459	t.Parallel()
460	got := runTestProg(t, "testprogcgo", "NumGoroutine")
461	want := "OK\n"
462	if got != want {
463		t.Errorf("expected %q got %v", want, got)
464	}
465}
466
467func TestCatchPanic(t *testing.T) {
468	t.Parallel()
469	switch runtime.GOOS {
470	case "plan9", "windows":
471		t.Skipf("no signals on %s", runtime.GOOS)
472	case "darwin":
473		if runtime.GOARCH == "amd64" {
474			t.Skipf("crash() on darwin/amd64 doesn't raise SIGABRT")
475		}
476	}
477
478	testenv.MustHaveGoRun(t)
479
480	exe, err := buildTestProg(t, "testprogcgo")
481	if err != nil {
482		t.Fatal(err)
483	}
484
485	for _, early := range []bool{true, false} {
486		cmd := testenv.CleanCmdEnv(exec.Command(exe, "CgoCatchPanic"))
487		// Make sure a panic results in a crash.
488		cmd.Env = append(cmd.Env, "GOTRACEBACK=crash")
489		if early {
490			// Tell testprogcgo to install an early signal handler for SIGABRT
491			cmd.Env = append(cmd.Env, "CGOCATCHPANIC_EARLY_HANDLER=1")
492		}
493		if out, err := cmd.CombinedOutput(); err != nil {
494			t.Errorf("testprogcgo CgoCatchPanic failed: %v\n%s", err, out)
495		}
496	}
497}
498
499func TestCgoLockOSThreadExit(t *testing.T) {
500	switch runtime.GOOS {
501	case "plan9", "windows":
502		t.Skipf("no pthreads on %s", runtime.GOOS)
503	}
504	t.Parallel()
505	testLockOSThreadExit(t, "testprogcgo")
506}
507
508func TestWindowsStackMemoryCgo(t *testing.T) {
509	if runtime.GOOS != "windows" {
510		t.Skip("skipping windows specific test")
511	}
512	testenv.SkipFlaky(t, 22575)
513	o := runTestProg(t, "testprogcgo", "StackMemory")
514	stackUsage, err := strconv.Atoi(o)
515	if err != nil {
516		t.Fatalf("Failed to read stack usage: %v", err)
517	}
518	if expected, got := 100<<10, stackUsage; got > expected {
519		t.Fatalf("expected < %d bytes of memory per thread, got %d", expected, got)
520	}
521}
522
523func TestSigStackSwapping(t *testing.T) {
524	switch runtime.GOOS {
525	case "plan9", "windows":
526		t.Skipf("no sigaltstack on %s", runtime.GOOS)
527	}
528	t.Parallel()
529	got := runTestProg(t, "testprogcgo", "SigStack")
530	want := "OK\n"
531	if got != want {
532		t.Errorf("expected %q got %v", want, got)
533	}
534}
535
536func TestCgoTracebackSigpanic(t *testing.T) {
537	// Test unwinding over a sigpanic in C code without a C
538	// symbolizer. See issue #23576.
539	if runtime.GOOS == "windows" {
540		// On Windows if we get an exception in C code, we let
541		// the Windows exception handler unwind it, rather
542		// than injecting a sigpanic.
543		t.Skip("no sigpanic in C on windows")
544	}
545	t.Parallel()
546	got := runTestProg(t, "testprogcgo", "TracebackSigpanic")
547	want := "runtime.sigpanic"
548	if !strings.Contains(got, want) {
549		t.Fatalf("want failure containing %q. output:\n%s\n", want, got)
550	}
551	nowant := "unexpected return pc"
552	if strings.Contains(got, nowant) {
553		t.Fatalf("failure incorrectly contains %q. output:\n%s\n", nowant, got)
554	}
555}
556
557// Test that C code called via cgo can use large Windows thread stacks
558// and call back in to Go without crashing. See issue #20975.
559//
560// See also TestBigStackCallbackSyscall.
561func TestBigStackCallbackCgo(t *testing.T) {
562	if runtime.GOOS != "windows" {
563		t.Skip("skipping windows specific test")
564	}
565	t.Parallel()
566	got := runTestProg(t, "testprogcgo", "BigStack")
567	want := "OK\n"
568	if got != want {
569		t.Errorf("expected %q got %v", want, got)
570	}
571}
572
573func nextTrace(lines []string) ([]string, []string) {
574	var trace []string
575	for n, line := range lines {
576		if strings.HasPrefix(line, "---") {
577			return trace, lines[n+1:]
578		}
579		fields := strings.Fields(strings.TrimSpace(line))
580		if len(fields) == 0 {
581			continue
582		}
583		// Last field contains the function name.
584		trace = append(trace, fields[len(fields)-1])
585	}
586	return nil, nil
587}
588
589func findTrace(text, top string) []string {
590	lines := strings.Split(text, "\n")
591	_, lines = nextTrace(lines) // Skip the header.
592	for len(lines) > 0 {
593		var t []string
594		t, lines = nextTrace(lines)
595		if len(t) == 0 {
596			continue
597		}
598		if t[0] == top {
599			return t
600		}
601	}
602	return nil
603}
604
605func TestSegv(t *testing.T) {
606	switch runtime.GOOS {
607	case "plan9", "windows":
608		t.Skipf("no signals on %s", runtime.GOOS)
609	}
610
611	for _, test := range []string{"Segv", "SegvInCgo"} {
612		t.Run(test, func(t *testing.T) {
613			t.Parallel()
614			got := runTestProg(t, "testprogcgo", test)
615			t.Log(got)
616			if !strings.Contains(got, "SIGSEGV") {
617				t.Errorf("expected crash from signal")
618			}
619		})
620	}
621}
622
623// TestEINTR tests that we handle EINTR correctly.
624// See issue #20400 and friends.
625func TestEINTR(t *testing.T) {
626	switch runtime.GOOS {
627	case "plan9", "windows":
628		t.Skipf("no EINTR on %s", runtime.GOOS)
629	case "linux":
630		if runtime.GOARCH == "386" {
631			// On linux-386 the Go signal handler sets
632			// a restorer function that is not preserved
633			// by the C sigaction call in the test,
634			// causing the signal handler to crash when
635			// returning the normal code. The test is not
636			// architecture-specific, so just skip on 386
637			// rather than doing a complicated workaround.
638			t.Skip("skipping on linux-386; C sigaction does not preserve Go restorer")
639		}
640	}
641
642	t.Parallel()
643	output := runTestProg(t, "testprogcgo", "EINTR")
644	want := "OK\n"
645	if output != want {
646		t.Fatalf("want %s, got %s\n", want, output)
647	}
648}
649
650// Issue #42207.
651func TestNeedmDeadlock(t *testing.T) {
652	switch runtime.GOOS {
653	case "plan9", "windows":
654		t.Skipf("no signals on %s", runtime.GOOS)
655	}
656	output := runTestProg(t, "testprogcgo", "NeedmDeadlock")
657	want := "OK\n"
658	if output != want {
659		t.Fatalf("want %s, got %s\n", want, output)
660	}
661}
662