]> Cypherpunks.ru repositories - gostls13.git/blob - src/runtime/debugcall.go
all: go fmt std cmd (but revert vendor)
[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 debugCallV1()
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 debugCallV1.
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                 var args struct {
112                         dispatch uintptr
113                         callingG *g
114                 }
115                 args.dispatch = dispatch
116                 args.callingG = gp
117                 fn := debugCallWrap1
118                 newg := newproc1(*(**funcval)(unsafe.Pointer(&fn)), unsafe.Pointer(&args), int32(unsafe.Sizeof(args)), gp, callerpc)
119
120                 // If the current G is locked, then transfer that
121                 // locked-ness to the new goroutine.
122                 if gp.lockedm != 0 {
123                         // Save lock state to restore later.
124                         mp := gp.m
125                         if mp != gp.lockedm.ptr() {
126                                 throw("inconsistent lockedm")
127                         }
128
129                         lockedm = true
130                         lockedExt = mp.lockedExt
131
132                         // Transfer external lock count to internal so
133                         // it can't be unlocked from the debug call.
134                         mp.lockedInt++
135                         mp.lockedExt = 0
136
137                         mp.lockedg.set(newg)
138                         newg.lockedm.set(mp)
139                         gp.lockedm = 0
140                 }
141
142                 // Mark the calling goroutine as being at an async
143                 // safe-point, since it has a few conservative frames
144                 // at the bottom of the stack. This also prevents
145                 // stack shrinks.
146                 gp.asyncSafePoint = true
147
148                 // Stash newg away so we can execute it below (mcall's
149                 // closure can't capture anything).
150                 gp.schedlink.set(newg)
151         })
152
153         // Switch to the new goroutine.
154         mcall(func(gp *g) {
155                 // Get newg.
156                 newg := gp.schedlink.ptr()
157                 gp.schedlink = 0
158
159                 // Park the calling goroutine.
160                 gp.waitreason = waitReasonDebugCall
161                 if trace.enabled {
162                         traceGoPark(traceEvGoBlock, 1)
163                 }
164                 casgstatus(gp, _Grunning, _Gwaiting)
165                 dropg()
166
167                 // Directly execute the new goroutine. The debug
168                 // protocol will continue on the new goroutine, so
169                 // it's important we not just let the scheduler do
170                 // this or it may resume a different goroutine.
171                 execute(newg, true)
172         })
173
174         // We'll resume here when the call returns.
175
176         // Restore locked state.
177         if lockedm {
178                 mp := gp.m
179                 mp.lockedExt = lockedExt
180                 mp.lockedInt--
181                 mp.lockedg.set(gp)
182                 gp.lockedm.set(mp)
183         }
184
185         gp.asyncSafePoint = false
186 }
187
188 // debugCallWrap1 is the continuation of debugCallWrap on the callee
189 // goroutine.
190 func debugCallWrap1(dispatch uintptr, callingG *g) {
191         // Dispatch call and trap panics.
192         debugCallWrap2(dispatch)
193
194         // Resume the caller goroutine.
195         getg().schedlink.set(callingG)
196         mcall(func(gp *g) {
197                 callingG := gp.schedlink.ptr()
198                 gp.schedlink = 0
199
200                 // Unlock this goroutine from the M if necessary. The
201                 // calling G will relock.
202                 if gp.lockedm != 0 {
203                         gp.lockedm = 0
204                         gp.m.lockedg = 0
205                 }
206
207                 // Switch back to the calling goroutine. At some point
208                 // the scheduler will schedule us again and we'll
209                 // finish exiting.
210                 if trace.enabled {
211                         traceGoSched()
212                 }
213                 casgstatus(gp, _Grunning, _Grunnable)
214                 dropg()
215                 lock(&sched.lock)
216                 globrunqput(gp)
217                 unlock(&sched.lock)
218
219                 if trace.enabled {
220                         traceGoUnpark(callingG, 0)
221                 }
222                 casgstatus(callingG, _Gwaiting, _Grunnable)
223                 execute(callingG, true)
224         })
225 }
226
227 func debugCallWrap2(dispatch uintptr) {
228         // Call the dispatch function and trap panics.
229         var dispatchF func()
230         dispatchFV := funcval{dispatch}
231         *(*unsafe.Pointer)(unsafe.Pointer(&dispatchF)) = noescape(unsafe.Pointer(&dispatchFV))
232
233         var ok bool
234         defer func() {
235                 if !ok {
236                         err := recover()
237                         debugCallPanicked(err)
238                 }
239         }()
240         dispatchF()
241         ok = true
242 }