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 package pprof_test 6 7 import ( 8 "bytes" 9 "fmt" 10 "reflect" 11 "regexp" 12 "runtime" 13 . "runtime/pprof" 14 "testing" 15 "unsafe" 16 ) 17 18 var memSink interface{} 19 20 func allocateTransient1M() { 21 for i := 0; i < 1024; i++ { 22 memSink = &struct{ x [1024]byte }{} 23 } 24 } 25 26 //go:noinline 27 func allocateTransient2M() { 28 memSink = make([]byte, 2<<20) 29 } 30 31 type Obj32 struct { 32 link *Obj32 33 pad [32 - unsafe.Sizeof(uintptr(0))]byte 34 } 35 36 var persistentMemSink *Obj32 37 38 func allocatePersistent1K() { 39 for i := 0; i < 32; i++ { 40 // Can't use slice because that will introduce implicit allocations. 41 obj := &Obj32{link: persistentMemSink} 42 persistentMemSink = obj 43 } 44 } 45 46 // Allocate transient memory using reflect.Call. 47 48 func allocateReflectTransient() { 49 memSink = make([]byte, 2<<20) 50 } 51 52 func allocateReflect() { 53 rv := reflect.ValueOf(allocateReflectTransient) 54 rv.Call(nil) 55 } 56 57 var memoryProfilerRun = 0 58 59 func TestMemoryProfiler(t *testing.T) { 60 // Disable sampling, otherwise it's difficult to assert anything. 61 oldRate := runtime.MemProfileRate 62 runtime.MemProfileRate = 1 63 defer func() { 64 runtime.MemProfileRate = oldRate 65 }() 66 67 // Allocate a meg to ensure that mcache.next_sample is updated to 1. 68 for i := 0; i < 1024; i++ { 69 memSink = make([]byte, 1024) 70 } 71 72 // Do the interesting allocations. 73 allocateTransient1M() 74 allocateTransient2M() 75 allocatePersistent1K() 76 allocateReflect() 77 memSink = nil 78 79 runtime.GC() // materialize stats 80 var buf bytes.Buffer 81 if err := Lookup("heap").WriteTo(&buf, 1); err != nil { 82 t.Fatalf("failed to write heap profile: %v", err) 83 } 84 85 memoryProfilerRun++ 86 87 tests := []string{ 88 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]+ 89 # 0x[0-9,a-f]+ runtime/pprof_test\.allocatePersistent1K\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test\.go:41 90 # 0x[0-9,a-f]+ runtime/pprof_test\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test\.go:75 91 `, 32*memoryProfilerRun, 1024*memoryProfilerRun, 32*memoryProfilerRun, 1024*memoryProfilerRun), 92 93 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]+ 94 # 0x[0-9,a-f]+ runtime/pprof_test\.allocateTransient1M\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:22 95 # 0x[0-9,a-f]+ runtime/pprof_test\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:73 96 `, (1<<10)*memoryProfilerRun, (1<<20)*memoryProfilerRun), 97 98 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]+ 99 # 0x[0-9,a-f]+ runtime/pprof_test\.allocateTransient2M\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:28 100 # 0x[0-9,a-f]+ runtime/pprof_test\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:74 101 `, memoryProfilerRun, (2<<20)*memoryProfilerRun), 102 103 fmt.Sprintf(`0: 0 \[%v: %v\] @( 0x[0-9,a-f]+)+ 104 # 0x[0-9,a-f]+ runtime/pprof_test\.allocateReflectTransient\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:49 105 `, memoryProfilerRun, (2<<20)*memoryProfilerRun), 106 } 107 108 for _, test := range tests { 109 if !regexp.MustCompile(test).Match(buf.Bytes()) { 110 t.Fatalf("The entry did not match:\n%v\n\nProfile:\n%v\n", test, buf.String()) 111 } 112 } 113 } 114