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.
5 //go:build (amd64 || arm64 || ppc64le) && linux
14 // InjectDebugCall injects a debugger call to fn into g. regArgs must
15 // contain any arguments to fn that are passed in registers, according
16 // to the internal Go ABI. It may be nil if no arguments are passed in
17 // registers to fn. args must be a pointer to a valid call frame (including
18 // arguments and return space) for fn, or nil. tkill must be a function that
19 // will send SIGTRAP to thread ID tid. gp must be locked to its OS thread and
22 // On success, InjectDebugCall returns the panic value of fn or nil.
23 // If fn did not panic, its results will be available in args.
24 func InjectDebugCall(gp *g, fn any, regArgs *abi.RegArgs, stackArgs any, tkill func(tid int) error, returnOnUnsafePoint bool) (any, error) {
26 return nil, plainError("goroutine not locked to thread")
29 tid := int(gp.lockedm.ptr().procid)
31 return nil, plainError("missing tid")
35 if f._type == nil || f._type.Kind_&kindMask != kindFunc {
36 return nil, plainError("fn must be a function")
38 fv := (*funcval)(f.data)
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")
47 argSize = (*ptrtype)(unsafe.Pointer(a._type)).Elem.Size_
50 h := new(debugCallHandler)
52 // gp may not be running right now, but we can still get the M
53 // it will run on since it's locked.
54 h.mp = gp.lockedm.ptr()
55 h.fv, h.regArgs, h.argp, h.argSize = fv, regArgs, argp, argSize
56 h.handleF = h.handle // Avoid allocating closure during signal
58 defer func() { testSigtrap = nil }()
60 testSigtrap = h.inject
64 if err := tkill(tid); err != nil {
67 // Wait for completion.
68 notetsleepg(&h.done, -1)
71 case "call not at safe point":
72 if returnOnUnsafePoint {
73 // This is for TestDebugCallUnsafePoint.
77 case "retry _Grunnable", "executing on Go runtime stack", "call from within the Go runtime":
78 // These are transient states. Try to get out of them.
91 type debugCallHandler struct {
100 handleF func(info *siginfo, ctxt *sigctxt, gp2 *g) bool
107 func (h *debugCallHandler) inject(info *siginfo, ctxt *sigctxt, gp2 *g) bool {
108 // TODO(49370): This code is riddled with write barriers, but called from
109 // a signal handler. Add the go:nowritebarrierrec annotation and restructure
110 // this to avoid write barriers.
112 switch h.gp.atomicstatus.Load() {
114 if getg().m != h.mp {
115 println("trap on wrong M", getg().m, h.mp)
118 // Save the signal context
119 h.saveSigContext(ctxt)
120 // Set PC to debugCallV2.
121 ctxt.setsigpc(uint64(abi.FuncPCABIInternal(debugCallV2)))
122 // Call injected. Switch to the debugCall protocol.
123 testSigtrap = h.handleF
125 // Ask InjectDebugCall to pause for a bit and then try
126 // again to interrupt this goroutine.
127 h.err = plainError("retry _Grunnable")
130 h.err = plainError("goroutine in unexpected state at call inject")
137 func (h *debugCallHandler) handle(info *siginfo, ctxt *sigctxt, gp2 *g) bool {
138 // TODO(49370): This code is riddled with write barriers, but called from
139 // a signal handler. Add the go:nowritebarrierrec annotation and restructure
140 // this to avoid write barriers.
143 if getg().m != h.mp {
144 println("trap on wrong M", getg().m, h.mp)
147 f := findfunc(ctxt.sigpc())
148 if !(hasPrefix(funcname(f), "runtime.debugCall") || hasPrefix(funcname(f), "debugCall")) {
149 println("trap in unknown function", funcname(f))
152 if !sigctxtAtTrapInstruction(ctxt) {
153 println("trap at non-INT3 instruction pc =", hex(ctxt.sigpc()))
157 switch status := sigctxtStatus(ctxt); status {
159 // Frame is ready. Copy the arguments to the frame and to registers.
160 // Call the debug function.
163 // Function returned. Copy frame and result registers back out.
164 h.debugCallReturn(ctxt)
166 // Function panicked. Copy panic out.
167 h.debugCallPanicOut(ctxt)
169 // Call isn't safe. Get the reason.
170 h.debugCallUnsafe(ctxt)
171 // Don't wake h.done. We need to transition to status 16 first.
173 h.restoreSigContext(ctxt)
177 h.err = plainError("unexpected debugCallV2 status")