github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/runtime/debugcall.go (about) 1 // Copyright 2018 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 //go:build amd64 || arm64 6 7 package runtime 8 9 import ( 10 "internal/abi" 11 "unsafe" 12 ) 13 14 const ( 15 debugCallSystemStack = "executing on Go runtime stack" 16 debugCallUnknownFunc = "call from unknown function" 17 debugCallRuntime = "call from within the Go runtime" 18 debugCallUnsafePoint = "call not at safe point" 19 ) 20 21 func debugCallV2() 22 func debugCallPanicked(val any) 23 24 // debugCallCheck checks whether it is safe to inject a debugger 25 // function call with return PC pc. If not, it returns a string 26 // explaining why. 27 // 28 //go:nosplit 29 func debugCallCheck(pc uintptr) string { 30 // No user calls from the system stack. 31 if getg() != getg().m.curg { 32 return debugCallSystemStack 33 } 34 if sp := getcallersp(); !(getg().stack.lo < sp && sp <= getg().stack.hi) { 35 // Fast syscalls (nanotime) and racecall switch to the 36 // g0 stack without switching g. We can't safely make 37 // a call in this state. (We can't even safely 38 // systemstack.) 39 return debugCallSystemStack 40 } 41 42 // Switch to the system stack to avoid overflowing the user 43 // stack. 44 var ret string 45 systemstack(func() { 46 f := findfunc(pc) 47 if !f.valid() { 48 ret = debugCallUnknownFunc 49 return 50 } 51 52 name := funcname(f) 53 54 switch name { 55 case "debugCall32", 56 "debugCall64", 57 "debugCall128", 58 "debugCall256", 59 "debugCall512", 60 "debugCall1024", 61 "debugCall2048", 62 "debugCall4096", 63 "debugCall8192", 64 "debugCall16384", 65 "debugCall32768", 66 "debugCall65536": 67 // These functions are allowed so that the debugger can initiate multiple function calls. 68 // See: https://golang.org/cl/161137/ 69 return 70 } 71 72 // Disallow calls from the runtime. We could 73 // potentially make this condition tighter (e.g., not 74 // when locks are held), but there are enough tightly 75 // coded sequences (e.g., defer handling) that it's 76 // better to play it safe. 77 if pfx := "runtime."; len(name) > len(pfx) && name[:len(pfx)] == pfx { 78 ret = debugCallRuntime 79 return 80 } 81 82 // Check that this isn't an unsafe-point. 83 if pc != f.entry() { 84 pc-- 85 } 86 up := pcdatavalue(f, abi.PCDATA_UnsafePoint, pc, nil) 87 if up != abi.UnsafePointSafe { 88 // Not at a safe point. 89 ret = debugCallUnsafePoint 90 } 91 }) 92 return ret 93 } 94 95 // debugCallWrap starts a new goroutine to run a debug call and blocks 96 // the calling goroutine. On the goroutine, it prepares to recover 97 // panics from the debug call, and then calls the call dispatching 98 // function at PC dispatch. 99 // 100 // This must be deeply nosplit because there are untyped values on the 101 // stack from debugCallV2. 102 // 103 //go:nosplit 104 func debugCallWrap(dispatch uintptr) { 105 var lockedm bool 106 var lockedExt uint32 107 callerpc := getcallerpc() 108 gp := getg() 109 110 // Create a new goroutine to execute the call on. Run this on 111 // the system stack to avoid growing our stack. 112 systemstack(func() { 113 // TODO(mknyszek): It would be nice to wrap these arguments in an allocated 114 // closure and start the goroutine with that closure, but the compiler disallows 115 // implicit closure allocation in the runtime. 116 fn := debugCallWrap1 117 newg := newproc1(*(**funcval)(unsafe.Pointer(&fn)), gp, callerpc) 118 args := &debugCallWrapArgs{ 119 dispatch: dispatch, 120 callingG: gp, 121 } 122 newg.param = unsafe.Pointer(args) 123 124 // If the current G is locked, then transfer that 125 // locked-ness to the new goroutine. 126 if gp.lockedm != 0 { 127 // Save lock state to restore later. 128 mp := gp.m 129 if mp != gp.lockedm.ptr() { 130 throw("inconsistent lockedm") 131 } 132 133 lockedm = true 134 lockedExt = mp.lockedExt 135 136 // Transfer external lock count to internal so 137 // it can't be unlocked from the debug call. 138 mp.lockedInt++ 139 mp.lockedExt = 0 140 141 mp.lockedg.set(newg) 142 newg.lockedm.set(mp) 143 gp.lockedm = 0 144 } 145 146 // Mark the calling goroutine as being at an async 147 // safe-point, since it has a few conservative frames 148 // at the bottom of the stack. This also prevents 149 // stack shrinks. 150 gp.asyncSafePoint = true 151 152 // Stash newg away so we can execute it below (mcall's 153 // closure can't capture anything). 154 gp.schedlink.set(newg) 155 }) 156 157 // Switch to the new goroutine. 158 mcall(func(gp *g) { 159 // Get newg. 160 newg := gp.schedlink.ptr() 161 gp.schedlink = 0 162 163 // Park the calling goroutine. 164 if traceEnabled() { 165 traceGoPark(traceEvGoBlock, 1) 166 } 167 casGToWaiting(gp, _Grunning, waitReasonDebugCall) 168 dropg() 169 170 // Directly execute the new goroutine. The debug 171 // protocol will continue on the new goroutine, so 172 // it's important we not just let the scheduler do 173 // this or it may resume a different goroutine. 174 execute(newg, true) 175 }) 176 177 // We'll resume here when the call returns. 178 179 // Restore locked state. 180 if lockedm { 181 mp := gp.m 182 mp.lockedExt = lockedExt 183 mp.lockedInt-- 184 mp.lockedg.set(gp) 185 gp.lockedm.set(mp) 186 } 187 188 gp.asyncSafePoint = false 189 } 190 191 type debugCallWrapArgs struct { 192 dispatch uintptr 193 callingG *g 194 } 195 196 // debugCallWrap1 is the continuation of debugCallWrap on the callee 197 // goroutine. 198 func debugCallWrap1() { 199 gp := getg() 200 args := (*debugCallWrapArgs)(gp.param) 201 dispatch, callingG := args.dispatch, args.callingG 202 gp.param = nil 203 204 // Dispatch call and trap panics. 205 debugCallWrap2(dispatch) 206 207 // Resume the caller goroutine. 208 getg().schedlink.set(callingG) 209 mcall(func(gp *g) { 210 callingG := gp.schedlink.ptr() 211 gp.schedlink = 0 212 213 // Unlock this goroutine from the M if necessary. The 214 // calling G will relock. 215 if gp.lockedm != 0 { 216 gp.lockedm = 0 217 gp.m.lockedg = 0 218 } 219 220 // Switch back to the calling goroutine. At some point 221 // the scheduler will schedule us again and we'll 222 // finish exiting. 223 if traceEnabled() { 224 traceGoSched() 225 } 226 casgstatus(gp, _Grunning, _Grunnable) 227 dropg() 228 lock(&sched.lock) 229 globrunqput(gp) 230 unlock(&sched.lock) 231 232 if traceEnabled() { 233 traceGoUnpark(callingG, 0) 234 } 235 casgstatus(callingG, _Gwaiting, _Grunnable) 236 execute(callingG, true) 237 }) 238 } 239 240 func debugCallWrap2(dispatch uintptr) { 241 // Call the dispatch function and trap panics. 242 var dispatchF func() 243 dispatchFV := funcval{dispatch} 244 *(*unsafe.Pointer)(unsafe.Pointer(&dispatchF)) = noescape(unsafe.Pointer(&dispatchFV)) 245 246 var ok bool 247 defer func() { 248 if !ok { 249 err := recover() 250 debugCallPanicked(err) 251 } 252 }() 253 dispatchF() 254 ok = true 255 }