Source file
src/runtime/export_debug_test.go
Documentation: runtime
1
2
3
4
5
6
7
8 package runtime
9
10 import (
11 "internal/abi"
12 "runtime/internal/sys"
13 "unsafe"
14 )
15
16
17
18
19
20
21
22
23
24
25
26 func InjectDebugCall(gp *g, fn interface{}, regArgs *abi.RegArgs, stackArgs interface{}, tkill func(tid int) error, returnOnUnsafePoint bool) (interface{}, error) {
27 if gp.lockedm == 0 {
28 return nil, plainError("goroutine not locked to thread")
29 }
30
31 tid := int(gp.lockedm.ptr().procid)
32 if tid == 0 {
33 return nil, plainError("missing tid")
34 }
35
36 f := efaceOf(&fn)
37 if f._type == nil || f._type.kind&kindMask != kindFunc {
38 return nil, plainError("fn must be a function")
39 }
40 fv := (*funcval)(f.data)
41
42 a := efaceOf(&stackArgs)
43 if a._type != nil && a._type.kind&kindMask != kindPtr {
44 return nil, plainError("args must be a pointer or nil")
45 }
46 argp := a.data
47 var argSize uintptr
48 if argp != nil {
49 argSize = (*ptrtype)(unsafe.Pointer(a._type)).elem.size
50 }
51
52 h := new(debugCallHandler)
53 h.gp = gp
54
55
56 h.mp = gp.lockedm.ptr()
57 h.fv, h.regArgs, h.argp, h.argSize = fv, regArgs, argp, argSize
58 h.handleF = h.handle
59
60 defer func() { testSigtrap = nil }()
61 for i := 0; ; i++ {
62 testSigtrap = h.inject
63 noteclear(&h.done)
64 h.err = ""
65
66 if err := tkill(tid); err != nil {
67 return nil, err
68 }
69
70 notetsleepg(&h.done, -1)
71 if h.err != "" {
72 switch h.err {
73 case "call not at safe point":
74 if returnOnUnsafePoint {
75
76 return nil, h.err
77 }
78 fallthrough
79 case "retry _Grunnable", "executing on Go runtime stack", "call from within the Go runtime":
80
81 if i < 100 {
82 usleep(100)
83 Gosched()
84 continue
85 }
86 }
87 return nil, h.err
88 }
89 return h.panic, nil
90 }
91 }
92
93 type debugCallHandler struct {
94 gp *g
95 mp *m
96 fv *funcval
97 regArgs *abi.RegArgs
98 argp unsafe.Pointer
99 argSize uintptr
100 panic interface{}
101
102 handleF func(info *siginfo, ctxt *sigctxt, gp2 *g) bool
103
104 err plainError
105 done note
106 savedRegs sigcontext
107 savedFP fpstate1
108 }
109
110 func (h *debugCallHandler) inject(info *siginfo, ctxt *sigctxt, gp2 *g) bool {
111 switch h.gp.atomicstatus {
112 case _Grunning:
113 if getg().m != h.mp {
114 println("trap on wrong M", getg().m, h.mp)
115 return false
116 }
117
118 rsp := ctxt.rsp() - sys.PtrSize
119 *(*uint64)(unsafe.Pointer(uintptr(rsp))) = ctxt.rip()
120 ctxt.set_rsp(rsp)
121
122 *(*uintptr)(unsafe.Pointer(uintptr(rsp - 16))) = h.argSize
123
124 h.savedRegs = *ctxt.regs()
125 h.savedFP = *h.savedRegs.fpstate
126 h.savedRegs.fpstate = nil
127
128 ctxt.set_rip(uint64(funcPC(debugCallV2)))
129
130 testSigtrap = h.handleF
131 case _Grunnable:
132
133
134 h.err = plainError("retry _Grunnable")
135 notewakeup(&h.done)
136 default:
137 h.err = plainError("goroutine in unexpected state at call inject")
138 notewakeup(&h.done)
139 }
140
141 return true
142 }
143
144 func (h *debugCallHandler) handle(info *siginfo, ctxt *sigctxt, gp2 *g) bool {
145
146 if getg().m != h.mp {
147 println("trap on wrong M", getg().m, h.mp)
148 return false
149 }
150 f := findfunc(uintptr(ctxt.rip()))
151 if !(hasPrefix(funcname(f), "runtime.debugCall") || hasPrefix(funcname(f), "debugCall")) {
152 println("trap in unknown function", funcname(f))
153 return false
154 }
155 if *(*byte)(unsafe.Pointer(uintptr(ctxt.rip() - 1))) != 0xcc {
156 println("trap at non-INT3 instruction pc =", hex(ctxt.rip()))
157 return false
158 }
159
160 switch status := ctxt.r12(); status {
161 case 0:
162
163 sp := ctxt.rsp()
164 memmove(unsafe.Pointer(uintptr(sp)), h.argp, h.argSize)
165 if h.regArgs != nil {
166 storeRegArgs(ctxt.regs(), h.regArgs)
167 }
168
169 sp -= sys.PtrSize
170 ctxt.set_rsp(sp)
171 *(*uint64)(unsafe.Pointer(uintptr(sp))) = ctxt.rip()
172
173 ctxt.set_rip(uint64(h.fv.fn))
174 ctxt.regs().rdx = uint64(uintptr(unsafe.Pointer(h.fv)))
175 case 1:
176
177 sp := ctxt.rsp()
178 memmove(h.argp, unsafe.Pointer(uintptr(sp)), h.argSize)
179 if h.regArgs != nil {
180 loadRegArgs(h.regArgs, ctxt.regs())
181 }
182 case 2:
183
184 sp := ctxt.rsp()
185 memmove(unsafe.Pointer(&h.panic), unsafe.Pointer(uintptr(sp)), 2*sys.PtrSize)
186 case 8:
187
188 sp := ctxt.rsp()
189 reason := *(*string)(unsafe.Pointer(uintptr(sp)))
190 h.err = plainError(reason)
191
192 case 16:
193
194 rip, rsp := ctxt.rip(), ctxt.rsp()
195 fp := ctxt.regs().fpstate
196 *ctxt.regs() = h.savedRegs
197 ctxt.regs().fpstate = fp
198 *fp = h.savedFP
199 ctxt.set_rip(rip)
200 ctxt.set_rsp(rsp)
201
202 notewakeup(&h.done)
203 default:
204 h.err = plainError("unexpected debugCallV2 status")
205 notewakeup(&h.done)
206 }
207
208 return true
209 }
210
View as plain text