|  | // 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 | 
|  |  | 
|  | // 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 | 
|  | } | 
|  |  | 
|  | flags := int32(_SigThrow) | 
|  | if sig < uint32(len(sigtable)) { | 
|  | flags = sigtable[sig].flags | 
|  | } | 
|  | 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() | 
|  | } | 
|  |  | 
|  | 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 != nil && _g_.m.ncgo > 0 && gp == _g_.m.g0 { | 
|  | print("signal arrived during cgo execution\n") | 
|  | gp = _g_.m.lockedg | 
|  | } | 
|  | 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, gp) | 
|  | } else if crashing == 0 { | 
|  | tracebackothers(gp) | 
|  | print("\n") | 
|  | } | 
|  | dumpregs(c) | 
|  | } | 
|  |  | 
|  | if docrash { | 
|  | crashing++ | 
|  | if crashing < sched.mcount { | 
|  | // 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) | 
|  | } |