github.com/m10x/go/src@v0.0.0-20220112094212-ba61592315da/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 "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 "runtime: 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 }