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