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	"strings"
17	"testing"
18	"time"
19)
20
21func TestCgoCrashHandler(t *testing.T) {
22	t.Parallel()
23	testCrashHandler(t, true)
24}
25
26func TestCgoSignalDeadlock(t *testing.T) {
27	// Don't call t.Parallel, since too much work going on at the
28	// same time can cause the testprogcgo code to overrun its
29	// timeouts (issue #18598).
30
31	if testing.Short() && runtime.GOOS == "windows" {
32		t.Skip("Skipping in short mode") // takes up to 64 seconds
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
41func 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
50func 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	got := runTestProg(t, "testprogcgo", "CgoCallbackGC")
67	want := "OK\n"
68	if got != want {
69		t.Fatalf("expected %q, but got:\n%s", want, got)
70	}
71}
72
73func TestCgoExternalThreadPanic(t *testing.T) {
74	t.Parallel()
75	if runtime.GOOS == "plan9" {
76		t.Skipf("no pthreads on %s", runtime.GOOS)
77	}
78	got := runTestProg(t, "testprogcgo", "CgoExternalThreadPanic")
79	want := "panic: BOOM"
80	if !strings.Contains(got, want) {
81		t.Fatalf("want failure containing %q. output:\n%s\n", want, got)
82	}
83}
84
85func TestCgoExternalThreadSIGPROF(t *testing.T) {
86	t.Parallel()
87	// issue 9456.
88	switch runtime.GOOS {
89	case "plan9", "windows":
90		t.Skipf("no pthreads on %s", runtime.GOOS)
91	case "darwin":
92		if runtime.GOARCH != "arm" && runtime.GOARCH != "arm64" {
93			// static constructor needs external linking, but we don't support
94			// external linking on OS X 10.6.
95			out, err := exec.Command("uname", "-r").Output()
96			if err != nil {
97				t.Fatalf("uname -r failed: %v", err)
98			}
99			// OS X 10.6 == Darwin 10.x
100			if strings.HasPrefix(string(out), "10.") {
101				t.Skipf("no external linking on OS X 10.6")
102			}
103		}
104	}
105	if runtime.GOARCH == "ppc64" {
106		// TODO(austin) External linking not implemented on
107		// ppc64 (issue #8912)
108		t.Skipf("no external linking on ppc64")
109	}
110
111	exe, err := buildTestProg(t, "testprogcgo", "-tags=threadprof")
112	if err != nil {
113		t.Fatal(err)
114	}
115
116	got, err := testEnv(exec.Command(exe, "CgoExternalThreadSIGPROF")).CombinedOutput()
117	if err != nil {
118		t.Fatalf("exit status: %v\n%s", err, got)
119	}
120
121	if want := "OK\n"; string(got) != want {
122		t.Fatalf("expected %q, but got:\n%s", want, got)
123	}
124}
125
126func TestCgoExternalThreadSignal(t *testing.T) {
127	t.Parallel()
128	// issue 10139
129	switch runtime.GOOS {
130	case "plan9", "windows":
131		t.Skipf("no pthreads on %s", runtime.GOOS)
132	}
133
134	exe, err := buildTestProg(t, "testprogcgo", "-tags=threadprof")
135	if err != nil {
136		t.Fatal(err)
137	}
138
139	got, err := testEnv(exec.Command(exe, "CgoExternalThreadSIGPROF")).CombinedOutput()
140	if err != nil {
141		t.Fatalf("exit status: %v\n%s", err, got)
142	}
143
144	want := []byte("OK\n")
145	if !bytes.Equal(got, want) {
146		t.Fatalf("expected %q, but got:\n%s", want, got)
147	}
148}
149
150func TestCgoDLLImports(t *testing.T) {
151	// test issue 9356
152	if runtime.GOOS != "windows" {
153		t.Skip("skipping windows specific test")
154	}
155	got := runTestProg(t, "testprogcgo", "CgoDLLImportsMain")
156	want := "OK\n"
157	if got != want {
158		t.Fatalf("expected %q, but got %v", want, got)
159	}
160}
161
162func TestCgoExecSignalMask(t *testing.T) {
163	t.Parallel()
164	// Test issue 13164.
165	switch runtime.GOOS {
166	case "windows", "plan9":
167		t.Skipf("skipping signal mask test on %s", runtime.GOOS)
168	}
169	got := runTestProg(t, "testprogcgo", "CgoExecSignalMask")
170	want := "OK\n"
171	if got != want {
172		t.Errorf("expected %q, got %v", want, got)
173	}
174}
175
176func TestEnsureDropM(t *testing.T) {
177	t.Parallel()
178	// Test for issue 13881.
179	switch runtime.GOOS {
180	case "windows", "plan9":
181		t.Skipf("skipping dropm test on %s", runtime.GOOS)
182	}
183	got := runTestProg(t, "testprogcgo", "EnsureDropM")
184	want := "OK\n"
185	if got != want {
186		t.Errorf("expected %q, got %v", want, got)
187	}
188}
189
190// Test for issue 14387.
191// Test that the program that doesn't need any cgo pointer checking
192// takes about the same amount of time with it as without it.
193func TestCgoCheckBytes(t *testing.T) {
194	t.Parallel()
195	// Make sure we don't count the build time as part of the run time.
196	testenv.MustHaveGoBuild(t)
197	exe, err := buildTestProg(t, "testprogcgo")
198	if err != nil {
199		t.Fatal(err)
200	}
201
202	// Try it 10 times to avoid flakiness.
203	const tries = 10
204	var tot1, tot2 time.Duration
205	for i := 0; i < tries; i++ {
206		cmd := testEnv(exec.Command(exe, "CgoCheckBytes"))
207		cmd.Env = append(cmd.Env, "GODEBUG=cgocheck=0", fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i))
208
209		start := time.Now()
210		cmd.Run()
211		d1 := time.Since(start)
212
213		cmd = testEnv(exec.Command(exe, "CgoCheckBytes"))
214		cmd.Env = append(cmd.Env, fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i))
215
216		start = time.Now()
217		cmd.Run()
218		d2 := time.Since(start)
219
220		if d1*20 > d2 {
221			// The slow version (d2) was less than 20 times
222			// slower than the fast version (d1), so OK.
223			return
224		}
225
226		tot1 += d1
227		tot2 += d2
228	}
229
230	t.Errorf("cgo check too slow: got %v, expected at most %v", tot2/tries, (tot1/tries)*20)
231}
232
233func TestCgoPanicDeadlock(t *testing.T) {
234	t.Parallel()
235	// test issue 14432
236	got := runTestProg(t, "testprogcgo", "CgoPanicDeadlock")
237	want := "panic: cgo error\n\n"
238	if !strings.HasPrefix(got, want) {
239		t.Fatalf("output does not start with %q:\n%s", want, got)
240	}
241}
242
243func TestCgoCCodeSIGPROF(t *testing.T) {
244	t.Parallel()
245	got := runTestProg(t, "testprogcgo", "CgoCCodeSIGPROF")
246	want := "OK\n"
247	if got != want {
248		t.Errorf("expected %q got %v", want, got)
249	}
250}
251
252func TestCgoCrashTraceback(t *testing.T) {
253	t.Parallel()
254	if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
255		t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
256	}
257	got := runTestProg(t, "testprogcgo", "CrashTraceback")
258	for i := 1; i <= 3; i++ {
259		if !strings.Contains(got, fmt.Sprintf("cgo symbolizer:%d", i)) {
260			t.Errorf("missing cgo symbolizer:%d", i)
261		}
262	}
263}
264
265func TestCgoTracebackContext(t *testing.T) {
266	t.Parallel()
267	got := runTestProg(t, "testprogcgo", "TracebackContext")
268	want := "OK\n"
269	if got != want {
270		t.Errorf("expected %q got %v", want, got)
271	}
272}
273
274func testCgoPprof(t *testing.T, buildArg, runArg string) {
275	t.Parallel()
276	if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
277		t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
278	}
279	testenv.MustHaveGoRun(t)
280
281	exe, err := buildTestProg(t, "testprogcgo", buildArg)
282	if err != nil {
283		t.Fatal(err)
284	}
285
286	got, err := testEnv(exec.Command(exe, runArg)).CombinedOutput()
287	if err != nil {
288		if testenv.Builder() == "linux-amd64-alpine" {
289			// See Issue 18243 and Issue 19938.
290			t.Skipf("Skipping failing test on Alpine (golang.org/issue/18243). Ignoring error: %v", err)
291		}
292		t.Fatal(err)
293	}
294	fn := strings.TrimSpace(string(got))
295	defer os.Remove(fn)
296
297	for try := 0; try < 2; try++ {
298		cmd := testEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-top", "-nodecount=1"))
299		// Check that pprof works both with and without explicit executable on command line.
300		if try == 0 {
301			cmd.Args = append(cmd.Args, exe, fn)
302		} else {
303			cmd.Args = append(cmd.Args, fn)
304		}
305
306		found := false
307		for i, e := range cmd.Env {
308			if strings.HasPrefix(e, "PPROF_TMPDIR=") {
309				cmd.Env[i] = "PPROF_TMPDIR=" + os.TempDir()
310				found = true
311				break
312			}
313		}
314		if !found {
315			cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
316		}
317
318		top, err := cmd.CombinedOutput()
319		t.Logf("%s:\n%s", cmd.Args, top)
320		if err != nil {
321			t.Error(err)
322		} else if !bytes.Contains(top, []byte("cpuHog")) {
323			t.Error("missing cpuHog in pprof output")
324		}
325	}
326}
327
328func TestCgoPprof(t *testing.T) {
329	testCgoPprof(t, "", "CgoPprof")
330}
331
332func TestCgoPprofPIE(t *testing.T) {
333	testCgoPprof(t, "-ldflags=-extldflags=-pie", "CgoPprof")
334}
335
336func TestCgoPprofThread(t *testing.T) {
337	testCgoPprof(t, "", "CgoPprofThread")
338}
339
340func TestCgoPprofThreadNoTraceback(t *testing.T) {
341	testCgoPprof(t, "", "CgoPprofThreadNoTraceback")
342}
343
344func TestRaceProf(t *testing.T) {
345	if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
346		t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
347	}
348
349	testenv.MustHaveGoRun(t)
350
351	// This test requires building various packages with -race, so
352	// it's somewhat slow.
353	if testing.Short() {
354		t.Skip("skipping test in -short mode")
355	}
356
357	exe, err := buildTestProg(t, "testprogcgo", "-race")
358	if err != nil {
359		t.Fatal(err)
360	}
361
362	got, err := testEnv(exec.Command(exe, "CgoRaceprof")).CombinedOutput()
363	if err != nil {
364		t.Fatal(err)
365	}
366	want := "OK\n"
367	if string(got) != want {
368		t.Errorf("expected %q got %s", want, got)
369	}
370}
371
372func TestRaceSignal(t *testing.T) {
373	t.Parallel()
374	if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
375		t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
376	}
377
378	testenv.MustHaveGoRun(t)
379
380	// This test requires building various packages with -race, so
381	// it's somewhat slow.
382	if testing.Short() {
383		t.Skip("skipping test in -short mode")
384	}
385
386	exe, err := buildTestProg(t, "testprogcgo", "-race")
387	if err != nil {
388		t.Fatal(err)
389	}
390
391	got, err := testEnv(exec.Command(exe, "CgoRaceSignal")).CombinedOutput()
392	if err != nil {
393		t.Logf("%s\n", got)
394		t.Fatal(err)
395	}
396	want := "OK\n"
397	if string(got) != want {
398		t.Errorf("expected %q got %s", want, got)
399	}
400}
401
402func TestCgoNumGoroutine(t *testing.T) {
403	switch runtime.GOOS {
404	case "windows", "plan9":
405		t.Skipf("skipping numgoroutine test on %s", runtime.GOOS)
406	}
407	t.Parallel()
408	got := runTestProg(t, "testprogcgo", "NumGoroutine")
409	want := "OK\n"
410	if got != want {
411		t.Errorf("expected %q got %v", want, got)
412	}
413}
414
415func TestSigStackSwapping(t *testing.T) {
416	switch runtime.GOOS {
417	case "plan9", "windows":
418		t.Skip("no sigaltstack on %s", runtime.GOOS)
419	}
420	t.Parallel()
421	got := runTestProg(t, "testprogcgo", "SigStack")
422	want := "OK\n"
423	if got != want {
424		t.Errorf("expected %q got %v", want, got)
425	}
426}
427