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

View as plain text