...

Source file src/runtime/crash_test.go

Documentation: runtime

		 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  	"flag"
		10  	"fmt"
		11  	"internal/testenv"
		12  	"os"
		13  	"os/exec"
		14  	"path/filepath"
		15  	"regexp"
		16  	"runtime"
		17  	"strconv"
		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  	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  	var b bytes.Buffer
		73  	cmd.Stdout = &b
		74  	cmd.Stderr = &b
		75  	if err := cmd.Start(); err != nil {
		76  		t.Fatalf("starting %s %s: %v", exe, name, err)
		77  	}
		78  
		79  	// If the process doesn't complete within 1 minute,
		80  	// assume it is hanging and kill it to get a stack trace.
		81  	p := cmd.Process
		82  	done := make(chan bool)
		83  	go func() {
		84  		scale := 1
		85  		// This GOARCH/GOOS test is copied from cmd/dist/test.go.
		86  		// TODO(iant): Have cmd/dist update the environment variable.
		87  		if runtime.GOARCH == "arm" || runtime.GOOS == "windows" {
		88  			scale = 2
		89  		}
		90  		if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" {
		91  			if sc, err := strconv.Atoi(s); err == nil {
		92  				scale = sc
		93  			}
		94  		}
		95  
		96  		select {
		97  		case <-done:
		98  		case <-time.After(time.Duration(scale) * time.Minute):
		99  			p.Signal(sigquit)
	 100  		}
	 101  	}()
	 102  
	 103  	if err := cmd.Wait(); err != nil {
	 104  		t.Logf("%s %s exit status: %v", exe, name, err)
	 105  	}
	 106  	close(done)
	 107  
	 108  	return b.String()
	 109  }
	 110  
	 111  func buildTestProg(t *testing.T, binary string, flags ...string) (string, error) {
	 112  	if *flagQuick {
	 113  		t.Skip("-quick")
	 114  	}
	 115  
	 116  	testprog.Lock()
	 117  	defer testprog.Unlock()
	 118  	if testprog.dir == "" {
	 119  		dir, err := os.MkdirTemp("", "go-build")
	 120  		if err != nil {
	 121  			t.Fatalf("failed to create temp directory: %v", err)
	 122  		}
	 123  		testprog.dir = dir
	 124  		toRemove = append(toRemove, dir)
	 125  	}
	 126  
	 127  	if testprog.target == nil {
	 128  		testprog.target = make(map[string]buildexe)
	 129  	}
	 130  	name := binary
	 131  	if len(flags) > 0 {
	 132  		name += "_" + strings.Join(flags, "_")
	 133  	}
	 134  	target, ok := testprog.target[name]
	 135  	if ok {
	 136  		return target.exe, target.err
	 137  	}
	 138  
	 139  	exe := filepath.Join(testprog.dir, name+".exe")
	 140  	cmd := exec.Command(testenv.GoToolPath(t), append([]string{"build", "-o", exe}, flags...)...)
	 141  	cmd.Dir = "testdata/" + binary
	 142  	out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
	 143  	if err != nil {
	 144  		target.err = fmt.Errorf("building %s %v: %v\n%s", binary, flags, err, out)
	 145  		testprog.target[name] = target
	 146  		return "", target.err
	 147  	}
	 148  	target.exe = exe
	 149  	testprog.target[name] = target
	 150  	return exe, nil
	 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 interface{}) {
	 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  		"runtime: 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  	// Pass GOTRACEBACK for issue #41120 to try to get more
	 695  	// information on timeout.
	 696  	fn := runTestProg(t, "testprog", "TimeProf", "GOTRACEBACK=crash")
	 697  	fn = strings.TrimSpace(fn)
	 698  	defer os.Remove(fn)
	 699  
	 700  	cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-top", "-nodecount=1", fn))
	 701  	cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
	 702  	top, err := cmd.CombinedOutput()
	 703  	t.Logf("%s", top)
	 704  	if err != nil {
	 705  		t.Error(err)
	 706  	} else if bytes.Contains(top, []byte("ExternalCode")) {
	 707  		t.Error("profiler refers to ExternalCode")
	 708  	}
	 709  }
	 710  
	 711  // Test that runtime.abort does so.
	 712  func TestAbort(t *testing.T) {
	 713  	// Pass GOTRACEBACK to ensure we get runtime frames.
	 714  	output := runTestProg(t, "testprog", "Abort", "GOTRACEBACK=system")
	 715  	if want := "runtime.abort"; !strings.Contains(output, want) {
	 716  		t.Errorf("output does not contain %q:\n%s", want, output)
	 717  	}
	 718  	if strings.Contains(output, "BAD") {
	 719  		t.Errorf("output contains BAD:\n%s", output)
	 720  	}
	 721  	// Check that it's a signal traceback.
	 722  	want := "PC="
	 723  	// For systems that use a breakpoint, check specifically for that.
	 724  	switch runtime.GOARCH {
	 725  	case "386", "amd64":
	 726  		switch runtime.GOOS {
	 727  		case "plan9":
	 728  			want = "sys: breakpoint"
	 729  		case "windows":
	 730  			want = "Exception 0x80000003"
	 731  		default:
	 732  			want = "SIGTRAP"
	 733  		}
	 734  	}
	 735  	if !strings.Contains(output, want) {
	 736  		t.Errorf("output does not contain %q:\n%s", want, output)
	 737  	}
	 738  }
	 739  
	 740  // For TestRuntimePanic: test a panic in the runtime package without
	 741  // involving the testing harness.
	 742  func init() {
	 743  	if os.Getenv("GO_TEST_RUNTIME_PANIC") == "1" {
	 744  		defer func() {
	 745  			if r := recover(); r != nil {
	 746  				// We expect to crash, so exit 0
	 747  				// to indicate failure.
	 748  				os.Exit(0)
	 749  			}
	 750  		}()
	 751  		runtime.PanicForTesting(nil, 1)
	 752  		// We expect to crash, so exit 0 to indicate failure.
	 753  		os.Exit(0)
	 754  	}
	 755  }
	 756  
	 757  func TestRuntimePanic(t *testing.T) {
	 758  	testenv.MustHaveExec(t)
	 759  	cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestRuntimePanic"))
	 760  	cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_PANIC=1")
	 761  	out, err := cmd.CombinedOutput()
	 762  	t.Logf("%s", out)
	 763  	if err == nil {
	 764  		t.Error("child process did not fail")
	 765  	} else if want := "runtime.unexportedPanicForTesting"; !bytes.Contains(out, []byte(want)) {
	 766  		t.Errorf("output did not contain expected string %q", want)
	 767  	}
	 768  }
	 769  
	 770  // Test that g0 stack overflows are handled gracefully.
	 771  func TestG0StackOverflow(t *testing.T) {
	 772  	testenv.MustHaveExec(t)
	 773  
	 774  	switch runtime.GOOS {
	 775  	case "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "android":
	 776  		t.Skipf("g0 stack is wrong on pthread platforms (see golang.org/issue/26061)")
	 777  	}
	 778  
	 779  	if os.Getenv("TEST_G0_STACK_OVERFLOW") != "1" {
	 780  		cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestG0StackOverflow", "-test.v"))
	 781  		cmd.Env = append(cmd.Env, "TEST_G0_STACK_OVERFLOW=1")
	 782  		out, err := cmd.CombinedOutput()
	 783  		// Don't check err since it's expected to crash.
	 784  		if n := strings.Count(string(out), "morestack on g0\n"); n != 1 {
	 785  			t.Fatalf("%s\n(exit status %v)", out, err)
	 786  		}
	 787  		// Check that it's a signal-style traceback.
	 788  		if runtime.GOOS != "windows" {
	 789  			if want := "PC="; !strings.Contains(string(out), want) {
	 790  				t.Errorf("output does not contain %q:\n%s", want, out)
	 791  			}
	 792  		}
	 793  		return
	 794  	}
	 795  
	 796  	runtime.G0StackOverflow()
	 797  }
	 798  
	 799  // Test that panic message is not clobbered.
	 800  // See issue 30150.
	 801  func TestDoublePanic(t *testing.T) {
	 802  	output := runTestProg(t, "testprog", "DoublePanic", "GODEBUG=clobberfree=1")
	 803  	wants := []string{"panic: XXX", "panic: YYY"}
	 804  	for _, want := range wants {
	 805  		if !strings.Contains(output, want) {
	 806  			t.Errorf("output:\n%s\n\nwant output containing: %s", output, want)
	 807  		}
	 808  	}
	 809  }
	 810  

View as plain text