Skip to content

Commit d95e25e

Browse files
korniltsevprattmic
authored andcommitted
runtime/pprof: fix inlined generics locations
When generic function[a,b] is inlined to the same generic function[b,a] with different types (not recursion) it is expected to get a pprof with a single Location with two functions. However due to incorrect check for generics names using runtime.Frame.Function, the profileBuilder assumes it is a recursion and emits separate Location. This change fixes the recursion check for generics functions by using runtime_expandFinalInlineFrame Fixes #64641 Change-Id: I3f58818f08ee322b281daa377fa421555ad328c9 Reviewed-on: https://go-review.googlesource.com/c/go/+/549135 Run-TryBot: Michael Pratt <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Cherry Mui <[email protected]> Reviewed-by: Michael Pratt <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent cc5ccd7 commit d95e25e

File tree

2 files changed

+89
-12
lines changed

2 files changed

+89
-12
lines changed

src/runtime/pprof/proto.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ func (d *pcDeck) tryAdd(pc uintptr, frames []runtime.Frame, symbolizeResult symb
561561
if last.Entry != newFrame.Entry { // newFrame is for a different function.
562562
return false
563563
}
564-
if last.Function == newFrame.Function { // maybe recursion.
564+
if runtime_FrameSymbolName(&last) == runtime_FrameSymbolName(&newFrame) { // maybe recursion.
565565
return false
566566
}
567567
}

src/runtime/pprof/protomem_test.go

+88-11
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"bytes"
99
"fmt"
1010
"internal/profile"
11+
"internal/testenv"
1112
"runtime"
1213
"slices"
1314
"strings"
@@ -90,22 +91,31 @@ func genericAllocFunc[T interface{ uint32 | uint64 }](n int) []T {
9091
return make([]T, n)
9192
}
9293

93-
func profileToString(p *profile.Profile) []string {
94+
func profileToStrings(p *profile.Profile) []string {
9495
var res []string
9596
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))
97+
res = append(res, sampleToString(s))
10598
}
10699
return res
107100
}
108101

102+
func sampleToString(s *profile.Sample) string {
103+
var funcs []string
104+
for i := len(s.Location) - 1; i >= 0; i-- {
105+
loc := s.Location[i]
106+
funcs = locationToStrings(loc, funcs)
107+
}
108+
return fmt.Sprintf("%s %v", strings.Join(funcs, ";"), s.Value)
109+
}
110+
111+
func locationToStrings(loc *profile.Location, funcs []string) []string {
112+
for j := range loc.Line {
113+
line := loc.Line[len(loc.Line)-1-j]
114+
funcs = append(funcs, line.Function.Name)
115+
}
116+
return funcs
117+
}
118+
109119
// This is a regression test for https://go.dev/issue/64528 .
110120
func TestGenericsHashKeyInPprofBuilder(t *testing.T) {
111121
previousRate := runtime.MemProfileRate
@@ -130,7 +140,7 @@ func TestGenericsHashKeyInPprofBuilder(t *testing.T) {
130140
t.Fatalf("profile.Parse: %v", err)
131141
}
132142

133-
actual := profileToString(p)
143+
actual := profileToStrings(p)
134144
expected := []string{
135145
"testing.tRunner;runtime/pprof.TestGenericsHashKeyInPprofBuilder;runtime/pprof.genericAllocFunc[go.shape.uint32] [1 128 0 0]",
136146
"testing.tRunner;runtime/pprof.TestGenericsHashKeyInPprofBuilder;runtime/pprof.genericAllocFunc[go.shape.uint32] [1 256 0 0]",
@@ -144,3 +154,70 @@ func TestGenericsHashKeyInPprofBuilder(t *testing.T) {
144154
}
145155
}
146156
}
157+
158+
type opAlloc struct {
159+
buf [128]byte
160+
}
161+
162+
type opCall struct {
163+
}
164+
165+
var sink []byte
166+
167+
func storeAlloc() {
168+
sink = make([]byte, 16)
169+
}
170+
171+
func nonRecursiveGenericAllocFunction[CurrentOp any, OtherOp any](alloc bool) {
172+
if alloc {
173+
storeAlloc()
174+
} else {
175+
nonRecursiveGenericAllocFunction[OtherOp, CurrentOp](true)
176+
}
177+
}
178+
179+
func TestGenericsInlineLocations(t *testing.T) {
180+
if testenv.OptimizationOff() {
181+
t.Skip("skipping test with optimizations disabled")
182+
}
183+
184+
previousRate := runtime.MemProfileRate
185+
runtime.MemProfileRate = 1
186+
defer func() {
187+
runtime.MemProfileRate = previousRate
188+
sink = nil
189+
}()
190+
191+
nonRecursiveGenericAllocFunction[opAlloc, opCall](true)
192+
nonRecursiveGenericAllocFunction[opCall, opAlloc](false)
193+
194+
runtime.GC()
195+
196+
buf := bytes.NewBuffer(nil)
197+
if err := WriteHeapProfile(buf); err != nil {
198+
t.Fatalf("writing profile: %v", err)
199+
}
200+
p, err := profile.Parse(buf)
201+
if err != nil {
202+
t.Fatalf("profile.Parse: %v", err)
203+
}
204+
205+
const expectedSample = "testing.tRunner;runtime/pprof.TestGenericsInlineLocations;runtime/pprof.nonRecursiveGenericAllocFunction[go.shape.struct {},go.shape.struct { runtime/pprof.buf [128]uint8 }];runtime/pprof.nonRecursiveGenericAllocFunction[go.shape.struct { runtime/pprof.buf [128]uint8 },go.shape.struct {}];runtime/pprof.storeAlloc [1 16 1 16]"
206+
const expectedLocation = "runtime/pprof.nonRecursiveGenericAllocFunction[go.shape.struct {},go.shape.struct { runtime/pprof.buf [128]uint8 }];runtime/pprof.nonRecursiveGenericAllocFunction[go.shape.struct { runtime/pprof.buf [128]uint8 },go.shape.struct {}];runtime/pprof.storeAlloc"
207+
const expectedLocationNewInliner = "runtime/pprof.TestGenericsInlineLocations;" + expectedLocation
208+
var s *profile.Sample
209+
for _, sample := range p.Sample {
210+
if sampleToString(sample) == expectedSample {
211+
s = sample
212+
break
213+
}
214+
}
215+
if s == nil {
216+
t.Fatalf("expected \n%s\ngot\n%s", expectedSample, strings.Join(profileToStrings(p), "\n"))
217+
}
218+
loc := s.Location[0]
219+
actual := strings.Join(locationToStrings(loc, nil), ";")
220+
if expectedLocation != actual && expectedLocationNewInliner != actual {
221+
t.Errorf("expected a location with at least 3 functions\n%s\ngot\n%s\n", expectedLocation, actual)
222+
}
223+
}

0 commit comments

Comments
 (0)