[1/7] Preprocessor cleanup
[official-gcc.git] / libgo / go / runtime / export_debug_test.go
blob2d2d5356edc09823e018aa05447b6c00911d930c
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 // +build ignore_for_gccgo
6 // +build amd64
7 // +build linux
9 package runtime
11 import (
12 "runtime/internal/sys"
13 "unsafe"
16 // InjectDebugCall injects a debugger call to fn into g. args must be
17 // a pointer to a valid call frame (including arguments and return
18 // space) for fn, or nil. tkill must be a function that will send
19 // SIGTRAP to thread ID tid. gp must be locked to its OS thread and
20 // running.
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, args interface{}, tkill func(tid int) error) (interface{}, error) {
25 if gp.lockedm == 0 {
26 return nil, plainError("goroutine not locked to thread")
29 tid := int(gp.lockedm.ptr().procid)
30 if tid == 0 {
31 return nil, plainError("missing tid")
34 f := efaceOf(&fn)
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(&args)
41 if a._type != nil && a._type.kind&kindMask != kindPtr {
42 return nil, plainError("args must be a pointer or nil")
44 argp := a.data
45 var argSize uintptr
46 if argp != nil {
47 argSize = (*ptrtype)(unsafe.Pointer(a._type)).elem.size
50 h := new(debugCallHandler)
51 h.gp = gp
52 h.fv, h.argp, h.argSize = fv, argp, argSize
53 h.handleF = h.handle // Avoid allocating closure during signal
54 noteclear(&h.done)
56 defer func() { testSigtrap = nil }()
57 testSigtrap = h.inject
58 if err := tkill(tid); err != nil {
59 return nil, err
61 // Wait for completion.
62 notetsleepg(&h.done, -1)
63 if len(h.err) != 0 {
64 return nil, h.err
66 return h.panic, nil
69 type debugCallHandler struct {
70 gp *g
71 fv *funcval
72 argp unsafe.Pointer
73 argSize uintptr
74 panic interface{}
76 handleF func(info *siginfo, ctxt *sigctxt, gp2 *g) bool
78 err plainError
79 done note
80 savedRegs sigcontext
81 savedFP fpstate1
84 func (h *debugCallHandler) inject(info *siginfo, ctxt *sigctxt, gp2 *g) bool {
85 switch h.gp.atomicstatus {
86 case _Grunning:
87 if getg().m != h.gp.m {
88 println("trap on wrong M", getg().m, h.gp.m)
89 return false
91 // Push current PC on the stack.
92 rsp := ctxt.rsp() - sys.PtrSize
93 *(*uint64)(unsafe.Pointer(uintptr(rsp))) = ctxt.rip()
94 ctxt.set_rsp(rsp)
95 // Write the argument frame size.
96 *(*uintptr)(unsafe.Pointer(uintptr(rsp - 16))) = h.argSize
97 // Save current registers.
98 h.savedRegs = *ctxt.regs()
99 h.savedFP = *h.savedRegs.fpstate
100 h.savedRegs.fpstate = nil
101 // Set PC to debugCallV1.
102 ctxt.set_rip(uint64(funcPC(debugCallV1)))
103 default:
104 h.err = plainError("goroutine in unexpected state at call inject")
105 return true
107 // Switch to the debugCall protocol and resume execution.
108 testSigtrap = h.handleF
109 return true
112 func (h *debugCallHandler) handle(info *siginfo, ctxt *sigctxt, gp2 *g) bool {
113 // Sanity check.
114 if getg().m != h.gp.m {
115 println("trap on wrong M", getg().m, h.gp.m)
116 return false
118 f := findfunc(uintptr(ctxt.rip()))
119 if !(hasprefix(funcname(f), "runtime.debugCall") || hasprefix(funcname(f), "debugCall")) {
120 println("trap in unknown function", funcname(f))
121 return false
123 if *(*byte)(unsafe.Pointer(uintptr(ctxt.rip() - 1))) != 0xcc {
124 println("trap at non-INT3 instruction pc =", hex(ctxt.rip()))
125 return false
128 switch status := ctxt.rax(); status {
129 case 0:
130 // Frame is ready. Copy the arguments to the frame.
131 sp := ctxt.rsp()
132 memmove(unsafe.Pointer(uintptr(sp)), h.argp, h.argSize)
133 // Push return PC.
134 sp -= sys.PtrSize
135 ctxt.set_rsp(sp)
136 *(*uint64)(unsafe.Pointer(uintptr(sp))) = ctxt.rip()
137 // Set PC to call and context register.
138 ctxt.set_rip(uint64(h.fv.fn))
139 ctxt.regs().rcx = uint64(uintptr(unsafe.Pointer(h.fv)))
140 case 1:
141 // Function returned. Copy frame back out.
142 sp := ctxt.rsp()
143 memmove(h.argp, unsafe.Pointer(uintptr(sp)), h.argSize)
144 case 2:
145 // Function panicked. Copy panic out.
146 sp := ctxt.rsp()
147 memmove(unsafe.Pointer(&h.panic), unsafe.Pointer(uintptr(sp)), 2*sys.PtrSize)
148 case 8:
149 // Call isn't safe. Get the reason.
150 sp := ctxt.rsp()
151 reason := *(*string)(unsafe.Pointer(uintptr(sp)))
152 h.err = plainError(reason)
153 case 16:
154 // Restore all registers except RIP and RSP.
155 rip, rsp := ctxt.rip(), ctxt.rsp()
156 fp := ctxt.regs().fpstate
157 *ctxt.regs() = h.savedRegs
158 ctxt.regs().fpstate = fp
159 *fp = h.savedFP
160 ctxt.set_rip(rip)
161 ctxt.set_rsp(rsp)
162 // Done
163 notewakeup(&h.done)
164 default:
165 h.err = plainError("unexpected debugCallV1 status")
167 // Resume execution.
168 return true