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