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