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