Source file src/runtime/crash_test.go

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

View as plain text