From f9e90f7e7401b7222ea10420140dc43687e4017f Mon Sep 17 00:00:00 2001 From: Rhys Hiltner Date: Fri, 13 Aug 2021 08:51:46 -0700 Subject: [PATCH] runtime: allow per-OS changes to unix profiler Updates #35057 Change-Id: I56ea8f4750022847f0866c85e237a2cea40e0ff7 Reviewed-on: https://go-review.googlesource.com/c/go/+/342053 Run-TryBot: Rhys Hiltner TryBot-Result: Go Bot Reviewed-by: Michael Pratt Trust: Michael Knyszek --- src/runtime/os3_solaris.go | 13 +++++++++++++ src/runtime/os_aix.go | 13 +++++++++++++ src/runtime/os_darwin.go | 13 +++++++++++++ src/runtime/os_dragonfly.go | 13 +++++++++++++ src/runtime/os_freebsd.go | 13 +++++++++++++ src/runtime/os_linux.go | 16 ++++++++++++++++ src/runtime/os_netbsd.go | 13 +++++++++++++ src/runtime/os_openbsd.go | 13 +++++++++++++ src/runtime/signal_unix.go | 25 +++++++++++++++++-------- 9 files changed, 124 insertions(+), 8 deletions(-) diff --git a/src/runtime/os3_solaris.go b/src/runtime/os3_solaris.go index 84194a3050..3149d13869 100644 --- a/src/runtime/os3_solaris.go +++ b/src/runtime/os3_solaris.go @@ -289,6 +289,19 @@ func sigdelset(mask *sigset, i int) { func (c *sigctxt) fixsigcode(sig uint32) { } +func setProcessCPUProfiler(hz int32) { + setProcessCPUProfilerTimer(hz) +} + +func setThreadCPUProfiler(hz int32) { + setThreadCPUProfilerHz(hz) +} + +//go:nosplit +func validSIGPROF(mp *m, c *sigctxt) bool { + return true +} + //go:nosplit func semacreate(mp *m) { if mp.waitsema != 0 { diff --git a/src/runtime/os_aix.go b/src/runtime/os_aix.go index 478dde2fc3..54e0cfbb8d 100644 --- a/src/runtime/os_aix.go +++ b/src/runtime/os_aix.go @@ -323,6 +323,19 @@ func sigdelset(mask *sigset, i int) { (*mask)[(i-1)/64] &^= 1 << ((uint32(i) - 1) & 63) } +func setProcessCPUProfiler(hz int32) { + setProcessCPUProfilerTimer(hz) +} + +func setThreadCPUProfiler(hz int32) { + setThreadCPUProfilerHz(hz) +} + +//go:nosplit +func validSIGPROF(mp *m, c *sigctxt) bool { + return true +} + const ( _CLOCK_REALTIME = 9 _CLOCK_MONOTONIC = 10 diff --git a/src/runtime/os_darwin.go b/src/runtime/os_darwin.go index ca61f20e8a..0f0eb6c6fd 100644 --- a/src/runtime/os_darwin.go +++ b/src/runtime/os_darwin.go @@ -425,6 +425,19 @@ func sigdelset(mask *sigset, i int) { *mask &^= 1 << (uint32(i) - 1) } +func setProcessCPUProfiler(hz int32) { + setProcessCPUProfilerTimer(hz) +} + +func setThreadCPUProfiler(hz int32) { + setThreadCPUProfilerHz(hz) +} + +//go:nosplit +func validSIGPROF(mp *m, c *sigctxt) bool { + return true +} + //go:linkname executablePath os.executablePath var executablePath string diff --git a/src/runtime/os_dragonfly.go b/src/runtime/os_dragonfly.go index 191a560667..cba2e42ab0 100644 --- a/src/runtime/os_dragonfly.go +++ b/src/runtime/os_dragonfly.go @@ -268,6 +268,19 @@ func sigdelset(mask *sigset, i int) { func (c *sigctxt) fixsigcode(sig uint32) { } +func setProcessCPUProfiler(hz int32) { + setProcessCPUProfilerTimer(hz) +} + +func setThreadCPUProfiler(hz int32) { + setThreadCPUProfilerHz(hz) +} + +//go:nosplit +func validSIGPROF(mp *m, c *sigctxt) bool { + return true +} + func sysargs(argc int32, argv **byte) { n := argc + 1 diff --git a/src/runtime/os_freebsd.go b/src/runtime/os_freebsd.go index 5a8121a420..c63b0e3d69 100644 --- a/src/runtime/os_freebsd.go +++ b/src/runtime/os_freebsd.go @@ -380,6 +380,19 @@ func sigdelset(mask *sigset, i int) { func (c *sigctxt) fixsigcode(sig uint32) { } +func setProcessCPUProfiler(hz int32) { + setProcessCPUProfilerTimer(hz) +} + +func setThreadCPUProfiler(hz int32) { + setThreadCPUProfilerHz(hz) +} + +//go:nosplit +func validSIGPROF(mp *m, c *sigctxt) bool { + return true +} + func sysargs(argc int32, argv **byte) { n := argc + 1 diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go index b60dc9ea01..d307c472b8 100644 --- a/src/runtime/os_linux.go +++ b/src/runtime/os_linux.go @@ -517,3 +517,19 @@ func tgkill(tgid, tid, sig int) func signalM(mp *m, sig int) { tgkill(getpid(), int(mp.procid), sig) } + +func setProcessCPUProfiler(hz int32) { + setProcessCPUProfilerTimer(hz) +} + +func setThreadCPUProfiler(hz int32) { + setThreadCPUProfilerHz(hz) +} + +//go:nosplit +func validSIGPROF(mp *m, c *sigctxt) bool { + // Once the profiler for Linux uses timer_create, this function will need to + // do more than return true. But so long as the only profiling timer in use + // is setitimer, returning true is correct. + return true +} diff --git a/src/runtime/os_netbsd.go b/src/runtime/os_netbsd.go index 0b95fa7a6e..cd9508c706 100644 --- a/src/runtime/os_netbsd.go +++ b/src/runtime/os_netbsd.go @@ -371,6 +371,19 @@ func sigdelset(mask *sigset, i int) { func (c *sigctxt) fixsigcode(sig uint32) { } +func setProcessCPUProfiler(hz int32) { + setProcessCPUProfilerTimer(hz) +} + +func setThreadCPUProfiler(hz int32) { + setThreadCPUProfilerHz(hz) +} + +//go:nosplit +func validSIGPROF(mp *m, c *sigctxt) bool { + return true +} + func sysargs(argc int32, argv **byte) { n := argc + 1 diff --git a/src/runtime/os_openbsd.go b/src/runtime/os_openbsd.go index 54f36c6ebf..2d0e71de53 100644 --- a/src/runtime/os_openbsd.go +++ b/src/runtime/os_openbsd.go @@ -233,6 +233,19 @@ func sigdelset(mask *sigset, i int) { func (c *sigctxt) fixsigcode(sig uint32) { } +func setProcessCPUProfiler(hz int32) { + setProcessCPUProfilerTimer(hz) +} + +func setThreadCPUProfiler(hz int32) { + setThreadCPUProfilerHz(hz) +} + +//go:nosplit +func validSIGPROF(mp *m, c *sigctxt) bool { + return true +} + var haveMapStack = false func osStackAlloc(s *mspan) { diff --git a/src/runtime/signal_unix.go b/src/runtime/signal_unix.go index cab5c879d3..228497c508 100644 --- a/src/runtime/signal_unix.go +++ b/src/runtime/signal_unix.go @@ -263,11 +263,11 @@ 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) { @@ -309,10 +309,10 @@ func setProcessCPUProfiler(hz int32) { } } -// 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 } @@ -423,7 +423,11 @@ func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) { setg(g) if g == nil { if sig == _SIGPROF { - sigprofNonGoPC(c.sigpc()) + // Some platforms (Linux) have per-thread timers, which we use in + // combination with the process-wide timer. Avoid double-counting. + if validSIGPROF(nil, c) { + sigprofNonGoPC(c.sigpc()) + } return } if sig == sigPreempt && preemptMSupported && debug.asyncpreemptoff == 0 { @@ -540,7 +544,12 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) { c := &sigctxt{info, ctxt} if sig == _SIGPROF { - sigprof(c.sigpc(), c.sigsp(), c.siglr(), gp, _g_.m) + mp := _g_.m + // Some platforms (Linux) have per-thread timers, which we use in + // combination with the process-wide timer. Avoid double-counting. + if validSIGPROF(mp, c) { + sigprof(c.sigpc(), c.sigsp(), c.siglr(), gp, mp) + } return } -- 2.50.0