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