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