aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/runtime/signal_unix.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/runtime/signal_unix.go')
-rw-r--r--libgo/go/runtime/signal_unix.go87
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.