1
2
3
4
5
6
7
8 package pprof
9
10 import (
11 "bytes"
12 "fmt"
13 "internal/profile"
14 "reflect"
15 "regexp"
16 "runtime"
17 "testing"
18 "unsafe"
19 )
20
21 var memSink interface{}
22
23 func allocateTransient1M() {
24 for i := 0; i < 1024; i++ {
25 memSink = &struct{ x [1024]byte }{}
26 }
27 }
28
29
30 func allocateTransient2M() {
31 memSink = make([]byte, 2<<20)
32 }
33
34 func allocateTransient2MInline() {
35 memSink = make([]byte, 2<<20)
36 }
37
38 type Obj32 struct {
39 link *Obj32
40 pad [32 - unsafe.Sizeof(uintptr(0))]byte
41 }
42
43 var persistentMemSink *Obj32
44
45 func allocatePersistent1K() {
46 for i := 0; i < 32; i++ {
47
48 obj := &Obj32{link: persistentMemSink}
49 persistentMemSink = obj
50 }
51 }
52
53
54
55 func allocateReflectTransient() {
56 memSink = make([]byte, 2<<20)
57 }
58
59 func allocateReflect() {
60 rv := reflect.ValueOf(allocateReflectTransient)
61 rv.Call(nil)
62 }
63
64 var memoryProfilerRun = 0
65
66 func TestMemoryProfiler(t *testing.T) {
67
68 oldRate := runtime.MemProfileRate
69 runtime.MemProfileRate = 1
70 defer func() {
71 runtime.MemProfileRate = oldRate
72 }()
73
74
75 for i := 0; i < 1024; i++ {
76 memSink = make([]byte, 1024)
77 }
78
79
80 allocateTransient1M()
81 allocateTransient2M()
82 allocateTransient2MInline()
83 allocatePersistent1K()
84 allocateReflect()
85 memSink = nil
86
87 runtime.GC()
88
89
90
91
92
93
94
95
96
97
98 runtime.GC()
99
100 memoryProfilerRun++
101
102 tests := []struct {
103 stk []string
104 legacy string
105 }{{
106 stk: []string{"runtime/pprof.allocatePersistent1K", "runtime/pprof.TestMemoryProfiler"},
107 legacy: fmt.Sprintf(`%v: %v \[%v: %v\] @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
108 # 0x[0-9,a-f]+ runtime/pprof\.allocatePersistent1K\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test\.go:48
109 # 0x[0-9,a-f]+ runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test\.go:83
110 `, 32*memoryProfilerRun, 1024*memoryProfilerRun, 32*memoryProfilerRun, 1024*memoryProfilerRun),
111 }, {
112 stk: []string{"runtime/pprof.allocateTransient1M", "runtime/pprof.TestMemoryProfiler"},
113 legacy: fmt.Sprintf(`0: 0 \[%v: %v\] @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
114 # 0x[0-9,a-f]+ runtime/pprof\.allocateTransient1M\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:25
115 # 0x[0-9,a-f]+ runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:80
116 `, (1<<10)*memoryProfilerRun, (1<<20)*memoryProfilerRun),
117 }, {
118 stk: []string{"runtime/pprof.allocateTransient2M", "runtime/pprof.TestMemoryProfiler"},
119 legacy: fmt.Sprintf(`0: 0 \[%v: %v\] @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
120 # 0x[0-9,a-f]+ runtime/pprof\.allocateTransient2M\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:31
121 # 0x[0-9,a-f]+ runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:81
122 `, memoryProfilerRun, (2<<20)*memoryProfilerRun),
123 }, {
124 stk: []string{"runtime/pprof.allocateTransient2MInline", "runtime/pprof.TestMemoryProfiler"},
125 legacy: fmt.Sprintf(`0: 0 \[%v: %v\] @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
126 # 0x[0-9,a-f]+ runtime/pprof\.allocateTransient2MInline\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:35
127 # 0x[0-9,a-f]+ runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:82
128 `, memoryProfilerRun, (2<<20)*memoryProfilerRun),
129 }, {
130 stk: []string{"runtime/pprof.allocateReflectTransient"},
131 legacy: fmt.Sprintf(`0: 0 \[%v: %v\] @( 0x[0-9,a-f]+)+
132 # 0x[0-9,a-f]+ runtime/pprof\.allocateReflectTransient\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:56
133 `, memoryProfilerRun, (2<<20)*memoryProfilerRun),
134 }}
135
136 t.Run("debug=1", func(t *testing.T) {
137 var buf bytes.Buffer
138 if err := Lookup("heap").WriteTo(&buf, 1); err != nil {
139 t.Fatalf("failed to write heap profile: %v", err)
140 }
141
142 for _, test := range tests {
143 if !regexp.MustCompile(test.legacy).Match(buf.Bytes()) {
144 t.Fatalf("The entry did not match:\n%v\n\nProfile:\n%v\n", test.legacy, buf.String())
145 }
146 }
147 })
148
149 t.Run("proto", func(t *testing.T) {
150 var buf bytes.Buffer
151 if err := Lookup("heap").WriteTo(&buf, 0); err != nil {
152 t.Fatalf("failed to write heap profile: %v", err)
153 }
154 p, err := profile.Parse(&buf)
155 if err != nil {
156 t.Fatalf("failed to parse heap profile: %v", err)
157 }
158 t.Logf("Profile = %v", p)
159
160 stks := stacks(p)
161 for _, test := range tests {
162 if !containsStack(stks, test.stk) {
163 t.Fatalf("No matching stack entry for %q\n\nProfile:\n%v\n", test.stk, p)
164 }
165 }
166
167 if !containsInlinedCall(TestMemoryProfiler, 4<<10) {
168 t.Logf("Can't determine whether allocateTransient2MInline was inlined into TestMemoryProfiler.")
169 return
170 }
171
172
173 for _, loc := range p.Location {
174 inlinedCaller, inlinedCallee := false, false
175 for _, line := range loc.Line {
176 if line.Function.Name == "runtime/pprof.allocateTransient2MInline" {
177 inlinedCallee = true
178 }
179 if inlinedCallee && line.Function.Name == "runtime/pprof.TestMemoryProfiler" {
180 inlinedCaller = true
181 }
182 }
183 if inlinedCallee != inlinedCaller {
184 t.Errorf("want allocateTransient2MInline after TestMemoryProfiler in one location, got separate location entries:\n%v", loc)
185 }
186 }
187 })
188 }
189
View as plain text