github.com/guyezi/gofrontend@v0.0.0-20200228202240-7a62a49e62c0/libgo/go/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 "reflect" 13 "regexp" 14 "runtime" 15 "runtime/pprof/internal/profile" 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, 4<<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, 3<<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.next_sample 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{"pprof.allocatePersistent1K", "runtime/pprof.TestMemoryProfiler"}, 95 legacy: fmt.Sprintf(`%v: %v \[%v: %v\] @ 0x[0-9,a-f x]+ 96 # 0x[0-9,a-f]+ pprof\.allocatePersistent1K\+0x[0-9,a-f]+ .*/mprof_test\.go:47 97 # 0x[0-9,a-f]+ runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/mprof_test\.go:82 98 `, 32*memoryProfilerRun, 1024*memoryProfilerRun, 32*memoryProfilerRun, 1024*memoryProfilerRun), 99 }, { 100 stk: []string{"pprof.allocateTransient1M", "runtime/pprof.TestMemoryProfiler"}, 101 legacy: fmt.Sprintf(`(0|%v): (0|%v) \[%v: %v\] @ 0x[0-9,a-f x]+ 102 # 0x[0-9,a-f]+ pprof\.allocateTransient1M\+0x[0-9,a-f]+ .*/mprof_test.go:24 103 # 0x[0-9,a-f]+ runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/mprof_test.go:79 104 `, (1<<10)*memoryProfilerRun, (1<<20)*memoryProfilerRun, (1<<10)*memoryProfilerRun, (1<<20)*memoryProfilerRun), 105 }, { 106 stk: []string{"pprof.allocateTransient2M", "runtime/pprof.TestMemoryProfiler"}, 107 // This should start with "0: 0" but gccgo's imprecise 108 // GC means that sometimes the value is not collected. 109 legacy: fmt.Sprintf(`(0|%v): (0|%v) \[%v: %v\] @ 0x[0-9,a-f x]+ 110 # 0x[0-9,a-f]+ pprof\.allocateTransient2M\+0x[0-9,a-f]+ .*/mprof_test.go:30 111 # 0x[0-9,a-f]+ runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/mprof_test.go:80 112 `, memoryProfilerRun, (2<<20)*memoryProfilerRun, memoryProfilerRun, (2<<20)*memoryProfilerRun), 113 }, { 114 stk: []string{"pprof.allocateTransient2MInline", "runtime/pprof.TestMemoryProfiler"}, 115 legacy: fmt.Sprintf(`(0|%v): (0|%v) \[%v: %v\] @ 0x[0-9,a-f x]+ 116 # 0x[0-9,a-f]+ pprof\.allocateTransient2MInline\+0x[0-9,a-f]+ .*/mprof_test.go:34 117 # 0x[0-9,a-f]+ runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/mprof_test.go:81 118 `, memoryProfilerRun, (4<<20)*memoryProfilerRun, memoryProfilerRun, (4<<20)*memoryProfilerRun), 119 }, { 120 stk: []string{"pprof.allocateReflectTransient"}, 121 legacy: fmt.Sprintf(`(0|%v): (0|%v) \[%v: %v\] @( 0x[0-9,a-f]+)+ 122 # 0x[0-9,a-f]+ pprof\.allocateReflectTransient\+0x[0-9,a-f]+ .*/mprof_test.go:55 123 `, memoryProfilerRun, (3<<20)*memoryProfilerRun, memoryProfilerRun, (3<<20)*memoryProfilerRun), 124 }} 125 126 t.Run("debug=1", func(t *testing.T) { 127 var buf bytes.Buffer 128 if err := Lookup("heap").WriteTo(&buf, 1); err != nil { 129 t.Fatalf("failed to write heap profile: %v", err) 130 } 131 132 for _, test := range tests { 133 if !regexp.MustCompile(test.legacy).Match(buf.Bytes()) { 134 t.Fatalf("The entry did not match:\n%v\n\nProfile:\n%v\n", test.legacy, buf.String()) 135 } 136 } 137 }) 138 139 t.Run("proto", func(t *testing.T) { 140 var buf bytes.Buffer 141 if err := Lookup("heap").WriteTo(&buf, 0); err != nil { 142 t.Fatalf("failed to write heap profile: %v", err) 143 } 144 p, err := profile.Parse(&buf) 145 if err != nil { 146 t.Fatalf("failed to parse heap profile: %v", err) 147 } 148 t.Logf("Profile = %v", p) 149 150 stks := stacks(p) 151 for _, test := range tests { 152 if !containsStack(stks, test.stk) { 153 t.Logf("stks:\n%v", stks) 154 t.Fatalf("No matching stack entry for %q\n\nProfile:\n%v\n", test.stk, p) 155 } 156 } 157 158 if !containsInlinedCall(TestMemoryProfiler, 4<<10) { 159 t.Logf("Can't determine whether allocateTransient2MInline was inlined into TestMemoryProfiler.") 160 return 161 } 162 163 // Check the inlined function location is encoded correctly. 164 for _, loc := range p.Location { 165 inlinedCaller, inlinedCallee := false, false 166 for _, line := range loc.Line { 167 if line.Function.Name == "runtime/pprof.allocateTransient2MInline" { 168 inlinedCallee = true 169 } 170 if inlinedCallee && line.Function.Name == "runtime/pprof.TestMemoryProfiler" { 171 inlinedCaller = true 172 } 173 } 174 if inlinedCallee != inlinedCaller { 175 t.Errorf("want allocateTransient2MInline after TestMemoryProfiler in one location, got separate location entries:\n%v", loc) 176 } 177 } 178 }) 179 }