| // 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 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_t, ctxt unsafe.Pointer, gp *g) { |
| _g_ := getg() |
| c := sigctxt{info, ctxt} |
| |
| sigfault, sigpc := getSiginfo(info, ctxt) |
| |
| if sig == _SIGPROF { |
| sigprof(sigpc, gp, _g_.m) |
| 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 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() |
| } |
| |
| 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) |
| } |