Skip to content

Commit d0c9142

Browse files
prattmicgopherbot
authored andcommitted
runtime/pprof: hide map runtime frames from heap profiles
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]>
1 parent c7c4420 commit d0c9142

File tree

3 files changed

+61
-3
lines changed

3 files changed

+61
-3
lines changed

src/runtime/pprof/pprof.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ func printStackRecord(w io.Writer, stk []uintptr, allFrames bool) {
555555
if name == "" {
556556
show = true
557557
fmt.Fprintf(w, "#\t%#x\n", frame.PC)
558-
} else if name != "runtime.goexit" && (show || !strings.HasPrefix(name, "runtime.")) {
558+
} else if name != "runtime.goexit" && (show || !(strings.HasPrefix(name, "runtime.") || strings.HasPrefix(name, "internal/runtime/"))) {
559559
// Hide runtime.goexit and any runtime functions at the beginning.
560560
// This is useful mainly for allocation traces.
561561
show = true

src/runtime/pprof/protomem.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func writeHeapProto(w io.Writer, p []profilerecord.MemProfileRecord, rate int64,
3636
// what appendLocsForStack expects.
3737
if hideRuntime {
3838
for i, addr := range stk {
39-
if f := runtime.FuncForPC(addr); f != nil && strings.HasPrefix(f.Name(), "runtime.") {
39+
if f := runtime.FuncForPC(addr); f != nil && (strings.HasPrefix(f.Name(), "runtime.") || strings.HasPrefix(f.Name(), "internal/runtime/")) {
4040
continue
4141
}
4242
// Found non-runtime. Show any runtime uses above it.

src/runtime/pprof/protomem_test.go

+59-1
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ func locationToStrings(loc *profile.Location, funcs []string) []string {
118118
return funcs
119119
}
120120

121-
// This is a regression test for https://go.dev/issue/64528 .
121+
// This is a regression test for https://go.dev/issue/64528.
122122
func TestGenericsHashKeyInPprofBuilder(t *testing.T) {
123123
if asan.Enabled {
124124
t.Skip("extra allocations with -asan throw off the test; see #70079")
@@ -229,3 +229,61 @@ func TestGenericsInlineLocations(t *testing.T) {
229229
t.Errorf("expected a location with at least 3 functions\n%s\ngot\n%s\n", expectedLocation, actual)
230230
}
231231
}
232+
233+
func growMap() {
234+
m := make(map[int]int)
235+
for i := range 512 {
236+
m[i] = i
237+
}
238+
}
239+
240+
// Runtime frames are hidden in heap profiles.
241+
// This is a regression test for https://go.dev/issue/71174.
242+
func TestHeapRuntimeFrames(t *testing.T) {
243+
previousRate := runtime.MemProfileRate
244+
runtime.MemProfileRate = 1
245+
defer func() {
246+
runtime.MemProfileRate = previousRate
247+
}()
248+
249+
growMap()
250+
251+
runtime.GC()
252+
buf := bytes.NewBuffer(nil)
253+
if err := WriteHeapProfile(buf); err != nil {
254+
t.Fatalf("writing profile: %v", err)
255+
}
256+
p, err := profile.Parse(buf)
257+
if err != nil {
258+
t.Fatalf("profile.Parse: %v", err)
259+
}
260+
261+
actual := profileToStrings(p)
262+
263+
// We must see growMap at least once.
264+
foundGrowMap := false
265+
for _, l := range actual {
266+
if !strings.Contains(l, "runtime/pprof.growMap") {
267+
continue
268+
}
269+
foundGrowMap = true
270+
271+
// Runtime frames like mapassign and map internals should be hidden.
272+
if strings.Contains(l, "runtime.") {
273+
t.Errorf("Sample got %s, want no runtime frames", l)
274+
}
275+
if strings.Contains(l, "internal/runtime/") {
276+
t.Errorf("Sample got %s, want no runtime frames", l)
277+
}
278+
if strings.Contains(l, "runtime/internal/") {
279+
t.Errorf("Sample got %s, want no runtime frames", l)
280+
}
281+
if strings.Contains(l, "mapassign") { // in case mapassign moves to a package not matching above paths.
282+
t.Errorf("Sample got %s, want no mapassign frames", l)
283+
}
284+
}
285+
286+
if !foundGrowMap {
287+
t.Errorf("Profile got:\n%s\nwant sample in runtime/pprof.growMap", strings.Join(actual, "\n"))
288+
}
289+
}

0 commit comments

Comments
 (0)