github.com/code-reading/golang@v0.0.0-20220303082512-ba5bc0e589a3/go/src/runtime/pprof/mprof_test.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build !js 6 // +build !js 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 //go:noinline 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 // Can't use slice because that will introduce implicit allocations. 48 obj := &Obj32{link: persistentMemSink} 49 persistentMemSink = obj 50 } 51 } 52 53 // Allocate transient memory using reflect.Call. 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 // Disable sampling, otherwise it's difficult to assert anything. 68 oldRate := runtime.MemProfileRate 69 runtime.MemProfileRate = 1 70 defer func() { 71 runtime.MemProfileRate = oldRate 72 }() 73 74 // Allocate a meg to ensure that mcache.nextSample is updated to 1. 75 for i := 0; i < 1024; i++ { 76 memSink = make([]byte, 1024) 77 } 78 79 // Do the interesting allocations. 80 allocateTransient1M() 81 allocateTransient2M() 82 allocateTransient2MInline() 83 allocatePersistent1K() 84 allocateReflect() 85 memSink = nil 86 87 runtime.GC() // materialize stats 88 89 // TODO(mknyszek): Fix #45315 and remove this extra call. 90 // 91 // Unfortunately, it's possible for the sweep termination condition 92 // to flap, so with just one runtime.GC call, a freed object could be 93 // missed, leading this test to fail. A second call reduces the chance 94 // of this happening to zero, because sweeping actually has to finish 95 // to move on to the next GC, during which nothing will happen. 96 // 97 // See #46500 for more details. 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 // Check the inlined function location is encoded correctly. 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 }