...

Source file src/runtime/pprof/mprof_test.go

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

View as plain text