diff options
Diffstat (limited to 'libgo/go/runtime/signal_unix.go')
-rw-r--r-- | libgo/go/runtime/signal_unix.go | 87 |
1 files changed, 59 insertions, 28 deletions
diff --git a/libgo/go/runtime/signal_unix.go b/libgo/go/runtime/signal_unix.go index a291d2b..188598d 100644 --- a/libgo/go/runtime/signal_unix.go +++ b/libgo/go/runtime/signal_unix.go @@ -3,12 +3,13 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd hurd linux netbsd openbsd solaris package runtime import ( + "internal/abi" "runtime/internal/atomic" + "runtime/internal/sys" "unsafe" ) @@ -175,8 +176,8 @@ func sigInstallGoHandler(sig uint32) bool { } // When built using c-archive or c-shared, only install signal - // handlers for synchronous signals, SIGPIPE, and SIGURG. - if (isarchive || islibrary) && t.flags&_SigPanic == 0 && sig != _SIGPIPE && sig != _SIGURG { + // handlers for synchronous signals and SIGPIPE and sigPreempt. + if (isarchive || islibrary) && t.flags&_SigPanic == 0 && sig != _SIGPIPE && sig != sigPreempt { return false } @@ -271,15 +272,32 @@ func clearSignalHandlers() { } } -// setProcessCPUProfiler is called when the profiling timer changes. -// It is called with prof.lock held. hz is the new timer, and is 0 if +// setProcessCPUProfilerTimer is called when the profiling timer changes. +// It is called with prof.signalLock held. hz is the new timer, and is 0 if // profiling is being disabled. Enable or disable the signal as // required for -buildmode=c-archive. -func setProcessCPUProfiler(hz int32) { +func setProcessCPUProfilerTimer(hz int32) { if hz != 0 { // Enable the Go signal handler if not enabled. if atomic.Cas(&handlingSig[_SIGPROF], 0, 1) { - atomic.Storeuintptr(&fwdSig[_SIGPROF], getsig(_SIGPROF)) + h := getsig(_SIGPROF) + // If no signal handler was installed before, then we record + // _SIG_IGN here. When we turn off profiling (below) we'll start + // ignoring SIGPROF signals. We do this, rather than change + // to SIG_DFL, because there may be a pending SIGPROF + // signal that has not yet been delivered to some other thread. + // If we change to SIG_DFL when turning off profiling, the + // program will crash when that SIGPROF is delivered. We assume + // that programs that use profiling don't want to crash on a + // stray SIGPROF. See issue 19320. + // We do the change here instead of when turning off profiling, + // because there we may race with a signal handler running + // concurrently, in particular, sigfwdgo may observe _SIG_DFL and + // die. See issue 43828. + if h == _SIG_DFL { + h = _SIG_IGN + } + atomic.Storeuintptr(&fwdSig[_SIGPROF], h) setsig(_SIGPROF, getSigtramp()) } @@ -296,31 +314,19 @@ func setProcessCPUProfiler(hz int32) { // when we enabled profiling. We don't try to handle the case // of a program that changes the SIGPROF handler while Go // profiling is enabled. - // - // If no signal handler was installed before, then start - // ignoring SIGPROF signals. We do this, rather than change - // to SIG_DFL, because there may be a pending SIGPROF - // signal that has not yet been delivered to some other thread. - // If we change to SIG_DFL here, the program will crash - // when that SIGPROF is delivered. We assume that programs - // that use profiling don't want to crash on a stray SIGPROF. - // See issue 19320. if !sigInstallGoHandler(_SIGPROF) { if atomic.Cas(&handlingSig[_SIGPROF], 1, 0) { h := atomic.Loaduintptr(&fwdSig[_SIGPROF]) - if h == _SIG_DFL { - h = _SIG_IGN - } setsig(_SIGPROF, h) } } } } -// setThreadCPUProfiler makes any thread-specific changes required to +// setThreadCPUProfilerHz makes any thread-specific changes required to // implement profiling at a rate of hz. -// No changes required on Unix systems. -func setThreadCPUProfiler(hz int32) { +// No changes required on Unix systems when using setitimer. +func setThreadCPUProfilerHz(hz int32) { getg().m.profilehz = hz } @@ -338,7 +344,7 @@ func doSigPreempt(gp *g, ctxt *sigctxt, sigpc uintptr) { if wantAsyncPreempt(gp) { if ok, newpc := isAsyncSafePoint(gp, sigpc); ok { // Adjust the PC and inject a call to asyncPreempt. - // ctxt.pushCall(funcPC(asyncPreempt), newpc) + // ctxt.pushCall(abi.FuncPCABI0(asyncPreempt), newpc) throw("pushCall not implemented") _ = newpc } @@ -407,8 +413,12 @@ func sigtrampgo(sig uint32, info *_siginfo_t, ctx unsafe.Pointer) { if g == nil { c := sigctxt{info, ctx} if sig == _SIGPROF { - _, pc := getSiginfo(info, ctx) - sigprofNonGo(pc) + // Some platforms (Linux) have per-thread timers, which we use in + // combination with the process-wide timer. Avoid double-counting. + if validSIGPROF(nil, &c) { + _, pc := getSiginfo(info, ctx) + sigprofNonGoPC(pc) + } return } if sig == sigPreempt && preemptMSupported && debug.asyncpreemptoff == 0 { @@ -433,6 +443,21 @@ func sigtrampgo(sig uint32, info *_siginfo_t, ctx unsafe.Pointer) { setg(g) } +// sigprofNonGoPC is called when a profiling signal arrived on a +// non-Go thread and we have a single PC value, not a stack trace. +// g is nil, and what we can do is very limited. +//go:nosplit +//go:nowritebarrierrec +func sigprofNonGoPC(pc uintptr) { + if prof.hz != 0 { + stk := []uintptr{ + pc, + abi.FuncPCABIInternal(_ExternalCode) + sys.PCQuantum, + } + cpuprof.addNonGo(stk) + } +} + // crashing is the number of m's we have waited for when implementing // GOTRACEBACK=crash when a signal is received. var crashing int32 @@ -482,7 +507,11 @@ func sighandler(sig uint32, info *_siginfo_t, ctxt unsafe.Pointer, gp *g) { } if sig == _SIGPROF { - sigprof(sigpc, gp, _g_.m) + // Some platforms (Linux) have per-thread timers, which we use in + // combination with the process-wide timer. Avoid double-counting. + if validSIGPROF(_g_.m, c) { + sigprof(sigpc, gp, _g_.m) + } return } @@ -572,9 +601,11 @@ func sighandler(sig uint32, info *_siginfo_t, ctxt unsafe.Pointer, gp *g) { } 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 { + if _g_.m.incgo && gp == _g_.m.g0 && _g_.m.curg != nil { print("signal arrived during cgo execution\n") - gp = _g_.m.lockedg.ptr() + // Switch to curg so that we get a traceback of the Go code + // leading up to the cgocall, which switched from curg to g0. + gp = _g_.m.curg } if sig == _SIGILL || sig == _SIGFPE { // It would be nice to know how long the instruction is. |