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//go:build (amd64 || arm64 || ppc64le) && linux 6 7package runtime 8 9import ( 10 "internal/abi" 11 "internal/stringslite" 12 "unsafe" 13) 14 15// InjectDebugCall injects a debugger call to fn into g. regArgs must 16// contain any arguments to fn that are passed in registers, according 17// to the internal Go ABI. It may be nil if no arguments are passed in 18// registers to fn. args must be a pointer to a valid call frame (including 19// arguments and return space) for fn, or nil. tkill must be a function that 20// will send SIGTRAP to thread ID tid. gp must be locked to its OS thread and 21// running. 22// 23// On success, InjectDebugCall returns the panic value of fn or nil. 24// If fn did not panic, its results will be available in args. 25func 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 // gp may not be running right now, but we can still get the M 54 // it will run on since it's locked. 55 h.mp = gp.lockedm.ptr() 56 h.fv, h.regArgs, h.argp, h.argSize = fv, regArgs, argp, argSize 57 h.handleF = h.handle // Avoid allocating closure during signal 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 // Wait for completion. 69 notetsleepg(&h.done, -1) 70 if h.err != "" { 71 switch h.err { 72 case "call not at safe point": 73 if returnOnUnsafePoint { 74 // This is for TestDebugCallUnsafePoint. 75 return nil, h.err 76 } 77 fallthrough 78 case "retry _Grunnable", "executing on Go runtime stack", "call from within the Go runtime": 79 // These are transient states. Try to get out of them. 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 92type 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 108func (h *debugCallHandler) inject(info *siginfo, ctxt *sigctxt, gp2 *g) bool { 109 // TODO(49370): This code is riddled with write barriers, but called from 110 // a signal handler. Add the go:nowritebarrierrec annotation and restructure 111 // this to avoid write barriers. 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 // Save the signal context 120 h.saveSigContext(ctxt) 121 // Set PC to debugCallV2. 122 ctxt.setsigpc(uint64(abi.FuncPCABIInternal(debugCallV2))) 123 // Call injected. Switch to the debugCall protocol. 124 testSigtrap = h.handleF 125 case _Grunnable: 126 // Ask InjectDebugCall to pause for a bit and then try 127 // again to interrupt this goroutine. 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 // Resume execution. 135 return true 136} 137 138func (h *debugCallHandler) handle(info *siginfo, ctxt *sigctxt, gp2 *g) bool { 139 // TODO(49370): This code is riddled with write barriers, but called from 140 // a signal handler. Add the go:nowritebarrierrec annotation and restructure 141 // this to avoid write barriers. 142 143 // Double-check m. 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 // Frame is ready. Copy the arguments to the frame and to registers. 161 // Call the debug function. 162 h.debugCallRun(ctxt) 163 case 1: 164 // Function returned. Copy frame and result registers back out. 165 h.debugCallReturn(ctxt) 166 case 2: 167 // Function panicked. Copy panic out. 168 h.debugCallPanicOut(ctxt) 169 case 8: 170 // Call isn't safe. Get the reason. 171 h.debugCallUnsafe(ctxt) 172 // Don't wake h.done. We need to transition to status 16 first. 173 case 16: 174 h.restoreSigContext(ctxt) 175 // Done 176 notewakeup(&h.done) 177 default: 178 h.err = plainError("unexpected debugCallV2 status") 179 notewakeup(&h.done) 180 } 181 // Resume execution. 182 return true 183} 184