mirror of https://github.com/golang/go.git
runtime: enable/disable SIGPROF if needed when profiling
This ensures that SIGPROF is handled correctly when using runtime/pprof in a c-archive or c-shared library. Separate profiler handling into pre-process changes and per-thread changes. Simplify the Windows code slightly accordingly. Fixes #18220. Change-Id: I5060f7084c91ef0bbe797848978bdc527c312777 Reviewed-on: https://go-review.googlesource.com/34018 TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Austin Clements <austin@google.com> Run-TryBot: Austin Clements <austin@google.com>
This commit is contained in:
parent
6a29806e01
commit
e24228af25
|
|
@ -557,3 +557,38 @@ func hasDynTag(t *testing.T, f *elf.File, tag elf.DynTag) bool {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSIGPROF(t *testing.T) {
|
||||||
|
switch GOOS {
|
||||||
|
case "windows", "plan9":
|
||||||
|
t.Skipf("skipping SIGPROF test on %s", GOOS)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
os.Remove("testp6" + exeSuffix)
|
||||||
|
os.Remove("libgo6.a")
|
||||||
|
os.Remove("libgo6.h")
|
||||||
|
}()
|
||||||
|
|
||||||
|
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo6.a", "libgo6")
|
||||||
|
cmd.Env = gopathEnv
|
||||||
|
if out, err := cmd.CombinedOutput(); err != nil {
|
||||||
|
t.Logf("%s", out)
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ccArgs := append(cc, "-o", "testp6"+exeSuffix, "main6.c", "libgo6.a")
|
||||||
|
if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
|
||||||
|
t.Logf("%s", out)
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
argv := cmdToRun("./testp6")
|
||||||
|
cmd = exec.Command(argv[0], argv[1:]...)
|
||||||
|
if out, err := cmd.CombinedOutput(); err != nil {
|
||||||
|
t.Logf("%s", out)
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright 2016 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.
|
||||||
|
|
||||||
|
// Test that using the Go profiler in a C program does not crash.
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#include "libgo6.h"
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
struct timeval tvstart, tvnow;
|
||||||
|
int diff;
|
||||||
|
|
||||||
|
gettimeofday(&tvstart, NULL);
|
||||||
|
|
||||||
|
go_start_profile();
|
||||||
|
|
||||||
|
// Busy wait so we have something to profile.
|
||||||
|
// If we just sleep the profiling signal will never fire.
|
||||||
|
while (1) {
|
||||||
|
gettimeofday(&tvnow, NULL);
|
||||||
|
diff = (tvnow.tv_sec - tvstart.tv_sec) * 1000 * 1000 + (tvnow.tv_usec - tvstart.tv_usec);
|
||||||
|
|
||||||
|
// Profile frequency is 100Hz so we should definitely
|
||||||
|
// get a signal in 50 milliseconds.
|
||||||
|
if (diff > 50 * 1000)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
go_stop_profile();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2016 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.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"runtime/pprof"
|
||||||
|
)
|
||||||
|
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
//export go_start_profile
|
||||||
|
func go_start_profile() {
|
||||||
|
pprof.StartCPUProfile(ioutil.Discard)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export go_stop_profile
|
||||||
|
func go_stop_profile() {
|
||||||
|
pprof.StopCPUProfile()
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
}
|
||||||
|
|
@ -146,7 +146,10 @@ func sigdisable(sig uint32) {
|
||||||
func sigignore(sig uint32) {
|
func sigignore(sig uint32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func resetcpuprofiler(hz int32) {
|
func setProcessCPUProfiler(hz int32) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func setThreadCPUProfiler(hz int32) {
|
||||||
// TODO: Enable profiling interrupts.
|
// TODO: Enable profiling interrupts.
|
||||||
getg().m.profilehz = hz
|
getg().m.profilehz = hz
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -273,7 +273,8 @@ func raisebadsignal(sig uint32) {
|
||||||
|
|
||||||
func madvise(addr unsafe.Pointer, n uintptr, flags int32) {}
|
func madvise(addr unsafe.Pointer, n uintptr, flags int32) {}
|
||||||
func munmap(addr unsafe.Pointer, n uintptr) {}
|
func munmap(addr unsafe.Pointer, n uintptr) {}
|
||||||
func resetcpuprofiler(hz int32) {}
|
func setProcessCPUProfiler(hz int32) {}
|
||||||
|
func setThreadCPUProfiler(hz int32) {}
|
||||||
func sigdisable(uint32) {}
|
func sigdisable(uint32) {}
|
||||||
func sigenable(uint32) {}
|
func sigenable(uint32) {}
|
||||||
func sigignore(uint32) {}
|
func sigignore(uint32) {}
|
||||||
|
|
|
||||||
|
|
@ -744,10 +744,7 @@ func profileloop1(param uintptr) uint32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var cpuprofilerlock mutex
|
func setProcessCPUProfiler(hz int32) {
|
||||||
|
|
||||||
func resetcpuprofiler(hz int32) {
|
|
||||||
lock(&cpuprofilerlock)
|
|
||||||
if profiletimer == 0 {
|
if profiletimer == 0 {
|
||||||
timer := stdcall3(_CreateWaitableTimerA, 0, 0, 0)
|
timer := stdcall3(_CreateWaitableTimerA, 0, 0, 0)
|
||||||
atomic.Storeuintptr(&profiletimer, timer)
|
atomic.Storeuintptr(&profiletimer, timer)
|
||||||
|
|
@ -755,8 +752,9 @@ func resetcpuprofiler(hz int32) {
|
||||||
stdcall2(_SetThreadPriority, thread, _THREAD_PRIORITY_HIGHEST)
|
stdcall2(_SetThreadPriority, thread, _THREAD_PRIORITY_HIGHEST)
|
||||||
stdcall1(_CloseHandle, thread)
|
stdcall1(_CloseHandle, thread)
|
||||||
}
|
}
|
||||||
unlock(&cpuprofilerlock)
|
}
|
||||||
|
|
||||||
|
func setThreadCPUProfiler(hz int32) {
|
||||||
ms := int32(0)
|
ms := int32(0)
|
||||||
due := ^int64(^uint64(1 << 63))
|
due := ^int64(^uint64(1 << 63))
|
||||||
if hz > 0 {
|
if hz > 0 {
|
||||||
|
|
|
||||||
|
|
@ -1879,7 +1879,7 @@ func execute(gp *g, inheritTime bool) {
|
||||||
// Check whether the profiler needs to be turned on or off.
|
// Check whether the profiler needs to be turned on or off.
|
||||||
hz := sched.profilehz
|
hz := sched.profilehz
|
||||||
if _g_.m.profilehz != hz {
|
if _g_.m.profilehz != hz {
|
||||||
resetcpuprofiler(hz)
|
setThreadCPUProfiler(hz)
|
||||||
}
|
}
|
||||||
|
|
||||||
if trace.enabled {
|
if trace.enabled {
|
||||||
|
|
@ -2780,7 +2780,7 @@ func beforefork() {
|
||||||
// Ensure that we stay on the same M where we disable profiling.
|
// Ensure that we stay on the same M where we disable profiling.
|
||||||
gp.m.locks++
|
gp.m.locks++
|
||||||
if gp.m.profilehz != 0 {
|
if gp.m.profilehz != 0 {
|
||||||
resetcpuprofiler(0)
|
setThreadCPUProfiler(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is called before fork in syscall package.
|
// This function is called before fork in syscall package.
|
||||||
|
|
@ -2805,7 +2805,7 @@ func afterfork() {
|
||||||
|
|
||||||
hz := sched.profilehz
|
hz := sched.profilehz
|
||||||
if hz != 0 {
|
if hz != 0 {
|
||||||
resetcpuprofiler(hz)
|
setThreadCPUProfiler(hz)
|
||||||
}
|
}
|
||||||
gp.m.locks--
|
gp.m.locks--
|
||||||
}
|
}
|
||||||
|
|
@ -3439,12 +3439,15 @@ func setcpuprofilerate_m(hz int32) {
|
||||||
// Stop profiler on this thread so that it is safe to lock prof.
|
// Stop profiler on this thread so that it is safe to lock prof.
|
||||||
// if a profiling signal came in while we had prof locked,
|
// if a profiling signal came in while we had prof locked,
|
||||||
// it would deadlock.
|
// it would deadlock.
|
||||||
resetcpuprofiler(0)
|
setThreadCPUProfiler(0)
|
||||||
|
|
||||||
for !atomic.Cas(&prof.lock, 0, 1) {
|
for !atomic.Cas(&prof.lock, 0, 1) {
|
||||||
osyield()
|
osyield()
|
||||||
}
|
}
|
||||||
prof.hz = hz
|
if prof.hz != hz {
|
||||||
|
setProcessCPUProfiler(hz)
|
||||||
|
prof.hz = hz
|
||||||
|
}
|
||||||
atomic.Store(&prof.lock, 0)
|
atomic.Store(&prof.lock, 0)
|
||||||
|
|
||||||
lock(&sched.lock)
|
lock(&sched.lock)
|
||||||
|
|
@ -3452,7 +3455,7 @@ func setcpuprofilerate_m(hz int32) {
|
||||||
unlock(&sched.lock)
|
unlock(&sched.lock)
|
||||||
|
|
||||||
if hz != 0 {
|
if hz != 0 {
|
||||||
resetcpuprofiler(hz)
|
setThreadCPUProfiler(hz)
|
||||||
}
|
}
|
||||||
|
|
||||||
_g_.m.locks--
|
_g_.m.locks--
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,11 @@ func sigenable(sig uint32) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SIGPROF is handled specially for profiling.
|
||||||
|
if sig == _SIGPROF {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
t := &sigtable[sig]
|
t := &sigtable[sig]
|
||||||
if t.flags&_SigNotify != 0 {
|
if t.flags&_SigNotify != 0 {
|
||||||
ensureSigM()
|
ensureSigM()
|
||||||
|
|
@ -158,6 +163,11 @@ func sigdisable(sig uint32) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SIGPROF is handled specially for profiling.
|
||||||
|
if sig == _SIGPROF {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
t := &sigtable[sig]
|
t := &sigtable[sig]
|
||||||
if t.flags&_SigNotify != 0 {
|
if t.flags&_SigNotify != 0 {
|
||||||
ensureSigM()
|
ensureSigM()
|
||||||
|
|
@ -182,6 +192,11 @@ func sigignore(sig uint32) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SIGPROF is handled specially for profiling.
|
||||||
|
if sig == _SIGPROF {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
t := &sigtable[sig]
|
t := &sigtable[sig]
|
||||||
if t.flags&_SigNotify != 0 {
|
if t.flags&_SigNotify != 0 {
|
||||||
atomic.Store(&handlingSig[sig], 0)
|
atomic.Store(&handlingSig[sig], 0)
|
||||||
|
|
@ -189,7 +204,31 @@ func sigignore(sig uint32) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func resetcpuprofiler(hz int32) {
|
// setProcessCPUProfiler is called when the profiling timer changes.
|
||||||
|
// It is called with prof.lock 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) {
|
||||||
|
if hz != 0 {
|
||||||
|
// Enable the Go signal handler if not enabled.
|
||||||
|
if atomic.Cas(&handlingSig[_SIGPROF], 0, 1) {
|
||||||
|
atomic.Storeuintptr(&fwdSig[_SIGPROF], getsig(_SIGPROF))
|
||||||
|
setsig(_SIGPROF, funcPC(sighandler))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If the Go signal handler should be disabled by default,
|
||||||
|
// disable it if it is enabled.
|
||||||
|
if !sigInstallGoHandler(_SIGPROF) {
|
||||||
|
if atomic.Cas(&handlingSig[_SIGPROF], 1, 0) {
|
||||||
|
setsig(_SIGPROF, atomic.Loaduintptr(&fwdSig[_SIGPROF]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setThreadCPUProfiler makes any thread-specific changes required to
|
||||||
|
// implement profiling at a rate of hz.
|
||||||
|
func setThreadCPUProfiler(hz int32) {
|
||||||
var it itimerval
|
var it itimerval
|
||||||
if hz == 0 {
|
if hz == 0 {
|
||||||
setitimer(_ITIMER_PROF, &it, nil)
|
setitimer(_ITIMER_PROF, &it, nil)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue