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

View as plain text