732 lines
19 KiB
Go
732 lines
19 KiB
Go
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||
|
// Use of this source code is governed by a BSD-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
//go:build cgo
|
||
|
|
||
|
package runtime_test
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"internal/testenv"
|
||
|
"os"
|
||
|
"os/exec"
|
||
|
"runtime"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
"testing"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
func TestCgoCrashHandler(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
testCrashHandler(t, true)
|
||
|
}
|
||
|
|
||
|
func TestCgoSignalDeadlock(t *testing.T) {
|
||
|
// Don't call t.Parallel, since too much work going on at the
|
||
|
// same time can cause the testprogcgo code to overrun its
|
||
|
// timeouts (issue #18598).
|
||
|
|
||
|
if testing.Short() && runtime.GOOS == "windows" {
|
||
|
t.Skip("Skipping in short mode") // takes up to 64 seconds
|
||
|
}
|
||
|
got := runTestProg(t, "testprogcgo", "CgoSignalDeadlock")
|
||
|
want := "OK\n"
|
||
|
if got != want {
|
||
|
t.Fatalf("expected %q, but got:\n%s", want, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCgoTraceback(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
got := runTestProg(t, "testprogcgo", "CgoTraceback")
|
||
|
want := "OK\n"
|
||
|
if got != want {
|
||
|
t.Fatalf("expected %q, but got:\n%s", want, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCgoCallbackGC(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
switch runtime.GOOS {
|
||
|
case "plan9", "windows":
|
||
|
t.Skipf("no pthreads on %s", runtime.GOOS)
|
||
|
}
|
||
|
if testing.Short() {
|
||
|
switch {
|
||
|
case runtime.GOOS == "dragonfly":
|
||
|
t.Skip("see golang.org/issue/11990")
|
||
|
case runtime.GOOS == "linux" && runtime.GOARCH == "arm":
|
||
|
t.Skip("too slow for arm builders")
|
||
|
case runtime.GOOS == "linux" && (runtime.GOARCH == "mips64" || runtime.GOARCH == "mips64le"):
|
||
|
t.Skip("too slow for mips64x builders")
|
||
|
}
|
||
|
}
|
||
|
if testenv.Builder() == "darwin-amd64-10_14" {
|
||
|
// TODO(#23011): When the 10.14 builders are gone, remove this skip.
|
||
|
t.Skip("skipping due to platform bug on macOS 10.14; see https://golang.org/issue/43926")
|
||
|
}
|
||
|
got := runTestProg(t, "testprogcgo", "CgoCallbackGC")
|
||
|
want := "OK\n"
|
||
|
if got != want {
|
||
|
t.Fatalf("expected %q, but got:\n%s", want, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCgoExternalThreadPanic(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
if runtime.GOOS == "plan9" {
|
||
|
t.Skipf("no pthreads on %s", runtime.GOOS)
|
||
|
}
|
||
|
got := runTestProg(t, "testprogcgo", "CgoExternalThreadPanic")
|
||
|
want := "panic: BOOM"
|
||
|
if !strings.Contains(got, want) {
|
||
|
t.Fatalf("want failure containing %q. output:\n%s\n", want, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCgoExternalThreadSIGPROF(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
// issue 9456.
|
||
|
switch runtime.GOOS {
|
||
|
case "plan9", "windows":
|
||
|
t.Skipf("no pthreads on %s", runtime.GOOS)
|
||
|
}
|
||
|
|
||
|
got := runTestProg(t, "testprogcgo", "CgoExternalThreadSIGPROF", "GO_START_SIGPROF_THREAD=1")
|
||
|
if want := "OK\n"; got != want {
|
||
|
t.Fatalf("expected %q, but got:\n%s", want, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCgoExternalThreadSignal(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
// issue 10139
|
||
|
switch runtime.GOOS {
|
||
|
case "plan9", "windows":
|
||
|
t.Skipf("no pthreads on %s", runtime.GOOS)
|
||
|
}
|
||
|
|
||
|
got := runTestProg(t, "testprogcgo", "CgoExternalThreadSignal")
|
||
|
if want := "OK\n"; got != want {
|
||
|
t.Fatalf("expected %q, but got:\n%s", want, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCgoDLLImports(t *testing.T) {
|
||
|
// test issue 9356
|
||
|
if runtime.GOOS != "windows" {
|
||
|
t.Skip("skipping windows specific test")
|
||
|
}
|
||
|
got := runTestProg(t, "testprogcgo", "CgoDLLImportsMain")
|
||
|
want := "OK\n"
|
||
|
if got != want {
|
||
|
t.Fatalf("expected %q, but got %v", want, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCgoExecSignalMask(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
// Test issue 13164.
|
||
|
switch runtime.GOOS {
|
||
|
case "windows", "plan9":
|
||
|
t.Skipf("skipping signal mask test on %s", runtime.GOOS)
|
||
|
}
|
||
|
got := runTestProg(t, "testprogcgo", "CgoExecSignalMask", "GOTRACEBACK=system")
|
||
|
want := "OK\n"
|
||
|
if got != want {
|
||
|
t.Errorf("expected %q, got %v", want, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestEnsureDropM(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
// Test for issue 13881.
|
||
|
switch runtime.GOOS {
|
||
|
case "windows", "plan9":
|
||
|
t.Skipf("skipping dropm test on %s", runtime.GOOS)
|
||
|
}
|
||
|
got := runTestProg(t, "testprogcgo", "EnsureDropM")
|
||
|
want := "OK\n"
|
||
|
if got != want {
|
||
|
t.Errorf("expected %q, got %v", want, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Test for issue 14387.
|
||
|
// Test that the program that doesn't need any cgo pointer checking
|
||
|
// takes about the same amount of time with it as without it.
|
||
|
func TestCgoCheckBytes(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
// Make sure we don't count the build time as part of the run time.
|
||
|
testenv.MustHaveGoBuild(t)
|
||
|
exe, err := buildTestProg(t, "testprogcgo")
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
// Try it 10 times to avoid flakiness.
|
||
|
const tries = 10
|
||
|
var tot1, tot2 time.Duration
|
||
|
for i := 0; i < tries; i++ {
|
||
|
cmd := testenv.CleanCmdEnv(exec.Command(exe, "CgoCheckBytes"))
|
||
|
cmd.Env = append(cmd.Env, "GODEBUG=cgocheck=0", fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i))
|
||
|
|
||
|
start := time.Now()
|
||
|
cmd.Run()
|
||
|
d1 := time.Since(start)
|
||
|
|
||
|
cmd = testenv.CleanCmdEnv(exec.Command(exe, "CgoCheckBytes"))
|
||
|
cmd.Env = append(cmd.Env, fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i))
|
||
|
|
||
|
start = time.Now()
|
||
|
cmd.Run()
|
||
|
d2 := time.Since(start)
|
||
|
|
||
|
if d1*20 > d2 {
|
||
|
// The slow version (d2) was less than 20 times
|
||
|
// slower than the fast version (d1), so OK.
|
||
|
return
|
||
|
}
|
||
|
|
||
|
tot1 += d1
|
||
|
tot2 += d2
|
||
|
}
|
||
|
|
||
|
t.Errorf("cgo check too slow: got %v, expected at most %v", tot2/tries, (tot1/tries)*20)
|
||
|
}
|
||
|
|
||
|
func TestCgoPanicDeadlock(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
// test issue 14432
|
||
|
got := runTestProg(t, "testprogcgo", "CgoPanicDeadlock")
|
||
|
want := "panic: cgo error\n\n"
|
||
|
if !strings.HasPrefix(got, want) {
|
||
|
t.Fatalf("output does not start with %q:\n%s", want, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCgoCCodeSIGPROF(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
got := runTestProg(t, "testprogcgo", "CgoCCodeSIGPROF")
|
||
|
want := "OK\n"
|
||
|
if got != want {
|
||
|
t.Errorf("expected %q got %v", want, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCgoPprofCallback(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
switch runtime.GOOS {
|
||
|
case "windows", "plan9":
|
||
|
t.Skipf("skipping cgo pprof callback test on %s", runtime.GOOS)
|
||
|
}
|
||
|
got := runTestProg(t, "testprogcgo", "CgoPprofCallback")
|
||
|
want := "OK\n"
|
||
|
if got != want {
|
||
|
t.Errorf("expected %q got %v", want, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCgoCrashTraceback(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
switch platform := runtime.GOOS + "/" + runtime.GOARCH; platform {
|
||
|
case "darwin/amd64":
|
||
|
case "linux/amd64":
|
||
|
case "linux/ppc64le":
|
||
|
default:
|
||
|
t.Skipf("not yet supported on %s", platform)
|
||
|
}
|
||
|
if runtime.Compiler == "gccgo" {
|
||
|
t.Skip("gccgo does not have SetCgoTraceback")
|
||
|
}
|
||
|
got := runTestProg(t, "testprogcgo", "CrashTraceback")
|
||
|
for i := 1; i <= 3; i++ {
|
||
|
if !strings.Contains(got, fmt.Sprintf("cgo symbolizer:%d", i)) {
|
||
|
t.Errorf("missing cgo symbolizer:%d", i)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCgoCrashTracebackGo(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
switch platform := runtime.GOOS + "/" + runtime.GOARCH; platform {
|
||
|
case "darwin/amd64":
|
||
|
case "linux/amd64":
|
||
|
case "linux/ppc64le":
|
||
|
default:
|
||
|
t.Skipf("not yet supported on %s", platform)
|
||
|
}
|
||
|
if runtime.Compiler == "gccgo" {
|
||
|
t.Skip("gccgo does not have SetCgoTraceback")
|
||
|
}
|
||
|
got := runTestProg(t, "testprogcgo", "CrashTracebackGo")
|
||
|
for i := 1; i <= 3; i++ {
|
||
|
want := fmt.Sprintf("main.h%d", i)
|
||
|
if !strings.Contains(got, want) {
|
||
|
t.Errorf("missing %s", want)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCgoTracebackContext(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
if runtime.Compiler == "gccgo" {
|
||
|
t.Skip("gccgo does not have SetCgoTraceback")
|
||
|
}
|
||
|
got := runTestProg(t, "testprogcgo", "TracebackContext")
|
||
|
want := "OK\n"
|
||
|
if got != want {
|
||
|
t.Errorf("expected %q got %v", want, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCgoTracebackContextPreemption(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
if runtime.Compiler == "gccgo" {
|
||
|
t.Skip("gccgo does not have SetCgoTraceback")
|
||
|
}
|
||
|
got := runTestProg(t, "testprogcgo", "TracebackContextPreemption")
|
||
|
want := "OK\n"
|
||
|
if got != want {
|
||
|
t.Errorf("expected %q got %v", want, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func testCgoPprof(t *testing.T, buildArg, runArg, top, bottom string) {
|
||
|
t.Parallel()
|
||
|
if runtime.GOOS != "linux" || (runtime.GOARCH != "amd64" && runtime.GOARCH != "ppc64le") {
|
||
|
t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||
|
}
|
||
|
if runtime.Compiler == "gccgo" {
|
||
|
t.Skip("gccgo does not have SetCgoTraceback")
|
||
|
}
|
||
|
testenv.MustHaveGoRun(t)
|
||
|
|
||
|
exe, err := buildTestProg(t, "testprogcgo", buildArg)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
cmd := testenv.CleanCmdEnv(exec.Command(exe, runArg))
|
||
|
got, err := cmd.CombinedOutput()
|
||
|
if err != nil {
|
||
|
if testenv.Builder() == "linux-amd64-alpine" {
|
||
|
// See Issue 18243 and Issue 19938.
|
||
|
t.Skipf("Skipping failing test on Alpine (golang.org/issue/18243). Ignoring error: %v", err)
|
||
|
}
|
||
|
t.Fatalf("%s\n\n%v", got, err)
|
||
|
}
|
||
|
fn := strings.TrimSpace(string(got))
|
||
|
defer os.Remove(fn)
|
||
|
|
||
|
for try := 0; try < 2; try++ {
|
||
|
cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-tagignore=ignore", "-traces"))
|
||
|
// Check that pprof works both with and without explicit executable on command line.
|
||
|
if try == 0 {
|
||
|
cmd.Args = append(cmd.Args, exe, fn)
|
||
|
} else {
|
||
|
cmd.Args = append(cmd.Args, fn)
|
||
|
}
|
||
|
|
||
|
found := false
|
||
|
for i, e := range cmd.Env {
|
||
|
if strings.HasPrefix(e, "PPROF_TMPDIR=") {
|
||
|
cmd.Env[i] = "PPROF_TMPDIR=" + os.TempDir()
|
||
|
found = true
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
if !found {
|
||
|
cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
|
||
|
}
|
||
|
|
||
|
out, err := cmd.CombinedOutput()
|
||
|
t.Logf("%s:\n%s", cmd.Args, out)
|
||
|
if err != nil {
|
||
|
t.Error(err)
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
trace := findTrace(string(out), top)
|
||
|
if len(trace) == 0 {
|
||
|
t.Errorf("%s traceback missing.", top)
|
||
|
continue
|
||
|
}
|
||
|
if trace[len(trace)-1] != bottom {
|
||
|
t.Errorf("invalid traceback origin: got=%v; want=[%s ... %s]", trace, top, bottom)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCgoPprof(t *testing.T) {
|
||
|
testCgoPprof(t, "", "CgoPprof", "cpuHog", "runtime.main")
|
||
|
}
|
||
|
|
||
|
func TestCgoPprofPIE(t *testing.T) {
|
||
|
testCgoPprof(t, "-buildmode=pie", "CgoPprof", "cpuHog", "runtime.main")
|
||
|
}
|
||
|
|
||
|
func TestCgoPprofThread(t *testing.T) {
|
||
|
testCgoPprof(t, "", "CgoPprofThread", "cpuHogThread", "cpuHogThread2")
|
||
|
}
|
||
|
|
||
|
func TestCgoPprofThreadNoTraceback(t *testing.T) {
|
||
|
testCgoPprof(t, "", "CgoPprofThreadNoTraceback", "cpuHogThread", "runtime._ExternalCode")
|
||
|
}
|
||
|
|
||
|
func TestRaceProf(t *testing.T) {
|
||
|
if (runtime.GOOS != "linux" && runtime.GOOS != "freebsd") || runtime.GOARCH != "amd64" {
|
||
|
t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||
|
}
|
||
|
if runtime.Compiler == "gccgo" {
|
||
|
t.Skip("gccgo does not have SetCgoTraceback")
|
||
|
}
|
||
|
|
||
|
testenv.MustHaveGoRun(t)
|
||
|
|
||
|
// This test requires building various packages with -race, so
|
||
|
// it's somewhat slow.
|
||
|
if testing.Short() {
|
||
|
t.Skip("skipping test in -short mode")
|
||
|
}
|
||
|
|
||
|
exe, err := buildTestProg(t, "testprogcgo", "-race")
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoRaceprof")).CombinedOutput()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
want := "OK\n"
|
||
|
if string(got) != want {
|
||
|
t.Errorf("expected %q got %s", want, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestRaceSignal(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
if (runtime.GOOS != "linux" && runtime.GOOS != "freebsd") || runtime.GOARCH != "amd64" {
|
||
|
t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||
|
}
|
||
|
|
||
|
testenv.MustHaveGoRun(t)
|
||
|
|
||
|
// This test requires building various packages with -race, so
|
||
|
// it's somewhat slow.
|
||
|
if testing.Short() {
|
||
|
t.Skip("skipping test in -short mode")
|
||
|
}
|
||
|
|
||
|
exe, err := buildTestProg(t, "testprogcgo", "-race")
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoRaceSignal")).CombinedOutput()
|
||
|
if err != nil {
|
||
|
t.Logf("%s\n", got)
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
want := "OK\n"
|
||
|
if string(got) != want {
|
||
|
t.Errorf("expected %q got %s", want, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCgoNumGoroutine(t *testing.T) {
|
||
|
switch runtime.GOOS {
|
||
|
case "windows", "plan9":
|
||
|
t.Skipf("skipping numgoroutine test on %s", runtime.GOOS)
|
||
|
}
|
||
|
t.Parallel()
|
||
|
got := runTestProg(t, "testprogcgo", "NumGoroutine")
|
||
|
want := "OK\n"
|
||
|
if got != want {
|
||
|
t.Errorf("expected %q got %v", want, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCatchPanic(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
switch runtime.GOOS {
|
||
|
case "plan9", "windows":
|
||
|
t.Skipf("no signals on %s", runtime.GOOS)
|
||
|
case "darwin":
|
||
|
if runtime.GOARCH == "amd64" {
|
||
|
t.Skipf("crash() on darwin/amd64 doesn't raise SIGABRT")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
testenv.MustHaveGoRun(t)
|
||
|
|
||
|
exe, err := buildTestProg(t, "testprogcgo")
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
for _, early := range []bool{true, false} {
|
||
|
cmd := testenv.CleanCmdEnv(exec.Command(exe, "CgoCatchPanic"))
|
||
|
// Make sure a panic results in a crash.
|
||
|
cmd.Env = append(cmd.Env, "GOTRACEBACK=crash")
|
||
|
if early {
|
||
|
// Tell testprogcgo to install an early signal handler for SIGABRT
|
||
|
cmd.Env = append(cmd.Env, "CGOCATCHPANIC_EARLY_HANDLER=1")
|
||
|
}
|
||
|
if out, err := cmd.CombinedOutput(); err != nil {
|
||
|
t.Errorf("testprogcgo CgoCatchPanic failed: %v\n%s", err, out)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCgoLockOSThreadExit(t *testing.T) {
|
||
|
switch runtime.GOOS {
|
||
|
case "plan9", "windows":
|
||
|
t.Skipf("no pthreads on %s", runtime.GOOS)
|
||
|
}
|
||
|
t.Parallel()
|
||
|
testLockOSThreadExit(t, "testprogcgo")
|
||
|
}
|
||
|
|
||
|
func TestWindowsStackMemoryCgo(t *testing.T) {
|
||
|
if runtime.GOOS != "windows" {
|
||
|
t.Skip("skipping windows specific test")
|
||
|
}
|
||
|
testenv.SkipFlaky(t, 22575)
|
||
|
o := runTestProg(t, "testprogcgo", "StackMemory")
|
||
|
stackUsage, err := strconv.Atoi(o)
|
||
|
if err != nil {
|
||
|
t.Fatalf("Failed to read stack usage: %v", err)
|
||
|
}
|
||
|
if expected, got := 100<<10, stackUsage; got > expected {
|
||
|
t.Fatalf("expected < %d bytes of memory per thread, got %d", expected, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSigStackSwapping(t *testing.T) {
|
||
|
switch runtime.GOOS {
|
||
|
case "plan9", "windows":
|
||
|
t.Skipf("no sigaltstack on %s", runtime.GOOS)
|
||
|
}
|
||
|
t.Parallel()
|
||
|
got := runTestProg(t, "testprogcgo", "SigStack")
|
||
|
want := "OK\n"
|
||
|
if got != want {
|
||
|
t.Errorf("expected %q got %v", want, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCgoTracebackSigpanic(t *testing.T) {
|
||
|
// Test unwinding over a sigpanic in C code without a C
|
||
|
// symbolizer. See issue #23576.
|
||
|
if runtime.GOOS == "windows" {
|
||
|
// On Windows if we get an exception in C code, we let
|
||
|
// the Windows exception handler unwind it, rather
|
||
|
// than injecting a sigpanic.
|
||
|
t.Skip("no sigpanic in C on windows")
|
||
|
}
|
||
|
t.Parallel()
|
||
|
got := runTestProg(t, "testprogcgo", "TracebackSigpanic")
|
||
|
t.Log(got)
|
||
|
want := "runtime.sigpanic"
|
||
|
if !strings.Contains(got, want) {
|
||
|
t.Errorf("did not see %q in output", want)
|
||
|
}
|
||
|
// No runtime errors like "runtime: unexpected return pc".
|
||
|
nowant := "runtime: "
|
||
|
if strings.Contains(got, nowant) {
|
||
|
t.Errorf("unexpectedly saw %q in output", nowant)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCgoPanicCallback(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
got := runTestProg(t, "testprogcgo", "PanicCallback")
|
||
|
t.Log(got)
|
||
|
want := "panic: runtime error: invalid memory address or nil pointer dereference"
|
||
|
if !strings.Contains(got, want) {
|
||
|
t.Errorf("did not see %q in output", want)
|
||
|
}
|
||
|
want = "panic_callback"
|
||
|
if !strings.Contains(got, want) {
|
||
|
t.Errorf("did not see %q in output", want)
|
||
|
}
|
||
|
want = "PanicCallback"
|
||
|
if !strings.Contains(got, want) {
|
||
|
t.Errorf("did not see %q in output", want)
|
||
|
}
|
||
|
// No runtime errors like "runtime: unexpected return pc".
|
||
|
nowant := "runtime: "
|
||
|
if strings.Contains(got, nowant) {
|
||
|
t.Errorf("did not see %q in output", want)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Test that C code called via cgo can use large Windows thread stacks
|
||
|
// and call back in to Go without crashing. See issue #20975.
|
||
|
//
|
||
|
// See also TestBigStackCallbackSyscall.
|
||
|
func TestBigStackCallbackCgo(t *testing.T) {
|
||
|
if runtime.GOOS != "windows" {
|
||
|
t.Skip("skipping windows specific test")
|
||
|
}
|
||
|
t.Parallel()
|
||
|
got := runTestProg(t, "testprogcgo", "BigStack")
|
||
|
want := "OK\n"
|
||
|
if got != want {
|
||
|
t.Errorf("expected %q got %v", want, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func nextTrace(lines []string) ([]string, []string) {
|
||
|
var trace []string
|
||
|
for n, line := range lines {
|
||
|
if strings.HasPrefix(line, "---") {
|
||
|
return trace, lines[n+1:]
|
||
|
}
|
||
|
fields := strings.Fields(strings.TrimSpace(line))
|
||
|
if len(fields) == 0 {
|
||
|
continue
|
||
|
}
|
||
|
// Last field contains the function name.
|
||
|
trace = append(trace, fields[len(fields)-1])
|
||
|
}
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
func findTrace(text, top string) []string {
|
||
|
lines := strings.Split(text, "\n")
|
||
|
_, lines = nextTrace(lines) // Skip the header.
|
||
|
for len(lines) > 0 {
|
||
|
var t []string
|
||
|
t, lines = nextTrace(lines)
|
||
|
if len(t) == 0 {
|
||
|
continue
|
||
|
}
|
||
|
if t[0] == top {
|
||
|
return t
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func TestSegv(t *testing.T) {
|
||
|
switch runtime.GOOS {
|
||
|
case "plan9", "windows":
|
||
|
t.Skipf("no signals on %s", runtime.GOOS)
|
||
|
}
|
||
|
|
||
|
for _, test := range []string{"Segv", "SegvInCgo"} {
|
||
|
test := test
|
||
|
t.Run(test, func(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
got := runTestProg(t, "testprogcgo", test)
|
||
|
t.Log(got)
|
||
|
want := "SIGSEGV"
|
||
|
if !strings.Contains(got, want) {
|
||
|
if runtime.GOOS == "darwin" && runtime.GOARCH == "amd64" && strings.Contains(got, "fatal: morestack on g0") {
|
||
|
testenv.SkipFlaky(t, 39457)
|
||
|
}
|
||
|
t.Errorf("did not see %q in output", want)
|
||
|
}
|
||
|
|
||
|
// No runtime errors like "runtime: unknown pc".
|
||
|
switch runtime.GOOS {
|
||
|
case "darwin", "illumos", "solaris":
|
||
|
// Runtime sometimes throws when generating the traceback.
|
||
|
testenv.SkipFlaky(t, 49182)
|
||
|
case "linux":
|
||
|
if runtime.GOARCH == "386" {
|
||
|
// Runtime throws when generating a traceback from
|
||
|
// a VDSO call via asmcgocall.
|
||
|
testenv.SkipFlaky(t, 50504)
|
||
|
}
|
||
|
}
|
||
|
if test == "SegvInCgo" && strings.Contains(got, "runtime: unknown pc") {
|
||
|
testenv.SkipFlaky(t, 50979)
|
||
|
}
|
||
|
|
||
|
nowant := "runtime: "
|
||
|
if strings.Contains(got, nowant) {
|
||
|
t.Errorf("unexpectedly saw %q in output", nowant)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestAbortInCgo(t *testing.T) {
|
||
|
switch runtime.GOOS {
|
||
|
case "plan9", "windows":
|
||
|
// N.B. On Windows, C abort() causes the program to exit
|
||
|
// without going through the runtime at all.
|
||
|
t.Skipf("no signals on %s", runtime.GOOS)
|
||
|
}
|
||
|
|
||
|
t.Parallel()
|
||
|
got := runTestProg(t, "testprogcgo", "Abort")
|
||
|
t.Log(got)
|
||
|
want := "SIGABRT"
|
||
|
if !strings.Contains(got, want) {
|
||
|
t.Errorf("did not see %q in output", want)
|
||
|
}
|
||
|
// No runtime errors like "runtime: unknown pc".
|
||
|
nowant := "runtime: "
|
||
|
if strings.Contains(got, nowant) {
|
||
|
t.Errorf("did not see %q in output", want)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// TestEINTR tests that we handle EINTR correctly.
|
||
|
// See issue #20400 and friends.
|
||
|
func TestEINTR(t *testing.T) {
|
||
|
switch runtime.GOOS {
|
||
|
case "plan9", "windows":
|
||
|
t.Skipf("no EINTR on %s", runtime.GOOS)
|
||
|
case "linux":
|
||
|
if runtime.GOARCH == "386" {
|
||
|
// On linux-386 the Go signal handler sets
|
||
|
// a restorer function that is not preserved
|
||
|
// by the C sigaction call in the test,
|
||
|
// causing the signal handler to crash when
|
||
|
// returning the normal code. The test is not
|
||
|
// architecture-specific, so just skip on 386
|
||
|
// rather than doing a complicated workaround.
|
||
|
t.Skip("skipping on linux-386; C sigaction does not preserve Go restorer")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
t.Parallel()
|
||
|
output := runTestProg(t, "testprogcgo", "EINTR")
|
||
|
want := "OK\n"
|
||
|
if output != want {
|
||
|
t.Fatalf("want %s, got %s\n", want, output)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Issue #42207.
|
||
|
func TestNeedmDeadlock(t *testing.T) {
|
||
|
switch runtime.GOOS {
|
||
|
case "plan9", "windows":
|
||
|
t.Skipf("no signals on %s", runtime.GOOS)
|
||
|
}
|
||
|
output := runTestProg(t, "testprogcgo", "NeedmDeadlock")
|
||
|
want := "OK\n"
|
||
|
if output != want {
|
||
|
t.Fatalf("want %s, got %s\n", want, output)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCgoTracebackGoroutineProfile(t *testing.T) {
|
||
|
if runtime.Compiler == "gccgo" {
|
||
|
t.Skip("gccgo does not have SetCgoTraceback")
|
||
|
}
|
||
|
output := runTestProg(t, "testprogcgo", "GoroutineProfile")
|
||
|
want := "OK\n"
|
||
|
if output != want {
|
||
|
t.Fatalf("want %s, got %s\n", want, output)
|
||
|
}
|
||
|
}
|