8
8
"bytes"
9
9
"fmt"
10
10
"internal/profile"
11
+ "internal/testenv"
11
12
"runtime"
12
13
"slices"
13
14
"strings"
@@ -90,22 +91,31 @@ func genericAllocFunc[T interface{ uint32 | uint64 }](n int) []T {
90
91
return make ([]T , n )
91
92
}
92
93
93
- func profileToString (p * profile.Profile ) []string {
94
+ func profileToStrings (p * profile.Profile ) []string {
94
95
var res []string
95
96
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 ))
105
98
}
106
99
return res
107
100
}
108
101
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
+
109
119
// This is a regression test for https://go.dev/issue/64528 .
110
120
func TestGenericsHashKeyInPprofBuilder (t * testing.T ) {
111
121
previousRate := runtime .MemProfileRate
@@ -130,7 +140,7 @@ func TestGenericsHashKeyInPprofBuilder(t *testing.T) {
130
140
t .Fatalf ("profile.Parse: %v" , err )
131
141
}
132
142
133
- actual := profileToString (p )
143
+ actual := profileToStrings (p )
134
144
expected := []string {
135
145
"testing.tRunner;runtime/pprof.TestGenericsHashKeyInPprofBuilder;runtime/pprof.genericAllocFunc[go.shape.uint32] [1 128 0 0]" ,
136
146
"testing.tRunner;runtime/pprof.TestGenericsHashKeyInPprofBuilder;runtime/pprof.genericAllocFunc[go.shape.uint32] [1 256 0 0]" ,
@@ -144,3 +154,70 @@ func TestGenericsHashKeyInPprofBuilder(t *testing.T) {
144
154
}
145
155
}
146
156
}
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\n got\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\n got\n %s\n " , expectedLocation , actual )
222
+ }
223
+ }
0 commit comments