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