diff options
author | Ian Lance Taylor <iant@google.com> | 2016-02-03 21:58:02 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2016-02-03 21:58:02 +0000 |
commit | f98dd1a338867a408f7c72d73fbad7fe7fc93e3a (patch) | |
tree | 2f8da9862a9c1fe0df138917f997b03439c02773 /libgo/go/runtime/pprof | |
parent | b081ed4efc144da0c45a6484aebfd10e0eb9fda3 (diff) | |
download | gcc-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.go | 16 | ||||
-rw-r--r-- | libgo/go/runtime/pprof/pprof.go | 12 | ||||
-rw-r--r-- | libgo/go/runtime/pprof/pprof_test.go | 174 |
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, |