github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/runtime/crash_test.go (about)

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