github.com/mdempsky/go@v0.0.0-20151201204031-5dd372bd1e70/src/runtime/mstkbar.go (about) 1 // Copyright 2015 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 // Garbage collector: stack barriers 6 // 7 // Stack barriers enable the garbage collector to determine how much 8 // of a gorountine stack has changed between when a stack is scanned 9 // during the concurrent scan phase and when it is re-scanned during 10 // the stop-the-world mark termination phase. Mark termination only 11 // needs to re-scan the changed part, so for deep stacks this can 12 // significantly reduce GC pause time compared to the alternative of 13 // re-scanning whole stacks. The deeper the stacks, the more stack 14 // barriers help. 15 // 16 // When stacks are scanned during the concurrent scan phase, the stack 17 // scan installs stack barriers by selecting stack frames and 18 // overwriting the saved return PCs (or link registers) of these 19 // frames with the PC of a "stack barrier trampoline". Later, when a 20 // selected frame returns, it "returns" to this trampoline instead of 21 // returning to its actual caller. The trampoline records that the 22 // stack has unwound past this frame and jumps to the original return 23 // PC recorded when the stack barrier was installed. Mark termination 24 // re-scans only as far as the first frame that hasn't hit a stack 25 // barrier and then removes and un-hit stack barriers. 26 // 27 // This scheme is very lightweight. No special code is required in the 28 // mutator to record stack unwinding and the trampoline is only a few 29 // assembly instructions. 30 // 31 // Book-keeping 32 // ------------ 33 // 34 // The primary cost of stack barriers is book-keeping: the runtime has 35 // to record the locations of all stack barriers and the original 36 // return PCs in order to return to the correct caller when a stack 37 // barrier is hit and so it can remove un-hit stack barriers. In order 38 // to minimize this cost, the Go runtime places stack barriers in 39 // exponentially-spaced frames, starting 1K past the current frame. 40 // The book-keeping structure hence grows logarithmically with the 41 // size of the stack and mark termination re-scans at most twice as 42 // much stack as necessary. 43 // 44 // The runtime reserves space for this book-keeping structure at the 45 // top of the stack allocation itself (just above the outermost 46 // frame). This is necessary because the regular memory allocator can 47 // itself grow the stack, and hence can't be used when allocating 48 // stack-related structures. 49 // 50 // For debugging, the runtime also supports installing stack barriers 51 // at every frame. However, this requires significantly more 52 // book-keeping space. 53 // 54 // Correctness 55 // ----------- 56 // 57 // The runtime and the compiler cooperate to ensure that all objects 58 // reachable from the stack as of mark termination are marked. 59 // Anything unchanged since the concurrent scan phase will be marked 60 // because it is marked by the concurrent scan. After the concurrent 61 // scan, there are three possible classes of stack modifications that 62 // must be tracked: 63 // 64 // 1) Mutator writes below the lowest un-hit stack barrier. This 65 // includes all writes performed by an executing function to its own 66 // stack frame. This part of the stack will be re-scanned by mark 67 // termination, which will mark any objects made reachable from 68 // modifications to this part of the stack. 69 // 70 // 2) Mutator writes above the lowest un-hit stack barrier. It's 71 // possible for a mutator to modify the stack above the lowest un-hit 72 // stack barrier if a higher frame has passed down a pointer to a 73 // stack variable in its frame. This is called an "up-pointer". The 74 // compiler ensures that writes through up-pointers have an 75 // accompanying write barrier (it simply doesn't distinguish between 76 // writes through up-pointers and writes through heap pointers). This 77 // write barrier marks any object made reachable from modifications to 78 // this part of the stack. 79 // 80 // 3) Runtime writes to the stack. Various runtime operations such as 81 // sends to unbuffered channels can write to arbitrary parts of the 82 // stack, including above the lowest un-hit stack barrier. We solve 83 // this in two ways. In many cases, the runtime can perform an 84 // explicit write barrier operation like in case 2. However, in the 85 // case of bulk memory move (typedmemmove), the runtime doesn't 86 // necessary have ready access to a pointer bitmap for the memory 87 // being copied, so it simply unwinds any stack barriers below the 88 // destination. 89 // 90 // Gotchas 91 // ------- 92 // 93 // Anything that inspects or manipulates the stack potentially needs 94 // to understand stack barriers. The most obvious case is that 95 // gentraceback needs to use the original return PC when it encounters 96 // the stack barrier trampoline. Anything that unwinds the stack such 97 // as panic/recover must unwind stack barriers in tandem with 98 // unwinding the stack. 99 // 100 // Stack barriers require that any goroutine whose stack has been 101 // scanned must execute write barriers. Go solves this by simply 102 // enabling write barriers globally during the concurrent scan phase. 103 // However, traditionally, write barriers are not enabled during this 104 // phase. 105 106 package runtime 107 108 import ( 109 "runtime/internal/atomic" 110 "runtime/internal/sys" 111 "unsafe" 112 ) 113 114 const debugStackBarrier = false 115 116 // firstStackBarrierOffset is the approximate byte offset at 117 // which to place the first stack barrier from the current SP. 118 // This is a lower bound on how much stack will have to be 119 // re-scanned during mark termination. Subsequent barriers are 120 // placed at firstStackBarrierOffset * 2^n offsets. 121 // 122 // For debugging, this can be set to 0, which will install a 123 // stack barrier at every frame. If you do this, you may also 124 // have to raise _StackMin, since the stack barrier 125 // bookkeeping will use a large amount of each stack. 126 var firstStackBarrierOffset = 1024 127 128 // gcMaxStackBarriers returns the maximum number of stack barriers 129 // that can be installed in a stack of stackSize bytes. 130 func gcMaxStackBarriers(stackSize int) (n int) { 131 if firstStackBarrierOffset == 0 { 132 // Special debugging case for inserting stack barriers 133 // at every frame. Steal half of the stack for the 134 // []stkbar. Technically, if the stack were to consist 135 // solely of return PCs we would need two thirds of 136 // the stack, but stealing that much breaks things and 137 // this doesn't happen in practice. 138 return stackSize / 2 / int(unsafe.Sizeof(stkbar{})) 139 } 140 141 offset := firstStackBarrierOffset 142 for offset < stackSize { 143 n++ 144 offset *= 2 145 } 146 return n + 1 147 } 148 149 // gcInstallStackBarrier installs a stack barrier over the return PC of frame. 150 //go:nowritebarrier 151 func gcInstallStackBarrier(gp *g, frame *stkframe) bool { 152 if frame.lr == 0 { 153 if debugStackBarrier { 154 print("not installing stack barrier with no LR, goid=", gp.goid, "\n") 155 } 156 return false 157 } 158 159 if frame.fn.entry == cgocallback_gofuncPC { 160 // cgocallback_gofunc doesn't return to its LR; 161 // instead, its return path puts LR in g.sched.pc and 162 // switches back to the system stack on which 163 // cgocallback_gofunc was originally called. We can't 164 // have a stack barrier in g.sched.pc, so don't 165 // install one in this frame. 166 if debugStackBarrier { 167 print("not installing stack barrier over LR of cgocallback_gofunc, goid=", gp.goid, "\n") 168 } 169 return false 170 } 171 172 // Save the return PC and overwrite it with stackBarrier. 173 var lrUintptr uintptr 174 if usesLR { 175 lrUintptr = frame.sp 176 } else { 177 lrUintptr = frame.fp - sys.RegSize 178 } 179 lrPtr := (*sys.Uintreg)(unsafe.Pointer(lrUintptr)) 180 if debugStackBarrier { 181 print("install stack barrier at ", hex(lrUintptr), " over ", hex(*lrPtr), ", goid=", gp.goid, "\n") 182 if uintptr(*lrPtr) != frame.lr { 183 print("frame.lr=", hex(frame.lr)) 184 throw("frame.lr differs from stack LR") 185 } 186 } 187 188 gp.stkbar = gp.stkbar[:len(gp.stkbar)+1] 189 stkbar := &gp.stkbar[len(gp.stkbar)-1] 190 stkbar.savedLRPtr = lrUintptr 191 stkbar.savedLRVal = uintptr(*lrPtr) 192 *lrPtr = sys.Uintreg(stackBarrierPC) 193 return true 194 } 195 196 // gcRemoveStackBarriers removes all stack barriers installed in gp's stack. 197 //go:nowritebarrier 198 func gcRemoveStackBarriers(gp *g) { 199 if debugStackBarrier && gp.stkbarPos != 0 { 200 print("hit ", gp.stkbarPos, " stack barriers, goid=", gp.goid, "\n") 201 } 202 203 gcLockStackBarriers(gp) 204 205 // Remove stack barriers that we didn't hit. 206 for _, stkbar := range gp.stkbar[gp.stkbarPos:] { 207 gcRemoveStackBarrier(gp, stkbar) 208 } 209 210 // Clear recorded stack barriers so copystack doesn't try to 211 // adjust them. 212 gp.stkbarPos = 0 213 gp.stkbar = gp.stkbar[:0] 214 215 gcUnlockStackBarriers(gp) 216 } 217 218 // gcRemoveStackBarrier removes a single stack barrier. It is the 219 // inverse operation of gcInstallStackBarrier. 220 // 221 // This is nosplit to ensure gp's stack does not move. 222 // 223 //go:nowritebarrier 224 //go:nosplit 225 func gcRemoveStackBarrier(gp *g, stkbar stkbar) { 226 if debugStackBarrier { 227 print("remove stack barrier at ", hex(stkbar.savedLRPtr), " with ", hex(stkbar.savedLRVal), ", goid=", gp.goid, "\n") 228 } 229 lrPtr := (*sys.Uintreg)(unsafe.Pointer(stkbar.savedLRPtr)) 230 if val := *lrPtr; val != sys.Uintreg(stackBarrierPC) { 231 printlock() 232 print("at *", hex(stkbar.savedLRPtr), " expected stack barrier PC ", hex(stackBarrierPC), ", found ", hex(val), ", goid=", gp.goid, "\n") 233 print("gp.stkbar=") 234 gcPrintStkbars(gp, -1) 235 print(", gp.stack=[", hex(gp.stack.lo), ",", hex(gp.stack.hi), ")\n") 236 throw("stack barrier lost") 237 } 238 *lrPtr = sys.Uintreg(stkbar.savedLRVal) 239 } 240 241 // gcPrintStkbars prints the stack barriers of gp for debugging. It 242 // places a "@@@" marker at gp.stkbarPos. If marker >= 0, it will also 243 // place a "==>" marker before the marker'th entry. 244 func gcPrintStkbars(gp *g, marker int) { 245 print("[") 246 for i, s := range gp.stkbar { 247 if i > 0 { 248 print(" ") 249 } 250 if i == int(gp.stkbarPos) { 251 print("@@@ ") 252 } 253 if i == marker { 254 print("==> ") 255 } 256 print("*", hex(s.savedLRPtr), "=", hex(s.savedLRVal)) 257 } 258 if int(gp.stkbarPos) == len(gp.stkbar) { 259 print(" @@@") 260 } 261 if marker == len(gp.stkbar) { 262 print(" ==>") 263 } 264 print("]") 265 } 266 267 // gcUnwindBarriers marks all stack barriers up the frame containing 268 // sp as hit and removes them. This is used during stack unwinding for 269 // panic/recover and by heapBitsBulkBarrier to force stack re-scanning 270 // when its destination is on the stack. 271 // 272 // This is nosplit to ensure gp's stack does not move. 273 // 274 //go:nosplit 275 func gcUnwindBarriers(gp *g, sp uintptr) { 276 gcLockStackBarriers(gp) 277 // On LR machines, if there is a stack barrier on the return 278 // from the frame containing sp, this will mark it as hit even 279 // though it isn't, but it's okay to be conservative. 280 before := gp.stkbarPos 281 for int(gp.stkbarPos) < len(gp.stkbar) && gp.stkbar[gp.stkbarPos].savedLRPtr < sp { 282 gcRemoveStackBarrier(gp, gp.stkbar[gp.stkbarPos]) 283 gp.stkbarPos++ 284 } 285 gcUnlockStackBarriers(gp) 286 if debugStackBarrier && gp.stkbarPos != before { 287 print("skip barriers below ", hex(sp), " in goid=", gp.goid, ": ") 288 // We skipped barriers between the "==>" marker 289 // (before) and the "@@@" marker (gp.stkbarPos). 290 gcPrintStkbars(gp, int(before)) 291 print("\n") 292 } 293 } 294 295 // nextBarrierPC returns the original return PC of the next stack barrier. 296 // Used by getcallerpc, so it must be nosplit. 297 //go:nosplit 298 func nextBarrierPC() uintptr { 299 gp := getg() 300 return gp.stkbar[gp.stkbarPos].savedLRVal 301 } 302 303 // setNextBarrierPC sets the return PC of the next stack barrier. 304 // Used by setcallerpc, so it must be nosplit. 305 //go:nosplit 306 func setNextBarrierPC(pc uintptr) { 307 gp := getg() 308 gp.stkbar[gp.stkbarPos].savedLRVal = pc 309 } 310 311 // gcLockStackBarriers synchronizes with tracebacks of gp's stack 312 // during sigprof for installation or removal of stack barriers. It 313 // blocks until any current sigprof is done tracebacking gp's stack 314 // and then disallows profiling tracebacks of gp's stack. 315 // 316 // This is necessary because a sigprof during barrier installation or 317 // removal could observe inconsistencies between the stkbar array and 318 // the stack itself and crash. 319 // 320 //go:nosplit 321 func gcLockStackBarriers(gp *g) { 322 acquirem() 323 for !atomic.Cas(&gp.stackLock, 0, 1) { 324 osyield() 325 } 326 } 327 328 //go:nosplit 329 func gcTryLockStackBarriers(gp *g) bool { 330 mp := acquirem() 331 result := atomic.Cas(&gp.stackLock, 0, 1) 332 if !result { 333 releasem(mp) 334 } 335 return result 336 } 337 338 func gcUnlockStackBarriers(gp *g) { 339 atomic.Store(&gp.stackLock, 0) 340 releasem(getg().m) 341 }