mirror of
https://github.com/golang/go
synced 2024-11-08 05:36:13 -07:00
c5ed10f3be
This adds a mechanism for debuggers to safely inject calls to Go functions on amd64. Debuggers must participate in a protocol with the runtime, and need to know how to lay out a call frame, but the runtime support takes care of the details of handling live pointers in registers, stack growth, and detecting the trickier conditions when it is unsafe to inject a user function call. Fixes #21678. Updates derekparker/delve#119. Change-Id: I56d8ca67700f1f77e19d89e7fc92ab337b228834 Reviewed-on: https://go-review.googlesource.com/109699 Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
153 lines
4.4 KiB
Go
153 lines
4.4 KiB
Go
// Copyright 2013 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
|
|
|
|
package runtime
|
|
|
|
import (
|
|
"unsafe"
|
|
)
|
|
|
|
// crashing is the number of m's we have waited for when implementing
|
|
// GOTRACEBACK=crash when a signal is received.
|
|
var crashing int32
|
|
|
|
// testSigtrap is used by the runtime tests. If non-nil, it is called
|
|
// on SIGTRAP. If it returns true, the normal behavior on SIGTRAP is
|
|
// suppressed.
|
|
var testSigtrap func(info *siginfo, ctxt *sigctxt, gp *g) bool
|
|
|
|
// sighandler is invoked when a signal occurs. The global g will be
|
|
// set to a gsignal goroutine and we will be running on the alternate
|
|
// signal stack. The parameter g will be the value of the global g
|
|
// when the signal occurred. The sig, info, and ctxt parameters are
|
|
// from the system signal handler: they are the parameters passed when
|
|
// the SA is passed to the sigaction system call.
|
|
//
|
|
// The garbage collector may have stopped the world, so write barriers
|
|
// are not allowed.
|
|
//
|
|
//go:nowritebarrierrec
|
|
func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
|
|
_g_ := getg()
|
|
c := &sigctxt{info, ctxt}
|
|
|
|
if sig == _SIGPROF {
|
|
sigprof(c.sigpc(), c.sigsp(), c.siglr(), gp, _g_.m)
|
|
return
|
|
}
|
|
|
|
if sig == _SIGTRAP && testSigtrap != nil && testSigtrap(info, (*sigctxt)(noescape(unsafe.Pointer(c))), gp) {
|
|
return
|
|
}
|
|
|
|
flags := int32(_SigThrow)
|
|
if sig < uint32(len(sigtable)) {
|
|
flags = sigtable[sig].flags
|
|
}
|
|
if flags&_SigPanic != 0 && gp.throwsplit {
|
|
// We can't safely sigpanic because it may grow the
|
|
// stack. Abort in the signal handler instead.
|
|
flags = (flags &^ _SigPanic) | _SigThrow
|
|
}
|
|
if isAbortPC(c.sigpc()) {
|
|
// On many architectures, the abort function just
|
|
// causes a memory fault. Don't turn that into a panic.
|
|
flags = _SigThrow
|
|
}
|
|
if c.sigcode() != _SI_USER && flags&_SigPanic != 0 {
|
|
// The signal is going to cause a panic.
|
|
// Arrange the stack so that it looks like the point
|
|
// where the signal occurred made a call to the
|
|
// function sigpanic. Then set the PC to sigpanic.
|
|
|
|
// Have to pass arguments out of band since
|
|
// augmenting the stack frame would break
|
|
// the unwinding code.
|
|
gp.sig = sig
|
|
gp.sigcode0 = uintptr(c.sigcode())
|
|
gp.sigcode1 = uintptr(c.fault())
|
|
gp.sigpc = c.sigpc()
|
|
|
|
c.preparePanic(sig, gp)
|
|
return
|
|
}
|
|
|
|
if c.sigcode() == _SI_USER || flags&_SigNotify != 0 {
|
|
if sigsend(sig) {
|
|
return
|
|
}
|
|
}
|
|
|
|
if c.sigcode() == _SI_USER && signal_ignored(sig) {
|
|
return
|
|
}
|
|
|
|
if flags&_SigKill != 0 {
|
|
dieFromSignal(sig)
|
|
}
|
|
|
|
if flags&_SigThrow == 0 {
|
|
return
|
|
}
|
|
|
|
_g_.m.throwing = 1
|
|
_g_.m.caughtsig.set(gp)
|
|
|
|
if crashing == 0 {
|
|
startpanic_m()
|
|
}
|
|
|
|
if sig < uint32(len(sigtable)) {
|
|
print(sigtable[sig].name, "\n")
|
|
} else {
|
|
print("Signal ", sig, "\n")
|
|
}
|
|
|
|
print("PC=", hex(c.sigpc()), " m=", _g_.m.id, " sigcode=", c.sigcode(), "\n")
|
|
if _g_.m.lockedg != 0 && _g_.m.ncgo > 0 && gp == _g_.m.g0 {
|
|
print("signal arrived during cgo execution\n")
|
|
gp = _g_.m.lockedg.ptr()
|
|
}
|
|
print("\n")
|
|
|
|
level, _, docrash := gotraceback()
|
|
if level > 0 {
|
|
goroutineheader(gp)
|
|
tracebacktrap(c.sigpc(), c.sigsp(), c.siglr(), gp)
|
|
if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning {
|
|
// tracebackothers on original m skipped this one; trace it now.
|
|
goroutineheader(_g_.m.curg)
|
|
traceback(^uintptr(0), ^uintptr(0), 0, _g_.m.curg)
|
|
} else if crashing == 0 {
|
|
tracebackothers(gp)
|
|
print("\n")
|
|
}
|
|
dumpregs(c)
|
|
}
|
|
|
|
if docrash {
|
|
crashing++
|
|
if crashing < mcount()-int32(extraMCount) {
|
|
// There are other m's that need to dump their stacks.
|
|
// Relay SIGQUIT to the next m by sending it to the current process.
|
|
// All m's that have already received SIGQUIT have signal masks blocking
|
|
// receipt of any signals, so the SIGQUIT will go to an m that hasn't seen it yet.
|
|
// When the last m receives the SIGQUIT, it will fall through to the call to
|
|
// crash below. Just in case the relaying gets botched, each m involved in
|
|
// the relay sleeps for 5 seconds and then does the crash/exit itself.
|
|
// In expected operation, the last m has received the SIGQUIT and run
|
|
// crash/exit and the process is gone, all long before any of the
|
|
// 5-second sleeps have finished.
|
|
print("\n-----\n\n")
|
|
raiseproc(_SIGQUIT)
|
|
usleep(5 * 1000 * 1000)
|
|
}
|
|
crash()
|
|
}
|
|
|
|
exit(2)
|
|
}
|