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