]> Cypherpunks.ru repositories - gostls13.git/blob - src/runtime/debugcall.go
runtime: always lock OS thread in debugcall
[gostls13.git] / src / runtime / debugcall.go
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)
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 lockedExt uint32
106         callerpc := getcallerpc()
107         gp := getg()
108
109         // Lock ourselves to the OS thread.
110         //
111         // Debuggers rely on us running on the same thread until we get to
112         // dispatch the function they asked as to.
113         //
114         // We're going to transfer this to the new G we just created.
115         lockOSThread()
116
117         // Create a new goroutine to execute the call on. Run this on
118         // the system stack to avoid growing our stack.
119         systemstack(func() {
120                 // TODO(mknyszek): It would be nice to wrap these arguments in an allocated
121                 // closure and start the goroutine with that closure, but the compiler disallows
122                 // implicit closure allocation in the runtime.
123                 fn := debugCallWrap1
124                 newg := newproc1(*(**funcval)(unsafe.Pointer(&fn)), gp, callerpc)
125                 args := &debugCallWrapArgs{
126                         dispatch: dispatch,
127                         callingG: gp,
128                 }
129                 newg.param = unsafe.Pointer(args)
130
131                 // Transfer locked-ness to the new goroutine.
132                 // Save lock state to restore later.
133                 mp := gp.m
134                 if mp != gp.lockedm.ptr() {
135                         throw("inconsistent lockedm")
136                 }
137                 // Save the external lock count and clear it so
138                 // that it can't be unlocked from the debug call.
139                 // Note: we already locked internally to the thread,
140                 // so if we were locked before we're still locked now.
141                 lockedExt = mp.lockedExt
142                 mp.lockedExt = 0
143
144                 mp.lockedg.set(newg)
145                 newg.lockedm.set(mp)
146                 gp.lockedm = 0
147
148                 // Mark the calling goroutine as being at an async
149                 // safe-point, since it has a few conservative frames
150                 // at the bottom of the stack. This also prevents
151                 // stack shrinks.
152                 gp.asyncSafePoint = true
153
154                 // Stash newg away so we can execute it below (mcall's
155                 // closure can't capture anything).
156                 gp.schedlink.set(newg)
157         })
158
159         // Switch to the new goroutine.
160         mcall(func(gp *g) {
161                 // Get newg.
162                 newg := gp.schedlink.ptr()
163                 gp.schedlink = 0
164
165                 // Park the calling goroutine.
166                 if traceEnabled() {
167                         traceGoPark(traceBlockDebugCall, 1)
168                 }
169                 casGToWaiting(gp, _Grunning, waitReasonDebugCall)
170                 dropg()
171
172                 // Directly execute the new goroutine. The debug
173                 // protocol will continue on the new goroutine, so
174                 // it's important we not just let the scheduler do
175                 // this or it may resume a different goroutine.
176                 execute(newg, true)
177         })
178
179         // We'll resume here when the call returns.
180
181         // Restore locked state.
182         mp := gp.m
183         mp.lockedExt = lockedExt
184         mp.lockedg.set(gp)
185         gp.lockedm.set(mp)
186
187         // Undo the lockOSThread we did earlier.
188         unlockOSThread()
189
190         gp.asyncSafePoint = false
191 }
192
193 type debugCallWrapArgs struct {
194         dispatch uintptr
195         callingG *g
196 }
197
198 // debugCallWrap1 is the continuation of debugCallWrap on the callee
199 // goroutine.
200 func debugCallWrap1() {
201         gp := getg()
202         args := (*debugCallWrapArgs)(gp.param)
203         dispatch, callingG := args.dispatch, args.callingG
204         gp.param = nil
205
206         // Dispatch call and trap panics.
207         debugCallWrap2(dispatch)
208
209         // Resume the caller goroutine.
210         getg().schedlink.set(callingG)
211         mcall(func(gp *g) {
212                 callingG := gp.schedlink.ptr()
213                 gp.schedlink = 0
214
215                 // Unlock this goroutine from the M if necessary. The
216                 // calling G will relock.
217                 if gp.lockedm != 0 {
218                         gp.lockedm = 0
219                         gp.m.lockedg = 0
220                 }
221
222                 // Switch back to the calling goroutine. At some point
223                 // the scheduler will schedule us again and we'll
224                 // finish exiting.
225                 if traceEnabled() {
226                         traceGoSched()
227                 }
228                 casgstatus(gp, _Grunning, _Grunnable)
229                 dropg()
230                 lock(&sched.lock)
231                 globrunqput(gp)
232                 unlock(&sched.lock)
233
234                 if traceEnabled() {
235                         traceGoUnpark(callingG, 0)
236                 }
237                 casgstatus(callingG, _Gwaiting, _Grunnable)
238                 execute(callingG, true)
239         })
240 }
241
242 func debugCallWrap2(dispatch uintptr) {
243         // Call the dispatch function and trap panics.
244         var dispatchF func()
245         dispatchFV := funcval{dispatch}
246         *(*unsafe.Pointer)(unsafe.Pointer(&dispatchF)) = noescape(unsafe.Pointer(&dispatchFV))
247
248         var ok bool
249         defer func() {
250                 if !ok {
251                         err := recover()
252                         debugCallPanicked(err)
253                 }
254         }()
255         dispatchF()
256         ok = true
257 }