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/testenv"
12 "runtime"
13 "slices"
14 "strings"
15 "testing"
16 )
17
18 func TestConvertMemProfile(t *testing.T) {
19 addr1, addr2, map1, map2 := testPCs(t)
20
21
22
23
24
25 a1, a2 := uintptr(addr1)+1, uintptr(addr2)+1
26 rate := int64(512 * 1024)
27 rec := []runtime.MemProfileRecord{
28 {AllocBytes: 4096, FreeBytes: 1024, AllocObjects: 4, FreeObjects: 1, Stack0: [32]uintptr{a1, a2}},
29 {AllocBytes: 512 * 1024, FreeBytes: 0, AllocObjects: 1, FreeObjects: 0, Stack0: [32]uintptr{a2 + 1, a2 + 2}},
30 {AllocBytes: 512 * 1024, FreeBytes: 512 * 1024, AllocObjects: 1, FreeObjects: 1, Stack0: [32]uintptr{a1 + 1, a1 + 2, a2 + 3}},
31 }
32
33 periodType := &profile.ValueType{Type: "space", Unit: "bytes"}
34 sampleType := []*profile.ValueType{
35 {Type: "alloc_objects", Unit: "count"},
36 {Type: "alloc_space", Unit: "bytes"},
37 {Type: "inuse_objects", Unit: "count"},
38 {Type: "inuse_space", Unit: "bytes"},
39 }
40 samples := []*profile.Sample{
41 {
42 Value: []int64{2050, 2099200, 1537, 1574400},
43 Location: []*profile.Location{
44 {ID: 1, Mapping: map1, Address: addr1},
45 {ID: 2, Mapping: map2, Address: addr2},
46 },
47 NumLabel: map[string][]int64{"bytes": {1024}},
48 },
49 {
50 Value: []int64{1, 829411, 1, 829411},
51 Location: []*profile.Location{
52 {ID: 3, Mapping: map2, Address: addr2 + 1},
53 {ID: 4, Mapping: map2, Address: addr2 + 2},
54 },
55 NumLabel: map[string][]int64{"bytes": {512 * 1024}},
56 },
57 {
58 Value: []int64{1, 829411, 0, 0},
59 Location: []*profile.Location{
60 {ID: 5, Mapping: map1, Address: addr1 + 1},
61 {ID: 6, Mapping: map1, Address: addr1 + 2},
62 {ID: 7, Mapping: map2, Address: addr2 + 3},
63 },
64 NumLabel: map[string][]int64{"bytes": {512 * 1024}},
65 },
66 }
67 for _, tc := range []struct {
68 name string
69 defaultSampleType string
70 }{
71 {"heap", ""},
72 {"allocs", "alloc_space"},
73 } {
74 t.Run(tc.name, func(t *testing.T) {
75 var buf bytes.Buffer
76 if err := writeHeapProto(&buf, rec, rate, tc.defaultSampleType); err != nil {
77 t.Fatalf("writing profile: %v", err)
78 }
79
80 p, err := profile.Parse(&buf)
81 if err != nil {
82 t.Fatalf("profile.Parse: %v", err)
83 }
84
85 checkProfile(t, p, rate, periodType, sampleType, samples, tc.defaultSampleType)
86 })
87 }
88 }
89
90 func genericAllocFunc[T interface{ uint32 | uint64 }](n int) []T {
91 return make([]T, n)
92 }
93
94 func profileToStrings(p *profile.Profile) []string {
95 var res []string
96 for _, s := range p.Sample {
97 res = append(res, sampleToString(s))
98 }
99 return res
100 }
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
119
120 func TestGenericsHashKeyInPprofBuilder(t *testing.T) {
121 previousRate := runtime.MemProfileRate
122 runtime.MemProfileRate = 1
123 defer func() {
124 runtime.MemProfileRate = previousRate
125 }()
126 for _, sz := range []int{128, 256} {
127 genericAllocFunc[uint32](sz / 4)
128 }
129 for _, sz := range []int{32, 64} {
130 genericAllocFunc[uint64](sz / 8)
131 }
132
133 runtime.GC()
134 buf := bytes.NewBuffer(nil)
135 if err := WriteHeapProfile(buf); err != nil {
136 t.Fatalf("writing profile: %v", err)
137 }
138 p, err := profile.Parse(buf)
139 if err != nil {
140 t.Fatalf("profile.Parse: %v", err)
141 }
142
143 actual := profileToStrings(p)
144 expected := []string{
145 "testing.tRunner;runtime/pprof.TestGenericsHashKeyInPprofBuilder;runtime/pprof.genericAllocFunc[go.shape.uint32] [1 128 0 0]",
146 "testing.tRunner;runtime/pprof.TestGenericsHashKeyInPprofBuilder;runtime/pprof.genericAllocFunc[go.shape.uint32] [1 256 0 0]",
147 "testing.tRunner;runtime/pprof.TestGenericsHashKeyInPprofBuilder;runtime/pprof.genericAllocFunc[go.shape.uint64] [1 32 0 0]",
148 "testing.tRunner;runtime/pprof.TestGenericsHashKeyInPprofBuilder;runtime/pprof.genericAllocFunc[go.shape.uint64] [1 64 0 0]",
149 }
150
151 for _, l := range expected {
152 if !slices.Contains(actual, l) {
153 t.Errorf("profile = %v\nwant = %v", strings.Join(actual, "\n"), l)
154 }
155 }
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\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 }
224
View as plain text