github.com/geraldss/go/src@v0.0.0-20210511222824-ac7d0ebfc235/runtime/stack_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 "fmt" 10 "os" 11 "reflect" 12 "regexp" 13 . "runtime" 14 "strconv" 15 "strings" 16 "sync" 17 "sync/atomic" 18 "testing" 19 "time" 20 _ "unsafe" // for go:linkname 21 ) 22 23 // TestStackMem measures per-thread stack segment cache behavior. 24 // The test consumed up to 500MB in the past. 25 func TestStackMem(t *testing.T) { 26 const ( 27 BatchSize = 32 28 BatchCount = 256 29 ArraySize = 1024 30 RecursionDepth = 128 31 ) 32 if testing.Short() { 33 return 34 } 35 defer GOMAXPROCS(GOMAXPROCS(BatchSize)) 36 s0 := new(MemStats) 37 ReadMemStats(s0) 38 for b := 0; b < BatchCount; b++ { 39 c := make(chan bool, BatchSize) 40 for i := 0; i < BatchSize; i++ { 41 go func() { 42 var f func(k int, a [ArraySize]byte) 43 f = func(k int, a [ArraySize]byte) { 44 if k == 0 { 45 time.Sleep(time.Millisecond) 46 return 47 } 48 f(k-1, a) 49 } 50 f(RecursionDepth, [ArraySize]byte{}) 51 c <- true 52 }() 53 } 54 for i := 0; i < BatchSize; i++ { 55 <-c 56 } 57 58 // The goroutines have signaled via c that they are ready to exit. 59 // Give them a chance to exit by sleeping. If we don't wait, we 60 // might not reuse them on the next batch. 61 time.Sleep(10 * time.Millisecond) 62 } 63 s1 := new(MemStats) 64 ReadMemStats(s1) 65 consumed := int64(s1.StackSys - s0.StackSys) 66 t.Logf("Consumed %vMB for stack mem", consumed>>20) 67 estimate := int64(8 * BatchSize * ArraySize * RecursionDepth) // 8 is to reduce flakiness. 68 if consumed > estimate { 69 t.Fatalf("Stack mem: want %v, got %v", estimate, consumed) 70 } 71 // Due to broken stack memory accounting (https://golang.org/issue/7468), 72 // StackInuse can decrease during function execution, so we cast the values to int64. 73 inuse := int64(s1.StackInuse) - int64(s0.StackInuse) 74 t.Logf("Inuse %vMB for stack mem", inuse>>20) 75 if inuse > 4<<20 { 76 t.Fatalf("Stack inuse: want %v, got %v", 4<<20, inuse) 77 } 78 } 79 80 // Test stack growing in different contexts. 81 func TestStackGrowth(t *testing.T) { 82 if *flagQuick { 83 t.Skip("-quick") 84 } 85 86 if GOARCH == "wasm" { 87 t.Skip("fails on wasm (too slow?)") 88 } 89 90 // Don't make this test parallel as this makes the 20 second 91 // timeout unreliable on slow builders. (See issue #19381.) 92 93 var wg sync.WaitGroup 94 95 // in a normal goroutine 96 var growDuration time.Duration // For debugging failures 97 wg.Add(1) 98 go func() { 99 defer wg.Done() 100 start := time.Now() 101 growStack(nil) 102 growDuration = time.Since(start) 103 }() 104 wg.Wait() 105 106 // in locked goroutine 107 wg.Add(1) 108 go func() { 109 defer wg.Done() 110 LockOSThread() 111 growStack(nil) 112 UnlockOSThread() 113 }() 114 wg.Wait() 115 116 // in finalizer 117 wg.Add(1) 118 go func() { 119 defer wg.Done() 120 done := make(chan bool) 121 var startTime time.Time 122 var started, progress uint32 123 go func() { 124 s := new(string) 125 SetFinalizer(s, func(ss *string) { 126 startTime = time.Now() 127 atomic.StoreUint32(&started, 1) 128 growStack(&progress) 129 done <- true 130 }) 131 s = nil 132 done <- true 133 }() 134 <-done 135 GC() 136 137 timeout := 20 * time.Second 138 if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" { 139 scale, err := strconv.Atoi(s) 140 if err == nil { 141 timeout *= time.Duration(scale) 142 } 143 } 144 145 select { 146 case <-done: 147 case <-time.After(timeout): 148 if atomic.LoadUint32(&started) == 0 { 149 t.Log("finalizer did not start") 150 } else { 151 t.Logf("finalizer started %s ago and finished %d iterations", time.Since(startTime), atomic.LoadUint32(&progress)) 152 } 153 t.Log("first growStack took", growDuration) 154 t.Error("finalizer did not run") 155 return 156 } 157 }() 158 wg.Wait() 159 } 160 161 // ... and in init 162 //func init() { 163 // growStack() 164 //} 165 166 func growStack(progress *uint32) { 167 n := 1 << 10 168 if testing.Short() { 169 n = 1 << 8 170 } 171 for i := 0; i < n; i++ { 172 x := 0 173 growStackIter(&x, i) 174 if x != i+1 { 175 panic("stack is corrupted") 176 } 177 if progress != nil { 178 atomic.StoreUint32(progress, uint32(i)) 179 } 180 } 181 GC() 182 } 183 184 // This function is not an anonymous func, so that the compiler can do escape 185 // analysis and place x on stack (and subsequently stack growth update the pointer). 186 func growStackIter(p *int, n int) { 187 if n == 0 { 188 *p = n + 1 189 GC() 190 return 191 } 192 *p = n + 1 193 x := 0 194 growStackIter(&x, n-1) 195 if x != n { 196 panic("stack is corrupted") 197 } 198 } 199 200 func TestStackGrowthCallback(t *testing.T) { 201 t.Parallel() 202 var wg sync.WaitGroup 203 204 // test stack growth at chan op 205 wg.Add(1) 206 go func() { 207 defer wg.Done() 208 c := make(chan int, 1) 209 growStackWithCallback(func() { 210 c <- 1 211 <-c 212 }) 213 }() 214 215 // test stack growth at map op 216 wg.Add(1) 217 go func() { 218 defer wg.Done() 219 m := make(map[int]int) 220 growStackWithCallback(func() { 221 _, _ = m[1] 222 m[1] = 1 223 }) 224 }() 225 226 // test stack growth at goroutine creation 227 wg.Add(1) 228 go func() { 229 defer wg.Done() 230 growStackWithCallback(func() { 231 done := make(chan bool) 232 go func() { 233 done <- true 234 }() 235 <-done 236 }) 237 }() 238 wg.Wait() 239 } 240 241 func growStackWithCallback(cb func()) { 242 var f func(n int) 243 f = func(n int) { 244 if n == 0 { 245 cb() 246 return 247 } 248 f(n - 1) 249 } 250 for i := 0; i < 1<<10; i++ { 251 f(i) 252 } 253 } 254 255 // TestDeferPtrs tests the adjustment of Defer's argument pointers (p aka &y) 256 // during a stack copy. 257 func set(p *int, x int) { 258 *p = x 259 } 260 func TestDeferPtrs(t *testing.T) { 261 var y int 262 263 defer func() { 264 if y != 42 { 265 t.Errorf("defer's stack references were not adjusted appropriately") 266 } 267 }() 268 defer set(&y, 42) 269 growStack(nil) 270 } 271 272 type bigBuf [4 * 1024]byte 273 274 // TestDeferPtrsGoexit is like TestDeferPtrs but exercises the possibility that the 275 // stack grows as part of starting the deferred function. It calls Goexit at various 276 // stack depths, forcing the deferred function (with >4kB of args) to be run at 277 // the bottom of the stack. The goal is to find a stack depth less than 4kB from 278 // the end of the stack. Each trial runs in a different goroutine so that an earlier 279 // stack growth does not invalidate a later attempt. 280 func TestDeferPtrsGoexit(t *testing.T) { 281 for i := 0; i < 100; i++ { 282 c := make(chan int, 1) 283 go testDeferPtrsGoexit(c, i) 284 if n := <-c; n != 42 { 285 t.Fatalf("defer's stack references were not adjusted appropriately (i=%d n=%d)", i, n) 286 } 287 } 288 } 289 290 func testDeferPtrsGoexit(c chan int, i int) { 291 var y int 292 defer func() { 293 c <- y 294 }() 295 defer setBig(&y, 42, bigBuf{}) 296 useStackAndCall(i, Goexit) 297 } 298 299 func setBig(p *int, x int, b bigBuf) { 300 *p = x 301 } 302 303 // TestDeferPtrsPanic is like TestDeferPtrsGoexit, but it's using panic instead 304 // of Goexit to run the Defers. Those two are different execution paths 305 // in the runtime. 306 func TestDeferPtrsPanic(t *testing.T) { 307 for i := 0; i < 100; i++ { 308 c := make(chan int, 1) 309 go testDeferPtrsGoexit(c, i) 310 if n := <-c; n != 42 { 311 t.Fatalf("defer's stack references were not adjusted appropriately (i=%d n=%d)", i, n) 312 } 313 } 314 } 315 316 func testDeferPtrsPanic(c chan int, i int) { 317 var y int 318 defer func() { 319 if recover() == nil { 320 c <- -1 321 return 322 } 323 c <- y 324 }() 325 defer setBig(&y, 42, bigBuf{}) 326 useStackAndCall(i, func() { panic(1) }) 327 } 328 329 //go:noinline 330 func testDeferLeafSigpanic1() { 331 // Cause a sigpanic to be injected in this frame. 332 // 333 // This function has to be declared before 334 // TestDeferLeafSigpanic so the runtime will crash if we think 335 // this function's continuation PC is in 336 // TestDeferLeafSigpanic. 337 *(*int)(nil) = 0 338 } 339 340 // TestDeferLeafSigpanic tests defer matching around leaf functions 341 // that sigpanic. This is tricky because on LR machines the outer 342 // function and the inner function have the same SP, but it's critical 343 // that we match up the defer correctly to get the right liveness map. 344 // See issue #25499. 345 func TestDeferLeafSigpanic(t *testing.T) { 346 // Push a defer that will walk the stack. 347 defer func() { 348 if err := recover(); err == nil { 349 t.Fatal("expected panic from nil pointer") 350 } 351 GC() 352 }() 353 // Call a leaf function. We must set up the exact call stack: 354 // 355 // defering function -> leaf function -> sigpanic 356 // 357 // On LR machines, the leaf function will have the same SP as 358 // the SP pushed for the defer frame. 359 testDeferLeafSigpanic1() 360 } 361 362 // TestPanicUseStack checks that a chain of Panic structs on the stack are 363 // updated correctly if the stack grows during the deferred execution that 364 // happens as a result of the panic. 365 func TestPanicUseStack(t *testing.T) { 366 pc := make([]uintptr, 10000) 367 defer func() { 368 recover() 369 Callers(0, pc) // force stack walk 370 useStackAndCall(100, func() { 371 defer func() { 372 recover() 373 Callers(0, pc) // force stack walk 374 useStackAndCall(200, func() { 375 defer func() { 376 recover() 377 Callers(0, pc) // force stack walk 378 }() 379 panic(3) 380 }) 381 }() 382 panic(2) 383 }) 384 }() 385 panic(1) 386 } 387 388 func TestPanicFar(t *testing.T) { 389 var xtree *xtreeNode 390 pc := make([]uintptr, 10000) 391 defer func() { 392 // At this point we created a large stack and unwound 393 // it via recovery. Force a stack walk, which will 394 // check the stack's consistency. 395 Callers(0, pc) 396 }() 397 defer func() { 398 recover() 399 }() 400 useStackAndCall(100, func() { 401 // Kick off the GC and make it do something nontrivial. 402 // (This used to force stack barriers to stick around.) 403 xtree = makeTree(18) 404 // Give the GC time to start scanning stacks. 405 time.Sleep(time.Millisecond) 406 panic(1) 407 }) 408 _ = xtree 409 } 410 411 type xtreeNode struct { 412 l, r *xtreeNode 413 } 414 415 func makeTree(d int) *xtreeNode { 416 if d == 0 { 417 return new(xtreeNode) 418 } 419 return &xtreeNode{makeTree(d - 1), makeTree(d - 1)} 420 } 421 422 // use about n KB of stack and call f 423 func useStackAndCall(n int, f func()) { 424 if n == 0 { 425 f() 426 return 427 } 428 var b [1024]byte // makes frame about 1KB 429 useStackAndCall(n-1+int(b[99]), f) 430 } 431 432 func useStack(n int) { 433 useStackAndCall(n, func() {}) 434 } 435 436 func growing(c chan int, done chan struct{}) { 437 for n := range c { 438 useStack(n) 439 done <- struct{}{} 440 } 441 done <- struct{}{} 442 } 443 444 func TestStackCache(t *testing.T) { 445 // Allocate a bunch of goroutines and grow their stacks. 446 // Repeat a few times to test the stack cache. 447 const ( 448 R = 4 449 G = 200 450 S = 5 451 ) 452 for i := 0; i < R; i++ { 453 var reqchans [G]chan int 454 done := make(chan struct{}) 455 for j := 0; j < G; j++ { 456 reqchans[j] = make(chan int) 457 go growing(reqchans[j], done) 458 } 459 for s := 0; s < S; s++ { 460 for j := 0; j < G; j++ { 461 reqchans[j] <- 1 << uint(s) 462 } 463 for j := 0; j < G; j++ { 464 <-done 465 } 466 } 467 for j := 0; j < G; j++ { 468 close(reqchans[j]) 469 } 470 for j := 0; j < G; j++ { 471 <-done 472 } 473 } 474 } 475 476 func TestStackOutput(t *testing.T) { 477 b := make([]byte, 1024) 478 stk := string(b[:Stack(b, false)]) 479 if !strings.HasPrefix(stk, "goroutine ") { 480 t.Errorf("Stack (len %d):\n%s", len(stk), stk) 481 t.Errorf("Stack output should begin with \"goroutine \"") 482 } 483 } 484 485 func TestStackAllOutput(t *testing.T) { 486 b := make([]byte, 1024) 487 stk := string(b[:Stack(b, true)]) 488 if !strings.HasPrefix(stk, "goroutine ") { 489 t.Errorf("Stack (len %d):\n%s", len(stk), stk) 490 t.Errorf("Stack output should begin with \"goroutine \"") 491 } 492 } 493 494 func TestStackPanic(t *testing.T) { 495 // Test that stack copying copies panics correctly. This is difficult 496 // to test because it is very unlikely that the stack will be copied 497 // in the middle of gopanic. But it can happen. 498 // To make this test effective, edit panic.go:gopanic and uncomment 499 // the GC() call just before freedefer(d). 500 defer func() { 501 if x := recover(); x == nil { 502 t.Errorf("recover failed") 503 } 504 }() 505 useStack(32) 506 panic("test panic") 507 } 508 509 func BenchmarkStackCopyPtr(b *testing.B) { 510 c := make(chan bool) 511 for i := 0; i < b.N; i++ { 512 go func() { 513 i := 1000000 514 countp(&i) 515 c <- true 516 }() 517 <-c 518 } 519 } 520 521 func countp(n *int) { 522 if *n == 0 { 523 return 524 } 525 *n-- 526 countp(n) 527 } 528 529 func BenchmarkStackCopy(b *testing.B) { 530 c := make(chan bool) 531 for i := 0; i < b.N; i++ { 532 go func() { 533 count(1000000) 534 c <- true 535 }() 536 <-c 537 } 538 } 539 540 func count(n int) int { 541 if n == 0 { 542 return 0 543 } 544 return 1 + count(n-1) 545 } 546 547 func BenchmarkStackCopyNoCache(b *testing.B) { 548 c := make(chan bool) 549 for i := 0; i < b.N; i++ { 550 go func() { 551 count1(1000000) 552 c <- true 553 }() 554 <-c 555 } 556 } 557 558 func count1(n int) int { 559 if n <= 0 { 560 return 0 561 } 562 return 1 + count2(n-1) 563 } 564 565 func count2(n int) int { return 1 + count3(n-1) } 566 func count3(n int) int { return 1 + count4(n-1) } 567 func count4(n int) int { return 1 + count5(n-1) } 568 func count5(n int) int { return 1 + count6(n-1) } 569 func count6(n int) int { return 1 + count7(n-1) } 570 func count7(n int) int { return 1 + count8(n-1) } 571 func count8(n int) int { return 1 + count9(n-1) } 572 func count9(n int) int { return 1 + count10(n-1) } 573 func count10(n int) int { return 1 + count11(n-1) } 574 func count11(n int) int { return 1 + count12(n-1) } 575 func count12(n int) int { return 1 + count13(n-1) } 576 func count13(n int) int { return 1 + count14(n-1) } 577 func count14(n int) int { return 1 + count15(n-1) } 578 func count15(n int) int { return 1 + count16(n-1) } 579 func count16(n int) int { return 1 + count17(n-1) } 580 func count17(n int) int { return 1 + count18(n-1) } 581 func count18(n int) int { return 1 + count19(n-1) } 582 func count19(n int) int { return 1 + count20(n-1) } 583 func count20(n int) int { return 1 + count21(n-1) } 584 func count21(n int) int { return 1 + count22(n-1) } 585 func count22(n int) int { return 1 + count23(n-1) } 586 func count23(n int) int { return 1 + count1(n-1) } 587 588 type structWithMethod struct{} 589 590 func (s structWithMethod) caller() string { 591 _, file, line, ok := Caller(1) 592 if !ok { 593 panic("Caller failed") 594 } 595 return fmt.Sprintf("%s:%d", file, line) 596 } 597 598 func (s structWithMethod) callers() []uintptr { 599 pc := make([]uintptr, 16) 600 return pc[:Callers(0, pc)] 601 } 602 603 func (s structWithMethod) stack() string { 604 buf := make([]byte, 4<<10) 605 return string(buf[:Stack(buf, false)]) 606 } 607 608 func (s structWithMethod) nop() {} 609 610 func TestStackWrapperCaller(t *testing.T) { 611 var d structWithMethod 612 // Force the compiler to construct a wrapper method. 613 wrapper := (*structWithMethod).caller 614 // Check that the wrapper doesn't affect the stack trace. 615 if dc, ic := d.caller(), wrapper(&d); dc != ic { 616 t.Fatalf("direct caller %q != indirect caller %q", dc, ic) 617 } 618 } 619 620 func TestStackWrapperCallers(t *testing.T) { 621 var d structWithMethod 622 wrapper := (*structWithMethod).callers 623 // Check that <autogenerated> doesn't appear in the stack trace. 624 pcs := wrapper(&d) 625 frames := CallersFrames(pcs) 626 for { 627 fr, more := frames.Next() 628 if fr.File == "<autogenerated>" { 629 t.Fatalf("<autogenerated> appears in stack trace: %+v", fr) 630 } 631 if !more { 632 break 633 } 634 } 635 } 636 637 func TestStackWrapperStack(t *testing.T) { 638 var d structWithMethod 639 wrapper := (*structWithMethod).stack 640 // Check that <autogenerated> doesn't appear in the stack trace. 641 stk := wrapper(&d) 642 if strings.Contains(stk, "<autogenerated>") { 643 t.Fatalf("<autogenerated> appears in stack trace:\n%s", stk) 644 } 645 } 646 647 type I interface { 648 M() 649 } 650 651 func TestStackWrapperStackPanic(t *testing.T) { 652 t.Run("sigpanic", func(t *testing.T) { 653 // nil calls to interface methods cause a sigpanic. 654 testStackWrapperPanic(t, func() { I.M(nil) }, "runtime_test.I.M") 655 }) 656 t.Run("panicwrap", func(t *testing.T) { 657 // Nil calls to value method wrappers call panicwrap. 658 wrapper := (*structWithMethod).nop 659 testStackWrapperPanic(t, func() { wrapper(nil) }, "runtime_test.(*structWithMethod).nop") 660 }) 661 } 662 663 func testStackWrapperPanic(t *testing.T, cb func(), expect string) { 664 // Test that the stack trace from a panicking wrapper includes 665 // the wrapper, even though elide these when they don't panic. 666 t.Run("CallersFrames", func(t *testing.T) { 667 defer func() { 668 err := recover() 669 if err == nil { 670 t.Fatalf("expected panic") 671 } 672 pcs := make([]uintptr, 10) 673 n := Callers(0, pcs) 674 frames := CallersFrames(pcs[:n]) 675 for { 676 frame, more := frames.Next() 677 t.Log(frame.Function) 678 if frame.Function == expect { 679 return 680 } 681 if !more { 682 break 683 } 684 } 685 t.Fatalf("panicking wrapper %s missing from stack trace", expect) 686 }() 687 cb() 688 }) 689 t.Run("Stack", func(t *testing.T) { 690 defer func() { 691 err := recover() 692 if err == nil { 693 t.Fatalf("expected panic") 694 } 695 buf := make([]byte, 4<<10) 696 stk := string(buf[:Stack(buf, false)]) 697 if !strings.Contains(stk, "\n"+expect) { 698 t.Fatalf("panicking wrapper %s missing from stack trace:\n%s", expect, stk) 699 } 700 }() 701 cb() 702 }) 703 } 704 705 func TestCallersFromWrapper(t *testing.T) { 706 // Test that invoking CallersFrames on a stack where the first 707 // PC is an autogenerated wrapper keeps the wrapper in the 708 // trace. Normally we elide these, assuming that the wrapper 709 // calls the thing you actually wanted to see, but in this 710 // case we need to keep it. 711 pc := reflect.ValueOf(I.M).Pointer() 712 frames := CallersFrames([]uintptr{pc}) 713 frame, more := frames.Next() 714 if frame.Function != "runtime_test.I.M" { 715 t.Fatalf("want function %s, got %s", "runtime_test.I.M", frame.Function) 716 } 717 if more { 718 t.Fatalf("want 1 frame, got > 1") 719 } 720 } 721 722 func TestTracebackSystemstack(t *testing.T) { 723 if GOARCH == "ppc64" || GOARCH == "ppc64le" { 724 t.Skip("systemstack tail call not implemented on ppc64x") 725 } 726 727 // Test that profiles correctly jump over systemstack, 728 // including nested systemstack calls. 729 pcs := make([]uintptr, 20) 730 pcs = pcs[:TracebackSystemstack(pcs, 5)] 731 // Check that runtime.TracebackSystemstack appears five times 732 // and that we see TestTracebackSystemstack. 733 countIn, countOut := 0, 0 734 frames := CallersFrames(pcs) 735 var tb bytes.Buffer 736 for { 737 frame, more := frames.Next() 738 fmt.Fprintf(&tb, "\n%s+0x%x %s:%d", frame.Function, frame.PC-frame.Entry, frame.File, frame.Line) 739 switch frame.Function { 740 case "runtime.TracebackSystemstack": 741 countIn++ 742 case "runtime_test.TestTracebackSystemstack": 743 countOut++ 744 } 745 if !more { 746 break 747 } 748 } 749 if countIn != 5 || countOut != 1 { 750 t.Fatalf("expected 5 calls to TracebackSystemstack and 1 call to TestTracebackSystemstack, got:%s", tb.String()) 751 } 752 } 753 754 func TestTracebackAncestors(t *testing.T) { 755 goroutineRegex := regexp.MustCompile(`goroutine [0-9]+ \[`) 756 for _, tracebackDepth := range []int{0, 1, 5, 50} { 757 output := runTestProg(t, "testprog", "TracebackAncestors", fmt.Sprintf("GODEBUG=tracebackancestors=%d", tracebackDepth)) 758 759 numGoroutines := 3 760 numFrames := 2 761 ancestorsExpected := numGoroutines 762 if numGoroutines > tracebackDepth { 763 ancestorsExpected = tracebackDepth 764 } 765 766 matches := goroutineRegex.FindAllStringSubmatch(output, -1) 767 if len(matches) != 2 { 768 t.Fatalf("want 2 goroutines, got:\n%s", output) 769 } 770 771 // Check functions in the traceback. 772 fns := []string{"main.recurseThenCallGo", "main.main", "main.printStack", "main.TracebackAncestors"} 773 for _, fn := range fns { 774 if !strings.Contains(output, "\n"+fn+"(") { 775 t.Fatalf("expected %q function in traceback:\n%s", fn, output) 776 } 777 } 778 779 if want, count := "originating from goroutine", ancestorsExpected; strings.Count(output, want) != count { 780 t.Errorf("output does not contain %d instances of %q:\n%s", count, want, output) 781 } 782 783 if want, count := "main.recurseThenCallGo(...)", ancestorsExpected*(numFrames+1); strings.Count(output, want) != count { 784 t.Errorf("output does not contain %d instances of %q:\n%s", count, want, output) 785 } 786 787 if want, count := "main.recurseThenCallGo(0x", 1; strings.Count(output, want) != count { 788 t.Errorf("output does not contain %d instances of %q:\n%s", count, want, output) 789 } 790 } 791 } 792 793 // Test that defer closure is correctly scanned when the stack is scanned. 794 func TestDeferLiveness(t *testing.T) { 795 output := runTestProg(t, "testprog", "DeferLiveness", "GODEBUG=clobberfree=1") 796 if output != "" { 797 t.Errorf("output:\n%s\n\nwant no output", output) 798 } 799 } 800 801 func TestDeferHeapAndStack(t *testing.T) { 802 P := 4 // processors 803 N := 10000 //iterations 804 D := 200 // stack depth 805 806 if testing.Short() { 807 P /= 2 808 N /= 10 809 D /= 10 810 } 811 c := make(chan bool) 812 for p := 0; p < P; p++ { 813 go func() { 814 for i := 0; i < N; i++ { 815 if deferHeapAndStack(D) != 2*D { 816 panic("bad result") 817 } 818 } 819 c <- true 820 }() 821 } 822 for p := 0; p < P; p++ { 823 <-c 824 } 825 } 826 827 // deferHeapAndStack(n) computes 2*n 828 func deferHeapAndStack(n int) (r int) { 829 if n == 0 { 830 return 0 831 } 832 if n%2 == 0 { 833 // heap-allocated defers 834 for i := 0; i < 2; i++ { 835 defer func() { 836 r++ 837 }() 838 } 839 } else { 840 // stack-allocated defers 841 defer func() { 842 r++ 843 }() 844 defer func() { 845 r++ 846 }() 847 } 848 r = deferHeapAndStack(n - 1) 849 escapeMe(new([1024]byte)) // force some GCs 850 return 851 } 852 853 // Pass a value to escapeMe to force it to escape. 854 var escapeMe = func(x interface{}) {} 855 856 // Test that when F -> G is inlined and F is excluded from stack 857 // traces, G still appears. 858 func TestTracebackInlineExcluded(t *testing.T) { 859 defer func() { 860 recover() 861 buf := make([]byte, 4<<10) 862 stk := string(buf[:Stack(buf, false)]) 863 864 t.Log(stk) 865 866 if not := "tracebackExcluded"; strings.Contains(stk, not) { 867 t.Errorf("found but did not expect %q", not) 868 } 869 if want := "tracebackNotExcluded"; !strings.Contains(stk, want) { 870 t.Errorf("expected %q in stack", want) 871 } 872 }() 873 tracebackExcluded() 874 } 875 876 // tracebackExcluded should be excluded from tracebacks. There are 877 // various ways this could come up. Linking it to a "runtime." name is 878 // rather synthetic, but it's easy and reliable. See issue #42754 for 879 // one way this happened in real code. 880 // 881 //go:linkname tracebackExcluded runtime.tracebackExcluded 882 //go:noinline 883 func tracebackExcluded() { 884 // Call an inlined function that should not itself be excluded 885 // from tracebacks. 886 tracebackNotExcluded() 887 } 888 889 // tracebackNotExcluded should be inlined into tracebackExcluded, but 890 // should not itself be excluded from the traceback. 891 func tracebackNotExcluded() { 892 var x *int 893 *x = 0 894 }