aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/pprof
diff options
context:
space:
mode:
authorFelix Geisendörfer <felix.geisendoerfer@datadoghq.com>2024-09-07 13:44:09 +0200
committerCarlos Amedee <carlos@golang.org>2024-09-25 17:30:33 +0000
commitc64ca8c6ef13723b9f25f4b5e1c7b6986b958d2e (patch)
tree9c4c48ed70b5faf7b648ffbf0724bc539e462c32 /src/runtime/pprof
parent8c8948c375a97f8cc09b3b6c3a06c75c31d83aa0 (diff)
downloadgo-c64ca8c6ef13723b9f25f4b5e1c7b6986b958d2e.tar.xz
runtime: fix MutexProfile missing root frames
Fix a regression introduced in CL 598515 causing runtime.MutexProfile stack traces to omit their root frames. In most cases this was merely causing the `runtime.goexit` frame to go missing. But in the case of runtime._LostContendedRuntimeLock, an empty stack trace was being produced. Add a test that catches this regression by checking for a stack trace with the `runtime.goexit` frame. Also fix a separate problem in expandFrame that could cause out-of-bounds panics when profstackdepth is set to a value below 32. There is no test for this fix because profstackdepth can't be changed at runtime right now. Fixes #69335 Change-Id: I1600fe62548ea84981df0916d25072c3ddf1ea1a Reviewed-on: https://go-review.googlesource.com/c/go/+/611615 Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Nick Ripley <nick.ripley@datadoghq.com> Reviewed-by: Michael Pratt <mpratt@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'src/runtime/pprof')
-rw-r--r--src/runtime/pprof/mprof_test.go2
-rw-r--r--src/runtime/pprof/pprof_test.go46
2 files changed, 43 insertions, 5 deletions
diff --git a/src/runtime/pprof/mprof_test.go b/src/runtime/pprof/mprof_test.go
index 391588d4ac..ef373b3684 100644
--- a/src/runtime/pprof/mprof_test.go
+++ b/src/runtime/pprof/mprof_test.go
@@ -145,7 +145,7 @@ func TestMemoryProfiler(t *testing.T) {
}
t.Logf("Profile = %v", p)
- stks := stacks(p)
+ stks := profileStacks(p)
for _, test := range tests {
if !containsStack(stks, test.stk) {
t.Fatalf("No matching stack entry for %q\n\nProfile:\n%v\n", test.stk, p)
diff --git a/src/runtime/pprof/pprof_test.go b/src/runtime/pprof/pprof_test.go
index 6d03c6464b..3492d7211d 100644
--- a/src/runtime/pprof/pprof_test.go
+++ b/src/runtime/pprof/pprof_test.go
@@ -973,7 +973,7 @@ func TestBlockProfile(t *testing.T) {
t.Fatalf("invalid profile: %v", err)
}
- stks := stacks(p)
+ stks := profileStacks(p)
for _, test := range tests {
if !containsStack(stks, test.stk) {
t.Errorf("No matching stack entry for %v, want %+v", test.name, test.stk)
@@ -983,7 +983,7 @@ func TestBlockProfile(t *testing.T) {
}
-func stacks(p *profile.Profile) (res [][]string) {
+func profileStacks(p *profile.Profile) (res [][]string) {
for _, s := range p.Sample {
var stk []string
for _, l := range s.Location {
@@ -996,6 +996,22 @@ func stacks(p *profile.Profile) (res [][]string) {
return res
}
+func blockRecordStacks(records []runtime.BlockProfileRecord) (res [][]string) {
+ for _, record := range records {
+ frames := runtime.CallersFrames(record.Stack())
+ var stk []string
+ for {
+ frame, more := frames.Next()
+ stk = append(stk, frame.Function)
+ if !more {
+ break
+ }
+ }
+ res = append(res, stk)
+ }
+ return res
+}
+
func containsStack(got [][]string, want []string) bool {
for _, stk := range got {
if len(stk) < len(want) {
@@ -1280,7 +1296,7 @@ func TestMutexProfile(t *testing.T) {
t.Fatalf("invalid profile: %v", err)
}
- stks := stacks(p)
+ stks := profileStacks(p)
for _, want := range [][]string{
{"sync.(*Mutex).Unlock", "runtime/pprof.blockMutexN.func1"},
} {
@@ -1320,6 +1336,28 @@ func TestMutexProfile(t *testing.T) {
t.Fatalf("profile samples total %v, want within range [%v, %v] (target: %v)", d, lo, hi, N*D)
}
})
+
+ t.Run("records", func(t *testing.T) {
+ // Record a mutex profile using the structured record API.
+ var records []runtime.BlockProfileRecord
+ for {
+ n, ok := runtime.MutexProfile(records)
+ if ok {
+ records = records[:n]
+ break
+ }
+ records = make([]runtime.BlockProfileRecord, n*2)
+ }
+
+ // Check that we see the same stack trace as the proto profile. For
+ // historical reason we expect a runtime.goexit root frame here that is
+ // omitted in the proto profile.
+ stks := blockRecordStacks(records)
+ want := []string{"sync.(*Mutex).Unlock", "runtime/pprof.blockMutexN.func1", "runtime.goexit"}
+ if !containsStack(stks, want) {
+ t.Errorf("No matching stack entry for %+v", want)
+ }
+ })
}
func TestMutexProfileRateAdjust(t *testing.T) {
@@ -2470,7 +2508,7 @@ func TestProfilerStackDepth(t *testing.T) {
}
t.Logf("Profile = %v", p)
- stks := stacks(p)
+ stks := profileStacks(p)
var stk []string
for _, s := range stks {
if hasPrefix(s, test.prefix) {