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

View as plain text