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

View as plain text