| // 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, "\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) |
| } |