github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/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 "context" 10 "errors" 11 "flag" 12 "fmt" 13 "internal/goexperiment" 14 "internal/testenv" 15 tracev2 "internal/trace/v2" 16 "io" 17 "log" 18 "os" 19 "os/exec" 20 "path/filepath" 21 "regexp" 22 "runtime" 23 "runtime/trace" 24 "strings" 25 "sync" 26 "testing" 27 "time" 28 ) 29 30 var toRemove []string 31 32 const entrypointVar = "RUNTIME_TEST_ENTRYPOINT" 33 34 func TestMain(m *testing.M) { 35 switch entrypoint := os.Getenv(entrypointVar); entrypoint { 36 case "crash": 37 crash() 38 panic("unreachable") 39 default: 40 log.Fatalf("invalid %s: %q", entrypointVar, entrypoint) 41 case "": 42 // fall through to normal behavior 43 } 44 45 _, coreErrBefore := os.Stat("core") 46 47 status := m.Run() 48 for _, file := range toRemove { 49 os.RemoveAll(file) 50 } 51 52 _, coreErrAfter := os.Stat("core") 53 if coreErrBefore != nil && coreErrAfter == nil { 54 fmt.Fprintln(os.Stderr, "runtime.test: some test left a core file behind") 55 if status == 0 { 56 status = 1 57 } 58 } 59 60 os.Exit(status) 61 } 62 63 var testprog struct { 64 sync.Mutex 65 dir string 66 target map[string]*buildexe 67 } 68 69 type buildexe struct { 70 once sync.Once 71 exe string 72 err error 73 } 74 75 func runTestProg(t *testing.T, binary, name string, env ...string) string { 76 if *flagQuick { 77 t.Skip("-quick") 78 } 79 80 testenv.MustHaveGoBuild(t) 81 t.Helper() 82 83 exe, err := buildTestProg(t, binary) 84 if err != nil { 85 t.Fatal(err) 86 } 87 88 return runBuiltTestProg(t, exe, name, env...) 89 } 90 91 func runBuiltTestProg(t *testing.T, exe, name string, env ...string) string { 92 t.Helper() 93 94 if *flagQuick { 95 t.Skip("-quick") 96 } 97 98 start := time.Now() 99 100 cmd := testenv.CleanCmdEnv(testenv.Command(t, exe, name)) 101 cmd.Env = append(cmd.Env, env...) 102 if testing.Short() { 103 cmd.Env = append(cmd.Env, "RUNTIME_TEST_SHORT=1") 104 } 105 out, err := cmd.CombinedOutput() 106 if err == nil { 107 t.Logf("%v (%v): ok", cmd, time.Since(start)) 108 } else { 109 if _, ok := err.(*exec.ExitError); ok { 110 t.Logf("%v: %v", cmd, err) 111 } else if errors.Is(err, exec.ErrWaitDelay) { 112 t.Fatalf("%v: %v", cmd, err) 113 } else { 114 t.Fatalf("%v failed to start: %v", cmd, err) 115 } 116 } 117 return string(out) 118 } 119 120 var serializeBuild = make(chan bool, 2) 121 122 func buildTestProg(t *testing.T, binary string, flags ...string) (string, error) { 123 if *flagQuick { 124 t.Skip("-quick") 125 } 126 testenv.MustHaveGoBuild(t) 127 128 testprog.Lock() 129 if testprog.dir == "" { 130 dir, err := os.MkdirTemp("", "go-build") 131 if err != nil { 132 t.Fatalf("failed to create temp directory: %v", err) 133 } 134 testprog.dir = dir 135 toRemove = append(toRemove, dir) 136 } 137 138 if testprog.target == nil { 139 testprog.target = make(map[string]*buildexe) 140 } 141 name := binary 142 if len(flags) > 0 { 143 name += "_" + strings.Join(flags, "_") 144 } 145 target, ok := testprog.target[name] 146 if !ok { 147 target = &buildexe{} 148 testprog.target[name] = target 149 } 150 151 dir := testprog.dir 152 153 // Unlock testprog while actually building, so that other 154 // tests can look up executables that were already built. 155 testprog.Unlock() 156 157 target.once.Do(func() { 158 // Only do two "go build"'s at a time, 159 // to keep load from getting too high. 160 serializeBuild <- true 161 defer func() { <-serializeBuild }() 162 163 // Don't get confused if testenv.GoToolPath calls t.Skip. 164 target.err = errors.New("building test called t.Skip") 165 166 exe := filepath.Join(dir, name+".exe") 167 168 start := time.Now() 169 cmd := exec.Command(testenv.GoToolPath(t), append([]string{"build", "-o", exe}, flags...)...) 170 t.Logf("running %v", cmd) 171 cmd.Dir = "testdata/" + binary 172 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput() 173 if err != nil { 174 target.err = fmt.Errorf("building %s %v: %v\n%s", binary, flags, err, out) 175 } else { 176 t.Logf("built %v in %v", name, time.Since(start)) 177 target.exe = exe 178 target.err = nil 179 } 180 }) 181 182 return target.exe, target.err 183 } 184 185 func TestVDSO(t *testing.T) { 186 t.Parallel() 187 output := runTestProg(t, "testprog", "SignalInVDSO") 188 want := "success\n" 189 if output != want { 190 t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want) 191 } 192 } 193 194 func testCrashHandler(t *testing.T, cgo bool) { 195 type crashTest struct { 196 Cgo bool 197 } 198 var output string 199 if cgo { 200 output = runTestProg(t, "testprogcgo", "Crash") 201 } else { 202 output = runTestProg(t, "testprog", "Crash") 203 } 204 want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n" 205 if output != want { 206 t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want) 207 } 208 } 209 210 func TestCrashHandler(t *testing.T) { 211 testCrashHandler(t, false) 212 } 213 214 func testDeadlock(t *testing.T, name string) { 215 // External linking brings in cgo, causing deadlock detection not working. 216 testenv.MustInternalLink(t, false) 217 218 output := runTestProg(t, "testprog", name) 219 want := "fatal error: all goroutines are asleep - deadlock!\n" 220 if !strings.HasPrefix(output, want) { 221 t.Fatalf("output does not start with %q:\n%s", want, output) 222 } 223 } 224 225 func TestSimpleDeadlock(t *testing.T) { 226 testDeadlock(t, "SimpleDeadlock") 227 } 228 229 func TestInitDeadlock(t *testing.T) { 230 testDeadlock(t, "InitDeadlock") 231 } 232 233 func TestLockedDeadlock(t *testing.T) { 234 testDeadlock(t, "LockedDeadlock") 235 } 236 237 func TestLockedDeadlock2(t *testing.T) { 238 testDeadlock(t, "LockedDeadlock2") 239 } 240 241 func TestGoexitDeadlock(t *testing.T) { 242 // External linking brings in cgo, causing deadlock detection not working. 243 testenv.MustInternalLink(t, false) 244 245 output := runTestProg(t, "testprog", "GoexitDeadlock") 246 want := "no goroutines (main called runtime.Goexit) - deadlock!" 247 if !strings.Contains(output, want) { 248 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want) 249 } 250 } 251 252 func TestStackOverflow(t *testing.T) { 253 output := runTestProg(t, "testprog", "StackOverflow") 254 want := []string{ 255 "runtime: goroutine stack exceeds 1474560-byte limit\n", 256 "fatal error: stack overflow", 257 // information about the current SP and stack bounds 258 "runtime: sp=", 259 "stack=[", 260 } 261 if !strings.HasPrefix(output, want[0]) { 262 t.Errorf("output does not start with %q", want[0]) 263 } 264 for _, s := range want[1:] { 265 if !strings.Contains(output, s) { 266 t.Errorf("output does not contain %q", s) 267 } 268 } 269 if t.Failed() { 270 t.Logf("output:\n%s", output) 271 } 272 } 273 274 func TestThreadExhaustion(t *testing.T) { 275 output := runTestProg(t, "testprog", "ThreadExhaustion") 276 want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion" 277 if !strings.HasPrefix(output, want) { 278 t.Fatalf("output does not start with %q:\n%s", want, output) 279 } 280 } 281 282 func TestRecursivePanic(t *testing.T) { 283 output := runTestProg(t, "testprog", "RecursivePanic") 284 want := `wrap: bad 285 panic: again 286 287 ` 288 if !strings.HasPrefix(output, want) { 289 t.Fatalf("output does not start with %q:\n%s", want, output) 290 } 291 292 } 293 294 func TestRecursivePanic2(t *testing.T) { 295 output := runTestProg(t, "testprog", "RecursivePanic2") 296 want := `first panic 297 second panic 298 panic: third panic 299 300 ` 301 if !strings.HasPrefix(output, want) { 302 t.Fatalf("output does not start with %q:\n%s", want, output) 303 } 304 305 } 306 307 func TestRecursivePanic3(t *testing.T) { 308 output := runTestProg(t, "testprog", "RecursivePanic3") 309 want := `panic: first panic 310 311 ` 312 if !strings.HasPrefix(output, want) { 313 t.Fatalf("output does not start with %q:\n%s", want, output) 314 } 315 316 } 317 318 func TestRecursivePanic4(t *testing.T) { 319 output := runTestProg(t, "testprog", "RecursivePanic4") 320 want := `panic: first panic [recovered] 321 panic: second panic 322 ` 323 if !strings.HasPrefix(output, want) { 324 t.Fatalf("output does not start with %q:\n%s", want, output) 325 } 326 327 } 328 329 func TestRecursivePanic5(t *testing.T) { 330 output := runTestProg(t, "testprog", "RecursivePanic5") 331 want := `first panic 332 second panic 333 panic: third panic 334 ` 335 if !strings.HasPrefix(output, want) { 336 t.Fatalf("output does not start with %q:\n%s", want, output) 337 } 338 339 } 340 341 func TestGoexitCrash(t *testing.T) { 342 // External linking brings in cgo, causing deadlock detection not working. 343 testenv.MustInternalLink(t, false) 344 345 output := runTestProg(t, "testprog", "GoexitExit") 346 want := "no goroutines (main called runtime.Goexit) - deadlock!" 347 if !strings.Contains(output, want) { 348 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want) 349 } 350 } 351 352 func TestGoexitDefer(t *testing.T) { 353 c := make(chan struct{}) 354 go func() { 355 defer func() { 356 r := recover() 357 if r != nil { 358 t.Errorf("non-nil recover during Goexit") 359 } 360 c <- struct{}{} 361 }() 362 runtime.Goexit() 363 }() 364 // Note: if the defer fails to run, we will get a deadlock here 365 <-c 366 } 367 368 func TestGoNil(t *testing.T) { 369 output := runTestProg(t, "testprog", "GoNil") 370 want := "go of nil func value" 371 if !strings.Contains(output, want) { 372 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want) 373 } 374 } 375 376 func TestMainGoroutineID(t *testing.T) { 377 output := runTestProg(t, "testprog", "MainGoroutineID") 378 want := "panic: test\n\ngoroutine 1 [running]:\n" 379 if !strings.HasPrefix(output, want) { 380 t.Fatalf("output does not start with %q:\n%s", want, output) 381 } 382 } 383 384 func TestNoHelperGoroutines(t *testing.T) { 385 output := runTestProg(t, "testprog", "NoHelperGoroutines") 386 matches := regexp.MustCompile(`goroutine [0-9]+ \[`).FindAllStringSubmatch(output, -1) 387 if len(matches) != 1 || matches[0][0] != "goroutine 1 [" { 388 t.Fatalf("want to see only goroutine 1, see:\n%s", output) 389 } 390 } 391 392 func TestBreakpoint(t *testing.T) { 393 output := runTestProg(t, "testprog", "Breakpoint") 394 // If runtime.Breakpoint() is inlined, then the stack trace prints 395 // "runtime.Breakpoint(...)" instead of "runtime.Breakpoint()". 396 want := "runtime.Breakpoint(" 397 if !strings.Contains(output, want) { 398 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want) 399 } 400 } 401 402 func TestGoexitInPanic(t *testing.T) { 403 // External linking brings in cgo, causing deadlock detection not working. 404 testenv.MustInternalLink(t, false) 405 406 // see issue 8774: this code used to trigger an infinite recursion 407 output := runTestProg(t, "testprog", "GoexitInPanic") 408 want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!" 409 if !strings.HasPrefix(output, want) { 410 t.Fatalf("output does not start with %q:\n%s", want, output) 411 } 412 } 413 414 // Issue 14965: Runtime panics should be of type runtime.Error 415 func TestRuntimePanicWithRuntimeError(t *testing.T) { 416 testCases := [...]func(){ 417 0: func() { 418 var m map[uint64]bool 419 m[1234] = true 420 }, 421 1: func() { 422 ch := make(chan struct{}) 423 close(ch) 424 close(ch) 425 }, 426 2: func() { 427 var ch = make(chan struct{}) 428 close(ch) 429 ch <- struct{}{} 430 }, 431 3: func() { 432 var s = make([]int, 2) 433 _ = s[2] 434 }, 435 4: func() { 436 n := -1 437 _ = make(chan bool, n) 438 }, 439 5: func() { 440 close((chan bool)(nil)) 441 }, 442 } 443 444 for i, fn := range testCases { 445 got := panicValue(fn) 446 if _, ok := got.(runtime.Error); !ok { 447 t.Errorf("test #%d: recovered value %v(type %T) does not implement runtime.Error", i, got, got) 448 } 449 } 450 } 451 452 func panicValue(fn func()) (recovered any) { 453 defer func() { 454 recovered = recover() 455 }() 456 fn() 457 return 458 } 459 460 func TestPanicAfterGoexit(t *testing.T) { 461 // an uncaught panic should still work after goexit 462 output := runTestProg(t, "testprog", "PanicAfterGoexit") 463 want := "panic: hello" 464 if !strings.HasPrefix(output, want) { 465 t.Fatalf("output does not start with %q:\n%s", want, output) 466 } 467 } 468 469 func TestRecoveredPanicAfterGoexit(t *testing.T) { 470 // External linking brings in cgo, causing deadlock detection not working. 471 testenv.MustInternalLink(t, false) 472 473 output := runTestProg(t, "testprog", "RecoveredPanicAfterGoexit") 474 want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!" 475 if !strings.HasPrefix(output, want) { 476 t.Fatalf("output does not start with %q:\n%s", want, output) 477 } 478 } 479 480 func TestRecoverBeforePanicAfterGoexit(t *testing.T) { 481 // External linking brings in cgo, causing deadlock detection not working. 482 testenv.MustInternalLink(t, false) 483 484 t.Parallel() 485 output := runTestProg(t, "testprog", "RecoverBeforePanicAfterGoexit") 486 want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!" 487 if !strings.HasPrefix(output, want) { 488 t.Fatalf("output does not start with %q:\n%s", want, output) 489 } 490 } 491 492 func TestRecoverBeforePanicAfterGoexit2(t *testing.T) { 493 // External linking brings in cgo, causing deadlock detection not working. 494 testenv.MustInternalLink(t, false) 495 496 t.Parallel() 497 output := runTestProg(t, "testprog", "RecoverBeforePanicAfterGoexit2") 498 want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!" 499 if !strings.HasPrefix(output, want) { 500 t.Fatalf("output does not start with %q:\n%s", want, output) 501 } 502 } 503 504 func TestNetpollDeadlock(t *testing.T) { 505 t.Parallel() 506 output := runTestProg(t, "testprognet", "NetpollDeadlock") 507 want := "done\n" 508 if !strings.HasSuffix(output, want) { 509 t.Fatalf("output does not start with %q:\n%s", want, output) 510 } 511 } 512 513 func TestPanicTraceback(t *testing.T) { 514 t.Parallel() 515 output := runTestProg(t, "testprog", "PanicTraceback") 516 want := "panic: hello\n\tpanic: panic pt2\n\tpanic: panic pt1\n" 517 if !strings.HasPrefix(output, want) { 518 t.Fatalf("output does not start with %q:\n%s", want, output) 519 } 520 521 // Check functions in the traceback. 522 fns := []string{"main.pt1.func1", "panic", "main.pt2.func1", "panic", "main.pt2", "main.pt1"} 523 for _, fn := range fns { 524 re := regexp.MustCompile(`(?m)^` + regexp.QuoteMeta(fn) + `\(.*\n`) 525 idx := re.FindStringIndex(output) 526 if idx == nil { 527 t.Fatalf("expected %q function in traceback:\n%s", fn, output) 528 } 529 output = output[idx[1]:] 530 } 531 } 532 533 func testPanicDeadlock(t *testing.T, name string, want string) { 534 // test issue 14432 535 output := runTestProg(t, "testprog", name) 536 if !strings.HasPrefix(output, want) { 537 t.Fatalf("output does not start with %q:\n%s", want, output) 538 } 539 } 540 541 func TestPanicDeadlockGosched(t *testing.T) { 542 testPanicDeadlock(t, "GoschedInPanic", "panic: errorThatGosched\n\n") 543 } 544 545 func TestPanicDeadlockSyscall(t *testing.T) { 546 testPanicDeadlock(t, "SyscallInPanic", "1\n2\npanic: 3\n\n") 547 } 548 549 func TestPanicLoop(t *testing.T) { 550 output := runTestProg(t, "testprog", "PanicLoop") 551 if want := "panic while printing panic value"; !strings.Contains(output, want) { 552 t.Errorf("output does not contain %q:\n%s", want, output) 553 } 554 } 555 556 func TestMemPprof(t *testing.T) { 557 testenv.MustHaveGoRun(t) 558 559 exe, err := buildTestProg(t, "testprog") 560 if err != nil { 561 t.Fatal(err) 562 } 563 564 got, err := testenv.CleanCmdEnv(exec.Command(exe, "MemProf")).CombinedOutput() 565 if err != nil { 566 t.Fatalf("testprog failed: %s, output:\n%s", err, got) 567 } 568 fn := strings.TrimSpace(string(got)) 569 defer os.Remove(fn) 570 571 for try := 0; try < 2; try++ { 572 cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-alloc_space", "-top")) 573 // Check that pprof works both with and without explicit executable on command line. 574 if try == 0 { 575 cmd.Args = append(cmd.Args, exe, fn) 576 } else { 577 cmd.Args = append(cmd.Args, fn) 578 } 579 found := false 580 for i, e := range cmd.Env { 581 if strings.HasPrefix(e, "PPROF_TMPDIR=") { 582 cmd.Env[i] = "PPROF_TMPDIR=" + os.TempDir() 583 found = true 584 break 585 } 586 } 587 if !found { 588 cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir()) 589 } 590 591 top, err := cmd.CombinedOutput() 592 t.Logf("%s:\n%s", cmd.Args, top) 593 if err != nil { 594 t.Error(err) 595 } else if !bytes.Contains(top, []byte("MemProf")) { 596 t.Error("missing MemProf in pprof output") 597 } 598 } 599 } 600 601 var concurrentMapTest = flag.Bool("run_concurrent_map_tests", false, "also run flaky concurrent map tests") 602 603 func TestConcurrentMapWrites(t *testing.T) { 604 if !*concurrentMapTest { 605 t.Skip("skipping without -run_concurrent_map_tests") 606 } 607 testenv.MustHaveGoRun(t) 608 output := runTestProg(t, "testprog", "concurrentMapWrites") 609 want := "fatal error: concurrent map writes" 610 if !strings.HasPrefix(output, want) { 611 t.Fatalf("output does not start with %q:\n%s", want, output) 612 } 613 } 614 func TestConcurrentMapReadWrite(t *testing.T) { 615 if !*concurrentMapTest { 616 t.Skip("skipping without -run_concurrent_map_tests") 617 } 618 testenv.MustHaveGoRun(t) 619 output := runTestProg(t, "testprog", "concurrentMapReadWrite") 620 want := "fatal error: concurrent map read and map write" 621 if !strings.HasPrefix(output, want) { 622 t.Fatalf("output does not start with %q:\n%s", want, output) 623 } 624 } 625 func TestConcurrentMapIterateWrite(t *testing.T) { 626 if !*concurrentMapTest { 627 t.Skip("skipping without -run_concurrent_map_tests") 628 } 629 testenv.MustHaveGoRun(t) 630 output := runTestProg(t, "testprog", "concurrentMapIterateWrite") 631 want := "fatal error: concurrent map iteration and map write" 632 if !strings.HasPrefix(output, want) { 633 t.Fatalf("output does not start with %q:\n%s", want, output) 634 } 635 } 636 637 type point struct { 638 x, y *int 639 } 640 641 func (p *point) negate() { 642 *p.x = *p.x * -1 643 *p.y = *p.y * -1 644 } 645 646 // Test for issue #10152. 647 func TestPanicInlined(t *testing.T) { 648 defer func() { 649 r := recover() 650 if r == nil { 651 t.Fatalf("recover failed") 652 } 653 buf := make([]byte, 2048) 654 n := runtime.Stack(buf, false) 655 buf = buf[:n] 656 if !bytes.Contains(buf, []byte("(*point).negate(")) { 657 t.Fatalf("expecting stack trace to contain call to (*point).negate()") 658 } 659 }() 660 661 pt := new(point) 662 pt.negate() 663 } 664 665 // Test for issues #3934 and #20018. 666 // We want to delay exiting until a panic print is complete. 667 func TestPanicRace(t *testing.T) { 668 testenv.MustHaveGoRun(t) 669 670 exe, err := buildTestProg(t, "testprog") 671 if err != nil { 672 t.Fatal(err) 673 } 674 675 // The test is intentionally racy, and in my testing does not 676 // produce the expected output about 0.05% of the time. 677 // So run the program in a loop and only fail the test if we 678 // get the wrong output ten times in a row. 679 const tries = 10 680 retry: 681 for i := 0; i < tries; i++ { 682 got, err := testenv.CleanCmdEnv(exec.Command(exe, "PanicRace")).CombinedOutput() 683 if err == nil { 684 t.Logf("try %d: program exited successfully, should have failed", i+1) 685 continue 686 } 687 688 if i > 0 { 689 t.Logf("try %d:\n", i+1) 690 } 691 t.Logf("%s\n", got) 692 693 wants := []string{ 694 "panic: crash", 695 "PanicRace", 696 "created by ", 697 } 698 for _, want := range wants { 699 if !bytes.Contains(got, []byte(want)) { 700 t.Logf("did not find expected string %q", want) 701 continue retry 702 } 703 } 704 705 // Test generated expected output. 706 return 707 } 708 t.Errorf("test ran %d times without producing expected output", tries) 709 } 710 711 func TestBadTraceback(t *testing.T) { 712 output := runTestProg(t, "testprog", "BadTraceback") 713 for _, want := range []string{ 714 "unexpected return pc", 715 "called from 0xbad", 716 "00000bad", // Smashed LR in hex dump 717 "<main.badLR", // Symbolization in hex dump (badLR1 or badLR2) 718 } { 719 if !strings.Contains(output, want) { 720 t.Errorf("output does not contain %q:\n%s", want, output) 721 } 722 } 723 } 724 725 func TestTimePprof(t *testing.T) { 726 // This test is unreliable on any system in which nanotime 727 // calls into libc. 728 switch runtime.GOOS { 729 case "aix", "darwin", "illumos", "openbsd", "solaris": 730 t.Skipf("skipping on %s because nanotime calls libc", runtime.GOOS) 731 } 732 733 // Pass GOTRACEBACK for issue #41120 to try to get more 734 // information on timeout. 735 fn := runTestProg(t, "testprog", "TimeProf", "GOTRACEBACK=crash") 736 fn = strings.TrimSpace(fn) 737 defer os.Remove(fn) 738 739 cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-top", "-nodecount=1", fn)) 740 cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir()) 741 top, err := cmd.CombinedOutput() 742 t.Logf("%s", top) 743 if err != nil { 744 t.Error(err) 745 } else if bytes.Contains(top, []byte("ExternalCode")) { 746 t.Error("profiler refers to ExternalCode") 747 } 748 } 749 750 // Test that runtime.abort does so. 751 func TestAbort(t *testing.T) { 752 // Pass GOTRACEBACK to ensure we get runtime frames. 753 output := runTestProg(t, "testprog", "Abort", "GOTRACEBACK=system") 754 if want := "runtime.abort"; !strings.Contains(output, want) { 755 t.Errorf("output does not contain %q:\n%s", want, output) 756 } 757 if strings.Contains(output, "BAD") { 758 t.Errorf("output contains BAD:\n%s", output) 759 } 760 // Check that it's a signal traceback. 761 want := "PC=" 762 // For systems that use a breakpoint, check specifically for that. 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 if !strings.Contains(output, want) { 775 t.Errorf("output does not contain %q:\n%s", want, output) 776 } 777 } 778 779 // For TestRuntimePanic: test a panic in the runtime package without 780 // involving the testing harness. 781 func init() { 782 if os.Getenv("GO_TEST_RUNTIME_PANIC") == "1" { 783 defer func() { 784 if r := recover(); r != nil { 785 // We expect to crash, so exit 0 786 // to indicate failure. 787 os.Exit(0) 788 } 789 }() 790 runtime.PanicForTesting(nil, 1) 791 // We expect to crash, so exit 0 to indicate failure. 792 os.Exit(0) 793 } 794 if os.Getenv("GO_TEST_RUNTIME_NPE_READMEMSTATS") == "1" { 795 runtime.ReadMemStats(nil) 796 os.Exit(0) 797 } 798 if os.Getenv("GO_TEST_RUNTIME_NPE_FUNCMETHOD") == "1" { 799 var f *runtime.Func 800 _ = f.Entry() 801 os.Exit(0) 802 } 803 804 } 805 806 func TestRuntimePanic(t *testing.T) { 807 testenv.MustHaveExec(t) 808 cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=^TestRuntimePanic$")) 809 cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_PANIC=1") 810 out, err := cmd.CombinedOutput() 811 t.Logf("%s", out) 812 if err == nil { 813 t.Error("child process did not fail") 814 } else if want := "runtime.unexportedPanicForTesting"; !bytes.Contains(out, []byte(want)) { 815 t.Errorf("output did not contain expected string %q", want) 816 } 817 } 818 819 func TestTracebackRuntimeFunction(t *testing.T) { 820 testenv.MustHaveExec(t) 821 cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestTracebackRuntimeFunction")) 822 cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_NPE_READMEMSTATS=1") 823 out, err := cmd.CombinedOutput() 824 t.Logf("%s", out) 825 if err == nil { 826 t.Error("child process did not fail") 827 } else if want := "runtime.ReadMemStats"; !bytes.Contains(out, []byte(want)) { 828 t.Errorf("output did not contain expected string %q", want) 829 } 830 } 831 832 func TestTracebackRuntimeMethod(t *testing.T) { 833 testenv.MustHaveExec(t) 834 cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestTracebackRuntimeMethod")) 835 cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_NPE_FUNCMETHOD=1") 836 out, err := cmd.CombinedOutput() 837 t.Logf("%s", out) 838 if err == nil { 839 t.Error("child process did not fail") 840 } else if want := "runtime.(*Func).Entry"; !bytes.Contains(out, []byte(want)) { 841 t.Errorf("output did not contain expected string %q", want) 842 } 843 } 844 845 // Test that g0 stack overflows are handled gracefully. 846 func TestG0StackOverflow(t *testing.T) { 847 testenv.MustHaveExec(t) 848 849 if runtime.GOOS == "ios" { 850 testenv.SkipFlaky(t, 62671) 851 } 852 853 if os.Getenv("TEST_G0_STACK_OVERFLOW") != "1" { 854 cmd := testenv.CleanCmdEnv(testenv.Command(t, os.Args[0], "-test.run=^TestG0StackOverflow$", "-test.v")) 855 cmd.Env = append(cmd.Env, "TEST_G0_STACK_OVERFLOW=1") 856 out, err := cmd.CombinedOutput() 857 t.Logf("output:\n%s", out) 858 // Don't check err since it's expected to crash. 859 if n := strings.Count(string(out), "morestack on g0\n"); n != 1 { 860 t.Fatalf("%s\n(exit status %v)", out, err) 861 } 862 if runtime.CrashStackImplemented { 863 // check for a stack trace 864 want := "runtime.stackOverflow" 865 if n := strings.Count(string(out), want); n < 5 { 866 t.Errorf("output does not contain %q at least 5 times:\n%s", want, out) 867 } 868 return // it's not a signal-style traceback 869 } 870 // Check that it's a signal-style traceback. 871 if runtime.GOOS != "windows" { 872 if want := "PC="; !strings.Contains(string(out), want) { 873 t.Errorf("output does not contain %q:\n%s", want, out) 874 } 875 } 876 return 877 } 878 879 runtime.G0StackOverflow() 880 } 881 882 // For TestCrashWhileTracing: test a panic without involving the testing 883 // harness, as we rely on stdout only containing trace output. 884 func init() { 885 if os.Getenv("TEST_CRASH_WHILE_TRACING") == "1" { 886 trace.Start(os.Stdout) 887 trace.Log(context.Background(), "xyzzy-cat", "xyzzy-msg") 888 panic("yzzyx") 889 } 890 } 891 892 func TestCrashWhileTracing(t *testing.T) { 893 if !goexperiment.ExecTracer2 { 894 t.Skip("skipping because this test is incompatible with the legacy tracer") 895 } 896 897 testenv.MustHaveExec(t) 898 899 cmd := testenv.CleanCmdEnv(testenv.Command(t, os.Args[0])) 900 cmd.Env = append(cmd.Env, "TEST_CRASH_WHILE_TRACING=1") 901 stdOut, err := cmd.StdoutPipe() 902 var errOut bytes.Buffer 903 cmd.Stderr = &errOut 904 905 if err := cmd.Start(); err != nil { 906 t.Fatalf("could not start subprocess: %v", err) 907 } 908 r, err := tracev2.NewReader(stdOut) 909 if err != nil { 910 t.Fatalf("could not create trace.NewReader: %v", err) 911 } 912 var seen bool 913 i := 1 914 loop: 915 for ; ; i++ { 916 ev, err := r.ReadEvent() 917 if err != nil { 918 if err != io.EOF { 919 t.Errorf("error at event %d: %v", i, err) 920 } 921 break loop 922 } 923 switch ev.Kind() { 924 case tracev2.EventLog: 925 v := ev.Log() 926 if v.Category == "xyzzy-cat" && v.Message == "xyzzy-msg" { 927 // Should we already stop reading here? More events may come, but 928 // we're not guaranteeing a fully unbroken trace until the last 929 // byte... 930 seen = true 931 } 932 } 933 } 934 if err := cmd.Wait(); err == nil { 935 t.Error("the process should have panicked") 936 } 937 if !seen { 938 t.Errorf("expected one matching log event matching, but none of the %d received trace events match", i) 939 } 940 t.Logf("stderr output:\n%s", errOut.String()) 941 needle := "yzzyx\n" 942 if n := strings.Count(errOut.String(), needle); n != 1 { 943 t.Fatalf("did not find expected panic message %q\n(exit status %v)", needle, err) 944 } 945 } 946 947 // Test that panic message is not clobbered. 948 // See issue 30150. 949 func TestDoublePanic(t *testing.T) { 950 output := runTestProg(t, "testprog", "DoublePanic", "GODEBUG=clobberfree=1") 951 wants := []string{"panic: XXX", "panic: YYY"} 952 for _, want := range wants { 953 if !strings.Contains(output, want) { 954 t.Errorf("output:\n%s\n\nwant output containing: %s", output, want) 955 } 956 } 957 } 958 959 // Test that panic while panicking discards error message 960 // See issue 52257 961 func TestPanicWhilePanicking(t *testing.T) { 962 tests := []struct { 963 Want string 964 Func string 965 }{ 966 { 967 "panic while printing panic value: important error message", 968 "ErrorPanic", 969 }, 970 { 971 "panic while printing panic value: important stringer message", 972 "StringerPanic", 973 }, 974 { 975 "panic while printing panic value: type", 976 "DoubleErrorPanic", 977 }, 978 { 979 "panic while printing panic value: type", 980 "DoubleStringerPanic", 981 }, 982 { 983 "panic while printing panic value: type", 984 "CircularPanic", 985 }, 986 { 987 "important string message", 988 "StringPanic", 989 }, 990 { 991 "nil", 992 "NilPanic", 993 }, 994 } 995 for _, x := range tests { 996 output := runTestProg(t, "testprog", x.Func) 997 if !strings.Contains(output, x.Want) { 998 t.Errorf("output does not contain %q:\n%s", x.Want, output) 999 } 1000 } 1001 } 1002 1003 func TestPanicOnUnsafeSlice(t *testing.T) { 1004 output := runTestProg(t, "testprog", "panicOnNilAndEleSizeIsZero") 1005 want := "panic: runtime error: unsafe.Slice: ptr is nil and len is not zero" 1006 if !strings.Contains(output, want) { 1007 t.Errorf("output does not contain %q:\n%s", want, output) 1008 } 1009 } 1010 1011 func TestNetpollWaiters(t *testing.T) { 1012 t.Parallel() 1013 output := runTestProg(t, "testprognet", "NetpollWaiters") 1014 want := "OK\n" 1015 if output != want { 1016 t.Fatalf("output is not %q\n%s", want, output) 1017 } 1018 }