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