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

View as plain text