return true
}
+func TestGCTestMoveStackRepeatedly(t *testing.T) {
+ // Move the stack repeatedly to make sure we're not doubling
+ // it each time.
+ for i := 0; i < 100; i++ {
+ runtime.GCTestMoveStackOnNextCall()
+ moveStack1(false)
+ }
+}
+
+//go:noinline
+func moveStack1(x bool) {
+ // Make sure this function doesn't get auto-nosplit.
+ if x {
+ println("x")
+ }
+}
+
func TestGCTestIsReachable(t *testing.T) {
var all, half []unsafe.Pointer
var want uint64
// there's a preemption between this call and the next.
func gcTestMoveStackOnNextCall() {
gp := getg()
- gp.stackguard0 = getcallersp()
+ gp.stackguard0 = stackForceMove
}
// gcTestIsReachable performs a GC and returns a bit set where bit i
// Stored into g->stackguard0 to cause split stack check failure.
// Must be greater than any real sp.
stackFork = uintptrMask & -1234
+
+ // Force a stack movement. Used for debugging.
+ // 0xfffffeed in hex.
+ stackForceMove = uintptrMask & -275
)
// Global pool of spans that have free stacks.
// recheck the bounds on return.)
if f := findfunc(gp.sched.pc); f.valid() {
max := uintptr(funcMaxSPDelta(f))
- for newsize-oldsize < max+_StackGuard {
+ for newsize-gp.sched.sp < max+_StackGuard {
newsize *= 2
}
}
+ if gp.stackguard0 == stackForceMove {
+ // Forced stack movement used for debugging.
+ // Don't double the stack (or we may quickly run out
+ // if this is done repeatedly).
+ newsize = oldsize
+ }
+
if newsize > maxstacksize || newsize > maxstackceiling {
if maxstacksize < maxstackceiling {
print("runtime: goroutine stack exceeds ", maxstacksize, "-byte limit\n")