summaryrefslogtreecommitdiff
path: root/libgo/go/runtime/pprof/pprof_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/runtime/pprof/pprof_test.go')
-rw-r--r--libgo/go/runtime/pprof/pprof_test.go122
1 files changed, 103 insertions, 19 deletions
diff --git a/libgo/go/runtime/pprof/pprof_test.go b/libgo/go/runtime/pprof/pprof_test.go
index fd05a049b5a..bf9f5266e29 100644
--- a/libgo/go/runtime/pprof/pprof_test.go
+++ b/libgo/go/runtime/pprof/pprof_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build !nacl,!js
+// +build !aix,!nacl,!js
package pprof
@@ -82,14 +82,14 @@ func avoidFunctions() []string {
}
func TestCPUProfile(t *testing.T) {
- testCPUProfile(t, []string{"pprof.cpuHog1"}, avoidFunctions(), func(dur time.Duration) {
+ testCPUProfile(t, stackContains, []string{"pprof.cpuHog1"}, avoidFunctions(), func(dur time.Duration) {
cpuHogger(cpuHog1, &salt1, dur)
})
}
func TestCPUProfileMultithreaded(t *testing.T) {
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
- testCPUProfile(t, []string{"pprof.cpuHog1", "pprof.cpuHog2"}, avoidFunctions(), func(dur time.Duration) {
+ testCPUProfile(t, stackContains, []string{"pprof.cpuHog1", "pprof.cpuHog2"}, avoidFunctions(), func(dur time.Duration) {
c := make(chan int)
go func() {
cpuHogger(cpuHog1, &salt1, dur)
@@ -101,7 +101,7 @@ func TestCPUProfileMultithreaded(t *testing.T) {
}
func TestCPUProfileInlining(t *testing.T) {
- testCPUProfile(t, []string{"pprof.inlinedCallee", "pprof.inlinedCaller"}, avoidFunctions(), func(dur time.Duration) {
+ testCPUProfile(t, stackContains, []string{"pprof.inlinedCallee", "pprof.inlinedCaller"}, avoidFunctions(), func(dur time.Duration) {
cpuHogger(inlinedCaller, &salt1, dur)
})
}
@@ -139,7 +139,9 @@ func parseProfile(t *testing.T, valBytes []byte, f func(uintptr, []*profile.Loca
}
}
-func testCPUProfile(t *testing.T, need []string, avoid []string, f func(dur time.Duration)) {
+// testCPUProfile runs f under the CPU profiler, checking for some conditions specified by need,
+// as interpreted by matches.
+func testCPUProfile(t *testing.T, matches matchFunc, need []string, avoid []string, f func(dur time.Duration)) {
switch runtime.GOOS {
case "darwin":
switch runtime.GOARCH {
@@ -178,7 +180,7 @@ func testCPUProfile(t *testing.T, need []string, avoid []string, f func(dur time
f(duration)
StopCPUProfile()
- if profileOk(t, need, avoid, prof, duration) {
+ if profileOk(t, matches, need, avoid, prof, duration) {
return
}
@@ -191,6 +193,10 @@ func testCPUProfile(t *testing.T, need []string, avoid []string, f func(dur time
switch runtime.GOOS {
case "darwin", "dragonfly", "netbsd", "solaris":
t.Skipf("ignoring failure on %s; see golang.org/issue/13841", runtime.GOOS)
+ case "openbsd":
+ if runtime.GOARCH == "arm" {
+ t.Skipf("ignoring failure on %s/%s; see golang.org/issue/13841", runtime.GOOS, runtime.GOARCH)
+ }
}
// Ignore the failure if the tests are running in a QEMU-based emulator,
// QEMU is not perfect at emulating everything.
@@ -211,7 +217,21 @@ func contains(slice []string, s string) bool {
return false
}
-func profileOk(t *testing.T, need []string, avoid []string, prof bytes.Buffer, duration time.Duration) (ok bool) {
+// stackContains matches if a function named spec appears anywhere in the stack trace.
+func stackContains(spec string, count uintptr, stk []*profile.Location, labels map[string][]string) bool {
+ for _, loc := range stk {
+ for _, line := range loc.Line {
+ if strings.Contains(line.Function.Name, spec) {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+type matchFunc func(spec string, count uintptr, stk []*profile.Location, labels map[string][]string) bool
+
+func profileOk(t *testing.T, matches matchFunc, need []string, avoid []string, prof bytes.Buffer, duration time.Duration) (ok bool) {
ok = true
// Check that profile is well formed, contains 'need', and does not contain
@@ -224,18 +244,16 @@ func profileOk(t *testing.T, need []string, avoid []string, prof bytes.Buffer, d
fmt.Fprintf(&buf, "%d:", count)
fprintStack(&buf, stk)
samples += count
- for i, name := range need {
- if semi := strings.Index(name, ";"); semi > -1 {
- kv := strings.SplitN(name[semi+1:], "=", 2)
- if len(kv) != 2 || !contains(labels[kv[0]], kv[1]) {
- continue
- }
- name = name[:semi]
+ for i, spec := range need {
+ if matches(spec, count, stk, labels) {
+ have[i] += count
}
+ }
+ for i, name := range avoid {
for _, loc := range stk {
for _, line := range loc.Line {
if strings.Contains(line.Function.Name, name) {
- have[i] += count
+ avoidSamples[i] += count
}
}
}
@@ -316,6 +334,10 @@ func TestCPUProfileWithFork(t *testing.T) {
// Use smaller size for Android to avoid crash.
heap = 100 << 20
}
+ if runtime.GOOS == "windows" && runtime.GOARCH == "arm" {
+ // Use smaller heap for Windows/ARM to avoid crash.
+ heap = 100 << 20
+ }
if testing.Short() {
heap = 100 << 20
}
@@ -408,7 +430,7 @@ func fprintStack(w io.Writer, stk []*profile.Location) {
// Test that profiling of division operations is okay, especially on ARM. See issue 6681.
func TestMathBigDivide(t *testing.T) {
- testCPUProfile(t, nil, nil, func(duration time.Duration) {
+ testCPUProfile(t, nil, nil, nil, func(duration time.Duration) {
t := time.After(duration)
pi := new(big.Int)
for {
@@ -426,6 +448,51 @@ func TestMathBigDivide(t *testing.T) {
})
}
+// stackContainsAll matches if all functions in spec (comma-separated) appear somewhere in the stack trace.
+func stackContainsAll(spec string, count uintptr, stk []*profile.Location, labels map[string][]string) bool {
+ for _, f := range strings.Split(spec, ",") {
+ if !stackContains(f, count, stk, labels) {
+ return false
+ }
+ }
+ return true
+}
+
+func TestMorestack(t *testing.T) {
+ if runtime.Compiler == "gccgo" {
+ t.Skip("no runtime.newstack in gccgo")
+ }
+ testCPUProfile(t, stackContainsAll, []string{"runtime.newstack,runtime/pprof.growstack"}, avoidFunctions(), func(duration time.Duration) {
+ t := time.After(duration)
+ c := make(chan bool)
+ for {
+ go func() {
+ growstack1()
+ c <- true
+ }()
+ select {
+ case <-t:
+ return
+ case <-c:
+ }
+ }
+ })
+}
+
+//go:noinline
+func growstack1() {
+ growstack()
+}
+
+//go:noinline
+func growstack() {
+ var buf [8 << 10]byte
+ use(buf)
+}
+
+//go:noinline
+func use(x [8 << 10]byte) {}
+
func TestBlockProfile(t *testing.T) {
t.Skip("lots of details are different for gccgo; FIXME")
type TestCase struct {
@@ -556,7 +623,7 @@ func TestBlockProfile(t *testing.T) {
}
for _, test := range tests {
- if !regexp.MustCompile(strings.Replace(test.re, "\t", "\t+", -1)).MatchString(prof) {
+ if !regexp.MustCompile(strings.ReplaceAll(test.re, "\t", "\t+")).MatchString(prof) {
t.Errorf("Bad %v entry, expect:\n%v\ngot:\n%v", test.name, test.re, prof)
}
}
@@ -887,8 +954,25 @@ func TestEmptyCallStack(t *testing.T) {
}
}
+// stackContainsLabeled takes a spec like funcname;key=value and matches if the stack has that key
+// and value and has funcname somewhere in the stack.
+func stackContainsLabeled(spec string, count uintptr, stk []*profile.Location, labels map[string][]string) bool {
+ semi := strings.Index(spec, ";")
+ if semi == -1 {
+ panic("no semicolon in key/value spec")
+ }
+ kv := strings.SplitN(spec[semi+1:], "=", 2)
+ if len(kv) != 2 {
+ panic("missing = in key/value spec")
+ }
+ if !contains(labels[kv[0]], kv[1]) {
+ return false
+ }
+ return stackContains(spec[:semi], count, stk, labels)
+}
+
func TestCPUProfileLabel(t *testing.T) {
- testCPUProfile(t, []string{"pprof.cpuHogger;key=value"}, avoidFunctions(), func(dur time.Duration) {
+ testCPUProfile(t, stackContainsLabeled, []string{"pprof.cpuHogger;key=value"}, avoidFunctions(), func(dur time.Duration) {
Do(context.Background(), Labels("key", "value"), func(context.Context) {
cpuHogger(cpuHog1, &salt1, dur)
})
@@ -899,7 +983,7 @@ func TestLabelRace(t *testing.T) {
// Test the race detector annotations for synchronization
// between settings labels and consuming them from the
// profile.
- testCPUProfile(t, []string{"pprof.cpuHogger;key=value"}, avoidFunctions(), func(dur time.Duration) {
+ testCPUProfile(t, stackContainsLabeled, []string{"pprof.cpuHogger;key=value"}, nil, func(dur time.Duration) {
start := time.Now()
var wg sync.WaitGroup
for time.Since(start) < dur {