github.com/eun/go@v0.0.0-20170811110501-92cfd07a6cfd/src/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 . "runtime" 9 "strings" 10 "sync" 11 "sync/atomic" 12 "testing" 13 "time" 14 ) 15 16 // TestStackMem measures per-thread stack segment cache behavior. 17 // The test consumed up to 500MB in the past. 18 func TestStackMem(t *testing.T) { 19 const ( 20 BatchSize = 32 21 BatchCount = 256 22 ArraySize = 1024 23 RecursionDepth = 128 24 ) 25 if testing.Short() { 26 return 27 } 28 defer GOMAXPROCS(GOMAXPROCS(BatchSize)) 29 s0 := new(MemStats) 30 ReadMemStats(s0) 31 for b := 0; b < BatchCount; b++ { 32 c := make(chan bool, BatchSize) 33 for i := 0; i < BatchSize; i++ { 34 go func() { 35 var f func(k int, a [ArraySize]byte) 36 f = func(k int, a [ArraySize]byte) { 37 if k == 0 { 38 time.Sleep(time.Millisecond) 39 return 40 } 41 f(k-1, a) 42 } 43 f(RecursionDepth, [ArraySize]byte{}) 44 c <- true 45 }() 46 } 47 for i := 0; i < BatchSize; i++ { 48 <-c 49 } 50 51 // The goroutines have signaled via c that they are ready to exit. 52 // Give them a chance to exit by sleeping. If we don't wait, we 53 // might not reuse them on the next batch. 54 time.Sleep(10 * time.Millisecond) 55 } 56 s1 := new(MemStats) 57 ReadMemStats(s1) 58 consumed := int64(s1.StackSys - s0.StackSys) 59 t.Logf("Consumed %vMB for stack mem", consumed>>20) 60 estimate := int64(8 * BatchSize * ArraySize * RecursionDepth) // 8 is to reduce flakiness. 61 if consumed > estimate { 62 t.Fatalf("Stack mem: want %v, got %v", estimate, consumed) 63 } 64 // Due to broken stack memory accounting (https://golang.org/issue/7468), 65 // StackInuse can decrease during function execution, so we cast the values to int64. 66 inuse := int64(s1.StackInuse) - int64(s0.StackInuse) 67 t.Logf("Inuse %vMB for stack mem", inuse>>20) 68 if inuse > 4<<20 { 69 t.Fatalf("Stack inuse: want %v, got %v", 4<<20, inuse) 70 } 71 } 72 73 // Test stack growing in different contexts. 74 func TestStackGrowth(t *testing.T) { 75 // Don't make this test parallel as this makes the 20 second 76 // timeout unreliable on slow builders. (See issue #19381.) 77 78 var wg sync.WaitGroup 79 80 // in a normal goroutine 81 wg.Add(1) 82 go func() { 83 defer wg.Done() 84 growStack() 85 }() 86 wg.Wait() 87 88 // in locked goroutine 89 wg.Add(1) 90 go func() { 91 defer wg.Done() 92 LockOSThread() 93 growStack() 94 UnlockOSThread() 95 }() 96 wg.Wait() 97 98 // in finalizer 99 wg.Add(1) 100 go func() { 101 defer wg.Done() 102 done := make(chan bool) 103 var started uint32 104 go func() { 105 s := new(string) 106 SetFinalizer(s, func(ss *string) { 107 atomic.StoreUint32(&started, 1) 108 growStack() 109 done <- true 110 }) 111 s = nil 112 done <- true 113 }() 114 <-done 115 GC() 116 select { 117 case <-done: 118 case <-time.After(20 * time.Second): 119 if atomic.LoadUint32(&started) == 0 { 120 t.Log("finalizer did not start") 121 } 122 t.Error("finalizer did not run") 123 return 124 } 125 }() 126 wg.Wait() 127 } 128 129 // ... and in init 130 //func init() { 131 // growStack() 132 //} 133 134 func growStack() { 135 n := 1 << 10 136 if testing.Short() { 137 n = 1 << 8 138 } 139 for i := 0; i < n; i++ { 140 x := 0 141 growStackIter(&x, i) 142 if x != i+1 { 143 panic("stack is corrupted") 144 } 145 } 146 GC() 147 } 148 149 // This function is not an anonymous func, so that the compiler can do escape 150 // analysis and place x on stack (and subsequently stack growth update the pointer). 151 func growStackIter(p *int, n int) { 152 if n == 0 { 153 *p = n + 1 154 GC() 155 return 156 } 157 *p = n + 1 158 x := 0 159 growStackIter(&x, n-1) 160 if x != n { 161 panic("stack is corrupted") 162 } 163 } 164 165 func TestStackGrowthCallback(t *testing.T) { 166 t.Parallel() 167 var wg sync.WaitGroup 168 169 // test stack growth at chan op 170 wg.Add(1) 171 go func() { 172 defer wg.Done() 173 c := make(chan int, 1) 174 growStackWithCallback(func() { 175 c <- 1 176 <-c 177 }) 178 }() 179 180 // test stack growth at map op 181 wg.Add(1) 182 go func() { 183 defer wg.Done() 184 m := make(map[int]int) 185 growStackWithCallback(func() { 186 _, _ = m[1] 187 m[1] = 1 188 }) 189 }() 190 191 // test stack growth at goroutine creation 192 wg.Add(1) 193 go func() { 194 defer wg.Done() 195 growStackWithCallback(func() { 196 done := make(chan bool) 197 go func() { 198 done <- true 199 }() 200 <-done 201 }) 202 }() 203 wg.Wait() 204 } 205 206 func growStackWithCallback(cb func()) { 207 var f func(n int) 208 f = func(n int) { 209 if n == 0 { 210 cb() 211 return 212 } 213 f(n - 1) 214 } 215 for i := 0; i < 1<<10; i++ { 216 f(i) 217 } 218 } 219 220 // TestDeferPtrs tests the adjustment of Defer's argument pointers (p aka &y) 221 // during a stack copy. 222 func set(p *int, x int) { 223 *p = x 224 } 225 func TestDeferPtrs(t *testing.T) { 226 var y int 227 228 defer func() { 229 if y != 42 { 230 t.Errorf("defer's stack references were not adjusted appropriately") 231 } 232 }() 233 defer set(&y, 42) 234 growStack() 235 } 236 237 type bigBuf [4 * 1024]byte 238 239 // TestDeferPtrsGoexit is like TestDeferPtrs but exercises the possibility that the 240 // stack grows as part of starting the deferred function. It calls Goexit at various 241 // stack depths, forcing the deferred function (with >4kB of args) to be run at 242 // the bottom of the stack. The goal is to find a stack depth less than 4kB from 243 // the end of the stack. Each trial runs in a different goroutine so that an earlier 244 // stack growth does not invalidate a later attempt. 245 func TestDeferPtrsGoexit(t *testing.T) { 246 for i := 0; i < 100; i++ { 247 c := make(chan int, 1) 248 go testDeferPtrsGoexit(c, i) 249 if n := <-c; n != 42 { 250 t.Fatalf("defer's stack references were not adjusted appropriately (i=%d n=%d)", i, n) 251 } 252 } 253 } 254 255 func testDeferPtrsGoexit(c chan int, i int) { 256 var y int 257 defer func() { 258 c <- y 259 }() 260 defer setBig(&y, 42, bigBuf{}) 261 useStackAndCall(i, Goexit) 262 } 263 264 func setBig(p *int, x int, b bigBuf) { 265 *p = x 266 } 267 268 // TestDeferPtrsPanic is like TestDeferPtrsGoexit, but it's using panic instead 269 // of Goexit to run the Defers. Those two are different execution paths 270 // in the runtime. 271 func TestDeferPtrsPanic(t *testing.T) { 272 for i := 0; i < 100; i++ { 273 c := make(chan int, 1) 274 go testDeferPtrsGoexit(c, i) 275 if n := <-c; n != 42 { 276 t.Fatalf("defer's stack references were not adjusted appropriately (i=%d n=%d)", i, n) 277 } 278 } 279 } 280 281 func testDeferPtrsPanic(c chan int, i int) { 282 var y int 283 defer func() { 284 if recover() == nil { 285 c <- -1 286 return 287 } 288 c <- y 289 }() 290 defer setBig(&y, 42, bigBuf{}) 291 useStackAndCall(i, func() { panic(1) }) 292 } 293 294 // TestPanicUseStack checks that a chain of Panic structs on the stack are 295 // updated correctly if the stack grows during the deferred execution that 296 // happens as a result of the panic. 297 func TestPanicUseStack(t *testing.T) { 298 pc := make([]uintptr, 10000) 299 defer func() { 300 recover() 301 Callers(0, pc) // force stack walk 302 useStackAndCall(100, func() { 303 defer func() { 304 recover() 305 Callers(0, pc) // force stack walk 306 useStackAndCall(200, func() { 307 defer func() { 308 recover() 309 Callers(0, pc) // force stack walk 310 }() 311 panic(3) 312 }) 313 }() 314 panic(2) 315 }) 316 }() 317 panic(1) 318 } 319 320 func TestPanicFar(t *testing.T) { 321 var xtree *xtreeNode 322 pc := make([]uintptr, 10000) 323 defer func() { 324 // At this point we created a large stack and unwound 325 // it via recovery. Force a stack walk, which will 326 // check the stack's consistency. 327 Callers(0, pc) 328 }() 329 defer func() { 330 recover() 331 }() 332 useStackAndCall(100, func() { 333 // Kick off the GC and make it do something nontrivial. 334 // (This used to force stack barriers to stick around.) 335 xtree = makeTree(18) 336 // Give the GC time to start scanning stacks. 337 time.Sleep(time.Millisecond) 338 panic(1) 339 }) 340 _ = xtree 341 } 342 343 type xtreeNode struct { 344 l, r *xtreeNode 345 } 346 347 func makeTree(d int) *xtreeNode { 348 if d == 0 { 349 return new(xtreeNode) 350 } 351 return &xtreeNode{makeTree(d - 1), makeTree(d - 1)} 352 } 353 354 // use about n KB of stack and call f 355 func useStackAndCall(n int, f func()) { 356 if n == 0 { 357 f() 358 return 359 } 360 var b [1024]byte // makes frame about 1KB 361 useStackAndCall(n-1+int(b[99]), f) 362 } 363 364 func useStack(n int) { 365 useStackAndCall(n, func() {}) 366 } 367 368 func growing(c chan int, done chan struct{}) { 369 for n := range c { 370 useStack(n) 371 done <- struct{}{} 372 } 373 done <- struct{}{} 374 } 375 376 func TestStackCache(t *testing.T) { 377 // Allocate a bunch of goroutines and grow their stacks. 378 // Repeat a few times to test the stack cache. 379 const ( 380 R = 4 381 G = 200 382 S = 5 383 ) 384 for i := 0; i < R; i++ { 385 var reqchans [G]chan int 386 done := make(chan struct{}) 387 for j := 0; j < G; j++ { 388 reqchans[j] = make(chan int) 389 go growing(reqchans[j], done) 390 } 391 for s := 0; s < S; s++ { 392 for j := 0; j < G; j++ { 393 reqchans[j] <- 1 << uint(s) 394 } 395 for j := 0; j < G; j++ { 396 <-done 397 } 398 } 399 for j := 0; j < G; j++ { 400 close(reqchans[j]) 401 } 402 for j := 0; j < G; j++ { 403 <-done 404 } 405 } 406 } 407 408 func TestStackOutput(t *testing.T) { 409 b := make([]byte, 1024) 410 stk := string(b[:Stack(b, false)]) 411 if !strings.HasPrefix(stk, "goroutine ") { 412 t.Errorf("Stack (len %d):\n%s", len(stk), stk) 413 t.Errorf("Stack output should begin with \"goroutine \"") 414 } 415 } 416 417 func TestStackAllOutput(t *testing.T) { 418 b := make([]byte, 1024) 419 stk := string(b[:Stack(b, true)]) 420 if !strings.HasPrefix(stk, "goroutine ") { 421 t.Errorf("Stack (len %d):\n%s", len(stk), stk) 422 t.Errorf("Stack output should begin with \"goroutine \"") 423 } 424 } 425 426 func TestStackPanic(t *testing.T) { 427 // Test that stack copying copies panics correctly. This is difficult 428 // to test because it is very unlikely that the stack will be copied 429 // in the middle of gopanic. But it can happen. 430 // To make this test effective, edit panic.go:gopanic and uncomment 431 // the GC() call just before freedefer(d). 432 defer func() { 433 if x := recover(); x == nil { 434 t.Errorf("recover failed") 435 } 436 }() 437 useStack(32) 438 panic("test panic") 439 } 440 441 func BenchmarkStackCopy(b *testing.B) { 442 c := make(chan bool) 443 for i := 0; i < b.N; i++ { 444 go func() { 445 count(1000000) 446 c <- true 447 }() 448 <-c 449 } 450 } 451 452 func count(n int) int { 453 if n == 0 { 454 return 0 455 } 456 return 1 + count(n-1) 457 } 458 459 func BenchmarkStackCopyNoCache(b *testing.B) { 460 c := make(chan bool) 461 for i := 0; i < b.N; i++ { 462 go func() { 463 count1(1000000) 464 c <- true 465 }() 466 <-c 467 } 468 } 469 470 func count1(n int) int { 471 if n == 0 { 472 return 0 473 } 474 return 1 + count2(n-1) 475 } 476 477 func count2(n int) int { 478 if n == 0 { 479 return 0 480 } 481 return 1 + count3(n-1) 482 } 483 484 func count3(n int) int { 485 if n == 0 { 486 return 0 487 } 488 return 1 + count4(n-1) 489 } 490 491 func count4(n int) int { 492 if n == 0 { 493 return 0 494 } 495 return 1 + count5(n-1) 496 } 497 498 func count5(n int) int { 499 if n == 0 { 500 return 0 501 } 502 return 1 + count6(n-1) 503 } 504 505 func count6(n int) int { 506 if n == 0 { 507 return 0 508 } 509 return 1 + count7(n-1) 510 } 511 512 func count7(n int) int { 513 if n == 0 { 514 return 0 515 } 516 return 1 + count8(n-1) 517 } 518 519 func count8(n int) int { 520 if n == 0 { 521 return 0 522 } 523 return 1 + count9(n-1) 524 } 525 526 func count9(n int) int { 527 if n == 0 { 528 return 0 529 } 530 return 1 + count10(n-1) 531 } 532 533 func count10(n int) int { 534 if n == 0 { 535 return 0 536 } 537 return 1 + count11(n-1) 538 } 539 540 func count11(n int) int { 541 if n == 0 { 542 return 0 543 } 544 return 1 + count12(n-1) 545 } 546 547 func count12(n int) int { 548 if n == 0 { 549 return 0 550 } 551 return 1 + count13(n-1) 552 } 553 554 func count13(n int) int { 555 if n == 0 { 556 return 0 557 } 558 return 1 + count14(n-1) 559 } 560 561 func count14(n int) int { 562 if n == 0 { 563 return 0 564 } 565 return 1 + count15(n-1) 566 } 567 568 func count15(n int) int { 569 if n == 0 { 570 return 0 571 } 572 return 1 + count16(n-1) 573 } 574 575 func count16(n int) int { 576 if n == 0 { 577 return 0 578 } 579 return 1 + count17(n-1) 580 } 581 582 func count17(n int) int { 583 if n == 0 { 584 return 0 585 } 586 return 1 + count18(n-1) 587 } 588 589 func count18(n int) int { 590 if n == 0 { 591 return 0 592 } 593 return 1 + count19(n-1) 594 } 595 596 func count19(n int) int { 597 if n == 0 { 598 return 0 599 } 600 return 1 + count20(n-1) 601 } 602 603 func count20(n int) int { 604 if n == 0 { 605 return 0 606 } 607 return 1 + count21(n-1) 608 } 609 610 func count21(n int) int { 611 if n == 0 { 612 return 0 613 } 614 return 1 + count22(n-1) 615 } 616 617 func count22(n int) int { 618 if n == 0 { 619 return 0 620 } 621 return 1 + count23(n-1) 622 } 623 624 func count23(n int) int { 625 if n == 0 { 626 return 0 627 } 628 return 1 + count1(n-1) 629 }