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