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