Home | History | Annotate | Download | only in runtime
      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 
      7 package runtime_test
      8 
      9 import (
     10 	"bytes"
     11 	"fmt"
     12 	"internal/testenv"
     13 	"os"
     14 	"os/exec"
     15 	"runtime"
     16 	"strconv"
     17 	"strings"
     18 	"testing"
     19 	"time"
     20 )
     21 
     22 func TestCgoCrashHandler(t *testing.T) {
     23 	t.Parallel()
     24 	testCrashHandler(t, true)
     25 }
     26 
     27 func 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 
     42 func 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 
     51 func 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 
     74 func 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 
     86 func 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 	case "darwin":
     93 		if runtime.GOARCH != "arm" && runtime.GOARCH != "arm64" {
     94 			// static constructor needs external linking, but we don't support
     95 			// external linking on OS X 10.6.
     96 			out, err := exec.Command("uname", "-r").Output()
     97 			if err != nil {
     98 				t.Fatalf("uname -r failed: %v", err)
     99 			}
    100 			// OS X 10.6 == Darwin 10.x
    101 			if strings.HasPrefix(string(out), "10.") {
    102 				t.Skipf("no external linking on OS X 10.6")
    103 			}
    104 		}
    105 	}
    106 	if runtime.GOARCH == "ppc64" {
    107 		// TODO(austin) External linking not implemented on
    108 		// ppc64 (issue #8912)
    109 		t.Skipf("no external linking on ppc64")
    110 	}
    111 
    112 	exe, err := buildTestProg(t, "testprogcgo", "-tags=threadprof")
    113 	if err != nil {
    114 		t.Fatal(err)
    115 	}
    116 
    117 	got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoExternalThreadSIGPROF")).CombinedOutput()
    118 	if err != nil {
    119 		t.Fatalf("exit status: %v\n%s", err, got)
    120 	}
    121 
    122 	if want := "OK\n"; string(got) != want {
    123 		t.Fatalf("expected %q, but got:\n%s", want, got)
    124 	}
    125 }
    126 
    127 func TestCgoExternalThreadSignal(t *testing.T) {
    128 	t.Parallel()
    129 	// issue 10139
    130 	switch runtime.GOOS {
    131 	case "plan9", "windows":
    132 		t.Skipf("no pthreads on %s", runtime.GOOS)
    133 	}
    134 
    135 	exe, err := buildTestProg(t, "testprogcgo", "-tags=threadprof")
    136 	if err != nil {
    137 		t.Fatal(err)
    138 	}
    139 
    140 	got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoExternalThreadSIGPROF")).CombinedOutput()
    141 	if err != nil {
    142 		t.Fatalf("exit status: %v\n%s", err, got)
    143 	}
    144 
    145 	want := []byte("OK\n")
    146 	if !bytes.Equal(got, want) {
    147 		t.Fatalf("expected %q, but got:\n%s", want, got)
    148 	}
    149 }
    150 
    151 func TestCgoDLLImports(t *testing.T) {
    152 	// test issue 9356
    153 	if runtime.GOOS != "windows" {
    154 		t.Skip("skipping windows specific test")
    155 	}
    156 	got := runTestProg(t, "testprogcgo", "CgoDLLImportsMain")
    157 	want := "OK\n"
    158 	if got != want {
    159 		t.Fatalf("expected %q, but got %v", want, got)
    160 	}
    161 }
    162 
    163 func TestCgoExecSignalMask(t *testing.T) {
    164 	t.Parallel()
    165 	// Test issue 13164.
    166 	switch runtime.GOOS {
    167 	case "windows", "plan9":
    168 		t.Skipf("skipping signal mask test on %s", runtime.GOOS)
    169 	}
    170 	got := runTestProg(t, "testprogcgo", "CgoExecSignalMask")
    171 	want := "OK\n"
    172 	if got != want {
    173 		t.Errorf("expected %q, got %v", want, got)
    174 	}
    175 }
    176 
    177 func TestEnsureDropM(t *testing.T) {
    178 	t.Parallel()
    179 	// Test for issue 13881.
    180 	switch runtime.GOOS {
    181 	case "windows", "plan9":
    182 		t.Skipf("skipping dropm test on %s", runtime.GOOS)
    183 	}
    184 	got := runTestProg(t, "testprogcgo", "EnsureDropM")
    185 	want := "OK\n"
    186 	if got != want {
    187 		t.Errorf("expected %q, got %v", want, got)
    188 	}
    189 }
    190 
    191 // Test for issue 14387.
    192 // Test that the program that doesn't need any cgo pointer checking
    193 // takes about the same amount of time with it as without it.
    194 func TestCgoCheckBytes(t *testing.T) {
    195 	t.Parallel()
    196 	// Make sure we don't count the build time as part of the run time.
    197 	testenv.MustHaveGoBuild(t)
    198 	exe, err := buildTestProg(t, "testprogcgo")
    199 	if err != nil {
    200 		t.Fatal(err)
    201 	}
    202 
    203 	// Try it 10 times to avoid flakiness.
    204 	const tries = 10
    205 	var tot1, tot2 time.Duration
    206 	for i := 0; i < tries; i++ {
    207 		cmd := testenv.CleanCmdEnv(exec.Command(exe, "CgoCheckBytes"))
    208 		cmd.Env = append(cmd.Env, "GODEBUG=cgocheck=0", fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i))
    209 
    210 		start := time.Now()
    211 		cmd.Run()
    212 		d1 := time.Since(start)
    213 
    214 		cmd = testenv.CleanCmdEnv(exec.Command(exe, "CgoCheckBytes"))
    215 		cmd.Env = append(cmd.Env, fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i))
    216 
    217 		start = time.Now()
    218 		cmd.Run()
    219 		d2 := time.Since(start)
    220 
    221 		if d1*20 > d2 {
    222 			// The slow version (d2) was less than 20 times
    223 			// slower than the fast version (d1), so OK.
    224 			return
    225 		}
    226 
    227 		tot1 += d1
    228 		tot2 += d2
    229 	}
    230 
    231 	t.Errorf("cgo check too slow: got %v, expected at most %v", tot2/tries, (tot1/tries)*20)
    232 }
    233 
    234 func TestCgoPanicDeadlock(t *testing.T) {
    235 	t.Parallel()
    236 	// test issue 14432
    237 	got := runTestProg(t, "testprogcgo", "CgoPanicDeadlock")
    238 	want := "panic: cgo error\n\n"
    239 	if !strings.HasPrefix(got, want) {
    240 		t.Fatalf("output does not start with %q:\n%s", want, got)
    241 	}
    242 }
    243 
    244 func TestCgoCCodeSIGPROF(t *testing.T) {
    245 	t.Parallel()
    246 	got := runTestProg(t, "testprogcgo", "CgoCCodeSIGPROF")
    247 	want := "OK\n"
    248 	if got != want {
    249 		t.Errorf("expected %q got %v", want, got)
    250 	}
    251 }
    252 
    253 func TestCgoCrashTraceback(t *testing.T) {
    254 	t.Parallel()
    255 	if runtime.GOOS != "linux" || (runtime.GOARCH != "amd64" && runtime.GOARCH != "ppc64le") {
    256 		t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
    257 	}
    258 	got := runTestProg(t, "testprogcgo", "CrashTraceback")
    259 	for i := 1; i <= 3; i++ {
    260 		if !strings.Contains(got, fmt.Sprintf("cgo symbolizer:%d", i)) {
    261 			t.Errorf("missing cgo symbolizer:%d", i)
    262 		}
    263 	}
    264 }
    265 
    266 func TestCgoTracebackContext(t *testing.T) {
    267 	t.Parallel()
    268 	got := runTestProg(t, "testprogcgo", "TracebackContext")
    269 	want := "OK\n"
    270 	if got != want {
    271 		t.Errorf("expected %q got %v", want, got)
    272 	}
    273 }
    274 
    275 func testCgoPprof(t *testing.T, buildArg, runArg string) {
    276 	t.Parallel()
    277 	if runtime.GOOS != "linux" || (runtime.GOARCH != "amd64" && runtime.GOARCH != "ppc64le") {
    278 		t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
    279 	}
    280 	testenv.MustHaveGoRun(t)
    281 
    282 	exe, err := buildTestProg(t, "testprogcgo", buildArg)
    283 	if err != nil {
    284 		t.Fatal(err)
    285 	}
    286 
    287 	got, err := testenv.CleanCmdEnv(exec.Command(exe, runArg)).CombinedOutput()
    288 	if err != nil {
    289 		if testenv.Builder() == "linux-amd64-alpine" {
    290 			// See Issue 18243 and Issue 19938.
    291 			t.Skipf("Skipping failing test on Alpine (golang.org/issue/18243). Ignoring error: %v", err)
    292 		}
    293 		t.Fatal(err)
    294 	}
    295 	fn := strings.TrimSpace(string(got))
    296 	defer os.Remove(fn)
    297 
    298 	for try := 0; try < 2; try++ {
    299 		cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-top", "-nodecount=1"))
    300 		// Check that pprof works both with and without explicit executable on command line.
    301 		if try == 0 {
    302 			cmd.Args = append(cmd.Args, exe, fn)
    303 		} else {
    304 			cmd.Args = append(cmd.Args, fn)
    305 		}
    306 
    307 		found := false
    308 		for i, e := range cmd.Env {
    309 			if strings.HasPrefix(e, "PPROF_TMPDIR=") {
    310 				cmd.Env[i] = "PPROF_TMPDIR=" + os.TempDir()
    311 				found = true
    312 				break
    313 			}
    314 		}
    315 		if !found {
    316 			cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
    317 		}
    318 
    319 		top, err := cmd.CombinedOutput()
    320 		t.Logf("%s:\n%s", cmd.Args, top)
    321 		if err != nil {
    322 			t.Error(err)
    323 		} else if !bytes.Contains(top, []byte("cpuHog")) {
    324 			t.Error("missing cpuHog in pprof output")
    325 		}
    326 	}
    327 }
    328 
    329 func TestCgoPprof(t *testing.T) {
    330 	testCgoPprof(t, "", "CgoPprof")
    331 }
    332 
    333 func TestCgoPprofPIE(t *testing.T) {
    334 	testCgoPprof(t, "-buildmode=pie", "CgoPprof")
    335 }
    336 
    337 func TestCgoPprofThread(t *testing.T) {
    338 	testCgoPprof(t, "", "CgoPprofThread")
    339 }
    340 
    341 func TestCgoPprofThreadNoTraceback(t *testing.T) {
    342 	testCgoPprof(t, "", "CgoPprofThreadNoTraceback")
    343 }
    344 
    345 func TestRaceProf(t *testing.T) {
    346 	if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
    347 		t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
    348 	}
    349 
    350 	testenv.MustHaveGoRun(t)
    351 
    352 	// This test requires building various packages with -race, so
    353 	// it's somewhat slow.
    354 	if testing.Short() {
    355 		t.Skip("skipping test in -short mode")
    356 	}
    357 
    358 	exe, err := buildTestProg(t, "testprogcgo", "-race")
    359 	if err != nil {
    360 		t.Fatal(err)
    361 	}
    362 
    363 	got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoRaceprof")).CombinedOutput()
    364 	if err != nil {
    365 		t.Fatal(err)
    366 	}
    367 	want := "OK\n"
    368 	if string(got) != want {
    369 		t.Errorf("expected %q got %s", want, got)
    370 	}
    371 }
    372 
    373 func TestRaceSignal(t *testing.T) {
    374 	t.Parallel()
    375 	if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
    376 		t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
    377 	}
    378 
    379 	testenv.MustHaveGoRun(t)
    380 
    381 	// This test requires building various packages with -race, so
    382 	// it's somewhat slow.
    383 	if testing.Short() {
    384 		t.Skip("skipping test in -short mode")
    385 	}
    386 
    387 	exe, err := buildTestProg(t, "testprogcgo", "-race")
    388 	if err != nil {
    389 		t.Fatal(err)
    390 	}
    391 
    392 	got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoRaceSignal")).CombinedOutput()
    393 	if err != nil {
    394 		t.Logf("%s\n", got)
    395 		t.Fatal(err)
    396 	}
    397 	want := "OK\n"
    398 	if string(got) != want {
    399 		t.Errorf("expected %q got %s", want, got)
    400 	}
    401 }
    402 
    403 func TestCgoNumGoroutine(t *testing.T) {
    404 	switch runtime.GOOS {
    405 	case "windows", "plan9":
    406 		t.Skipf("skipping numgoroutine test on %s", runtime.GOOS)
    407 	}
    408 	t.Parallel()
    409 	got := runTestProg(t, "testprogcgo", "NumGoroutine")
    410 	want := "OK\n"
    411 	if got != want {
    412 		t.Errorf("expected %q got %v", want, got)
    413 	}
    414 }
    415 
    416 func TestCatchPanic(t *testing.T) {
    417 	t.Parallel()
    418 	switch runtime.GOOS {
    419 	case "plan9", "windows":
    420 		t.Skipf("no signals on %s", runtime.GOOS)
    421 	case "darwin":
    422 		if runtime.GOARCH == "amd64" {
    423 			t.Skipf("crash() on darwin/amd64 doesn't raise SIGABRT")
    424 		}
    425 	}
    426 
    427 	testenv.MustHaveGoRun(t)
    428 
    429 	exe, err := buildTestProg(t, "testprogcgo")
    430 	if err != nil {
    431 		t.Fatal(err)
    432 	}
    433 
    434 	for _, early := range []bool{true, false} {
    435 		cmd := testenv.CleanCmdEnv(exec.Command(exe, "CgoCatchPanic"))
    436 		// Make sure a panic results in a crash.
    437 		cmd.Env = append(cmd.Env, "GOTRACEBACK=crash")
    438 		if early {
    439 			// Tell testprogcgo to install an early signal handler for SIGABRT
    440 			cmd.Env = append(cmd.Env, "CGOCATCHPANIC_EARLY_HANDLER=1")
    441 		}
    442 		if out, err := cmd.CombinedOutput(); err != nil {
    443 			t.Errorf("testprogcgo CgoCatchPanic failed: %v\n%s", err, out)
    444 		}
    445 	}
    446 }
    447 
    448 func TestCgoLockOSThreadExit(t *testing.T) {
    449 	switch runtime.GOOS {
    450 	case "plan9", "windows":
    451 		t.Skipf("no pthreads on %s", runtime.GOOS)
    452 	}
    453 	t.Parallel()
    454 	testLockOSThreadExit(t, "testprogcgo")
    455 }
    456 
    457 func TestWindowsStackMemoryCgo(t *testing.T) {
    458 	if runtime.GOOS != "windows" {
    459 		t.Skip("skipping windows specific test")
    460 	}
    461 	testenv.SkipFlaky(t, 22575)
    462 	o := runTestProg(t, "testprogcgo", "StackMemory")
    463 	stackUsage, err := strconv.Atoi(o)
    464 	if err != nil {
    465 		t.Fatalf("Failed to read stack usage: %v", err)
    466 	}
    467 	if expected, got := 100<<10, stackUsage; got > expected {
    468 		t.Fatalf("expected < %d bytes of memory per thread, got %d", expected, got)
    469 	}
    470 }
    471 
    472 func TestSigStackSwapping(t *testing.T) {
    473 	switch runtime.GOOS {
    474 	case "plan9", "windows":
    475 		t.Skipf("no sigaltstack on %s", runtime.GOOS)
    476 	}
    477 	t.Parallel()
    478 	got := runTestProg(t, "testprogcgo", "SigStack")
    479 	want := "OK\n"
    480 	if got != want {
    481 		t.Errorf("expected %q got %v", want, got)
    482 	}
    483 }
    484 
    485 func TestCgoTracebackSigpanic(t *testing.T) {
    486 	// Test unwinding over a sigpanic in C code without a C
    487 	// symbolizer. See issue #23576.
    488 	if runtime.GOOS == "windows" {
    489 		// On Windows if we get an exception in C code, we let
    490 		// the Windows exception handler unwind it, rather
    491 		// than injecting a sigpanic.
    492 		t.Skip("no sigpanic in C on windows")
    493 	}
    494 	t.Parallel()
    495 	got := runTestProg(t, "testprogcgo", "TracebackSigpanic")
    496 	want := "runtime.sigpanic"
    497 	if !strings.Contains(got, want) {
    498 		t.Fatalf("want failure containing %q. output:\n%s\n", want, got)
    499 	}
    500 	nowant := "unexpected return pc"
    501 	if strings.Contains(got, nowant) {
    502 		t.Fatalf("failure incorrectly contains %q. output:\n%s\n", nowant, got)
    503 	}
    504 }
    505