Source file src/runtime/crash_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  package runtime_test
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"errors"
    11  	"flag"
    12  	"fmt"
    13  	"internal/testenv"
    14  	traceparse "internal/trace"
    15  	"io"
    16  	"log"
    17  	"os"
    18  	"os/exec"
    19  	"path/filepath"
    20  	"regexp"
    21  	"runtime"
    22  	"runtime/trace"
    23  	"strings"
    24  	"sync"
    25  	"testing"
    26  	"time"
    27  )
    28  
    29  var toRemove []string
    30  
    31  const entrypointVar = "RUNTIME_TEST_ENTRYPOINT"
    32  
    33  func TestMain(m *testing.M) {
    34  	switch entrypoint := os.Getenv(entrypointVar); entrypoint {
    35  	case "crash":
    36  		crash()
    37  		panic("unreachable")
    38  	default:
    39  		log.Fatalf("invalid %s: %q", entrypointVar, entrypoint)
    40  	case "":
    41  		// fall through to normal behavior
    42  	}
    43  
    44  	_, coreErrBefore := os.Stat("core")
    45  
    46  	status := m.Run()
    47  	for _, file := range toRemove {
    48  		os.RemoveAll(file)
    49  	}
    50  
    51  	_, coreErrAfter := os.Stat("core")
    52  	if coreErrBefore != nil && coreErrAfter == nil {
    53  		fmt.Fprintln(os.Stderr, "runtime.test: some test left a core file behind")
    54  		if status == 0 {
    55  			status = 1
    56  		}
    57  	}
    58  
    59  	os.Exit(status)
    60  }
    61  
    62  var testprog struct {
    63  	sync.Mutex
    64  	dir    string
    65  	target map[string]*buildexe
    66  }
    67  
    68  type buildexe struct {
    69  	once sync.Once
    70  	exe  string
    71  	err  error
    72  }
    73  
    74  func runTestProg(t *testing.T, binary, name string, env ...string) string {
    75  	if *flagQuick {
    76  		t.Skip("-quick")
    77  	}
    78  
    79  	testenv.MustHaveGoBuild(t)
    80  	t.Helper()
    81  
    82  	exe, err := buildTestProg(t, binary)
    83  	if err != nil {
    84  		t.Fatal(err)
    85  	}
    86  
    87  	return runBuiltTestProg(t, exe, name, env...)
    88  }
    89  
    90  func runBuiltTestProg(t *testing.T, exe, name string, env ...string) string {
    91  	t.Helper()
    92  
    93  	if *flagQuick {
    94  		t.Skip("-quick")
    95  	}
    96  
    97  	start := time.Now()
    98  
    99  	cmd := testenv.CleanCmdEnv(testenv.Command(t, exe, name))
   100  	cmd.Env = append(cmd.Env, env...)
   101  	if testing.Short() {
   102  		cmd.Env = append(cmd.Env, "RUNTIME_TEST_SHORT=1")
   103  	}
   104  	out, err := cmd.CombinedOutput()
   105  	if err == nil {
   106  		t.Logf("%v (%v): ok", cmd, time.Since(start))
   107  	} else {
   108  		if _, ok := err.(*exec.ExitError); ok {
   109  			t.Logf("%v: %v", cmd, err)
   110  		} else if errors.Is(err, exec.ErrWaitDelay) {
   111  			t.Fatalf("%v: %v", cmd, err)
   112  		} else {
   113  			t.Fatalf("%v failed to start: %v", cmd, err)
   114  		}
   115  	}
   116  	return string(out)
   117  }
   118  
   119  var serializeBuild = make(chan bool, 2)
   120  
   121  func buildTestProg(t *testing.T, binary string, flags ...string) (string, error) {
   122  	if *flagQuick {
   123  		t.Skip("-quick")
   124  	}
   125  	testenv.MustHaveGoBuild(t)
   126  
   127  	testprog.Lock()
   128  	if testprog.dir == "" {
   129  		dir, err := os.MkdirTemp("", "go-build")
   130  		if err != nil {
   131  			t.Fatalf("failed to create temp directory: %v", err)
   132  		}
   133  		testprog.dir = dir
   134  		toRemove = append(toRemove, dir)
   135  	}
   136  
   137  	if testprog.target == nil {
   138  		testprog.target = make(map[string]*buildexe)
   139  	}
   140  	name := binary
   141  	if len(flags) > 0 {
   142  		name += "_" + strings.Join(flags, "_")
   143  	}
   144  	target, ok := testprog.target[name]
   145  	if !ok {
   146  		target = &buildexe{}
   147  		testprog.target[name] = target
   148  	}
   149  
   150  	dir := testprog.dir
   151  
   152  	// Unlock testprog while actually building, so that other
   153  	// tests can look up executables that were already built.
   154  	testprog.Unlock()
   155  
   156  	target.once.Do(func() {
   157  		// Only do two "go build"'s at a time,
   158  		// to keep load from getting too high.
   159  		serializeBuild <- true
   160  		defer func() { <-serializeBuild }()
   161  
   162  		// Don't get confused if testenv.GoToolPath calls t.Skip.
   163  		target.err = errors.New("building test called t.Skip")
   164  
   165  		exe := filepath.Join(dir, name+".exe")
   166  
   167  		start := time.Now()
   168  		cmd := exec.Command(testenv.GoToolPath(t), append([]string{"build", "-o", exe}, flags...)...)
   169  		t.Logf("running %v", cmd)
   170  		cmd.Dir = "testdata/" + binary
   171  		cmd = testenv.CleanCmdEnv(cmd)
   172  
   173  		// Add the rangefunc GOEXPERIMENT unconditionally since some tests depend on it.
   174  		// TODO(61405): Remove this once it's enabled by default.
   175  		edited := false
   176  		for i := range cmd.Env {
   177  			e := cmd.Env[i]
   178  			if _, vars, ok := strings.Cut(e, "GOEXPERIMENT="); ok {
   179  				cmd.Env[i] = "GOEXPERIMENT=" + vars + ",rangefunc"
   180  				edited = true
   181  			}
   182  		}
   183  		if !edited {
   184  			cmd.Env = append(cmd.Env, "GOEXPERIMENT=rangefunc")
   185  		}
   186  
   187  		out, err := cmd.CombinedOutput()
   188  		if err != nil {
   189  			target.err = fmt.Errorf("building %s %v: %v\n%s", binary, flags, err, out)
   190  		} else {
   191  			t.Logf("built %v in %v", name, time.Since(start))
   192  			target.exe = exe
   193  			target.err = nil
   194  		}
   195  	})
   196  
   197  	return target.exe, target.err
   198  }
   199  
   200  func TestVDSO(t *testing.T) {
   201  	t.Parallel()
   202  	output := runTestProg(t, "testprog", "SignalInVDSO")
   203  	want := "success\n"
   204  	if output != want {
   205  		t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
   206  	}
   207  }
   208  
   209  func testCrashHandler(t *testing.T, cgo bool) {
   210  	type crashTest struct {
   211  		Cgo bool
   212  	}
   213  	var output string
   214  	if cgo {
   215  		output = runTestProg(t, "testprogcgo", "Crash")
   216  	} else {
   217  		output = runTestProg(t, "testprog", "Crash")
   218  	}
   219  	want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n"
   220  	if output != want {
   221  		t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
   222  	}
   223  }
   224  
   225  func TestCrashHandler(t *testing.T) {
   226  	testCrashHandler(t, false)
   227  }
   228  
   229  func testDeadlock(t *testing.T, name string) {
   230  	// External linking brings in cgo, causing deadlock detection not working.
   231  	testenv.MustInternalLink(t, false)
   232  
   233  	output := runTestProg(t, "testprog", name)
   234  	want := "fatal error: all goroutines are asleep - deadlock!\n"
   235  	if !strings.HasPrefix(output, want) {
   236  		t.Fatalf("output does not start with %q:\n%s", want, output)
   237  	}
   238  }
   239  
   240  func TestSimpleDeadlock(t *testing.T) {
   241  	testDeadlock(t, "SimpleDeadlock")
   242  }
   243  
   244  func TestInitDeadlock(t *testing.T) {
   245  	testDeadlock(t, "InitDeadlock")
   246  }
   247  
   248  func TestLockedDeadlock(t *testing.T) {
   249  	testDeadlock(t, "LockedDeadlock")
   250  }
   251  
   252  func TestLockedDeadlock2(t *testing.T) {
   253  	testDeadlock(t, "LockedDeadlock2")
   254  }
   255  
   256  func TestGoexitDeadlock(t *testing.T) {
   257  	// External linking brings in cgo, causing deadlock detection not working.
   258  	testenv.MustInternalLink(t, false)
   259  
   260  	output := runTestProg(t, "testprog", "GoexitDeadlock")
   261  	want := "no goroutines (main called runtime.Goexit) - deadlock!"
   262  	if !strings.Contains(output, want) {
   263  		t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
   264  	}
   265  }
   266  
   267  func TestStackOverflow(t *testing.T) {
   268  	output := runTestProg(t, "testprog", "StackOverflow")
   269  	want := []string{
   270  		"runtime: goroutine stack exceeds 1474560-byte limit\n",
   271  		"fatal error: stack overflow",
   272  		// information about the current SP and stack bounds
   273  		"runtime: sp=",
   274  		"stack=[",
   275  	}
   276  	if !strings.HasPrefix(output, want[0]) {
   277  		t.Errorf("output does not start with %q", want[0])
   278  	}
   279  	for _, s := range want[1:] {
   280  		if !strings.Contains(output, s) {
   281  			t.Errorf("output does not contain %q", s)
   282  		}
   283  	}
   284  	if t.Failed() {
   285  		t.Logf("output:\n%s", output)
   286  	}
   287  }
   288  
   289  func TestThreadExhaustion(t *testing.T) {
   290  	output := runTestProg(t, "testprog", "ThreadExhaustion")
   291  	want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion"
   292  	if !strings.HasPrefix(output, want) {
   293  		t.Fatalf("output does not start with %q:\n%s", want, output)
   294  	}
   295  }
   296  
   297  func TestRecursivePanic(t *testing.T) {
   298  	output := runTestProg(t, "testprog", "RecursivePanic")
   299  	want := `wrap: bad
   300  panic: again
   301  
   302  `
   303  	if !strings.HasPrefix(output, want) {
   304  		t.Fatalf("output does not start with %q:\n%s", want, output)
   305  	}
   306  
   307  }
   308  
   309  func TestRecursivePanic2(t *testing.T) {
   310  	output := runTestProg(t, "testprog", "RecursivePanic2")
   311  	want := `first panic
   312  second panic
   313  panic: third panic
   314  
   315  `
   316  	if !strings.HasPrefix(output, want) {
   317  		t.Fatalf("output does not start with %q:\n%s", want, output)
   318  	}
   319  
   320  }
   321  
   322  func TestRecursivePanic3(t *testing.T) {
   323  	output := runTestProg(t, "testprog", "RecursivePanic3")
   324  	want := `panic: first panic
   325  
   326  `
   327  	if !strings.HasPrefix(output, want) {
   328  		t.Fatalf("output does not start with %q:\n%s", want, output)
   329  	}
   330  
   331  }
   332  
   333  func TestRecursivePanic4(t *testing.T) {
   334  	output := runTestProg(t, "testprog", "RecursivePanic4")
   335  	want := `panic: first panic [recovered]
   336  	panic: second panic
   337  `
   338  	if !strings.HasPrefix(output, want) {
   339  		t.Fatalf("output does not start with %q:\n%s", want, output)
   340  	}
   341  
   342  }
   343  
   344  func TestRecursivePanic5(t *testing.T) {
   345  	output := runTestProg(t, "testprog", "RecursivePanic5")
   346  	want := `first panic
   347  second panic
   348  panic: third panic
   349  `
   350  	if !strings.HasPrefix(output, want) {
   351  		t.Fatalf("output does not start with %q:\n%s", want, output)
   352  	}
   353  
   354  }
   355  
   356  func TestGoexitCrash(t *testing.T) {
   357  	// External linking brings in cgo, causing deadlock detection not working.
   358  	testenv.MustInternalLink(t, false)
   359  
   360  	output := runTestProg(t, "testprog", "GoexitExit")
   361  	want := "no goroutines (main called runtime.Goexit) - deadlock!"
   362  	if !strings.Contains(output, want) {
   363  		t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
   364  	}
   365  }
   366  
   367  func TestGoexitDefer(t *testing.T) {
   368  	c := make(chan struct{})
   369  	go func() {
   370  		defer func() {
   371  			r := recover()
   372  			if r != nil {
   373  				t.Errorf("non-nil recover during Goexit")
   374  			}
   375  			c <- struct{}{}
   376  		}()
   377  		runtime.Goexit()
   378  	}()
   379  	// Note: if the defer fails to run, we will get a deadlock here
   380  	<-c
   381  }
   382  
   383  func TestGoNil(t *testing.T) {
   384  	output := runTestProg(t, "testprog", "GoNil")
   385  	want := "go of nil func value"
   386  	if !strings.Contains(output, want) {
   387  		t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
   388  	}
   389  }
   390  
   391  func TestMainGoroutineID(t *testing.T) {
   392  	output := runTestProg(t, "testprog", "MainGoroutineID")
   393  	want := "panic: test\n\ngoroutine 1 [running]:\n"
   394  	if !strings.HasPrefix(output, want) {
   395  		t.Fatalf("output does not start with %q:\n%s", want, output)
   396  	}
   397  }
   398  
   399  func TestNoHelperGoroutines(t *testing.T) {
   400  	output := runTestProg(t, "testprog", "NoHelperGoroutines")
   401  	matches := regexp.MustCompile(`goroutine [0-9]+ \[`).FindAllStringSubmatch(output, -1)
   402  	if len(matches) != 1 || matches[0][0] != "goroutine 1 [" {
   403  		t.Fatalf("want to see only goroutine 1, see:\n%s", output)
   404  	}
   405  }
   406  
   407  func TestBreakpoint(t *testing.T) {
   408  	output := runTestProg(t, "testprog", "Breakpoint")
   409  	// If runtime.Breakpoint() is inlined, then the stack trace prints
   410  	// "runtime.Breakpoint(...)" instead of "runtime.Breakpoint()".
   411  	want := "runtime.Breakpoint("
   412  	if !strings.Contains(output, want) {
   413  		t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
   414  	}
   415  }
   416  
   417  func TestGoexitInPanic(t *testing.T) {
   418  	// External linking brings in cgo, causing deadlock detection not working.
   419  	testenv.MustInternalLink(t, false)
   420  
   421  	// see issue 8774: this code used to trigger an infinite recursion
   422  	output := runTestProg(t, "testprog", "GoexitInPanic")
   423  	want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
   424  	if !strings.HasPrefix(output, want) {
   425  		t.Fatalf("output does not start with %q:\n%s", want, output)
   426  	}
   427  }
   428  
   429  // Issue 14965: Runtime panics should be of type runtime.Error
   430  func TestRuntimePanicWithRuntimeError(t *testing.T) {
   431  	testCases := [...]func(){
   432  		0: func() {
   433  			var m map[uint64]bool
   434  			m[1234] = true
   435  		},
   436  		1: func() {
   437  			ch := make(chan struct{})
   438  			close(ch)
   439  			close(ch)
   440  		},
   441  		2: func() {
   442  			var ch = make(chan struct{})
   443  			close(ch)
   444  			ch <- struct{}{}
   445  		},
   446  		3: func() {
   447  			var s = make([]int, 2)
   448  			_ = s[2]
   449  		},
   450  		4: func() {
   451  			n := -1
   452  			_ = make(chan bool, n)
   453  		},
   454  		5: func() {
   455  			close((chan bool)(nil))
   456  		},
   457  	}
   458  
   459  	for i, fn := range testCases {
   460  		got := panicValue(fn)
   461  		if _, ok := got.(runtime.Error); !ok {
   462  			t.Errorf("test #%d: recovered value %v(type %T) does not implement runtime.Error", i, got, got)
   463  		}
   464  	}
   465  }
   466  
   467  func panicValue(fn func()) (recovered any) {
   468  	defer func() {
   469  		recovered = recover()
   470  	}()
   471  	fn()
   472  	return
   473  }
   474  
   475  func TestPanicAfterGoexit(t *testing.T) {
   476  	// an uncaught panic should still work after goexit
   477  	output := runTestProg(t, "testprog", "PanicAfterGoexit")
   478  	want := "panic: hello"
   479  	if !strings.HasPrefix(output, want) {
   480  		t.Fatalf("output does not start with %q:\n%s", want, output)
   481  	}
   482  }
   483  
   484  func TestRecoveredPanicAfterGoexit(t *testing.T) {
   485  	// External linking brings in cgo, causing deadlock detection not working.
   486  	testenv.MustInternalLink(t, false)
   487  
   488  	output := runTestProg(t, "testprog", "RecoveredPanicAfterGoexit")
   489  	want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
   490  	if !strings.HasPrefix(output, want) {
   491  		t.Fatalf("output does not start with %q:\n%s", want, output)
   492  	}
   493  }
   494  
   495  func TestRecoverBeforePanicAfterGoexit(t *testing.T) {
   496  	// External linking brings in cgo, causing deadlock detection not working.
   497  	testenv.MustInternalLink(t, false)
   498  
   499  	t.Parallel()
   500  	output := runTestProg(t, "testprog", "RecoverBeforePanicAfterGoexit")
   501  	want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
   502  	if !strings.HasPrefix(output, want) {
   503  		t.Fatalf("output does not start with %q:\n%s", want, output)
   504  	}
   505  }
   506  
   507  func TestRecoverBeforePanicAfterGoexit2(t *testing.T) {
   508  	// External linking brings in cgo, causing deadlock detection not working.
   509  	testenv.MustInternalLink(t, false)
   510  
   511  	t.Parallel()
   512  	output := runTestProg(t, "testprog", "RecoverBeforePanicAfterGoexit2")
   513  	want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
   514  	if !strings.HasPrefix(output, want) {
   515  		t.Fatalf("output does not start with %q:\n%s", want, output)
   516  	}
   517  }
   518  
   519  func TestNetpollDeadlock(t *testing.T) {
   520  	t.Parallel()
   521  	output := runTestProg(t, "testprognet", "NetpollDeadlock")
   522  	want := "done\n"
   523  	if !strings.HasSuffix(output, want) {
   524  		t.Fatalf("output does not start with %q:\n%s", want, output)
   525  	}
   526  }
   527  
   528  func TestPanicTraceback(t *testing.T) {
   529  	t.Parallel()
   530  	output := runTestProg(t, "testprog", "PanicTraceback")
   531  	want := "panic: hello\n\tpanic: panic pt2\n\tpanic: panic pt1\n"
   532  	if !strings.HasPrefix(output, want) {
   533  		t.Fatalf("output does not start with %q:\n%s", want, output)
   534  	}
   535  
   536  	// Check functions in the traceback.
   537  	fns := []string{"main.pt1.func1", "panic", "main.pt2.func1", "panic", "main.pt2", "main.pt1"}
   538  	for _, fn := range fns {
   539  		re := regexp.MustCompile(`(?m)^` + regexp.QuoteMeta(fn) + `\(.*\n`)
   540  		idx := re.FindStringIndex(output)
   541  		if idx == nil {
   542  			t.Fatalf("expected %q function in traceback:\n%s", fn, output)
   543  		}
   544  		output = output[idx[1]:]
   545  	}
   546  }
   547  
   548  func testPanicDeadlock(t *testing.T, name string, want string) {
   549  	// test issue 14432
   550  	output := runTestProg(t, "testprog", name)
   551  	if !strings.HasPrefix(output, want) {
   552  		t.Fatalf("output does not start with %q:\n%s", want, output)
   553  	}
   554  }
   555  
   556  func TestPanicDeadlockGosched(t *testing.T) {
   557  	testPanicDeadlock(t, "GoschedInPanic", "panic: errorThatGosched\n\n")
   558  }
   559  
   560  func TestPanicDeadlockSyscall(t *testing.T) {
   561  	testPanicDeadlock(t, "SyscallInPanic", "1\n2\npanic: 3\n\n")
   562  }
   563  
   564  func TestPanicLoop(t *testing.T) {
   565  	output := runTestProg(t, "testprog", "PanicLoop")
   566  	if want := "panic while printing panic value"; !strings.Contains(output, want) {
   567  		t.Errorf("output does not contain %q:\n%s", want, output)
   568  	}
   569  }
   570  
   571  func TestMemPprof(t *testing.T) {
   572  	testenv.MustHaveGoRun(t)
   573  
   574  	exe, err := buildTestProg(t, "testprog")
   575  	if err != nil {
   576  		t.Fatal(err)
   577  	}
   578  
   579  	got, err := testenv.CleanCmdEnv(exec.Command(exe, "MemProf")).CombinedOutput()
   580  	if err != nil {
   581  		t.Fatalf("testprog failed: %s, output:\n%s", err, got)
   582  	}
   583  	fn := strings.TrimSpace(string(got))
   584  	defer os.Remove(fn)
   585  
   586  	for try := 0; try < 2; try++ {
   587  		cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-alloc_space", "-top"))
   588  		// Check that pprof works both with and without explicit executable on command line.
   589  		if try == 0 {
   590  			cmd.Args = append(cmd.Args, exe, fn)
   591  		} else {
   592  			cmd.Args = append(cmd.Args, fn)
   593  		}
   594  		found := false
   595  		for i, e := range cmd.Env {
   596  			if strings.HasPrefix(e, "PPROF_TMPDIR=") {
   597  				cmd.Env[i] = "PPROF_TMPDIR=" + os.TempDir()
   598  				found = true
   599  				break
   600  			}
   601  		}
   602  		if !found {
   603  			cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
   604  		}
   605  
   606  		top, err := cmd.CombinedOutput()
   607  		t.Logf("%s:\n%s", cmd.Args, top)
   608  		if err != nil {
   609  			t.Error(err)
   610  		} else if !bytes.Contains(top, []byte("MemProf")) {
   611  			t.Error("missing MemProf in pprof output")
   612  		}
   613  	}
   614  }
   615  
   616  var concurrentMapTest = flag.Bool("run_concurrent_map_tests", false, "also run flaky concurrent map tests")
   617  
   618  func TestConcurrentMapWrites(t *testing.T) {
   619  	if !*concurrentMapTest {
   620  		t.Skip("skipping without -run_concurrent_map_tests")
   621  	}
   622  	testenv.MustHaveGoRun(t)
   623  	output := runTestProg(t, "testprog", "concurrentMapWrites")
   624  	want := "fatal error: concurrent map writes\n"
   625  	// Concurrent writes can corrupt the map in a way that we
   626  	// detect with a separate throw.
   627  	want2 := "fatal error: small map with no empty slot (concurrent map writes?)\n"
   628  	if !strings.HasPrefix(output, want) && !strings.HasPrefix(output, want2) {
   629  		t.Fatalf("output does not start with %q:\n%s", want, output)
   630  	}
   631  }
   632  func TestConcurrentMapReadWrite(t *testing.T) {
   633  	if !*concurrentMapTest {
   634  		t.Skip("skipping without -run_concurrent_map_tests")
   635  	}
   636  	testenv.MustHaveGoRun(t)
   637  	output := runTestProg(t, "testprog", "concurrentMapReadWrite")
   638  	want := "fatal error: concurrent map read and map write\n"
   639  	// Concurrent writes can corrupt the map in a way that we
   640  	// detect with a separate throw.
   641  	want2 := "fatal error: small map with no empty slot (concurrent map writes?)\n"
   642  	if !strings.HasPrefix(output, want) && !strings.HasPrefix(output, want2) {
   643  		t.Fatalf("output does not start with %q:\n%s", want, output)
   644  	}
   645  }
   646  func TestConcurrentMapIterateWrite(t *testing.T) {
   647  	if !*concurrentMapTest {
   648  		t.Skip("skipping without -run_concurrent_map_tests")
   649  	}
   650  	testenv.MustHaveGoRun(t)
   651  	output := runTestProg(t, "testprog", "concurrentMapIterateWrite")
   652  	want := "fatal error: concurrent map iteration and map write\n"
   653  	// Concurrent writes can corrupt the map in a way that we
   654  	// detect with a separate throw.
   655  	want2 := "fatal error: small map with no empty slot (concurrent map writes?)\n"
   656  	if !strings.HasPrefix(output, want) && !strings.HasPrefix(output, want2) {
   657  		t.Fatalf("output does not start with %q:\n%s", want, output)
   658  	}
   659  }
   660  
   661  func TestConcurrentMapWritesIssue69447(t *testing.T) {
   662  	testenv.MustHaveGoRun(t)
   663  	exe, err := buildTestProg(t, "testprog")
   664  	if err != nil {
   665  		t.Fatal(err)
   666  	}
   667  	for i := 0; i < 200; i++ {
   668  		output := runBuiltTestProg(t, exe, "concurrentMapWrites")
   669  		if output == "" {
   670  			// If we didn't detect an error, that's ok.
   671  			// This case makes this test not flaky like
   672  			// the other ones above.
   673  			// (More correctly, this case makes this test flaky
   674  			// in the other direction, in that it might not
   675  			// detect a problem even if there is one.)
   676  			continue
   677  		}
   678  		want := "fatal error: concurrent map writes\n"
   679  		// Concurrent writes can corrupt the map in a way that we
   680  		// detect with a separate throw.
   681  		want2 := "fatal error: small map with no empty slot (concurrent map writes?)\n"
   682  		if !strings.HasPrefix(output, want) && !strings.HasPrefix(output, want2) {
   683  			t.Fatalf("output does not start with %q:\n%s", want, output)
   684  		}
   685  	}
   686  }
   687  
   688  type point struct {
   689  	x, y *int
   690  }
   691  
   692  func (p *point) negate() {
   693  	*p.x = *p.x * -1
   694  	*p.y = *p.y * -1
   695  }
   696  
   697  // Test for issue #10152.
   698  func TestPanicInlined(t *testing.T) {
   699  	defer func() {
   700  		r := recover()
   701  		if r == nil {
   702  			t.Fatalf("recover failed")
   703  		}
   704  		buf := make([]byte, 2048)
   705  		n := runtime.Stack(buf, false)
   706  		buf = buf[:n]
   707  		if !bytes.Contains(buf, []byte("(*point).negate(")) {
   708  			t.Fatalf("expecting stack trace to contain call to (*point).negate()")
   709  		}
   710  	}()
   711  
   712  	pt := new(point)
   713  	pt.negate()
   714  }
   715  
   716  // Test for issues #3934 and #20018.
   717  // We want to delay exiting until a panic print is complete.
   718  func TestPanicRace(t *testing.T) {
   719  	testenv.MustHaveGoRun(t)
   720  
   721  	exe, err := buildTestProg(t, "testprog")
   722  	if err != nil {
   723  		t.Fatal(err)
   724  	}
   725  
   726  	// The test is intentionally racy, and in my testing does not
   727  	// produce the expected output about 0.05% of the time.
   728  	// So run the program in a loop and only fail the test if we
   729  	// get the wrong output ten times in a row.
   730  	const tries = 10
   731  retry:
   732  	for i := 0; i < tries; i++ {
   733  		got, err := testenv.CleanCmdEnv(exec.Command(exe, "PanicRace")).CombinedOutput()
   734  		if err == nil {
   735  			t.Logf("try %d: program exited successfully, should have failed", i+1)
   736  			continue
   737  		}
   738  
   739  		if i > 0 {
   740  			t.Logf("try %d:\n", i+1)
   741  		}
   742  		t.Logf("%s\n", got)
   743  
   744  		wants := []string{
   745  			"panic: crash",
   746  			"PanicRace",
   747  			"created by ",
   748  		}
   749  		for _, want := range wants {
   750  			if !bytes.Contains(got, []byte(want)) {
   751  				t.Logf("did not find expected string %q", want)
   752  				continue retry
   753  			}
   754  		}
   755  
   756  		// Test generated expected output.
   757  		return
   758  	}
   759  	t.Errorf("test ran %d times without producing expected output", tries)
   760  }
   761  
   762  func TestBadTraceback(t *testing.T) {
   763  	output := runTestProg(t, "testprog", "BadTraceback")
   764  	for _, want := range []string{
   765  		"unexpected return pc",
   766  		"called from 0xbad",
   767  		"00000bad",    // Smashed LR in hex dump
   768  		"<main.badLR", // Symbolization in hex dump (badLR1 or badLR2)
   769  	} {
   770  		if !strings.Contains(output, want) {
   771  			t.Errorf("output does not contain %q:\n%s", want, output)
   772  		}
   773  	}
   774  }
   775  
   776  func TestTimePprof(t *testing.T) {
   777  	// This test is unreliable on any system in which nanotime
   778  	// calls into libc.
   779  	switch runtime.GOOS {
   780  	case "aix", "darwin", "illumos", "openbsd", "solaris":
   781  		t.Skipf("skipping on %s because nanotime calls libc", runtime.GOOS)
   782  	}
   783  
   784  	// Pass GOTRACEBACK for issue #41120 to try to get more
   785  	// information on timeout.
   786  	fn := runTestProg(t, "testprog", "TimeProf", "GOTRACEBACK=crash")
   787  	fn = strings.TrimSpace(fn)
   788  	defer os.Remove(fn)
   789  
   790  	cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-top", "-nodecount=1", fn))
   791  	cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
   792  	top, err := cmd.CombinedOutput()
   793  	t.Logf("%s", top)
   794  	if err != nil {
   795  		t.Error(err)
   796  	} else if bytes.Contains(top, []byte("ExternalCode")) {
   797  		t.Error("profiler refers to ExternalCode")
   798  	}
   799  }
   800  
   801  // Test that runtime.abort does so.
   802  func TestAbort(t *testing.T) {
   803  	// Pass GOTRACEBACK to ensure we get runtime frames.
   804  	output := runTestProg(t, "testprog", "Abort", "GOTRACEBACK=system")
   805  	if want := "runtime.abort"; !strings.Contains(output, want) {
   806  		t.Errorf("output does not contain %q:\n%s", want, output)
   807  	}
   808  	if strings.Contains(output, "BAD") {
   809  		t.Errorf("output contains BAD:\n%s", output)
   810  	}
   811  	// Check that it's a signal traceback.
   812  	want := "PC="
   813  	// For systems that use a breakpoint, check specifically for that.
   814  	switch runtime.GOARCH {
   815  	case "386", "amd64":
   816  		switch runtime.GOOS {
   817  		case "plan9":
   818  			want = "sys: breakpoint"
   819  		case "windows":
   820  			want = "Exception 0x80000003"
   821  		default:
   822  			want = "SIGTRAP"
   823  		}
   824  	}
   825  	if !strings.Contains(output, want) {
   826  		t.Errorf("output does not contain %q:\n%s", want, output)
   827  	}
   828  }
   829  
   830  // For TestRuntimePanic: test a panic in the runtime package without
   831  // involving the testing harness.
   832  func init() {
   833  	if os.Getenv("GO_TEST_RUNTIME_PANIC") == "1" {
   834  		defer func() {
   835  			if r := recover(); r != nil {
   836  				// We expect to crash, so exit 0
   837  				// to indicate failure.
   838  				os.Exit(0)
   839  			}
   840  		}()
   841  		runtime.PanicForTesting(nil, 1)
   842  		// We expect to crash, so exit 0 to indicate failure.
   843  		os.Exit(0)
   844  	}
   845  	if os.Getenv("GO_TEST_RUNTIME_NPE_READMEMSTATS") == "1" {
   846  		runtime.ReadMemStats(nil)
   847  		os.Exit(0)
   848  	}
   849  	if os.Getenv("GO_TEST_RUNTIME_NPE_FUNCMETHOD") == "1" {
   850  		var f *runtime.Func
   851  		_ = f.Entry()
   852  		os.Exit(0)
   853  	}
   854  
   855  }
   856  
   857  func TestRuntimePanic(t *testing.T) {
   858  	testenv.MustHaveExec(t)
   859  	cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=^TestRuntimePanic$"))
   860  	cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_PANIC=1")
   861  	out, err := cmd.CombinedOutput()
   862  	t.Logf("%s", out)
   863  	if err == nil {
   864  		t.Error("child process did not fail")
   865  	} else if want := "runtime.unexportedPanicForTesting"; !bytes.Contains(out, []byte(want)) {
   866  		t.Errorf("output did not contain expected string %q", want)
   867  	}
   868  }
   869  
   870  func TestTracebackRuntimeFunction(t *testing.T) {
   871  	testenv.MustHaveExec(t)
   872  	cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestTracebackRuntimeFunction"))
   873  	cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_NPE_READMEMSTATS=1")
   874  	out, err := cmd.CombinedOutput()
   875  	t.Logf("%s", out)
   876  	if err == nil {
   877  		t.Error("child process did not fail")
   878  	} else if want := "runtime.ReadMemStats"; !bytes.Contains(out, []byte(want)) {
   879  		t.Errorf("output did not contain expected string %q", want)
   880  	}
   881  }
   882  
   883  func TestTracebackRuntimeMethod(t *testing.T) {
   884  	testenv.MustHaveExec(t)
   885  	cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestTracebackRuntimeMethod"))
   886  	cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_NPE_FUNCMETHOD=1")
   887  	out, err := cmd.CombinedOutput()
   888  	t.Logf("%s", out)
   889  	if err == nil {
   890  		t.Error("child process did not fail")
   891  	} else if want := "runtime.(*Func).Entry"; !bytes.Contains(out, []byte(want)) {
   892  		t.Errorf("output did not contain expected string %q", want)
   893  	}
   894  }
   895  
   896  // Test that g0 stack overflows are handled gracefully.
   897  func TestG0StackOverflow(t *testing.T) {
   898  	testenv.MustHaveExec(t)
   899  
   900  	if runtime.GOOS == "ios" {
   901  		testenv.SkipFlaky(t, 62671)
   902  	}
   903  
   904  	if os.Getenv("TEST_G0_STACK_OVERFLOW") != "1" {
   905  		cmd := testenv.CleanCmdEnv(testenv.Command(t, os.Args[0], "-test.run=^TestG0StackOverflow$", "-test.v"))
   906  		cmd.Env = append(cmd.Env, "TEST_G0_STACK_OVERFLOW=1")
   907  		out, err := cmd.CombinedOutput()
   908  		t.Logf("output:\n%s", out)
   909  		// Don't check err since it's expected to crash.
   910  		if n := strings.Count(string(out), "morestack on g0\n"); n != 1 {
   911  			t.Fatalf("%s\n(exit status %v)", out, err)
   912  		}
   913  		if runtime.CrashStackImplemented {
   914  			// check for a stack trace
   915  			want := "runtime.stackOverflow"
   916  			if n := strings.Count(string(out), want); n < 5 {
   917  				t.Errorf("output does not contain %q at least 5 times:\n%s", want, out)
   918  			}
   919  			return // it's not a signal-style traceback
   920  		}
   921  		// Check that it's a signal-style traceback.
   922  		if runtime.GOOS != "windows" {
   923  			if want := "PC="; !strings.Contains(string(out), want) {
   924  				t.Errorf("output does not contain %q:\n%s", want, out)
   925  			}
   926  		}
   927  		return
   928  	}
   929  
   930  	runtime.G0StackOverflow()
   931  }
   932  
   933  // For TestCrashWhileTracing: test a panic without involving the testing
   934  // harness, as we rely on stdout only containing trace output.
   935  func init() {
   936  	if os.Getenv("TEST_CRASH_WHILE_TRACING") == "1" {
   937  		trace.Start(os.Stdout)
   938  		trace.Log(context.Background(), "xyzzy-cat", "xyzzy-msg")
   939  		panic("yzzyx")
   940  	}
   941  }
   942  
   943  func TestCrashWhileTracing(t *testing.T) {
   944  	testenv.MustHaveExec(t)
   945  
   946  	cmd := testenv.CleanCmdEnv(testenv.Command(t, os.Args[0]))
   947  	cmd.Env = append(cmd.Env, "TEST_CRASH_WHILE_TRACING=1")
   948  	stdOut, err := cmd.StdoutPipe()
   949  	var errOut bytes.Buffer
   950  	cmd.Stderr = &errOut
   951  
   952  	if err := cmd.Start(); err != nil {
   953  		t.Fatalf("could not start subprocess: %v", err)
   954  	}
   955  	r, err := traceparse.NewReader(stdOut)
   956  	if err != nil {
   957  		t.Fatalf("could not create trace.NewReader: %v", err)
   958  	}
   959  	var seen, seenSync bool
   960  	i := 1
   961  loop:
   962  	for ; ; i++ {
   963  		ev, err := r.ReadEvent()
   964  		if err != nil {
   965  			// We may have a broken tail to the trace -- that's OK.
   966  			// We'll make sure we saw at least one complete generation.
   967  			if err != io.EOF {
   968  				t.Logf("error at event %d: %v", i, err)
   969  			}
   970  			break loop
   971  		}
   972  		switch ev.Kind() {
   973  		case traceparse.EventSync:
   974  			seenSync = true
   975  		case traceparse.EventLog:
   976  			v := ev.Log()
   977  			if v.Category == "xyzzy-cat" && v.Message == "xyzzy-msg" {
   978  				// Should we already stop reading here? More events may come, but
   979  				// we're not guaranteeing a fully unbroken trace until the last
   980  				// byte...
   981  				seen = true
   982  			}
   983  		}
   984  	}
   985  	if err := cmd.Wait(); err == nil {
   986  		t.Error("the process should have panicked")
   987  	}
   988  	if !seenSync {
   989  		t.Errorf("expected at least one full generation to have been emitted before the trace was considered broken")
   990  	}
   991  	if !seen {
   992  		t.Errorf("expected one matching log event matching, but none of the %d received trace events match", i)
   993  	}
   994  	t.Logf("stderr output:\n%s", errOut.String())
   995  	needle := "yzzyx\n"
   996  	if n := strings.Count(errOut.String(), needle); n != 1 {
   997  		t.Fatalf("did not find expected panic message %q\n(exit status %v)", needle, err)
   998  	}
   999  }
  1000  
  1001  // Test that panic message is not clobbered.
  1002  // See issue 30150.
  1003  func TestDoublePanic(t *testing.T) {
  1004  	output := runTestProg(t, "testprog", "DoublePanic", "GODEBUG=clobberfree=1")
  1005  	wants := []string{"panic: XXX", "panic: YYY"}
  1006  	for _, want := range wants {
  1007  		if !strings.Contains(output, want) {
  1008  			t.Errorf("output:\n%s\n\nwant output containing: %s", output, want)
  1009  		}
  1010  	}
  1011  }
  1012  
  1013  // Test that panic while panicking discards error message
  1014  // See issue 52257
  1015  func TestPanicWhilePanicking(t *testing.T) {
  1016  	tests := []struct {
  1017  		Want string
  1018  		Func string
  1019  	}{
  1020  		{
  1021  			"panic while printing panic value: important multi-line\n\terror message",
  1022  			"ErrorPanic",
  1023  		},
  1024  		{
  1025  			"panic while printing panic value: important multi-line\n\tstringer message",
  1026  			"StringerPanic",
  1027  		},
  1028  		{
  1029  			"panic while printing panic value: type",
  1030  			"DoubleErrorPanic",
  1031  		},
  1032  		{
  1033  			"panic while printing panic value: type",
  1034  			"DoubleStringerPanic",
  1035  		},
  1036  		{
  1037  			"panic while printing panic value: type",
  1038  			"CircularPanic",
  1039  		},
  1040  		{
  1041  			"important multi-line\n\tstring message",
  1042  			"StringPanic",
  1043  		},
  1044  		{
  1045  			"nil",
  1046  			"NilPanic",
  1047  		},
  1048  	}
  1049  	for _, x := range tests {
  1050  		output := runTestProg(t, "testprog", x.Func)
  1051  		if !strings.Contains(output, x.Want) {
  1052  			t.Errorf("output does not contain %q:\n%s", x.Want, output)
  1053  		}
  1054  	}
  1055  }
  1056  
  1057  func TestPanicOnUnsafeSlice(t *testing.T) {
  1058  	output := runTestProg(t, "testprog", "panicOnNilAndEleSizeIsZero")
  1059  	want := "panic: runtime error: unsafe.Slice: ptr is nil and len is not zero"
  1060  	if !strings.Contains(output, want) {
  1061  		t.Errorf("output does not contain %q:\n%s", want, output)
  1062  	}
  1063  }
  1064  
  1065  func TestNetpollWaiters(t *testing.T) {
  1066  	t.Parallel()
  1067  	output := runTestProg(t, "testprognet", "NetpollWaiters")
  1068  	want := "OK\n"
  1069  	if output != want {
  1070  		t.Fatalf("output is not %q\n%s", want, output)
  1071  	}
  1072  }
  1073  

View as plain text