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

View as plain text