diff options
author | Austin Clements <austin@google.com> | 2020-04-15 15:38:00 -0400 |
---|---|---|
committer | Austin Clements <austin@google.com> | 2020-04-29 21:29:11 +0000 |
commit | 3633d2c545cf21c2803103e1036f17f19b4ae6fa (patch) | |
tree | 553534fde5b03bcf11b4fb0747efad61a1d15e82 /src/runtime/export_debug_test.go | |
parent | b3863fbbc2fe1dbf516111992854aa9178d01410 (diff) | |
download | go-3633d2c545cf21c2803103e1036f17f19b4ae6fa.tar.gz go-3633d2c545cf21c2803103e1036f17f19b4ae6fa.zip |
runtime: perform debug call injection on a new goroutine
Currently, when a debugger injects a call, that call happens on the
goroutine where the debugger injected it. However, this requires
significant runtime complexity that we're about to remove.
To prepare for this, this CL switches to a different approach that
leaves the interrupted goroutine parked and runs the debug call on a
new goroutine. When the debug call returns, it resumes the original
goroutine.
This should be essentially transparent to debuggers. It follows the
exact same call injection protocol and ensures the whole protocol
executes indivisibly on a single OS thread. The only difference is
that the current G and stack now change part way through the protocol.
For #36365.
Change-Id: I68463bfd73cbee06cfc49999606410a59dd8f653
Reviewed-on: https://go-review.googlesource.com/c/go/+/229299
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Diffstat (limited to 'src/runtime/export_debug_test.go')
-rw-r--r-- | src/runtime/export_debug_test.go | 12 |
1 files changed, 8 insertions, 4 deletions
diff --git a/src/runtime/export_debug_test.go b/src/runtime/export_debug_test.go index 97bb7bd62a..ed4242ef24 100644 --- a/src/runtime/export_debug_test.go +++ b/src/runtime/export_debug_test.go @@ -48,6 +48,9 @@ func InjectDebugCall(gp *g, fn, args interface{}, tkill func(tid int) error, ret h := new(debugCallHandler) h.gp = gp + // gp may not be running right now, but we can still get the M + // it will run on since it's locked. + h.mp = gp.lockedm.ptr() h.fv, h.argp, h.argSize = fv, argp, argSize h.handleF = h.handle // Avoid allocating closure during signal @@ -86,6 +89,7 @@ func InjectDebugCall(gp *g, fn, args interface{}, tkill func(tid int) error, ret type debugCallHandler struct { gp *g + mp *m fv *funcval argp unsafe.Pointer argSize uintptr @@ -102,8 +106,8 @@ type debugCallHandler struct { func (h *debugCallHandler) inject(info *siginfo, ctxt *sigctxt, gp2 *g) bool { switch h.gp.atomicstatus { case _Grunning: - if getg().m != h.gp.m { - println("trap on wrong M", getg().m, h.gp.m) + if getg().m != h.mp { + println("trap on wrong M", getg().m, h.mp) return false } // Push current PC on the stack. @@ -135,8 +139,8 @@ func (h *debugCallHandler) inject(info *siginfo, ctxt *sigctxt, gp2 *g) bool { func (h *debugCallHandler) handle(info *siginfo, ctxt *sigctxt, gp2 *g) bool { // Sanity check. - if getg().m != h.gp.m { - println("trap on wrong M", getg().m, h.gp.m) + if getg().m != h.mp { + println("trap on wrong M", getg().m, h.mp) return false } f := findfunc(uintptr(ctxt.rip())) |