aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/runtime/pprof
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@google.com>2016-02-03 21:58:02 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2016-02-03 21:58:02 +0000
commitf98dd1a338867a408f7c72d73fbad7fe7fc93e3a (patch)
tree2f8da9862a9c1fe0df138917f997b03439c02773 /libgo/go/runtime/pprof
parentb081ed4efc144da0c45a6484aebfd10e0eb9fda3 (diff)
downloadgcc-f98dd1a338867a408f7c72d73fbad7fe7fc93e3a.zip
gcc-f98dd1a338867a408f7c72d73fbad7fe7fc93e3a.tar.gz
gcc-f98dd1a338867a408f7c72d73fbad7fe7fc93e3a.tar.bz2
libgo: Update to go1.6rc1.
Reviewed-on: https://go-review.googlesource.com/19200 From-SVN: r233110
Diffstat (limited to 'libgo/go/runtime/pprof')
-rw-r--r--libgo/go/runtime/pprof/mprof_test.go16
-rw-r--r--libgo/go/runtime/pprof/pprof.go12
-rw-r--r--libgo/go/runtime/pprof/pprof_test.go174
3 files changed, 152 insertions, 50 deletions
diff --git a/libgo/go/runtime/pprof/mprof_test.go b/libgo/go/runtime/pprof/mprof_test.go
index 44a3850..bfa7b3b 100644
--- a/libgo/go/runtime/pprof/mprof_test.go
+++ b/libgo/go/runtime/pprof/mprof_test.go
@@ -22,11 +22,8 @@ func allocateTransient1M() {
}
}
+//go:noinline
func allocateTransient2M() {
- // prevent inlining
- if memSink == nil {
- panic("bad")
- }
memSink = make([]byte, 2<<20)
}
@@ -75,21 +72,22 @@ func TestMemoryProfiler(t *testing.T) {
memoryProfilerRun++
tests := []string{
+
fmt.Sprintf(`%v: %v \[%v: %v\] @ 0x[0-9,a-f x]+
-# 0x[0-9,a-f]+ pprof_test\.allocatePersistent1K\+0x[0-9,a-f]+ .*/mprof_test\.go:43
-# 0x[0-9,a-f]+ runtime_pprof_test\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/mprof_test\.go:66
+# 0x[0-9,a-f]+ pprof_test\.allocatePersistent1K\+0x[0-9,a-f]+ .*/mprof_test\.go:40
+# 0x[0-9,a-f]+ runtime_pprof_test\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/mprof_test\.go:63
`, 32*memoryProfilerRun, 1024*memoryProfilerRun, 32*memoryProfilerRun, 1024*memoryProfilerRun),
fmt.Sprintf(`0: 0 \[%v: %v\] @ 0x[0-9,a-f x]+
# 0x[0-9,a-f]+ pprof_test\.allocateTransient1M\+0x[0-9,a-f]+ .*/mprof_test.go:21
-# 0x[0-9,a-f]+ runtime_pprof_test\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/mprof_test.go:64
+# 0x[0-9,a-f]+ runtime_pprof_test\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/mprof_test.go:61
`, (1<<10)*memoryProfilerRun, (1<<20)*memoryProfilerRun),
// This should start with "0: 0" but gccgo's imprecise
// GC means that sometimes the value is not collected.
fmt.Sprintf(`(0|%v): (0|%v) \[%v: %v\] @ 0x[0-9,a-f x]+
-# 0x[0-9,a-f]+ pprof_test\.allocateTransient2M\+0x[0-9,a-f]+ .*/mprof_test.go:30
-# 0x[0-9,a-f]+ runtime_pprof_test\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/mprof_test.go:65
+# 0x[0-9,a-f]+ pprof_test\.allocateTransient2M\+0x[0-9,a-f]+ .*/mprof_test.go:27
+# 0x[0-9,a-f]+ runtime_pprof_test\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/mprof_test.go:62
`, memoryProfilerRun, (2<<20)*memoryProfilerRun, memoryProfilerRun, (2<<20)*memoryProfilerRun),
}
diff --git a/libgo/go/runtime/pprof/pprof.go b/libgo/go/runtime/pprof/pprof.go
index dcf67cd..fa11fda 100644
--- a/libgo/go/runtime/pprof/pprof.go
+++ b/libgo/go/runtime/pprof/pprof.go
@@ -20,8 +20,8 @@ import (
"text/tabwriter"
)
-// BUG(rsc): Profiles are incomplete and inaccurate on NetBSD and OS X.
-// See https://golang.org/issue/6047 for details.
+// BUG(rsc): Profiles are only as good as the kernel support used to generate them.
+// See https://golang.org/issue/13841 for details about known problems.
// A Profile is a collection of stack traces showing the call sequences
// that led to instances of a particular event, such as allocation.
@@ -579,6 +579,14 @@ var cpu struct {
// StartCPUProfile enables CPU profiling for the current process.
// While profiling, the profile will be buffered and written to w.
// StartCPUProfile returns an error if profiling is already enabled.
+//
+// On Unix-like systems, StartCPUProfile does not work by default for
+// Go code built with -buildmode=c-archive or -buildmode=c-shared.
+// StartCPUProfile relies on the SIGPROF signal, but that signal will
+// be delivered to the main program's SIGPROF signal handler (if any)
+// not to the one used by Go. To make it work, call os/signal.Notify
+// for syscall.SIGPROF, but note that doing so may break any profiling
+// being done by the main program.
func StartCPUProfile(w io.Writer) error {
// The runtime routines allow a variable profiling rate,
// but in practice operating systems cannot trigger signals
diff --git a/libgo/go/runtime/pprof/pprof_test.go b/libgo/go/runtime/pprof/pprof_test.go
index c32b847..244be05 100644
--- a/libgo/go/runtime/pprof/pprof_test.go
+++ b/libgo/go/runtime/pprof/pprof_test.go
@@ -23,14 +23,14 @@ import (
"unsafe"
)
-func cpuHogger(f func()) {
+func cpuHogger(f func(), dur time.Duration) {
// We only need to get one 100 Hz clock tick, so we've got
- // a 25x safety buffer.
+ // a large safety buffer.
// But do at least 500 iterations (which should take about 100ms),
// otherwise TestCPUProfileMultithreaded can fail if only one
- // thread is scheduled during the 250ms period.
+ // thread is scheduled during the testing period.
t0 := time.Now()
- for i := 0; i < 500 || time.Since(t0) < 250*time.Millisecond; i++ {
+ for i := 0; i < 500 || time.Since(t0) < dur; i++ {
f()
}
}
@@ -68,20 +68,20 @@ func cpuHog2() {
}
func TestCPUProfile(t *testing.T) {
- testCPUProfile(t, []string{"pprof_test.cpuHog1"}, func() {
- cpuHogger(cpuHog1)
+ testCPUProfile(t, []string{"pprof_test.cpuHog1"}, func(dur time.Duration) {
+ cpuHogger(cpuHog1, dur)
})
}
func TestCPUProfileMultithreaded(t *testing.T) {
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
- testCPUProfile(t, []string{"pprof_test.cpuHog1", "pprof_test.cpuHog2"}, func() {
+ testCPUProfile(t, []string{"pprof_test.cpuHog1", "pprof_test.cpuHog2"}, func(dur time.Duration) {
c := make(chan int)
go func() {
- cpuHogger(cpuHog1)
+ cpuHogger(cpuHog1, dur)
c <- 1
}()
- cpuHogger(cpuHog2)
+ cpuHogger(cpuHog2, dur)
<-c
})
}
@@ -92,8 +92,8 @@ func parseProfile(t *testing.T, bytes []byte, f func(uintptr, []uintptr)) {
val := *(*[]uintptr)(unsafe.Pointer(&bytes))
val = val[:l]
- // 5 for the header, 2 for the per-sample header on at least one sample, 3 for the trailer.
- if l < 5+2+3 {
+ // 5 for the header, 3 for the trailer.
+ if l < 5+3 {
t.Logf("profile too short: %#x", val)
if badOS[runtime.GOOS] {
t.Skipf("ignoring failure on %s; see golang.org/issue/6047", runtime.GOOS)
@@ -120,7 +120,7 @@ func parseProfile(t *testing.T, bytes []byte, f func(uintptr, []uintptr)) {
}
}
-func testCPUProfile(t *testing.T, need []string, f func()) {
+func testCPUProfile(t *testing.T, need []string, f func(dur time.Duration)) {
switch runtime.GOOS {
case "darwin":
switch runtime.GOARCH {
@@ -138,12 +138,55 @@ func testCPUProfile(t *testing.T, need []string, f func()) {
t.Skip("skipping on plan9")
}
- var prof bytes.Buffer
- if err := StartCPUProfile(&prof); err != nil {
- t.Fatal(err)
+ const maxDuration = 5 * time.Second
+ // If we're running a long test, start with a long duration
+ // because some of the tests (e.g., TestStackBarrierProfiling)
+ // are trying to make sure something *doesn't* happen.
+ duration := 5 * time.Second
+ if testing.Short() {
+ duration = 200 * time.Millisecond
+ }
+
+ // Profiling tests are inherently flaky, especially on a
+ // loaded system, such as when this test is running with
+ // several others under go test std. If a test fails in a way
+ // that could mean it just didn't run long enough, try with a
+ // longer duration.
+ for duration <= maxDuration {
+ var prof bytes.Buffer
+ if err := StartCPUProfile(&prof); err != nil {
+ t.Fatal(err)
+ }
+ f(duration)
+ StopCPUProfile()
+
+ if profileOk(t, need, prof, duration) {
+ return
+ }
+
+ duration *= 2
+ if duration <= maxDuration {
+ t.Logf("retrying with %s duration", duration)
+ }
+ }
+
+ if badOS[runtime.GOOS] {
+ t.Skipf("ignoring failure on %s; see golang.org/issue/6047", runtime.GOOS)
+ return
}
- f()
- StopCPUProfile()
+ // Ignore the failure if the tests are running in a QEMU-based emulator,
+ // QEMU is not perfect at emulating everything.
+ // IN_QEMU environmental variable is set by some of the Go builders.
+ // IN_QEMU=1 indicates that the tests are running in QEMU. See issue 9605.
+ if os.Getenv("IN_QEMU") == "1" {
+ t.Skip("ignore the failure in QEMU; see golang.org/issue/9605")
+ return
+ }
+ t.FailNow()
+}
+
+func profileOk(t *testing.T, need []string, prof bytes.Buffer, duration time.Duration) (ok bool) {
+ ok = true
// Check that profile is well formed and contains need.
have := make([]uintptr, len(need))
@@ -161,6 +204,10 @@ func testCPUProfile(t *testing.T, need []string, f func()) {
have[i] += count
}
}
+ if strings.Contains(f.Name(), "stackBarrier") {
+ // The runtime should have unwound this.
+ t.Fatalf("profile includes stackBarrier")
+ }
}
})
t.Logf("total %d CPU profile samples collected", samples)
@@ -169,11 +216,18 @@ func testCPUProfile(t *testing.T, need []string, f func()) {
// On some windows machines we end up with
// not enough samples due to coarse timer
// resolution. Let it go.
- t.Skip("too few samples on Windows (golang.org/issue/10842)")
+ t.Log("too few samples on Windows (golang.org/issue/10842)")
+ return false
+ }
+
+ // Check that we got a reasonable number of samples.
+ if ideal := uintptr(duration * 100 / time.Second); samples == 0 || samples < ideal/4 {
+ t.Logf("too few samples; got %d, want at least %d, ideally %d", samples, ideal/4, ideal)
+ ok = false
}
if len(need) == 0 {
- return
+ return ok
}
var total uintptr
@@ -181,9 +235,8 @@ func testCPUProfile(t *testing.T, need []string, f func()) {
total += have[i]
t.Logf("%s: %d\n", name, have[i])
}
- ok := true
if total == 0 {
- t.Logf("no CPU profile samples collected")
+ t.Logf("no samples in expected functions")
ok = false
}
// We'd like to check a reasonable minimum, like
@@ -197,22 +250,7 @@ func testCPUProfile(t *testing.T, need []string, f func()) {
ok = false
}
}
-
- if !ok {
- if badOS[runtime.GOOS] {
- t.Skipf("ignoring failure on %s; see golang.org/issue/6047", runtime.GOOS)
- return
- }
- // Ignore the failure if the tests are running in a QEMU-based emulator,
- // QEMU is not perfect at emulating everything.
- // IN_QEMU environmental variable is set by some of the Go builders.
- // IN_QEMU=1 indicates that the tests are running in QEMU. See issue 9605.
- if os.Getenv("IN_QEMU") == "1" {
- t.Skip("ignore the failure in QEMU; see golang.org/issue/9605")
- return
- }
- t.FailNow()
- }
+ return ok
}
// Fork can hang if preempted with signals frequently enough (see issue 5517).
@@ -307,8 +345,8 @@ func TestGoroutineSwitch(t *testing.T) {
// Test that profiling of division operations is okay, especially on ARM. See issue 6681.
func TestMathBigDivide(t *testing.T) {
- testCPUProfile(t, nil, func() {
- t := time.After(5 * time.Second)
+ testCPUProfile(t, nil, func(duration time.Duration) {
+ t := time.After(duration)
pi := new(big.Int)
for {
for i := 0; i < 100; i++ {
@@ -325,6 +363,64 @@ func TestMathBigDivide(t *testing.T) {
})
}
+func TestStackBarrierProfiling(t *testing.T) {
+ if (runtime.GOOS == "linux" && runtime.GOARCH == "arm") || runtime.GOOS == "openbsd" || runtime.GOOS == "solaris" || runtime.GOOS == "dragonfly" || runtime.GOOS == "freebsd" {
+ // This test currently triggers a large number of
+ // usleep(100)s. These kernels/arches have poor
+ // resolution timers, so this gives up a whole
+ // scheduling quantum. On Linux and the BSDs (and
+ // probably Solaris), profiling signals are only
+ // generated when a process completes a whole
+ // scheduling quantum, so this test often gets zero
+ // profiling signals and fails.
+ t.Skipf("low resolution timers inhibit profiling signals (golang.org/issue/13405)")
+ return
+ }
+
+ if !strings.Contains(os.Getenv("GODEBUG"), "gcstackbarrierall=1") {
+ // Re-execute this test with constant GC and stack
+ // barriers at every frame.
+ testenv.MustHaveExec(t)
+ if runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" {
+ t.Skip("gcstackbarrierall doesn't work on ppc64")
+ }
+ args := []string{"-test.run=TestStackBarrierProfiling"}
+ if testing.Short() {
+ args = append(args, "-test.short")
+ }
+ cmd := exec.Command(os.Args[0], args...)
+ cmd.Env = append([]string{"GODEBUG=gcstackbarrierall=1", "GOGC=1"}, os.Environ()...)
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Fatalf("subprocess failed with %v:\n%s", err, out)
+ }
+ return
+ }
+
+ testCPUProfile(t, nil, func(duration time.Duration) {
+ // In long mode, we're likely to get one or two
+ // samples in stackBarrier.
+ t := time.After(duration)
+ for {
+ deepStack(1000)
+ select {
+ case <-t:
+ return
+ default:
+ }
+ }
+ })
+}
+
+var x []byte
+
+func deepStack(depth int) int {
+ if depth == 0 {
+ return 0
+ }
+ x = make([]byte, 1024)
+ return deepStack(depth-1) + 1
+}
+
// Operating systems that are expected to fail the tests. See issue 6047.
var badOS = map[string]bool{
"darwin": true,