github.com/x04/go/src@v0.0.0-20200202162449-3d481ceb3525/runtime/crash_test.go (about)

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