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

View as plain text