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