Source file
src/runtime/pprof/protomem_test.go
1
2
3
4
5 package pprof
6
7 import (
8 "bytes"
9 "fmt"
10 "internal/asan"
11 "internal/profile"
12 "internal/profilerecord"
13 "internal/testenv"
14 "runtime"
15 "slices"
16 "strings"
17 "testing"
18 )
19
20 func TestConvertMemProfile(t *testing.T) {
21 addr1, addr2, map1, map2 := testPCs(t)
22
23
24
25
26
27 a1, a2 := uintptr(addr1)+1, uintptr(addr2)+1
28 rate := int64(512 * 1024)
29 rec := []profilerecord.MemProfileRecord{
30 {AllocBytes: 4096, FreeBytes: 1024, AllocObjects: 4, FreeObjects: 1, Stack: []uintptr{a1, a2}},
31 {AllocBytes: 512 * 1024, FreeBytes: 0, AllocObjects: 1, FreeObjects: 0, Stack: []uintptr{a2 + 1, a2 + 2}},
32 {AllocBytes: 512 * 1024, FreeBytes: 512 * 1024, AllocObjects: 1, FreeObjects: 1, Stack: []uintptr{a1 + 1, a1 + 2, a2 + 3}},
33 }
34
35 periodType := &profile.ValueType{Type: "space", Unit: "bytes"}
36 sampleType := []*profile.ValueType{
37 {Type: "alloc_objects", Unit: "count"},
38 {Type: "alloc_space", Unit: "bytes"},
39 {Type: "inuse_objects", Unit: "count"},
40 {Type: "inuse_space", Unit: "bytes"},
41 }
42 samples := []*profile.Sample{
43 {
44 Value: []int64{2050, 2099200, 1537, 1574400},
45 Location: []*profile.Location{
46 {ID: 1, Mapping: map1, Address: addr1},
47 {ID: 2, Mapping: map2, Address: addr2},
48 },
49 NumLabel: map[string][]int64{"bytes": {1024}},
50 },
51 {
52 Value: []int64{1, 829411, 1, 829411},
53 Location: []*profile.Location{
54 {ID: 3, Mapping: map2, Address: addr2 + 1},
55 {ID: 4, Mapping: map2, Address: addr2 + 2},
56 },
57 NumLabel: map[string][]int64{"bytes": {512 * 1024}},
58 },
59 {
60 Value: []int64{1, 829411, 0, 0},
61 Location: []*profile.Location{
62 {ID: 5, Mapping: map1, Address: addr1 + 1},
63 {ID: 6, Mapping: map1, Address: addr1 + 2},
64 {ID: 7, Mapping: map2, Address: addr2 + 3},
65 },
66 NumLabel: map[string][]int64{"bytes": {512 * 1024}},
67 },
68 }
69 for _, tc := range []struct {
70 name string
71 defaultSampleType string
72 }{
73 {"heap", ""},
74 {"allocs", "alloc_space"},
75 } {
76 t.Run(tc.name, func(t *testing.T) {
77 var buf bytes.Buffer
78 if err := writeHeapProto(&buf, rec, rate, tc.defaultSampleType); err != nil {
79 t.Fatalf("writing profile: %v", err)
80 }
81
82 p, err := profile.Parse(&buf)
83 if err != nil {
84 t.Fatalf("profile.Parse: %v", err)
85 }
86
87 checkProfile(t, p, rate, periodType, sampleType, samples, tc.defaultSampleType)
88 })
89 }
90 }
91
92 func genericAllocFunc[T interface{ uint32 | uint64 }](n int) []T {
93 return make([]T, n)
94 }
95
96 func profileToStrings(p *profile.Profile) []string {
97 var res []string
98 for _, s := range p.Sample {
99 res = append(res, sampleToString(s))
100 }
101 return res
102 }
103
104 func sampleToString(s *profile.Sample) string {
105 var funcs []string
106 for i := len(s.Location) - 1; i >= 0; i-- {
107 loc := s.Location[i]
108 funcs = locationToStrings(loc, funcs)
109 }
110 return fmt.Sprintf("%s %v", strings.Join(funcs, ";"), s.Value)
111 }
112
113 func locationToStrings(loc *profile.Location, funcs []string) []string {
114 for j := range loc.Line {
115 line := loc.Line[len(loc.Line)-1-j]
116 funcs = append(funcs, line.Function.Name)
117 }
118 return funcs
119 }
120
121
122 func TestGenericsHashKeyInPprofBuilder(t *testing.T) {
123 if asan.Enabled {
124 t.Skip("extra allocations with -asan throw off the test; see #70079")
125 }
126 previousRate := runtime.MemProfileRate
127 runtime.MemProfileRate = 1
128 defer func() {
129 runtime.MemProfileRate = previousRate
130 }()
131 for _, sz := range []int{128, 256} {
132 genericAllocFunc[uint32](sz / 4)
133 }
134 for _, sz := range []int{32, 64} {
135 genericAllocFunc[uint64](sz / 8)
136 }
137
138 runtime.GC()
139 buf := bytes.NewBuffer(nil)
140 if err := WriteHeapProfile(buf); err != nil {
141 t.Fatalf("writing profile: %v", err)
142 }
143 p, err := profile.Parse(buf)
144 if err != nil {
145 t.Fatalf("profile.Parse: %v", err)
146 }
147
148 actual := profileToStrings(p)
149 expected := []string{
150 "testing.tRunner;runtime/pprof.TestGenericsHashKeyInPprofBuilder;runtime/pprof.genericAllocFunc[go.shape.uint32] [1 128 0 0]",
151 "testing.tRunner;runtime/pprof.TestGenericsHashKeyInPprofBuilder;runtime/pprof.genericAllocFunc[go.shape.uint32] [1 256 0 0]",
152 "testing.tRunner;runtime/pprof.TestGenericsHashKeyInPprofBuilder;runtime/pprof.genericAllocFunc[go.shape.uint64] [1 32 0 0]",
153 "testing.tRunner;runtime/pprof.TestGenericsHashKeyInPprofBuilder;runtime/pprof.genericAllocFunc[go.shape.uint64] [1 64 0 0]",
154 }
155
156 for _, l := range expected {
157 if !slices.Contains(actual, l) {
158 t.Errorf("profile = %v\nwant = %v", strings.Join(actual, "\n"), l)
159 }
160 }
161 }
162
163 type opAlloc struct {
164 buf [128]byte
165 }
166
167 type opCall struct {
168 }
169
170 var sink []byte
171
172 func storeAlloc() {
173 sink = make([]byte, 16)
174 }
175
176 func nonRecursiveGenericAllocFunction[CurrentOp any, OtherOp any](alloc bool) {
177 if alloc {
178 storeAlloc()
179 } else {
180 nonRecursiveGenericAllocFunction[OtherOp, CurrentOp](true)
181 }
182 }
183
184 func TestGenericsInlineLocations(t *testing.T) {
185 if asan.Enabled {
186 t.Skip("extra allocations with -asan throw off the test; see #70079")
187 }
188 if testenv.OptimizationOff() {
189 t.Skip("skipping test with optimizations disabled")
190 }
191
192 previousRate := runtime.MemProfileRate
193 runtime.MemProfileRate = 1
194 defer func() {
195 runtime.MemProfileRate = previousRate
196 sink = nil
197 }()
198
199 nonRecursiveGenericAllocFunction[opAlloc, opCall](true)
200 nonRecursiveGenericAllocFunction[opCall, opAlloc](false)
201
202 runtime.GC()
203
204 buf := bytes.NewBuffer(nil)
205 if err := WriteHeapProfile(buf); err != nil {
206 t.Fatalf("writing profile: %v", err)
207 }
208 p, err := profile.Parse(buf)
209 if err != nil {
210 t.Fatalf("profile.Parse: %v", err)
211 }
212
213 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]"
214 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"
215 const expectedLocationNewInliner = "runtime/pprof.TestGenericsInlineLocations;" + expectedLocation
216 var s *profile.Sample
217 for _, sample := range p.Sample {
218 if sampleToString(sample) == expectedSample {
219 s = sample
220 break
221 }
222 }
223 if s == nil {
224 t.Fatalf("expected \n%s\ngot\n%s", expectedSample, strings.Join(profileToStrings(p), "\n"))
225 }
226 loc := s.Location[0]
227 actual := strings.Join(locationToStrings(loc, nil), ";")
228 if expectedLocation != actual && expectedLocationNewInliner != actual {
229 t.Errorf("expected a location with at least 3 functions\n%s\ngot\n%s\n", expectedLocation, actual)
230 }
231 }
232
View as plain text