Skip to content

Commit

Permalink
runtime/pprof: hide map runtime frames from heap profiles
Browse files Browse the repository at this point in the history
Heap profiles hide "runtime" frames like runtime.mapassign. This broke
in 1.24 because the map implementation moved to internal/runtime/maps,
and runtime/pprof only considered literal "runtime." when looking for
runtime frames.

It would be nice to use cmd/internal/objabi.PkgSpecial to find runtime
packages, but that is hidden away in cmd.

Fixes #71174.

Change-Id: I6a6a636cb42aa17539e47da16854bd3fd8cb1bfe
Reviewed-on: https://go-review.googlesource.com/c/go/+/641775
Auto-Submit: Michael Pratt <[email protected]>
LUCI-TryBot-Result: Go LUCI <[email protected]>
Reviewed-by: Michael Knyszek <[email protected]>
  • Loading branch information
prattmic authored and gopherbot committed Jan 9, 2025
1 parent c7c4420 commit d0c9142
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 3 deletions.
2 changes: 1 addition & 1 deletion src/runtime/pprof/pprof.go
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ func printStackRecord(w io.Writer, stk []uintptr, allFrames bool) {
if name == "" {
show = true
fmt.Fprintf(w, "#\t%#x\n", frame.PC)
} else if name != "runtime.goexit" && (show || !strings.HasPrefix(name, "runtime.")) {
} else if name != "runtime.goexit" && (show || !(strings.HasPrefix(name, "runtime.") || strings.HasPrefix(name, "internal/runtime/"))) {
// Hide runtime.goexit and any runtime functions at the beginning.
// This is useful mainly for allocation traces.
show = true
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/pprof/protomem.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func writeHeapProto(w io.Writer, p []profilerecord.MemProfileRecord, rate int64,
// what appendLocsForStack expects.
if hideRuntime {
for i, addr := range stk {
if f := runtime.FuncForPC(addr); f != nil && strings.HasPrefix(f.Name(), "runtime.") {
if f := runtime.FuncForPC(addr); f != nil && (strings.HasPrefix(f.Name(), "runtime.") || strings.HasPrefix(f.Name(), "internal/runtime/")) {
continue
}
// Found non-runtime. Show any runtime uses above it.
Expand Down
60 changes: 59 additions & 1 deletion src/runtime/pprof/protomem_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func locationToStrings(loc *profile.Location, funcs []string) []string {
return funcs
}

// This is a regression test for https://go.dev/issue/64528 .
// This is a regression test for https://go.dev/issue/64528.
func TestGenericsHashKeyInPprofBuilder(t *testing.T) {
if asan.Enabled {
t.Skip("extra allocations with -asan throw off the test; see #70079")
Expand Down Expand Up @@ -229,3 +229,61 @@ func TestGenericsInlineLocations(t *testing.T) {
t.Errorf("expected a location with at least 3 functions\n%s\ngot\n%s\n", expectedLocation, actual)
}
}

func growMap() {
m := make(map[int]int)
for i := range 512 {
m[i] = i
}
}

// Runtime frames are hidden in heap profiles.
// This is a regression test for https://go.dev/issue/71174.
func TestHeapRuntimeFrames(t *testing.T) {
previousRate := runtime.MemProfileRate
runtime.MemProfileRate = 1
defer func() {
runtime.MemProfileRate = previousRate
}()

growMap()

runtime.GC()
buf := bytes.NewBuffer(nil)
if err := WriteHeapProfile(buf); err != nil {
t.Fatalf("writing profile: %v", err)
}
p, err := profile.Parse(buf)
if err != nil {
t.Fatalf("profile.Parse: %v", err)
}

actual := profileToStrings(p)

// We must see growMap at least once.
foundGrowMap := false
for _, l := range actual {
if !strings.Contains(l, "runtime/pprof.growMap") {
continue
}
foundGrowMap = true

// Runtime frames like mapassign and map internals should be hidden.
if strings.Contains(l, "runtime.") {
t.Errorf("Sample got %s, want no runtime frames", l)
}
if strings.Contains(l, "internal/runtime/") {
t.Errorf("Sample got %s, want no runtime frames", l)
}
if strings.Contains(l, "runtime/internal/") {
t.Errorf("Sample got %s, want no runtime frames", l)
}
if strings.Contains(l, "mapassign") { // in case mapassign moves to a package not matching above paths.
t.Errorf("Sample got %s, want no mapassign frames", l)
}
}

if !foundGrowMap {
t.Errorf("Profile got:\n%s\nwant sample in runtime/pprof.growMap", strings.Join(actual, "\n"))
}
}

0 comments on commit d0c9142

Please sign in to comment.