github.com/comwrg/go/src@v0.0.0-20220319063731-c238d0440370/runtime/export_debug_test.go (about) 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 && linux 6 // +build amd64,linux 7 8 package runtime 9 10 import ( 11 "github.com/comwrg/go/src/internal/abi" 12 "runtime/internal/sys" 13 "unsafe" 14 ) 15 16 // InjectDebugCall injects a debugger call to fn into g. regArgs must 17 // contain any arguments to fn that are passed in registers, according 18 // to the internal Go ABI. It may be nil if no arguments are passed in 19 // registers to fn. args must be a pointer to a valid call frame (including 20 // arguments and return space) for fn, or nil. tkill must be a function that 21 // will send SIGTRAP to thread ID tid. gp must be locked to its OS thread and 22 // running. 23 // 24 // On success, InjectDebugCall returns the panic value of fn or nil. 25 // If fn did not panic, its results will be available in args. 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 // gp may not be running right now, but we can still get the M 55 // it will run on since it's locked. 56 h.mp = gp.lockedm.ptr() 57 h.fv, h.regArgs, h.argp, h.argSize = fv, regArgs, argp, argSize 58 h.handleF = h.handle // Avoid allocating closure during signal 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 // Wait for completion. 70 notetsleepg(&h.done, -1) 71 if h.err != "" { 72 switch h.err { 73 case "call not at safe point": 74 if returnOnUnsafePoint { 75 // This is for TestDebugCallUnsafePoint. 76 return nil, h.err 77 } 78 fallthrough 79 case "retry _Grunnable", "executing on Go runtime stack", "call from within the Go runtime": 80 // These are transient states. Try to get out of them. 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 // Push current PC on the stack. 118 rsp := ctxt.rsp() - sys.PtrSize 119 *(*uint64)(unsafe.Pointer(uintptr(rsp))) = ctxt.rip() 120 ctxt.set_rsp(rsp) 121 // Write the argument frame size. 122 *(*uintptr)(unsafe.Pointer(uintptr(rsp - 16))) = h.argSize 123 // Save current registers. 124 h.savedRegs = *ctxt.regs() 125 h.savedFP = *h.savedRegs.fpstate 126 h.savedRegs.fpstate = nil 127 // Set PC to debugCallV2. 128 ctxt.set_rip(uint64(funcPC(debugCallV2))) 129 // Call injected. Switch to the debugCall protocol. 130 testSigtrap = h.handleF 131 case _Grunnable: 132 // Ask InjectDebugCall to pause for a bit and then try 133 // again to interrupt this goroutine. 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 // Resume execution. 141 return true 142 } 143 144 func (h *debugCallHandler) handle(info *siginfo, ctxt *sigctxt, gp2 *g) bool { 145 // Sanity check. 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 // Frame is ready. Copy the arguments to the frame and to registers. 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 // Push return PC. 169 sp -= sys.PtrSize 170 ctxt.set_rsp(sp) 171 *(*uint64)(unsafe.Pointer(uintptr(sp))) = ctxt.rip() 172 // Set PC to call and context register. 173 ctxt.set_rip(uint64(h.fv.fn)) 174 ctxt.regs().rdx = uint64(uintptr(unsafe.Pointer(h.fv))) 175 case 1: 176 // Function returned. Copy frame and result registers back out. 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 // Function panicked. Copy panic out. 184 sp := ctxt.rsp() 185 memmove(unsafe.Pointer(&h.panic), unsafe.Pointer(uintptr(sp)), 2*sys.PtrSize) 186 case 8: 187 // Call isn't safe. Get the reason. 188 sp := ctxt.rsp() 189 reason := *(*string)(unsafe.Pointer(uintptr(sp))) 190 h.err = plainError(reason) 191 // Don't wake h.done. We need to transition to status 16 first. 192 case 16: 193 // Restore all registers except RIP and RSP. 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 // Done 202 notewakeup(&h.done) 203 default: 204 h.err = plainError("unexpected debugCallV2 status") 205 notewakeup(&h.done) 206 } 207 // Resume execution. 208 return true 209 }