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