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