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