Source file src/runtime/crash_cgo_test.go

     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  
     7  package runtime_test
     8  
     9  import (
    10  	"fmt"
    11  	"internal/asan"
    12  	"internal/goos"
    13  	"internal/msan"
    14  	"internal/race"
    15  	"internal/testenv"
    16  	"os"
    17  	"os/exec"
    18  	"path/filepath"
    19  	"runtime"
    20  	"strconv"
    21  	"strings"
    22  	"testing"
    23  	"time"
    24  )
    25  
    26  func TestCgoCrashHandler(t *testing.T) {
    27  	t.Parallel()
    28  	testCrashHandler(t, true)
    29  }
    30  
    31  func TestCgoSignalDeadlock(t *testing.T) {
    32  	// Don't call t.Parallel, since too much work going on at the
    33  	// same time can cause the testprogcgo code to overrun its
    34  	// timeouts (issue #18598).
    35  
    36  	if testing.Short() && runtime.GOOS == "windows" {
    37  		t.Skip("Skipping in short mode") // takes up to 64 seconds
    38  	}
    39  	if runtime.GOOS == "freebsd" && race.Enabled {
    40  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
    41  	}
    42  	got := runTestProg(t, "testprogcgo", "CgoSignalDeadlock")
    43  	want := "OK\n"
    44  	if got != want {
    45  		t.Fatalf("expected %q, but got:\n%s", want, got)
    46  	}
    47  }
    48  
    49  func TestCgoTraceback(t *testing.T) {
    50  	if runtime.GOOS == "freebsd" && race.Enabled {
    51  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
    52  	}
    53  
    54  	t.Parallel()
    55  	got := runTestProg(t, "testprogcgo", "CgoTraceback")
    56  	want := "OK\n"
    57  	if got != want {
    58  		t.Fatalf("expected %q, but got:\n%s", want, got)
    59  	}
    60  }
    61  
    62  func TestCgoCallbackGC(t *testing.T) {
    63  	t.Parallel()
    64  	switch runtime.GOOS {
    65  	case "plan9", "windows":
    66  		t.Skipf("no pthreads on %s", runtime.GOOS)
    67  	}
    68  	if runtime.GOOS == "freebsd" && race.Enabled {
    69  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
    70  	}
    71  	if testing.Short() {
    72  		switch {
    73  		case runtime.GOOS == "dragonfly":
    74  			t.Skip("see golang.org/issue/11990")
    75  		case runtime.GOOS == "linux" && runtime.GOARCH == "arm":
    76  			t.Skip("too slow for arm builders")
    77  		case runtime.GOOS == "linux" && (runtime.GOARCH == "mips64" || runtime.GOARCH == "mips64le"):
    78  			t.Skip("too slow for mips64x builders")
    79  		}
    80  	}
    81  	got := runTestProg(t, "testprogcgo", "CgoCallbackGC")
    82  	want := "OK\n"
    83  	if got != want {
    84  		t.Fatalf("expected %q, but got:\n%s", want, got)
    85  	}
    86  }
    87  
    88  func TestCgoCallbackPprof(t *testing.T) {
    89  	t.Parallel()
    90  	switch runtime.GOOS {
    91  	case "plan9", "windows":
    92  		t.Skipf("no pthreads on %s", runtime.GOOS)
    93  	}
    94  	if runtime.GOOS == "freebsd" && race.Enabled {
    95  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
    96  	}
    97  	if testenv.CPUProfilingBroken() {
    98  		t.Skip("skipping on platform with broken profiling")
    99  	}
   100  
   101  	got := runTestProg(t, "testprogcgo", "CgoCallbackPprof")
   102  	if want := "OK\n"; got != want {
   103  		t.Fatalf("expected %q, but got:\n%s", want, got)
   104  	}
   105  }
   106  
   107  func TestCgoCallbackX15(t *testing.T) {
   108  	t.Parallel()
   109  	if runtime.GOARCH != "amd64" {
   110  		t.Skipf("X15 test only relevant on amd64")
   111  	}
   112  	if runtime.GOOS == "freebsd" && race.Enabled {
   113  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   114  	}
   115  
   116  	got := runTestProg(t, "testprogcgo", "CgoCallbackX15")
   117  	if want := "OK\n"; got != want {
   118  		t.Fatalf("expected %q, but got:\n%s", want, got)
   119  	}
   120  }
   121  
   122  func TestSecretCgo(t *testing.T) {
   123  	// secret.Do only takes effect on the platforms listed here; everywhere
   124  	// else it invokes f directly, so there is nothing for this test to
   125  	// exercise. Skip before paying for a GOEXPERIMENT=runtimesecret build,
   126  	// which rebuilds the whole dependency graph from scratch. See #79751.
   127  	switch runtime.GOOS + "/" + runtime.GOARCH {
   128  	case "linux/amd64", "linux/arm64":
   129  	default:
   130  		t.Skipf("runtime/secret not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
   131  	}
   132  	t.Parallel()
   133  	testenv.MustHaveGoBuild(t)
   134  	testenv.MustHaveCGO(t)
   135  
   136  	exe := filepath.Join(t.TempDir(), "secretcgo.exe")
   137  	// Use testenv.Command, not exec.Command: if the build hangs (#76314),
   138  	// this kills it and dumps its stacks instead of wedging the whole
   139  	// runtime package until its timeout. See #79751, #79754.
   140  	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe)
   141  	cmd.Dir = "testdata/testprogcgo"
   142  	cmd = testenv.CleanCmdEnv(cmd)
   143  	cmd.Env = append(cmd.Env, "GOEXPERIMENT=runtimesecret")
   144  	out, err := cmd.CombinedOutput()
   145  	if err != nil {
   146  		t.Fatalf("building testprogcgo with runtimesecret: %v\n%s", err, out)
   147  	}
   148  
   149  	got := runBuiltTestProg(t, exe, "SecretCgo")
   150  	if want := "OK\n"; got != want {
   151  		t.Fatalf("expected %q, got:\n%s", want, got)
   152  	}
   153  }
   154  
   155  func TestCgoExternalThreadPanic(t *testing.T) {
   156  	t.Parallel()
   157  	if runtime.GOOS == "plan9" {
   158  		t.Skipf("no pthreads on %s", runtime.GOOS)
   159  	}
   160  	if runtime.GOOS == "freebsd" && race.Enabled {
   161  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   162  	}
   163  	got := runTestProg(t, "testprogcgo", "CgoExternalThreadPanic")
   164  	want := "panic: BOOM"
   165  	if !strings.Contains(got, want) {
   166  		t.Fatalf("want failure containing %q. output:\n%s\n", want, got)
   167  	}
   168  }
   169  
   170  func TestCgoExternalThreadSIGPROF(t *testing.T) {
   171  	t.Parallel()
   172  	// issue 9456.
   173  	switch runtime.GOOS {
   174  	case "plan9", "windows":
   175  		t.Skipf("no pthreads on %s", runtime.GOOS)
   176  	}
   177  	if runtime.GOOS == "freebsd" && race.Enabled {
   178  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   179  	}
   180  
   181  	got := runTestProg(t, "testprogcgo", "CgoExternalThreadSIGPROF", "GO_START_SIGPROF_THREAD=1")
   182  	if want := "OK\n"; got != want {
   183  		t.Fatalf("expected %q, but got:\n%s", want, got)
   184  	}
   185  }
   186  
   187  func TestCgoExternalThreadSignal(t *testing.T) {
   188  	t.Parallel()
   189  	// issue 10139
   190  	switch runtime.GOOS {
   191  	case "plan9", "windows":
   192  		t.Skipf("no pthreads on %s", runtime.GOOS)
   193  	}
   194  	if runtime.GOOS == "freebsd" && race.Enabled {
   195  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   196  	}
   197  
   198  	got := runTestProg(t, "testprogcgo", "CgoExternalThreadSignal")
   199  	if want := "OK\n"; got != want {
   200  		if runtime.GOOS == "ios" && strings.Contains(got, "C signal did not crash as expected") {
   201  			testenv.SkipFlaky(t, 59913)
   202  		}
   203  		t.Fatalf("expected %q, but got:\n%s", want, got)
   204  	}
   205  }
   206  
   207  func TestCgoDLLImports(t *testing.T) {
   208  	// test issue 9356
   209  	if runtime.GOOS != "windows" {
   210  		t.Skip("skipping windows specific test")
   211  	}
   212  	if runtime.GOOS == "freebsd" && race.Enabled {
   213  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   214  	}
   215  	got := runTestProg(t, "testprogcgo", "CgoDLLImportsMain")
   216  	want := "OK\n"
   217  	if got != want {
   218  		t.Fatalf("expected %q, but got %v", want, got)
   219  	}
   220  }
   221  
   222  func TestCgoExecSignalMask(t *testing.T) {
   223  	t.Parallel()
   224  	// Test issue 13164.
   225  	switch runtime.GOOS {
   226  	case "windows", "plan9":
   227  		t.Skipf("skipping signal mask test on %s", runtime.GOOS)
   228  	}
   229  	if runtime.GOOS == "freebsd" && race.Enabled {
   230  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   231  	}
   232  	got := runTestProg(t, "testprogcgo", "CgoExecSignalMask", "GOTRACEBACK=system")
   233  	want := "OK\n"
   234  	if got != want {
   235  		t.Errorf("expected %q, got %v", want, got)
   236  	}
   237  }
   238  
   239  func TestEnsureDropM(t *testing.T) {
   240  	t.Parallel()
   241  	// Test for issue 13881.
   242  	switch runtime.GOOS {
   243  	case "windows", "plan9":
   244  		t.Skipf("skipping dropm test on %s", runtime.GOOS)
   245  	}
   246  	if runtime.GOOS == "freebsd" && race.Enabled {
   247  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   248  	}
   249  	got := runTestProg(t, "testprogcgo", "EnsureDropM")
   250  	want := "OK\n"
   251  	if got != want {
   252  		t.Errorf("expected %q, got %v", want, got)
   253  	}
   254  }
   255  
   256  // Test for issue 14387.
   257  // Test that the program that doesn't need any cgo pointer checking
   258  // takes about the same amount of time with it as without it.
   259  func TestCgoCheckBytes(t *testing.T) {
   260  	t.Parallel()
   261  	// Make sure we don't count the build time as part of the run time.
   262  	testenv.MustHaveGoBuild(t)
   263  	if runtime.GOOS == "freebsd" && race.Enabled {
   264  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   265  	}
   266  	exe, err := buildTestProg(t, "testprogcgo")
   267  	if err != nil {
   268  		t.Fatal(err)
   269  	}
   270  
   271  	// Try it 10 times to avoid flakiness.
   272  	const tries = 10
   273  	var tot1, tot2 time.Duration
   274  	for i := 0; i < tries; i++ {
   275  		cmd := testenv.CleanCmdEnv(exec.Command(exe, "CgoCheckBytes"))
   276  		cmd.Env = append(cmd.Env, "GODEBUG=cgocheck=0", fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i))
   277  
   278  		start := time.Now()
   279  		cmd.Run()
   280  		d1 := time.Since(start)
   281  
   282  		cmd = testenv.CleanCmdEnv(exec.Command(exe, "CgoCheckBytes"))
   283  		cmd.Env = append(cmd.Env, fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i))
   284  
   285  		start = time.Now()
   286  		cmd.Run()
   287  		d2 := time.Since(start)
   288  
   289  		if d1*20 > d2 {
   290  			// The slow version (d2) was less than 20 times
   291  			// slower than the fast version (d1), so OK.
   292  			return
   293  		}
   294  
   295  		tot1 += d1
   296  		tot2 += d2
   297  	}
   298  
   299  	t.Errorf("cgo check too slow: got %v, expected at most %v", tot2/tries, (tot1/tries)*20)
   300  }
   301  
   302  func TestCgoPanicDeadlock(t *testing.T) {
   303  	t.Parallel()
   304  	if runtime.GOOS == "freebsd" && race.Enabled {
   305  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   306  	}
   307  	// test issue 14432
   308  	got := runTestProg(t, "testprogcgo", "CgoPanicDeadlock")
   309  	want := "panic: cgo error\n\n"
   310  	if !strings.HasPrefix(got, want) {
   311  		t.Fatalf("output does not start with %q:\n%s", want, got)
   312  	}
   313  }
   314  
   315  func TestCgoCCodeSIGPROF(t *testing.T) {
   316  	t.Parallel()
   317  	if runtime.GOOS == "freebsd" && race.Enabled {
   318  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   319  	}
   320  	got := runTestProg(t, "testprogcgo", "CgoCCodeSIGPROF")
   321  	want := "OK\n"
   322  	if got != want {
   323  		t.Errorf("expected %q got %v", want, got)
   324  	}
   325  }
   326  
   327  func TestCgoPprofCallback(t *testing.T) {
   328  	if testing.Short() {
   329  		t.Skip("skipping in short mode") // takes a full second
   330  	}
   331  	switch runtime.GOOS {
   332  	case "windows", "plan9":
   333  		t.Skipf("skipping cgo pprof callback test on %s", runtime.GOOS)
   334  	}
   335  	if runtime.GOOS == "freebsd" && race.Enabled {
   336  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   337  	}
   338  	got := runTestProg(t, "testprogcgo", "CgoPprofCallback")
   339  	want := "OK\n"
   340  	if got != want {
   341  		t.Errorf("expected %q got %v", want, got)
   342  	}
   343  }
   344  
   345  func TestCgoCrashTraceback(t *testing.T) {
   346  	t.Parallel()
   347  	switch platform := runtime.GOOS + "/" + runtime.GOARCH; platform {
   348  	case "darwin/amd64":
   349  	case "linux/amd64":
   350  	case "linux/arm64":
   351  	case "linux/loong64":
   352  	case "linux/ppc64":
   353  	case "linux/ppc64le":
   354  	default:
   355  		t.Skipf("not yet supported on %s", platform)
   356  	}
   357  	if asan.Enabled || msan.Enabled {
   358  		t.Skip("skipping test on ASAN/MSAN: triggers SIGSEGV in sanitizer runtime")
   359  	}
   360  	if runtime.GOOS == "freebsd" && race.Enabled {
   361  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   362  	}
   363  	got := runTestProg(t, "testprogcgo", "CrashTraceback")
   364  	for i := 1; i <= 3; i++ {
   365  		if !strings.Contains(got, fmt.Sprintf("cgo symbolizer:%d", i)) {
   366  			t.Errorf("missing cgo symbolizer:%d in %s", i, got)
   367  		}
   368  	}
   369  }
   370  
   371  func TestCgoCrashTracebackGo(t *testing.T) {
   372  	t.Parallel()
   373  	switch platform := runtime.GOOS + "/" + runtime.GOARCH; platform {
   374  	case "darwin/amd64":
   375  	case "linux/amd64":
   376  	case "linux/arm64":
   377  	case "linux/loong64":
   378  	case "linux/ppc64":
   379  	case "linux/ppc64le":
   380  	default:
   381  		t.Skipf("not yet supported on %s", platform)
   382  	}
   383  	if runtime.GOOS == "freebsd" && race.Enabled {
   384  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   385  	}
   386  	got := runTestProg(t, "testprogcgo", "CrashTracebackGo")
   387  	for i := 1; i <= 3; i++ {
   388  		want := fmt.Sprintf("main.h%d", i)
   389  		if !strings.Contains(got, want) {
   390  			t.Errorf("missing %s", want)
   391  		}
   392  	}
   393  }
   394  
   395  func TestCgoTracebackContext(t *testing.T) {
   396  	t.Parallel()
   397  	if runtime.GOOS == "freebsd" && race.Enabled {
   398  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   399  	}
   400  	got := runTestProg(t, "testprogcgo", "TracebackContext")
   401  	want := "OK\n"
   402  	if got != want {
   403  		t.Errorf("expected %q got %v", want, got)
   404  	}
   405  }
   406  
   407  func TestCgoTracebackContextPreemption(t *testing.T) {
   408  	t.Parallel()
   409  	if runtime.GOOS == "freebsd" && race.Enabled {
   410  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   411  	}
   412  	got := runTestProg(t, "testprogcgo", "TracebackContextPreemption")
   413  	want := "OK\n"
   414  	if got != want {
   415  		t.Errorf("expected %q got %v", want, got)
   416  	}
   417  }
   418  
   419  func TestCgoTracebackContextProfile(t *testing.T) {
   420  	t.Parallel()
   421  	if runtime.GOOS == "freebsd" && race.Enabled {
   422  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   423  	}
   424  	got := runTestProg(t, "testprogcgo", "TracebackContextProfile")
   425  	want := "OK\n"
   426  	if got != want {
   427  		t.Errorf("expected %q got %v", want, got)
   428  	}
   429  }
   430  
   431  func testCgoPprof(t *testing.T, buildArg, runArg, top, bottom string) {
   432  	t.Parallel()
   433  	if runtime.GOOS != "linux" || (runtime.GOARCH != "amd64" && runtime.GOARCH != "ppc64" && runtime.GOARCH != "ppc64le" && runtime.GOARCH != "arm64" && runtime.GOARCH != "loong64") {
   434  		t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
   435  	}
   436  	if runtime.GOOS == "freebsd" && race.Enabled {
   437  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   438  	}
   439  	testenv.MustHaveGoRun(t)
   440  
   441  	var args []string
   442  	if buildArg != "" {
   443  		args = append(args, buildArg)
   444  	}
   445  	exe, err := buildTestProg(t, "testprogcgo", args...)
   446  	if err != nil {
   447  		t.Fatal(err)
   448  	}
   449  
   450  	cmd := testenv.CleanCmdEnv(exec.Command(exe, runArg))
   451  	got, err := cmd.CombinedOutput()
   452  	if err != nil {
   453  		if testenv.Builder() == "linux-amd64-alpine" {
   454  			// See Issue 18243 and Issue 19938.
   455  			t.Skipf("Skipping failing test on Alpine (golang.org/issue/18243). Ignoring error: %v", err)
   456  		}
   457  		t.Fatalf("%s\n\n%v", got, err)
   458  	}
   459  	fn := strings.TrimSpace(string(got))
   460  	defer os.Remove(fn)
   461  
   462  	for try := 0; try < 2; try++ {
   463  		cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-tagignore=ignore", "-traces"))
   464  		// Check that pprof works both with and without explicit executable on command line.
   465  		if try == 0 {
   466  			cmd.Args = append(cmd.Args, exe, fn)
   467  		} else {
   468  			cmd.Args = append(cmd.Args, fn)
   469  		}
   470  
   471  		found := false
   472  		for i, e := range cmd.Env {
   473  			if strings.HasPrefix(e, "PPROF_TMPDIR=") {
   474  				cmd.Env[i] = "PPROF_TMPDIR=" + os.TempDir()
   475  				found = true
   476  				break
   477  			}
   478  		}
   479  		if !found {
   480  			cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
   481  		}
   482  
   483  		out, err := cmd.CombinedOutput()
   484  		t.Logf("%s:\n%s", cmd.Args, out)
   485  		if err != nil {
   486  			t.Error(err)
   487  			continue
   488  		}
   489  
   490  		trace := findTrace(string(out), top)
   491  		if len(trace) == 0 {
   492  			t.Errorf("%s traceback missing.", top)
   493  			continue
   494  		}
   495  		if trace[len(trace)-1] != bottom {
   496  			t.Errorf("invalid traceback origin: got=%v; want=[%s ... %s]", trace, top, bottom)
   497  		}
   498  	}
   499  }
   500  
   501  func TestCgoPprof(t *testing.T) {
   502  	testCgoPprof(t, "", "CgoPprof", "cpuHog", "runtime.main")
   503  }
   504  
   505  func TestCgoPprofPIE(t *testing.T) {
   506  	if race.Enabled {
   507  		t.Skip("skipping test: -race + PIE not supported")
   508  	}
   509  	testCgoPprof(t, "-buildmode=pie", "CgoPprof", "cpuHog", "runtime.main")
   510  }
   511  
   512  func TestCgoPprofThread(t *testing.T) {
   513  	testCgoPprof(t, "", "CgoPprofThread", "cpuHogThread", "cpuHogThread2")
   514  }
   515  
   516  func TestCgoPprofThreadNoTraceback(t *testing.T) {
   517  	testCgoPprof(t, "", "CgoPprofThreadNoTraceback", "cpuHogThread", "runtime._ExternalCode")
   518  }
   519  
   520  func TestRaceProf(t *testing.T) {
   521  	if !race.Enabled {
   522  		t.Skip("skipping: race detector not enabled")
   523  	}
   524  	if runtime.GOOS == "windows" {
   525  		t.Skipf("skipping: test requires pthread support")
   526  		// TODO: Can this test be rewritten to use the C11 thread API instead?
   527  	}
   528  	if runtime.GOOS == "freebsd" && race.Enabled {
   529  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   530  	}
   531  
   532  	testenv.MustHaveGoRun(t)
   533  
   534  	exe, err := buildTestProg(t, "testprogcgo")
   535  	if err != nil {
   536  		t.Fatal(err)
   537  	}
   538  
   539  	got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoRaceprof")).CombinedOutput()
   540  	if err != nil {
   541  		t.Fatal(err)
   542  	}
   543  	want := "OK\n"
   544  	if string(got) != want {
   545  		t.Errorf("expected %q got %s", want, got)
   546  	}
   547  }
   548  
   549  func TestRaceSignal(t *testing.T) {
   550  	if !race.Enabled {
   551  		t.Skip("skipping: race detector not enabled")
   552  	}
   553  	if runtime.GOOS == "windows" {
   554  		t.Skipf("skipping: test requires pthread support")
   555  		// TODO: Can this test be rewritten to use the C11 thread API instead?
   556  	}
   557  	if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
   558  		testenv.SkipFlaky(t, 60316)
   559  	}
   560  	if runtime.GOOS == "freebsd" && race.Enabled {
   561  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   562  	}
   563  
   564  	t.Parallel()
   565  
   566  	testenv.MustHaveGoRun(t)
   567  
   568  	exe, err := buildTestProg(t, "testprogcgo")
   569  	if err != nil {
   570  		t.Fatal(err)
   571  	}
   572  
   573  	got, err := testenv.CleanCmdEnv(testenv.Command(t, exe, "CgoRaceSignal")).CombinedOutput()
   574  	if err != nil {
   575  		t.Logf("%s\n", got)
   576  		t.Fatal(err)
   577  	}
   578  	want := "OK\n"
   579  	if string(got) != want {
   580  		t.Errorf("expected %q got %s", want, got)
   581  	}
   582  }
   583  
   584  func TestCgoNumGoroutine(t *testing.T) {
   585  	switch runtime.GOOS {
   586  	case "windows", "plan9":
   587  		t.Skipf("skipping numgoroutine test on %s", runtime.GOOS)
   588  	}
   589  	if runtime.GOOS == "freebsd" && race.Enabled {
   590  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   591  	}
   592  	t.Parallel()
   593  	got := runTestProg(t, "testprogcgo", "NumGoroutine")
   594  	want := "OK\n"
   595  	if got != want {
   596  		t.Errorf("expected %q got %v", want, got)
   597  	}
   598  }
   599  
   600  func TestCatchPanic(t *testing.T) {
   601  	t.Parallel()
   602  	switch runtime.GOOS {
   603  	case "plan9", "windows":
   604  		t.Skipf("no signals on %s", runtime.GOOS)
   605  	case "darwin":
   606  		if runtime.GOARCH == "amd64" {
   607  			t.Skipf("crash() on darwin/amd64 doesn't raise SIGABRT")
   608  		}
   609  	}
   610  	if runtime.GOOS == "freebsd" && race.Enabled {
   611  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   612  	}
   613  
   614  	testenv.MustHaveGoRun(t)
   615  
   616  	exe, err := buildTestProg(t, "testprogcgo")
   617  	if err != nil {
   618  		t.Fatal(err)
   619  	}
   620  
   621  	for _, early := range []bool{true, false} {
   622  		cmd := testenv.CleanCmdEnv(exec.Command(exe, "CgoCatchPanic"))
   623  		// Make sure a panic results in a crash.
   624  		cmd.Env = append(cmd.Env, "GOTRACEBACK=crash")
   625  		if early {
   626  			// Tell testprogcgo to install an early signal handler for SIGABRT
   627  			cmd.Env = append(cmd.Env, "CGOCATCHPANIC_EARLY_HANDLER=1")
   628  		}
   629  		if out, err := cmd.CombinedOutput(); err != nil {
   630  			t.Errorf("testprogcgo CgoCatchPanic failed: %v\n%s", err, out)
   631  		}
   632  	}
   633  }
   634  
   635  func TestCgoLockOSThreadExit(t *testing.T) {
   636  	switch runtime.GOOS {
   637  	case "plan9", "windows":
   638  		t.Skipf("no pthreads on %s", runtime.GOOS)
   639  	}
   640  	if runtime.GOOS == "freebsd" && race.Enabled {
   641  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   642  	}
   643  	t.Parallel()
   644  	testLockOSThreadExit(t, "testprogcgo")
   645  }
   646  
   647  func TestWindowsStackMemoryCgo(t *testing.T) {
   648  	if runtime.GOOS != "windows" {
   649  		t.Skip("skipping windows specific test")
   650  	}
   651  	if race.Enabled {
   652  		t.Skip("skipping test: race mode uses more stack memory")
   653  	}
   654  	testenv.SkipFlaky(t, 22575)
   655  	o := runTestProg(t, "testprogcgo", "StackMemory")
   656  	stackUsage, err := strconv.Atoi(o)
   657  	if err != nil {
   658  		t.Fatalf("Failed to read stack usage: %v", err)
   659  	}
   660  	if expected, got := 100<<10, stackUsage; got > expected {
   661  		t.Fatalf("expected < %d bytes of memory per thread, got %d", expected, got)
   662  	}
   663  }
   664  
   665  func TestSigStackSwapping(t *testing.T) {
   666  	switch runtime.GOOS {
   667  	case "plan9", "windows":
   668  		t.Skipf("no sigaltstack on %s", runtime.GOOS)
   669  	}
   670  	if runtime.GOOS == "freebsd" && race.Enabled {
   671  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   672  	}
   673  	t.Parallel()
   674  	got := runTestProg(t, "testprogcgo", "SigStack")
   675  	want := "OK\n"
   676  	if got != want {
   677  		t.Errorf("expected %q got %v", want, got)
   678  	}
   679  }
   680  
   681  func TestCgoTracebackSigpanic(t *testing.T) {
   682  	// Test unwinding over a sigpanic in C code without a C
   683  	// symbolizer. See issue #23576.
   684  	if runtime.GOOS == "windows" {
   685  		// On Windows if we get an exception in C code, we let
   686  		// the Windows exception handler unwind it, rather
   687  		// than injecting a sigpanic.
   688  		t.Skip("no sigpanic in C on windows")
   689  	}
   690  	if asan.Enabled || msan.Enabled {
   691  		t.Skip("skipping test on ASAN/MSAN: triggers SIGSEGV in sanitizer runtime")
   692  	}
   693  	if runtime.GOOS == "freebsd" && race.Enabled {
   694  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   695  	}
   696  	if runtime.GOOS == "ios" {
   697  		testenv.SkipFlaky(t, 59912)
   698  	}
   699  	t.Parallel()
   700  	got := runTestProg(t, "testprogcgo", "TracebackSigpanic")
   701  	t.Log(got)
   702  	// We should see the function that calls the C function.
   703  	want := "main.TracebackSigpanic"
   704  	if !strings.Contains(got, want) {
   705  		if runtime.GOOS == "android" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
   706  			testenv.SkipFlaky(t, 58794)
   707  		}
   708  		t.Errorf("did not see %q in output", want)
   709  	}
   710  	// We shouldn't inject a sigpanic call. (see issue 57698)
   711  	nowant := "runtime.sigpanic"
   712  	if strings.Contains(got, nowant) {
   713  		t.Errorf("unexpectedly saw %q in output", nowant)
   714  	}
   715  	// No runtime errors like "runtime: unexpected return pc".
   716  	nowant = "runtime: "
   717  	if strings.Contains(got, nowant) {
   718  		t.Errorf("unexpectedly saw %q in output", nowant)
   719  	}
   720  }
   721  
   722  func TestCgoPanicCallback(t *testing.T) {
   723  	if runtime.GOOS == "freebsd" && race.Enabled {
   724  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   725  	}
   726  	t.Parallel()
   727  	got := runTestProg(t, "testprogcgo", "PanicCallback")
   728  	t.Log(got)
   729  	want := "panic: runtime error: invalid memory address or nil pointer dereference"
   730  	if !strings.Contains(got, want) {
   731  		t.Errorf("did not see %q in output", want)
   732  	}
   733  	want = "panic_callback"
   734  	if !strings.Contains(got, want) {
   735  		t.Errorf("did not see %q in output", want)
   736  	}
   737  	want = "PanicCallback"
   738  	if !strings.Contains(got, want) {
   739  		t.Errorf("did not see %q in output", want)
   740  	}
   741  	// No runtime errors like "runtime: unexpected return pc".
   742  	nowant := "runtime: "
   743  	if strings.Contains(got, nowant) {
   744  		t.Errorf("did not see %q in output", want)
   745  	}
   746  }
   747  
   748  // Test that C code called via cgo can use large Windows thread stacks
   749  // and call back in to Go without crashing. See issue #20975.
   750  //
   751  // See also TestBigStackCallbackSyscall.
   752  func TestBigStackCallbackCgo(t *testing.T) {
   753  	if runtime.GOOS != "windows" {
   754  		t.Skip("skipping windows specific test")
   755  	}
   756  	t.Parallel()
   757  	got := runTestProg(t, "testprogcgo", "BigStack")
   758  	want := "OK\n"
   759  	if got != want {
   760  		t.Errorf("expected %q got %v", want, got)
   761  	}
   762  }
   763  
   764  func nextTrace(lines []string) ([]string, []string) {
   765  	var trace []string
   766  	for n, line := range lines {
   767  		if strings.HasPrefix(line, "---") {
   768  			return trace, lines[n+1:]
   769  		}
   770  		fields := strings.Fields(strings.TrimSpace(line))
   771  		if len(fields) == 0 {
   772  			continue
   773  		}
   774  		// Last field contains the function name.
   775  		trace = append(trace, fields[len(fields)-1])
   776  	}
   777  	return nil, nil
   778  }
   779  
   780  func findTrace(text, top string) []string {
   781  	lines := strings.Split(text, "\n")
   782  	_, lines = nextTrace(lines) // Skip the header.
   783  	for len(lines) > 0 {
   784  		var t []string
   785  		t, lines = nextTrace(lines)
   786  		if len(t) == 0 {
   787  			continue
   788  		}
   789  		if t[0] == top {
   790  			return t
   791  		}
   792  	}
   793  	return nil
   794  }
   795  
   796  func TestSegv(t *testing.T) {
   797  	switch runtime.GOOS {
   798  	case "plan9", "windows":
   799  		t.Skipf("no signals on %s", runtime.GOOS)
   800  	}
   801  	if race.Enabled || asan.Enabled || msan.Enabled {
   802  		t.Skip("skipping test on race/ASAN/MSAN: triggers SIGSEGV in sanitizer runtime")
   803  	}
   804  
   805  	for _, test := range []string{"Segv", "SegvInCgo", "TgkillSegv", "TgkillSegvInCgo"} {
   806  		// The tgkill variants only run on Linux.
   807  		if runtime.GOOS != "linux" && strings.HasPrefix(test, "Tgkill") {
   808  			continue
   809  		}
   810  
   811  		t.Run(test, func(t *testing.T) {
   812  			if test == "SegvInCgo" && runtime.GOOS == "ios" {
   813  				testenv.SkipFlaky(t, 59947) // Don't even try, in case it times out.
   814  			}
   815  			if strings.HasSuffix(test, "InCgo") && runtime.GOOS == "freebsd" && race.Enabled {
   816  				t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   817  			}
   818  
   819  			t.Parallel()
   820  			prog := "testprog"
   821  			if strings.HasSuffix(test, "InCgo") {
   822  				prog = "testprogcgo"
   823  			}
   824  			got := runTestProg(t, prog, test)
   825  			t.Log(got)
   826  			want := "SIGSEGV"
   827  			if !strings.Contains(got, want) {
   828  				if runtime.GOOS == "darwin" && runtime.GOARCH == "amd64" && strings.Contains(got, "fatal: morestack on g0") {
   829  					testenv.SkipFlaky(t, 39457)
   830  				}
   831  				t.Errorf("did not see %q in output", want)
   832  			}
   833  
   834  			// No runtime errors like "runtime: unknown pc".
   835  			switch runtime.GOOS {
   836  			case "darwin", "ios", "illumos", "solaris":
   837  				// Runtime sometimes throws when generating the traceback.
   838  				testenv.SkipFlaky(t, 49182)
   839  			case "linux":
   840  				if runtime.GOARCH == "386" {
   841  					// Runtime throws when generating a traceback from
   842  					// a VDSO call via asmcgocall.
   843  					testenv.SkipFlaky(t, 50504)
   844  				}
   845  			}
   846  			if test == "SegvInCgo" && strings.Contains(got, "unknown pc") {
   847  				testenv.SkipFlaky(t, 50979)
   848  			}
   849  
   850  			for _, nowant := range []string{"fatal error: ", "runtime: "} {
   851  				if strings.Contains(got, nowant) {
   852  					if runtime.GOOS == "darwin" && strings.Contains(got, "0xb01dfacedebac1e") {
   853  						// See the comment in signal_darwin_amd64.go.
   854  						t.Skip("skipping due to Darwin handling of malformed addresses")
   855  					}
   856  					t.Errorf("unexpectedly saw %q in output", nowant)
   857  				}
   858  			}
   859  		})
   860  	}
   861  }
   862  
   863  func TestAbortInCgo(t *testing.T) {
   864  	switch runtime.GOOS {
   865  	case "plan9", "windows":
   866  		// N.B. On Windows, C abort() causes the program to exit
   867  		// without going through the runtime at all.
   868  		t.Skipf("no signals on %s", runtime.GOOS)
   869  	}
   870  	if runtime.GOOS == "freebsd" && race.Enabled {
   871  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   872  	}
   873  
   874  	t.Parallel()
   875  	got := runTestProg(t, "testprogcgo", "Abort")
   876  	t.Log(got)
   877  	want := "SIGABRT"
   878  	if !strings.Contains(got, want) {
   879  		t.Errorf("did not see %q in output", want)
   880  	}
   881  	// No runtime errors like "runtime: unknown pc".
   882  	nowant := "runtime: "
   883  	if strings.Contains(got, nowant) {
   884  		t.Errorf("did not see %q in output", want)
   885  	}
   886  }
   887  
   888  // TestEINTR tests that we handle EINTR correctly.
   889  // See issue #20400 and friends.
   890  func TestEINTR(t *testing.T) {
   891  	switch runtime.GOOS {
   892  	case "plan9", "windows":
   893  		t.Skipf("no EINTR on %s", runtime.GOOS)
   894  	}
   895  	if runtime.GOOS == "freebsd" && race.Enabled {
   896  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   897  	}
   898  
   899  	t.Parallel()
   900  	output := runTestProg(t, "testprogcgo", "EINTR")
   901  	want := "OK\n"
   902  	if output != want {
   903  		t.Fatalf("want %s, got %s\n", want, output)
   904  	}
   905  }
   906  
   907  // Issue #42207.
   908  func TestNeedmDeadlock(t *testing.T) {
   909  	switch runtime.GOOS {
   910  	case "plan9", "windows":
   911  		t.Skipf("no signals on %s", runtime.GOOS)
   912  	}
   913  	if runtime.GOOS == "freebsd" && race.Enabled {
   914  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   915  	}
   916  	output := runTestProg(t, "testprogcgo", "NeedmDeadlock")
   917  	want := "OK\n"
   918  	if output != want {
   919  		t.Fatalf("want %s, got %s\n", want, output)
   920  	}
   921  }
   922  
   923  func TestCgoNoCallback(t *testing.T) {
   924  	if runtime.GOOS == "freebsd" && race.Enabled {
   925  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   926  	}
   927  	got := runTestProg(t, "testprogcgo", "CgoNoCallback")
   928  	want := "function marked with #cgo nocallback called back into Go"
   929  	if !strings.Contains(got, want) {
   930  		t.Fatalf("did not see %q in output:\n%s", want, got)
   931  	}
   932  }
   933  
   934  func TestCgoNoEscape(t *testing.T) {
   935  	if asan.Enabled {
   936  		t.Skip("skipping test: ASAN forces extra heap allocations")
   937  	}
   938  	if runtime.GOOS == "freebsd" && race.Enabled {
   939  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   940  	}
   941  	got := runTestProg(t, "testprogcgo", "CgoNoEscape")
   942  	want := "OK\n"
   943  	if got != want {
   944  		t.Fatalf("want %s, got %s\n", want, got)
   945  	}
   946  }
   947  
   948  // Issue #63739.
   949  func TestCgoEscapeWithMultiplePointers(t *testing.T) {
   950  	if runtime.GOOS == "freebsd" && race.Enabled {
   951  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   952  	}
   953  	got := runTestProg(t, "testprogcgo", "CgoEscapeWithMultiplePointers")
   954  	want := "OK\n"
   955  	if got != want {
   956  		t.Fatalf("output is %s; want %s", got, want)
   957  	}
   958  }
   959  
   960  func TestCgoTracebackGoroutineProfile(t *testing.T) {
   961  	if runtime.GOOS == "freebsd" && race.Enabled {
   962  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   963  	}
   964  	output := runTestProg(t, "testprogcgo", "GoroutineProfile")
   965  	want := "OK\n"
   966  	if output != want {
   967  		t.Fatalf("want %s, got %s\n", want, output)
   968  	}
   969  }
   970  
   971  func TestCgoSigfwd(t *testing.T) {
   972  	t.Parallel()
   973  	if !goos.IsUnix {
   974  		t.Skipf("no signals on %s", runtime.GOOS)
   975  	}
   976  	if runtime.GOOS == "freebsd" && race.Enabled {
   977  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   978  	}
   979  
   980  	got := runTestProg(t, "testprogcgo", "CgoSigfwd", "GO_TEST_CGOSIGFWD=1")
   981  	if want := "OK\n"; got != want {
   982  		t.Fatalf("expected %q, but got:\n%s", want, got)
   983  	}
   984  }
   985  
   986  func TestDestructorCallback(t *testing.T) {
   987  	if runtime.GOOS == "freebsd" && race.Enabled {
   988  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   989  	}
   990  	t.Parallel()
   991  	got := runTestProg(t, "testprogcgo", "DestructorCallback")
   992  	if want := "OK\n"; got != want {
   993  		t.Errorf("expected %q, but got:\n%s", want, got)
   994  	}
   995  }
   996  
   997  func TestEnsureBindM(t *testing.T) {
   998  	t.Parallel()
   999  	switch runtime.GOOS {
  1000  	case "windows", "plan9":
  1001  		t.Skipf("skipping bindm test on %s", runtime.GOOS)
  1002  	}
  1003  	if runtime.GOOS == "freebsd" && race.Enabled {
  1004  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
  1005  	}
  1006  	got := runTestProg(t, "testprogcgo", "EnsureBindM")
  1007  	want := "OK\n"
  1008  	if got != want {
  1009  		t.Errorf("expected %q, got %v", want, got)
  1010  	}
  1011  }
  1012  
  1013  func TestStackSwitchCallback(t *testing.T) {
  1014  	t.Parallel()
  1015  	switch runtime.GOOS {
  1016  	case "windows", "plan9", "android", "ios", "openbsd": // no getcontext
  1017  		t.Skipf("skipping test on %s", runtime.GOOS)
  1018  	}
  1019  	if asan.Enabled {
  1020  		// ASAN prints this as a warning.
  1021  		t.Skip("skipping test on ASAN because ASAN doesn't fully support makecontext/swapcontext functions")
  1022  	}
  1023  	if runtime.GOOS == "freebsd" && race.Enabled {
  1024  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
  1025  	}
  1026  	got := runTestProg(t, "testprogcgo", "StackSwitchCallback")
  1027  	skip := "SKIP\n"
  1028  	if got == skip {
  1029  		t.Skip("skipping on musl/bionic libc")
  1030  	}
  1031  	want := "OK\n"
  1032  	if got != want {
  1033  		t.Errorf("expected %q, got %v", want, got)
  1034  	}
  1035  }
  1036  
  1037  func TestCgoToGoCallGoexit(t *testing.T) {
  1038  	if runtime.GOOS == "plan9" || runtime.GOOS == "windows" {
  1039  		t.Skipf("no pthreads on %s", runtime.GOOS)
  1040  	}
  1041  	if runtime.GOOS == "freebsd" && race.Enabled {
  1042  		t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
  1043  	}
  1044  	output := runTestProg(t, "testprogcgo", "CgoToGoCallGoexit")
  1045  	if !strings.Contains(output, "runtime.Goexit called in a thread that was not created by the Go runtime") {
  1046  		t.Fatalf("output should contain %s, got %s", "runtime.Goexit called in a thread that was not created by the Go runtime", output)
  1047  	}
  1048  }
  1049  

View as plain text