27d6b51071
Patch by Svante Signell. Reviewed-on: https://go-review.googlesource.com/c/160822 From-SVN: r268459
175 lines
4.7 KiB
Go
175 lines
4.7 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 aix darwin dragonfly freebsd hurd 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_t, 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_t, ctxt unsafe.Pointer, gp *g) {
|
|
_g_ := getg()
|
|
c := &sigctxt{info, ctxt}
|
|
|
|
sigfault, sigpc := getSiginfo(info, ctxt)
|
|
|
|
if sig == _SIGURG && usestackmaps {
|
|
// We may be signaled to do a stack scan.
|
|
// The signal delivery races with enter/exitsyscall.
|
|
// We may be on g0 stack now. gp.m.curg is the g we
|
|
// want to scan.
|
|
// If we're not on g stack, give up. The sender will
|
|
// try again later.
|
|
// If we're not stopped at a safepoint (doscanstack will
|
|
// return false), also give up.
|
|
if s := readgstatus(gp.m.curg); s == _Gscansyscall {
|
|
if gp == gp.m.curg {
|
|
if doscanstack(gp, (*gcWork)(unsafe.Pointer(gp.scangcw))) {
|
|
gp.gcscanvalid = true
|
|
gp.gcscandone = true
|
|
}
|
|
}
|
|
gp.m.curg.scangcw = 0
|
|
notewakeup(&gp.m.scannote)
|
|
return
|
|
}
|
|
}
|
|
|
|
if sig == _SIGPROF {
|
|
sigprof(sigpc, 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(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 {
|
|
// Emulate gc by passing arguments out of band,
|
|
// although we don't really have to.
|
|
gp.sig = sig
|
|
gp.sigcode0 = uintptr(c.sigcode())
|
|
gp.sigcode1 = sigfault
|
|
gp.sigpc = sigpc
|
|
|
|
setg(gp)
|
|
|
|
// All signals were blocked due to the sigaction mask;
|
|
// unblock them.
|
|
var set sigset
|
|
sigfillset(&set)
|
|
sigprocmask(_SIG_UNBLOCK, &set, nil)
|
|
|
|
sigpanic()
|
|
throw("sigpanic returned")
|
|
}
|
|
|
|
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(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)
|
|
traceback(0)
|
|
if crashing == 0 {
|
|
tracebackothers(gp)
|
|
print("\n")
|
|
}
|
|
dumpregs(info, ctxt)
|
|
}
|
|
|
|
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)
|
|
}
|