Source file src/runtime/crash_test.go

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

View as plain text