Skip to content

Commit d377670

Browse files
committed
fix(godeltaprof): port fix for golang/go#64641
1 parent 81ae45c commit d377670

File tree

2 files changed

+100
-14
lines changed

2 files changed

+100
-14
lines changed

godeltaprof/compat/generics_go21_test.go

Lines changed: 99 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -200,22 +200,31 @@ func TestMutex(t *testing.T) {
200200
})
201201
}
202202

203-
func profileToString(p *profile.Profile) []string {
203+
func profileToStrings(p *profile.Profile) []string {
204204
var res []string
205205
for _, s := range p.Sample {
206-
var funcs []string
207-
for i := len(s.Location) - 1; i >= 0; i-- {
208-
loc := s.Location[i]
209-
for j := len(loc.Line) - 1; j >= 0; j-- {
210-
line := loc.Line[j]
211-
funcs = append(funcs, line.Function.Name)
212-
}
213-
}
214-
res = append(res, fmt.Sprintf("%s %v", strings.Join(funcs, ";"), s.Value))
206+
res = append(res, sampleToString(s))
215207
}
216208
return res
217209
}
218210

211+
func sampleToString(s *profile.Sample) string {
212+
var funcs []string
213+
for i := len(s.Location) - 1; i >= 0; i-- {
214+
loc := s.Location[i]
215+
funcs = locationToStrings(loc, funcs)
216+
}
217+
return fmt.Sprintf("%s %v", strings.Join(funcs, ";"), s.Value)
218+
}
219+
220+
func locationToStrings(loc *profile.Location, funcs []string) []string {
221+
for j := range loc.Line {
222+
line := loc.Line[len(loc.Line)-1-j]
223+
funcs = append(funcs, line.Function.Name)
224+
}
225+
return funcs
226+
}
227+
219228
// This is a regression test for https://go.dev/issue/64528 .
220229
func TestGenericsHashKeyInPprofBuilder(t *testing.T) {
221230
previousRate := runtime.MemProfileRate
@@ -232,15 +241,15 @@ func TestGenericsHashKeyInPprofBuilder(t *testing.T) {
232241

233242
runtime.GC()
234243
buf := bytes.NewBuffer(nil)
235-
if err := writeHeapProfile(buf); err != nil {
244+
if err := WriteHeapProfile(buf); err != nil {
236245
t.Fatalf("writing profile: %v", err)
237246
}
238247
p, err := profile.Parse(buf)
239248
if err != nil {
240249
t.Fatalf("profile.Parse: %v", err)
241250
}
242251

243-
actual := profileToString(p)
252+
actual := profileToStrings(p)
244253
expected := []string{
245254
"testing.tRunner;github.com/grafana/pyroscope-go/godeltaprof/compat.TestGenericsHashKeyInPprofBuilder;github.com/grafana/pyroscope-go/godeltaprof/compat.genericAllocFunc[go.shape.uint32] [1 128 0 0]",
246255
"testing.tRunner;github.com/grafana/pyroscope-go/godeltaprof/compat.TestGenericsHashKeyInPprofBuilder;github.com/grafana/pyroscope-go/godeltaprof/compat.genericAllocFunc[go.shape.uint32] [1 256 0 0]",
@@ -255,7 +264,84 @@ func TestGenericsHashKeyInPprofBuilder(t *testing.T) {
255264
}
256265
}
257266

258-
func writeHeapProfile(w io.Writer) error {
267+
type opAlloc struct {
268+
buf [128]byte
269+
}
270+
271+
type opCall struct {
272+
}
273+
274+
var sink []byte
275+
276+
func storeAlloc() {
277+
sink = make([]byte, 16)
278+
}
279+
280+
func nonRecursiveGenericAllocFunction[CurrentOp any, OtherOp any](alloc bool) {
281+
if alloc {
282+
storeAlloc()
283+
} else {
284+
nonRecursiveGenericAllocFunction[OtherOp, CurrentOp](true)
285+
}
286+
}
287+
288+
func TestGenericsInlineLocations(t *testing.T) {
289+
if OptimizationOff() {
290+
t.Skip("skipping test with optimizations disabled")
291+
}
292+
293+
previousRate := runtime.MemProfileRate
294+
runtime.MemProfileRate = 1
295+
defer func() {
296+
runtime.MemProfileRate = previousRate
297+
sink = nil
298+
}()
299+
300+
nonRecursiveGenericAllocFunction[opAlloc, opCall](true)
301+
nonRecursiveGenericAllocFunction[opCall, opAlloc](false)
302+
303+
runtime.GC()
304+
305+
buf := bytes.NewBuffer(nil)
306+
if err := WriteHeapProfile(buf); err != nil {
307+
t.Fatalf("writing profile: %v", err)
308+
}
309+
p, err := profile.Parse(buf)
310+
if err != nil {
311+
t.Fatalf("profile.Parse: %v", err)
312+
}
313+
314+
const expectedSample = "testing.tRunner;github.com/grafana/pyroscope-go/godeltaprof/compat.TestGenericsInlineLocations;github.com/grafana/pyroscope-go/godeltaprof/compat.nonRecursiveGenericAllocFunction[go.shape.struct {},go.shape.struct { github.com/grafana/pyroscope-go/godeltaprof/compat.buf [128]uint8 }];github.com/grafana/pyroscope-go/godeltaprof/compat.nonRecursiveGenericAllocFunction[go.shape.struct { github.com/grafana/pyroscope-go/godeltaprof/compat.buf [128]uint8 },go.shape.struct {}];github.com/grafana/pyroscope-go/godeltaprof/compat.storeAlloc [1 16 1 16]"
315+
const expectedLocation = "github.com/grafana/pyroscope-go/godeltaprof/compat.nonRecursiveGenericAllocFunction[go.shape.struct {},go.shape.struct { github.com/grafana/pyroscope-go/godeltaprof/compat.buf [128]uint8 }];github.com/grafana/pyroscope-go/godeltaprof/compat.nonRecursiveGenericAllocFunction[go.shape.struct { github.com/grafana/pyroscope-go/godeltaprof/compat.buf [128]uint8 },go.shape.struct {}];github.com/grafana/pyroscope-go/godeltaprof/compat.storeAlloc"
316+
const expectedLocationNewInliner = "github.com/grafana/pyroscope-go/godeltaprof/compat.TestGenericsInlineLocations;" + expectedLocation
317+
var s *profile.Sample
318+
for _, sample := range p.Sample {
319+
if sampleToString(sample) == expectedSample {
320+
s = sample
321+
break
322+
}
323+
}
324+
if s == nil {
325+
t.Fatalf("expected \n%s\ngot\n%s", expectedSample, strings.Join(profileToStrings(p), "\n"))
326+
}
327+
loc := s.Location[0]
328+
actual := strings.Join(locationToStrings(loc, nil), ";")
329+
if expectedLocation != actual && expectedLocationNewInliner != actual {
330+
t.Errorf("expected a location with at least 3 functions\n%s\ngot\n%s\n", expectedLocation, actual)
331+
}
332+
}
333+
334+
func OptimizationOff() bool {
335+
optimizationMarker := func() uintptr {
336+
pc, _, _, _ := runtime.Caller(0)
337+
return pc
338+
}
339+
pc := optimizationMarker()
340+
f := runtime.FuncForPC(runtime.FuncForPC(pc).Entry())
341+
return f.Name() == "github.com/grafana/pyroscope-go/godeltaprof/compat.OptimizationOff.func1"
342+
}
343+
344+
func WriteHeapProfile(w io.Writer) error {
259345
runtime.GC()
260346
dh := godeltaprof.NewHeapProfilerWithOptions(godeltaprof.ProfileOptions{
261347
GenericsFrames: true,

godeltaprof/internal/pprof/proto.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ func (d *pcDeck) tryAdd(pc uintptr, frames []runtime.Frame, symbolizeResult symb
474474
if last.Entry != newFrame.Entry { // newFrame is for a different function.
475475
return false
476476
}
477-
if last.Function == newFrame.Function { // maybe recursion.
477+
if runtime_FrameSymbolName(&last) == runtime_FrameSymbolName(&newFrame) { // maybe recursion.
478478
return false
479479
}
480480
}

0 commit comments

Comments
 (0)