Home | History | Annotate | Download | only in pprof
      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