]> Cypherpunks.ru repositories - gostls13.git/blob - src/runtime/export_debug_test.go
cmd/compile/internal/inline: score call sites exposed by inlines
[gostls13.git] / src / runtime / export_debug_test.go
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
7 package runtime
8
9 import (
10         "internal/abi"
11         "unsafe"
12 )
13
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
20 // running.
21 //
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) {
25         if gp.lockedm == 0 {
26                 return nil, plainError("goroutine not locked to thread")
27         }
28
29         tid := int(gp.lockedm.ptr().procid)
30         if tid == 0 {
31                 return nil, plainError("missing tid")
32         }
33
34         f := efaceOf(&fn)
35         if f._type == nil || f._type.Kind_&kindMask != kindFunc {
36                 return nil, plainError("fn must be a function")
37         }
38         fv := (*funcval)(f.data)
39
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")
43         }
44         argp := a.data
45         var argSize uintptr
46         if argp != nil {
47                 argSize = (*ptrtype)(unsafe.Pointer(a._type)).Elem.Size_
48         }
49
50         h := new(debugCallHandler)
51         h.gp = gp
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
57
58         defer func() { testSigtrap = nil }()
59         for i := 0; ; i++ {
60                 testSigtrap = h.inject
61                 noteclear(&h.done)
62                 h.err = ""
63
64                 if err := tkill(tid); err != nil {
65                         return nil, err
66                 }
67                 // Wait for completion.
68                 notetsleepg(&h.done, -1)
69                 if h.err != "" {
70                         switch h.err {
71                         case "call not at safe point":
72                                 if returnOnUnsafePoint {
73                                         // This is for TestDebugCallUnsafePoint.
74                                         return nil, h.err
75                                 }
76                                 fallthrough
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.
79                                 if i < 100 {
80                                         usleep(100)
81                                         Gosched()
82                                         continue
83                                 }
84                         }
85                         return nil, h.err
86                 }
87                 return h.panic, nil
88         }
89 }
90
91 type debugCallHandler struct {
92         gp      *g
93         mp      *m
94         fv      *funcval
95         regArgs *abi.RegArgs
96         argp    unsafe.Pointer
97         argSize uintptr
98         panic   any
99
100         handleF func(info *siginfo, ctxt *sigctxt, gp2 *g) bool
101
102         err     plainError
103         done    note
104         sigCtxt sigContext
105 }
106
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.
111
112         switch h.gp.atomicstatus.Load() {
113         case _Grunning:
114                 if getg().m != h.mp {
115                         println("trap on wrong M", getg().m, h.mp)
116                         return false
117                 }
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
124         case _Grunnable:
125                 // Ask InjectDebugCall to pause for a bit and then try
126                 // again to interrupt this goroutine.
127                 h.err = plainError("retry _Grunnable")
128                 notewakeup(&h.done)
129         default:
130                 h.err = plainError("goroutine in unexpected state at call inject")
131                 notewakeup(&h.done)
132         }
133         // Resume execution.
134         return true
135 }
136
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.
141
142         // Double-check m.
143         if getg().m != h.mp {
144                 println("trap on wrong M", getg().m, h.mp)
145                 return false
146         }
147         f := findfunc(ctxt.sigpc())
148         if !(hasPrefix(funcname(f), "runtime.debugCall") || hasPrefix(funcname(f), "debugCall")) {
149                 println("trap in unknown function", funcname(f))
150                 return false
151         }
152         if !sigctxtAtTrapInstruction(ctxt) {
153                 println("trap at non-INT3 instruction pc =", hex(ctxt.sigpc()))
154                 return false
155         }
156
157         switch status := sigctxtStatus(ctxt); status {
158         case 0:
159                 // Frame is ready. Copy the arguments to the frame and to registers.
160                 // Call the debug function.
161                 h.debugCallRun(ctxt)
162         case 1:
163                 // Function returned. Copy frame and result registers back out.
164                 h.debugCallReturn(ctxt)
165         case 2:
166                 // Function panicked. Copy panic out.
167                 h.debugCallPanicOut(ctxt)
168         case 8:
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.
172         case 16:
173                 h.restoreSigContext(ctxt)
174                 // Done
175                 notewakeup(&h.done)
176         default:
177                 h.err = plainError("unexpected debugCallV2 status")
178                 notewakeup(&h.done)
179         }
180         // Resume execution.
181         return true
182 }