Skip to content

Commit 20a03fc

Browse files
korniltsevcherrymui
authored andcommitted
runtime/pprof: fix generics function names
profileBuilder is using Frame->Function as key for checking if we already emitted a function. However for generics functions it has dots there [...], so sometimes for different functions with different generics types, the profileBuilder emits wrong functions. Fixes #64528 Change-Id: I8b39245e0b18f4288ce758c912c6748f87cba39a Reviewed-on: https://go-review.googlesource.com/c/go/+/546815 Reviewed-by: Cherry Mui <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Michael Pratt <[email protected]>
1 parent 052d402 commit 20a03fc

File tree

2 files changed

+66
-3
lines changed

2 files changed

+66
-3
lines changed

src/runtime/pprof/proto.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -611,13 +611,14 @@ func (b *profileBuilder) emitLocation() uint64 {
611611
b.pb.uint64Opt(tagLocation_Address, uint64(firstFrame.PC))
612612
for _, frame := range b.deck.frames {
613613
// Write out each line in frame expansion.
614-
funcID := uint64(b.funcs[frame.Function])
614+
funcName := runtime_FrameSymbolName(&frame)
615+
funcID := uint64(b.funcs[funcName])
615616
if funcID == 0 {
616617
funcID = uint64(len(b.funcs)) + 1
617-
b.funcs[frame.Function] = int(funcID)
618+
b.funcs[funcName] = int(funcID)
618619
newFuncs = append(newFuncs, newFunc{
619620
id: funcID,
620-
name: runtime_FrameSymbolName(&frame),
621+
name: funcName,
621622
file: frame.File,
622623
startLine: int64(runtime_FrameStartLine(&frame)),
623624
})

src/runtime/pprof/protomem_test.go

+62
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ package pprof
66

77
import (
88
"bytes"
9+
"fmt"
910
"internal/profile"
1011
"runtime"
12+
"slices"
13+
"strings"
1114
"testing"
1215
)
1316

@@ -82,3 +85,62 @@ func TestConvertMemProfile(t *testing.T) {
8285
})
8386
}
8487
}
88+
89+
func genericAllocFunc[T interface{ uint32 | uint64 }](n int) []T {
90+
return make([]T, n)
91+
}
92+
93+
func profileToString(p *profile.Profile) []string {
94+
var res []string
95+
for _, s := range p.Sample {
96+
var funcs []string
97+
for i := len(s.Location) - 1; i >= 0; i-- {
98+
loc := s.Location[i]
99+
for j := len(loc.Line) - 1; j >= 0; j-- {
100+
line := loc.Line[j]
101+
funcs = append(funcs, line.Function.Name)
102+
}
103+
}
104+
res = append(res, fmt.Sprintf("%s %v", strings.Join(funcs, ";"), s.Value))
105+
}
106+
return res
107+
}
108+
109+
// This is a regression test for https://go.dev/issue/64528 .
110+
func TestGenericsHashKeyInPprofBuilder(t *testing.T) {
111+
previousRate := runtime.MemProfileRate
112+
runtime.MemProfileRate = 1
113+
defer func() {
114+
runtime.MemProfileRate = previousRate
115+
}()
116+
for _, sz := range []int{128, 256} {
117+
genericAllocFunc[uint32](sz / 4)
118+
}
119+
for _, sz := range []int{32, 64} {
120+
genericAllocFunc[uint64](sz / 8)
121+
}
122+
123+
runtime.GC()
124+
buf := bytes.NewBuffer(nil)
125+
if err := WriteHeapProfile(buf); err != nil {
126+
t.Fatalf("writing profile: %v", err)
127+
}
128+
p, err := profile.Parse(buf)
129+
if err != nil {
130+
t.Fatalf("profile.Parse: %v", err)
131+
}
132+
133+
actual := profileToString(p)
134+
expected := []string{
135+
"testing.tRunner;runtime/pprof.TestGenericsHashKeyInPprofBuilder;runtime/pprof.genericAllocFunc[go.shape.uint32] [1 128 0 0]",
136+
"testing.tRunner;runtime/pprof.TestGenericsHashKeyInPprofBuilder;runtime/pprof.genericAllocFunc[go.shape.uint32] [1 256 0 0]",
137+
"testing.tRunner;runtime/pprof.TestGenericsHashKeyInPprofBuilder;runtime/pprof.genericAllocFunc[go.shape.uint64] [1 32 0 0]",
138+
"testing.tRunner;runtime/pprof.TestGenericsHashKeyInPprofBuilder;runtime/pprof.genericAllocFunc[go.shape.uint64] [1 64 0 0]",
139+
}
140+
141+
for _, l := range expected {
142+
if !slices.Contains(actual, l) {
143+
t.Errorf("profile = %v\nwant = %v", strings.Join(actual, "\n"), l)
144+
}
145+
}
146+
}

0 commit comments

Comments
 (0)