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

View as plain text