github.com/gidoBOSSftw5731/go/src@v0.0.0-20210226122457-d24b0edbf019/runtime/pprof/pprof_test.go (about)

     1  // Copyright 2011 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  	"context"
    13  	"fmt"
    14  	"internal/profile"
    15  	"internal/race"
    16  	"internal/testenv"
    17  	"io"
    18  	"math/big"
    19  	"os"
    20  	"os/exec"
    21  	"regexp"
    22  	"runtime"
    23  	"strings"
    24  	"sync"
    25  	"sync/atomic"
    26  	"testing"
    27  	"time"
    28  )
    29  
    30  func cpuHogger(f func(x int) int, y *int, dur time.Duration) {
    31  	// We only need to get one 100 Hz clock tick, so we've got
    32  	// a large safety buffer.
    33  	// But do at least 500 iterations (which should take about 100ms),
    34  	// otherwise TestCPUProfileMultithreaded can fail if only one
    35  	// thread is scheduled during the testing period.
    36  	t0 := time.Now()
    37  	accum := *y
    38  	for i := 0; i < 500 || time.Since(t0) < dur; i++ {
    39  		accum = f(accum)
    40  	}
    41  	*y = accum
    42  }
    43  
    44  var (
    45  	salt1 = 0
    46  	salt2 = 0
    47  )
    48  
    49  // The actual CPU hogging function.
    50  // Must not call other functions nor access heap/globals in the loop,
    51  // otherwise under race detector the samples will be in the race runtime.
    52  func cpuHog1(x int) int {
    53  	return cpuHog0(x, 1e5)
    54  }
    55  
    56  func cpuHog0(x, n int) int {
    57  	foo := x
    58  	for i := 0; i < n; i++ {
    59  		if foo > 0 {
    60  			foo *= foo
    61  		} else {
    62  			foo *= foo + 1
    63  		}
    64  	}
    65  	return foo
    66  }
    67  
    68  func cpuHog2(x int) int {
    69  	foo := x
    70  	for i := 0; i < 1e5; i++ {
    71  		if foo > 0 {
    72  			foo *= foo
    73  		} else {
    74  			foo *= foo + 2
    75  		}
    76  	}
    77  	return foo
    78  }
    79  
    80  // Return a list of functions that we don't want to ever appear in CPU
    81  // profiles. For gccgo, that list includes the sigprof handler itself.
    82  func avoidFunctions() []string {
    83  	if runtime.Compiler == "gccgo" {
    84  		return []string{"runtime.sigprof"}
    85  	}
    86  	return nil
    87  }
    88  
    89  func TestCPUProfile(t *testing.T) {
    90  	testCPUProfile(t, stackContains, []string{"runtime/pprof.cpuHog1"}, avoidFunctions(), func(dur time.Duration) {
    91  		cpuHogger(cpuHog1, &salt1, dur)
    92  	})
    93  }
    94  
    95  func TestCPUProfileMultithreaded(t *testing.T) {
    96  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
    97  	testCPUProfile(t, stackContains, []string{"runtime/pprof.cpuHog1", "runtime/pprof.cpuHog2"}, avoidFunctions(), func(dur time.Duration) {
    98  		c := make(chan int)
    99  		go func() {
   100  			cpuHogger(cpuHog1, &salt1, dur)
   101  			c <- 1
   102  		}()
   103  		cpuHogger(cpuHog2, &salt2, dur)
   104  		<-c
   105  	})
   106  }
   107  
   108  // containsInlinedCall reports whether the function body for the function f is
   109  // known to contain an inlined function call within the first maxBytes bytes.
   110  func containsInlinedCall(f interface{}, maxBytes int) bool {
   111  	_, found := findInlinedCall(f, maxBytes)
   112  	return found
   113  }
   114  
   115  // findInlinedCall returns the PC of an inlined function call within
   116  // the function body for the function f if any.
   117  func findInlinedCall(f interface{}, maxBytes int) (pc uint64, found bool) {
   118  	fFunc := runtime.FuncForPC(uintptr(funcPC(f)))
   119  	if fFunc == nil || fFunc.Entry() == 0 {
   120  		panic("failed to locate function entry")
   121  	}
   122  
   123  	for offset := 0; offset < maxBytes; offset++ {
   124  		innerPC := fFunc.Entry() + uintptr(offset)
   125  		inner := runtime.FuncForPC(innerPC)
   126  		if inner == nil {
   127  			// No function known for this PC value.
   128  			// It might simply be misaligned, so keep searching.
   129  			continue
   130  		}
   131  		if inner.Entry() != fFunc.Entry() {
   132  			// Scanned past f and didn't find any inlined functions.
   133  			break
   134  		}
   135  		if inner.Name() != fFunc.Name() {
   136  			// This PC has f as its entry-point, but is not f. Therefore, it must be a
   137  			// function inlined into f.
   138  			return uint64(innerPC), true
   139  		}
   140  	}
   141  
   142  	return 0, false
   143  }
   144  
   145  func TestCPUProfileInlining(t *testing.T) {
   146  	if !containsInlinedCall(inlinedCaller, 4<<10) {
   147  		t.Skip("Can't determine whether inlinedCallee was inlined into inlinedCaller.")
   148  	}
   149  
   150  	p := testCPUProfile(t, stackContains, []string{"runtime/pprof.inlinedCallee", "runtime/pprof.inlinedCaller"}, avoidFunctions(), func(dur time.Duration) {
   151  		cpuHogger(inlinedCaller, &salt1, dur)
   152  	})
   153  
   154  	// Check if inlined function locations are encoded correctly. The inlinedCalee and inlinedCaller should be in one location.
   155  	for _, loc := range p.Location {
   156  		hasInlinedCallerAfterInlinedCallee, hasInlinedCallee := false, false
   157  		for _, line := range loc.Line {
   158  			if line.Function.Name == "runtime/pprof.inlinedCallee" {
   159  				hasInlinedCallee = true
   160  			}
   161  			if hasInlinedCallee && line.Function.Name == "runtime/pprof.inlinedCaller" {
   162  				hasInlinedCallerAfterInlinedCallee = true
   163  			}
   164  		}
   165  		if hasInlinedCallee != hasInlinedCallerAfterInlinedCallee {
   166  			t.Fatalf("want inlinedCallee followed by inlinedCaller, got separate Location entries:\n%v", p)
   167  		}
   168  	}
   169  }
   170  
   171  func inlinedCaller(x int) int {
   172  	x = inlinedCallee(x, 1e5)
   173  	return x
   174  }
   175  
   176  func inlinedCallee(x, n int) int {
   177  	return cpuHog0(x, n)
   178  }
   179  
   180  //go:noinline
   181  func dumpCallers(pcs []uintptr) {
   182  	if pcs == nil {
   183  		return
   184  	}
   185  
   186  	skip := 2 // Callers and dumpCallers
   187  	runtime.Callers(skip, pcs)
   188  }
   189  
   190  //go:noinline
   191  func inlinedCallerDump(pcs []uintptr) {
   192  	inlinedCalleeDump(pcs)
   193  }
   194  
   195  func inlinedCalleeDump(pcs []uintptr) {
   196  	dumpCallers(pcs)
   197  }
   198  
   199  func TestCPUProfileRecursion(t *testing.T) {
   200  	p := testCPUProfile(t, stackContains, []string{"runtime/pprof.inlinedCallee", "runtime/pprof.recursionCallee", "runtime/pprof.recursionCaller"}, avoidFunctions(), func(dur time.Duration) {
   201  		cpuHogger(recursionCaller, &salt1, dur)
   202  	})
   203  
   204  	// check the Location encoding was not confused by recursive calls.
   205  	for i, loc := range p.Location {
   206  		recursionFunc := 0
   207  		for _, line := range loc.Line {
   208  			if name := line.Function.Name; name == "runtime/pprof.recursionCaller" || name == "runtime/pprof.recursionCallee" {
   209  				recursionFunc++
   210  			}
   211  		}
   212  		if recursionFunc > 1 {
   213  			t.Fatalf("want at most one recursionCaller or recursionCallee in one Location, got a violating Location (index: %d):\n%v", i, p)
   214  		}
   215  	}
   216  }
   217  
   218  func recursionCaller(x int) int {
   219  	y := recursionCallee(3, x)
   220  	return y
   221  }
   222  
   223  func recursionCallee(n, x int) int {
   224  	if n == 0 {
   225  		return 1
   226  	}
   227  	y := inlinedCallee(x, 1e4)
   228  	return y * recursionCallee(n-1, x)
   229  }
   230  
   231  func recursionChainTop(x int, pcs []uintptr) {
   232  	if x < 0 {
   233  		return
   234  	}
   235  	recursionChainMiddle(x, pcs)
   236  }
   237  
   238  func recursionChainMiddle(x int, pcs []uintptr) {
   239  	recursionChainBottom(x, pcs)
   240  }
   241  
   242  func recursionChainBottom(x int, pcs []uintptr) {
   243  	// This will be called each time, we only care about the last. We
   244  	// can't make this conditional or this function won't be inlined.
   245  	dumpCallers(pcs)
   246  
   247  	recursionChainTop(x-1, pcs)
   248  }
   249  
   250  func parseProfile(t *testing.T, valBytes []byte, f func(uintptr, []*profile.Location, map[string][]string)) *profile.Profile {
   251  	p, err := profile.Parse(bytes.NewReader(valBytes))
   252  	if err != nil {
   253  		t.Fatal(err)
   254  	}
   255  	for _, sample := range p.Sample {
   256  		count := uintptr(sample.Value[0])
   257  		f(count, sample.Location, sample.Label)
   258  	}
   259  	return p
   260  }
   261  
   262  // testCPUProfile runs f under the CPU profiler, checking for some conditions specified by need,
   263  // as interpreted by matches, and returns the parsed profile.
   264  func testCPUProfile(t *testing.T, matches matchFunc, need []string, avoid []string, f func(dur time.Duration)) *profile.Profile {
   265  	switch runtime.GOOS {
   266  	case "darwin":
   267  		out, err := exec.Command("uname", "-a").CombinedOutput()
   268  		if err != nil {
   269  			t.Fatal(err)
   270  		}
   271  		vers := string(out)
   272  		t.Logf("uname -a: %v", vers)
   273  	case "plan9":
   274  		t.Skip("skipping on plan9")
   275  	}
   276  
   277  	broken := false
   278  	switch runtime.GOOS {
   279  	case "ios", "dragonfly", "netbsd", "illumos", "solaris":
   280  		broken = true
   281  	case "openbsd":
   282  		if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" {
   283  			broken = true
   284  		}
   285  	}
   286  
   287  	maxDuration := 5 * time.Second
   288  	if testing.Short() && broken {
   289  		// If it's expected to be broken, no point waiting around.
   290  		maxDuration /= 10
   291  	}
   292  
   293  	// If we're running a long test, start with a long duration
   294  	// for tests that try to make sure something *doesn't* happen.
   295  	duration := 5 * time.Second
   296  	if testing.Short() {
   297  		duration = 100 * time.Millisecond
   298  	}
   299  
   300  	// Profiling tests are inherently flaky, especially on a
   301  	// loaded system, such as when this test is running with
   302  	// several others under go test std. If a test fails in a way
   303  	// that could mean it just didn't run long enough, try with a
   304  	// longer duration.
   305  	for duration <= maxDuration {
   306  		var prof bytes.Buffer
   307  		if err := StartCPUProfile(&prof); err != nil {
   308  			t.Fatal(err)
   309  		}
   310  		f(duration)
   311  		StopCPUProfile()
   312  
   313  		if p, ok := profileOk(t, matches, need, avoid, prof, duration); ok {
   314  			return p
   315  		}
   316  
   317  		duration *= 2
   318  		if duration <= maxDuration {
   319  			t.Logf("retrying with %s duration", duration)
   320  		}
   321  	}
   322  
   323  	if broken {
   324  		t.Skipf("ignoring failure on %s/%s; see golang.org/issue/13841", runtime.GOOS, runtime.GOARCH)
   325  	}
   326  
   327  	// Ignore the failure if the tests are running in a QEMU-based emulator,
   328  	// QEMU is not perfect at emulating everything.
   329  	// IN_QEMU environmental variable is set by some of the Go builders.
   330  	// IN_QEMU=1 indicates that the tests are running in QEMU. See issue 9605.
   331  	if os.Getenv("IN_QEMU") == "1" {
   332  		t.Skip("ignore the failure in QEMU; see golang.org/issue/9605")
   333  	}
   334  	t.FailNow()
   335  	return nil
   336  }
   337  
   338  func contains(slice []string, s string) bool {
   339  	for i := range slice {
   340  		if slice[i] == s {
   341  			return true
   342  		}
   343  	}
   344  	return false
   345  }
   346  
   347  // stackContains matches if a function named spec appears anywhere in the stack trace.
   348  func stackContains(spec string, count uintptr, stk []*profile.Location, labels map[string][]string) bool {
   349  	for _, loc := range stk {
   350  		for _, line := range loc.Line {
   351  			if strings.Contains(line.Function.Name, spec) {
   352  				return true
   353  			}
   354  		}
   355  	}
   356  	return false
   357  }
   358  
   359  type matchFunc func(spec string, count uintptr, stk []*profile.Location, labels map[string][]string) bool
   360  
   361  func profileOk(t *testing.T, matches matchFunc, need []string, avoid []string, prof bytes.Buffer, duration time.Duration) (_ *profile.Profile, ok bool) {
   362  	ok = true
   363  
   364  	// Check that profile is well formed, contains 'need', and does not contain
   365  	// anything from 'avoid'.
   366  	have := make([]uintptr, len(need))
   367  	avoidSamples := make([]uintptr, len(avoid))
   368  	var samples uintptr
   369  	var buf bytes.Buffer
   370  	p := parseProfile(t, prof.Bytes(), func(count uintptr, stk []*profile.Location, labels map[string][]string) {
   371  		fmt.Fprintf(&buf, "%d:", count)
   372  		fprintStack(&buf, stk)
   373  		samples += count
   374  		for i, spec := range need {
   375  			if matches(spec, count, stk, labels) {
   376  				have[i] += count
   377  			}
   378  		}
   379  		for i, name := range avoid {
   380  			for _, loc := range stk {
   381  				for _, line := range loc.Line {
   382  					if strings.Contains(line.Function.Name, name) {
   383  						avoidSamples[i] += count
   384  					}
   385  				}
   386  			}
   387  		}
   388  		fmt.Fprintf(&buf, "\n")
   389  	})
   390  	t.Logf("total %d CPU profile samples collected:\n%s", samples, buf.String())
   391  
   392  	if samples < 10 && runtime.GOOS == "windows" {
   393  		// On some windows machines we end up with
   394  		// not enough samples due to coarse timer
   395  		// resolution. Let it go.
   396  		t.Log("too few samples on Windows (golang.org/issue/10842)")
   397  		return p, false
   398  	}
   399  
   400  	// Check that we got a reasonable number of samples.
   401  	// We used to always require at least ideal/4 samples,
   402  	// but that is too hard to guarantee on a loaded system.
   403  	// Now we accept 10 or more samples, which we take to be
   404  	// enough to show that at least some profiling is occurring.
   405  	if ideal := uintptr(duration * 100 / time.Second); samples == 0 || (samples < ideal/4 && samples < 10) {
   406  		t.Logf("too few samples; got %d, want at least %d, ideally %d", samples, ideal/4, ideal)
   407  		ok = false
   408  	}
   409  
   410  	for i, name := range avoid {
   411  		bad := avoidSamples[i]
   412  		if bad != 0 {
   413  			t.Logf("found %d samples in avoid-function %s\n", bad, name)
   414  			ok = false
   415  		}
   416  	}
   417  
   418  	if len(need) == 0 {
   419  		return p, ok
   420  	}
   421  
   422  	var total uintptr
   423  	for i, name := range need {
   424  		total += have[i]
   425  		t.Logf("%s: %d\n", name, have[i])
   426  	}
   427  	if total == 0 {
   428  		t.Logf("no samples in expected functions")
   429  		ok = false
   430  	}
   431  	// We'd like to check a reasonable minimum, like
   432  	// total / len(have) / smallconstant, but this test is
   433  	// pretty flaky (see bug 7095).  So we'll just test to
   434  	// make sure we got at least one sample.
   435  	min := uintptr(1)
   436  	for i, name := range need {
   437  		if have[i] < min {
   438  			t.Logf("%s has %d samples out of %d, want at least %d, ideally %d", name, have[i], total, min, total/uintptr(len(have)))
   439  			ok = false
   440  		}
   441  	}
   442  	return p, ok
   443  }
   444  
   445  // Fork can hang if preempted with signals frequently enough (see issue 5517).
   446  // Ensure that we do not do this.
   447  func TestCPUProfileWithFork(t *testing.T) {
   448  	testenv.MustHaveExec(t)
   449  
   450  	heap := 1 << 30
   451  	if runtime.GOOS == "android" {
   452  		// Use smaller size for Android to avoid crash.
   453  		heap = 100 << 20
   454  	}
   455  	if runtime.GOOS == "windows" && runtime.GOARCH == "arm" {
   456  		// Use smaller heap for Windows/ARM to avoid crash.
   457  		heap = 100 << 20
   458  	}
   459  	if testing.Short() {
   460  		heap = 100 << 20
   461  	}
   462  	// This makes fork slower.
   463  	garbage := make([]byte, heap)
   464  	// Need to touch the slice, otherwise it won't be paged in.
   465  	done := make(chan bool)
   466  	go func() {
   467  		for i := range garbage {
   468  			garbage[i] = 42
   469  		}
   470  		done <- true
   471  	}()
   472  	<-done
   473  
   474  	var prof bytes.Buffer
   475  	if err := StartCPUProfile(&prof); err != nil {
   476  		t.Fatal(err)
   477  	}
   478  	defer StopCPUProfile()
   479  
   480  	for i := 0; i < 10; i++ {
   481  		exec.Command(os.Args[0], "-h").CombinedOutput()
   482  	}
   483  }
   484  
   485  // Test that profiler does not observe runtime.gogo as "user" goroutine execution.
   486  // If it did, it would see inconsistent state and would either record an incorrect stack
   487  // or crash because the stack was malformed.
   488  func TestGoroutineSwitch(t *testing.T) {
   489  	if runtime.Compiler == "gccgo" {
   490  		t.Skip("not applicable for gccgo")
   491  	}
   492  	// How much to try. These defaults take about 1 seconds
   493  	// on a 2012 MacBook Pro. The ones in short mode take
   494  	// about 0.1 seconds.
   495  	tries := 10
   496  	count := 1000000
   497  	if testing.Short() {
   498  		tries = 1
   499  	}
   500  	for try := 0; try < tries; try++ {
   501  		var prof bytes.Buffer
   502  		if err := StartCPUProfile(&prof); err != nil {
   503  			t.Fatal(err)
   504  		}
   505  		for i := 0; i < count; i++ {
   506  			runtime.Gosched()
   507  		}
   508  		StopCPUProfile()
   509  
   510  		// Read profile to look for entries for gogo with an attempt at a traceback.
   511  		// "runtime.gogo" is OK, because that's the part of the context switch
   512  		// before the actual switch begins. But we should not see "gogo",
   513  		// aka "gogo<>(SB)", which does the actual switch and is marked SPWRITE.
   514  		parseProfile(t, prof.Bytes(), func(count uintptr, stk []*profile.Location, _ map[string][]string) {
   515  			// An entry with two frames with 'System' in its top frame
   516  			// exists to record a PC without a traceback. Those are okay.
   517  			if len(stk) == 2 {
   518  				name := stk[1].Line[0].Function.Name
   519  				if name == "runtime._System" || name == "runtime._ExternalCode" || name == "runtime._GC" {
   520  					return
   521  				}
   522  			}
   523  
   524  			// An entry with just one frame is OK too:
   525  			// it knew to stop at gogo.
   526  			if len(stk) == 1 {
   527  				return
   528  			}
   529  
   530  			// Otherwise, should not see gogo.
   531  			// The place we'd see it would be the inner most frame.
   532  			name := stk[0].Line[0].Function.Name
   533  			if name == "gogo" {
   534  				var buf bytes.Buffer
   535  				fprintStack(&buf, stk)
   536  				t.Fatalf("found profile entry for gogo:\n%s", buf.String())
   537  			}
   538  		})
   539  	}
   540  }
   541  
   542  func fprintStack(w io.Writer, stk []*profile.Location) {
   543  	for _, loc := range stk {
   544  		fmt.Fprintf(w, " %#x", loc.Address)
   545  		fmt.Fprintf(w, " (")
   546  		for i, line := range loc.Line {
   547  			if i > 0 {
   548  				fmt.Fprintf(w, " ")
   549  			}
   550  			fmt.Fprintf(w, "%s:%d", line.Function.Name, line.Line)
   551  		}
   552  		fmt.Fprintf(w, ")")
   553  	}
   554  	fmt.Fprintf(w, "\n")
   555  }
   556  
   557  // Test that profiling of division operations is okay, especially on ARM. See issue 6681.
   558  func TestMathBigDivide(t *testing.T) {
   559  	testCPUProfile(t, nil, nil, nil, func(duration time.Duration) {
   560  		t := time.After(duration)
   561  		pi := new(big.Int)
   562  		for {
   563  			for i := 0; i < 100; i++ {
   564  				n := big.NewInt(2646693125139304345)
   565  				d := big.NewInt(842468587426513207)
   566  				pi.Div(n, d)
   567  			}
   568  			select {
   569  			case <-t:
   570  				return
   571  			default:
   572  			}
   573  		}
   574  	})
   575  }
   576  
   577  // stackContainsAll matches if all functions in spec (comma-separated) appear somewhere in the stack trace.
   578  func stackContainsAll(spec string, count uintptr, stk []*profile.Location, labels map[string][]string) bool {
   579  	for _, f := range strings.Split(spec, ",") {
   580  		if !stackContains(f, count, stk, labels) {
   581  			return false
   582  		}
   583  	}
   584  	return true
   585  }
   586  
   587  func TestMorestack(t *testing.T) {
   588  	if runtime.GOOS == "darwin" && race.Enabled {
   589  		// For whatever reason, using the race detector on macOS keeps us
   590  		// from finding the newstack/growstack calls in the profile.
   591  		// Not worth worrying about.
   592  		// https://build.golang.org/log/280d387327806e17c8aabeb38b9503dbbd942ed1
   593  		t.Skip("skipping on darwin race detector")
   594  	}
   595  	if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
   596  		// For whatever reason, darwin/arm64 also doesn't work.
   597  		// https://build.golang.org/log/c45e82cc25f152642e6fb90d882ef5a8cd130ce5
   598  		t.Skip("skipping on darwin/arm64")
   599  	}
   600  	testCPUProfile(t, stackContainsAll, []string{"runtime.newstack,runtime/pprof.growstack"}, avoidFunctions(), func(duration time.Duration) {
   601  		t := time.After(duration)
   602  		c := make(chan bool)
   603  		for {
   604  			go func() {
   605  				growstack1()
   606  				c <- true
   607  			}()
   608  			select {
   609  			case <-t:
   610  				return
   611  			case <-c:
   612  			}
   613  		}
   614  	})
   615  }
   616  
   617  //go:noinline
   618  func growstack1() {
   619  	growstack()
   620  }
   621  
   622  //go:noinline
   623  func growstack() {
   624  	var buf [8 << 10]byte
   625  	use(buf)
   626  }
   627  
   628  //go:noinline
   629  func use(x [8 << 10]byte) {}
   630  
   631  func TestBlockProfile(t *testing.T) {
   632  	type TestCase struct {
   633  		name string
   634  		f    func()
   635  		stk  []string
   636  		re   string
   637  	}
   638  	tests := [...]TestCase{
   639  		{
   640  			name: "chan recv",
   641  			f:    blockChanRecv,
   642  			stk: []string{
   643  				"runtime.chanrecv1",
   644  				"runtime/pprof.blockChanRecv",
   645  				"runtime/pprof.TestBlockProfile",
   646  			},
   647  			re: `
   648  [0-9]+ [0-9]+ @( 0x[[:xdigit:]]+)+
   649  #	0x[0-9a-f]+	runtime\.chanrecv1\+0x[0-9a-f]+	.*/src/runtime/chan.go:[0-9]+
   650  #	0x[0-9a-f]+	runtime/pprof\.blockChanRecv\+0x[0-9a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   651  #	0x[0-9a-f]+	runtime/pprof\.TestBlockProfile\+0x[0-9a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   652  `},
   653  		{
   654  			name: "chan send",
   655  			f:    blockChanSend,
   656  			stk: []string{
   657  				"runtime.chansend1",
   658  				"runtime/pprof.blockChanSend",
   659  				"runtime/pprof.TestBlockProfile",
   660  			},
   661  			re: `
   662  [0-9]+ [0-9]+ @( 0x[[:xdigit:]]+)+
   663  #	0x[0-9a-f]+	runtime\.chansend1\+0x[0-9a-f]+	.*/src/runtime/chan.go:[0-9]+
   664  #	0x[0-9a-f]+	runtime/pprof\.blockChanSend\+0x[0-9a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   665  #	0x[0-9a-f]+	runtime/pprof\.TestBlockProfile\+0x[0-9a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   666  `},
   667  		{
   668  			name: "chan close",
   669  			f:    blockChanClose,
   670  			stk: []string{
   671  				"runtime.chanrecv1",
   672  				"runtime/pprof.blockChanClose",
   673  				"runtime/pprof.TestBlockProfile",
   674  			},
   675  			re: `
   676  [0-9]+ [0-9]+ @( 0x[[:xdigit:]]+)+
   677  #	0x[0-9a-f]+	runtime\.chanrecv1\+0x[0-9a-f]+	.*/src/runtime/chan.go:[0-9]+
   678  #	0x[0-9a-f]+	runtime/pprof\.blockChanClose\+0x[0-9a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   679  #	0x[0-9a-f]+	runtime/pprof\.TestBlockProfile\+0x[0-9a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   680  `},
   681  		{
   682  			name: "select recv async",
   683  			f:    blockSelectRecvAsync,
   684  			stk: []string{
   685  				"runtime.selectgo",
   686  				"runtime/pprof.blockSelectRecvAsync",
   687  				"runtime/pprof.TestBlockProfile",
   688  			},
   689  			re: `
   690  [0-9]+ [0-9]+ @( 0x[[:xdigit:]]+)+
   691  #	0x[0-9a-f]+	runtime\.selectgo\+0x[0-9a-f]+	.*/src/runtime/select.go:[0-9]+
   692  #	0x[0-9a-f]+	runtime/pprof\.blockSelectRecvAsync\+0x[0-9a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   693  #	0x[0-9a-f]+	runtime/pprof\.TestBlockProfile\+0x[0-9a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   694  `},
   695  		{
   696  			name: "select send sync",
   697  			f:    blockSelectSendSync,
   698  			stk: []string{
   699  				"runtime.selectgo",
   700  				"runtime/pprof.blockSelectSendSync",
   701  				"runtime/pprof.TestBlockProfile",
   702  			},
   703  			re: `
   704  [0-9]+ [0-9]+ @( 0x[[:xdigit:]]+)+
   705  #	0x[0-9a-f]+	runtime\.selectgo\+0x[0-9a-f]+	.*/src/runtime/select.go:[0-9]+
   706  #	0x[0-9a-f]+	runtime/pprof\.blockSelectSendSync\+0x[0-9a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   707  #	0x[0-9a-f]+	runtime/pprof\.TestBlockProfile\+0x[0-9a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   708  `},
   709  		{
   710  			name: "mutex",
   711  			f:    blockMutex,
   712  			stk: []string{
   713  				"sync.(*Mutex).Lock",
   714  				"runtime/pprof.blockMutex",
   715  				"runtime/pprof.TestBlockProfile",
   716  			},
   717  			re: `
   718  [0-9]+ [0-9]+ @( 0x[[:xdigit:]]+)+
   719  #	0x[0-9a-f]+	sync\.\(\*Mutex\)\.Lock\+0x[0-9a-f]+	.*/src/sync/mutex\.go:[0-9]+
   720  #	0x[0-9a-f]+	runtime/pprof\.blockMutex\+0x[0-9a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   721  #	0x[0-9a-f]+	runtime/pprof\.TestBlockProfile\+0x[0-9a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   722  `},
   723  		{
   724  			name: "cond",
   725  			f:    blockCond,
   726  			stk: []string{
   727  				"sync.(*Cond).Wait",
   728  				"runtime/pprof.blockCond",
   729  				"runtime/pprof.TestBlockProfile",
   730  			},
   731  			re: `
   732  [0-9]+ [0-9]+ @( 0x[[:xdigit:]]+)+
   733  #	0x[0-9a-f]+	sync\.\(\*Cond\)\.Wait\+0x[0-9a-f]+	.*/src/sync/cond\.go:[0-9]+
   734  #	0x[0-9a-f]+	runtime/pprof\.blockCond\+0x[0-9a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   735  #	0x[0-9a-f]+	runtime/pprof\.TestBlockProfile\+0x[0-9a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   736  `},
   737  	}
   738  
   739  	// Generate block profile
   740  	runtime.SetBlockProfileRate(1)
   741  	defer runtime.SetBlockProfileRate(0)
   742  	for _, test := range tests {
   743  		test.f()
   744  	}
   745  
   746  	t.Run("debug=1", func(t *testing.T) {
   747  		var w bytes.Buffer
   748  		Lookup("block").WriteTo(&w, 1)
   749  		prof := w.String()
   750  
   751  		if !strings.HasPrefix(prof, "--- contention:\ncycles/second=") {
   752  			t.Fatalf("Bad profile header:\n%v", prof)
   753  		}
   754  
   755  		if strings.HasSuffix(prof, "#\t0x0\n\n") {
   756  			t.Errorf("Useless 0 suffix:\n%v", prof)
   757  		}
   758  
   759  		for _, test := range tests {
   760  			if !regexp.MustCompile(strings.ReplaceAll(test.re, "\t", "\t+")).MatchString(prof) {
   761  				t.Errorf("Bad %v entry, expect:\n%v\ngot:\n%v", test.name, test.re, prof)
   762  			}
   763  		}
   764  	})
   765  
   766  	t.Run("proto", func(t *testing.T) {
   767  		// proto format
   768  		var w bytes.Buffer
   769  		Lookup("block").WriteTo(&w, 0)
   770  		p, err := profile.Parse(&w)
   771  		if err != nil {
   772  			t.Fatalf("failed to parse profile: %v", err)
   773  		}
   774  		t.Logf("parsed proto: %s", p)
   775  		if err := p.CheckValid(); err != nil {
   776  			t.Fatalf("invalid profile: %v", err)
   777  		}
   778  
   779  		stks := stacks(p)
   780  		for _, test := range tests {
   781  			if !containsStack(stks, test.stk) {
   782  				t.Errorf("No matching stack entry for %v, want %+v", test.name, test.stk)
   783  			}
   784  		}
   785  	})
   786  
   787  }
   788  
   789  func stacks(p *profile.Profile) (res [][]string) {
   790  	for _, s := range p.Sample {
   791  		var stk []string
   792  		for _, l := range s.Location {
   793  			for _, line := range l.Line {
   794  				stk = append(stk, line.Function.Name)
   795  			}
   796  		}
   797  		res = append(res, stk)
   798  	}
   799  	return res
   800  }
   801  
   802  func containsStack(got [][]string, want []string) bool {
   803  	for _, stk := range got {
   804  		if len(stk) < len(want) {
   805  			continue
   806  		}
   807  		for i, f := range want {
   808  			if f != stk[i] {
   809  				break
   810  			}
   811  			if i == len(want)-1 {
   812  				return true
   813  			}
   814  		}
   815  	}
   816  	return false
   817  }
   818  
   819  const blockDelay = 10 * time.Millisecond
   820  
   821  func blockChanRecv() {
   822  	c := make(chan bool)
   823  	go func() {
   824  		time.Sleep(blockDelay)
   825  		c <- true
   826  	}()
   827  	<-c
   828  }
   829  
   830  func blockChanSend() {
   831  	c := make(chan bool)
   832  	go func() {
   833  		time.Sleep(blockDelay)
   834  		<-c
   835  	}()
   836  	c <- true
   837  }
   838  
   839  func blockChanClose() {
   840  	c := make(chan bool)
   841  	go func() {
   842  		time.Sleep(blockDelay)
   843  		close(c)
   844  	}()
   845  	<-c
   846  }
   847  
   848  func blockSelectRecvAsync() {
   849  	const numTries = 3
   850  	c := make(chan bool, 1)
   851  	c2 := make(chan bool, 1)
   852  	go func() {
   853  		for i := 0; i < numTries; i++ {
   854  			time.Sleep(blockDelay)
   855  			c <- true
   856  		}
   857  	}()
   858  	for i := 0; i < numTries; i++ {
   859  		select {
   860  		case <-c:
   861  		case <-c2:
   862  		}
   863  	}
   864  }
   865  
   866  func blockSelectSendSync() {
   867  	c := make(chan bool)
   868  	c2 := make(chan bool)
   869  	go func() {
   870  		time.Sleep(blockDelay)
   871  		<-c
   872  	}()
   873  	select {
   874  	case c <- true:
   875  	case c2 <- true:
   876  	}
   877  }
   878  
   879  func blockMutex() {
   880  	var mu sync.Mutex
   881  	mu.Lock()
   882  	go func() {
   883  		time.Sleep(blockDelay)
   884  		mu.Unlock()
   885  	}()
   886  	// Note: Unlock releases mu before recording the mutex event,
   887  	// so it's theoretically possible for this to proceed and
   888  	// capture the profile before the event is recorded. As long
   889  	// as this is blocked before the unlock happens, it's okay.
   890  	mu.Lock()
   891  }
   892  
   893  func blockCond() {
   894  	var mu sync.Mutex
   895  	c := sync.NewCond(&mu)
   896  	mu.Lock()
   897  	go func() {
   898  		time.Sleep(blockDelay)
   899  		mu.Lock()
   900  		c.Signal()
   901  		mu.Unlock()
   902  	}()
   903  	c.Wait()
   904  	mu.Unlock()
   905  }
   906  
   907  func TestMutexProfile(t *testing.T) {
   908  	// Generate mutex profile
   909  
   910  	old := runtime.SetMutexProfileFraction(1)
   911  	defer runtime.SetMutexProfileFraction(old)
   912  	if old != 0 {
   913  		t.Fatalf("need MutexProfileRate 0, got %d", old)
   914  	}
   915  
   916  	blockMutex()
   917  
   918  	t.Run("debug=1", func(t *testing.T) {
   919  		var w bytes.Buffer
   920  		Lookup("mutex").WriteTo(&w, 1)
   921  		prof := w.String()
   922  		t.Logf("received profile: %v", prof)
   923  
   924  		if !strings.HasPrefix(prof, "--- mutex:\ncycles/second=") {
   925  			t.Errorf("Bad profile header:\n%v", prof)
   926  		}
   927  		prof = strings.Trim(prof, "\n")
   928  		lines := strings.Split(prof, "\n")
   929  		if len(lines) != 6 {
   930  			t.Errorf("expected 6 lines, got %d %q\n%s", len(lines), prof, prof)
   931  		}
   932  		if len(lines) < 6 {
   933  			return
   934  		}
   935  		// checking that the line is like "35258904 1 @ 0x48288d 0x47cd28 0x458931"
   936  		r2 := `^\d+ \d+ @(?: 0x[[:xdigit:]]+)+`
   937  		//r2 := "^[0-9]+ 1 @ 0x[0-9a-f x]+$"
   938  		if ok, err := regexp.MatchString(r2, lines[3]); err != nil || !ok {
   939  			t.Errorf("%q didn't match %q", lines[3], r2)
   940  		}
   941  		r3 := "^#.*runtime/pprof.blockMutex.*$"
   942  		if ok, err := regexp.MatchString(r3, lines[5]); err != nil || !ok {
   943  			t.Errorf("%q didn't match %q", lines[5], r3)
   944  		}
   945  		t.Logf(prof)
   946  	})
   947  	t.Run("proto", func(t *testing.T) {
   948  		// proto format
   949  		var w bytes.Buffer
   950  		Lookup("mutex").WriteTo(&w, 0)
   951  		p, err := profile.Parse(&w)
   952  		if err != nil {
   953  			t.Fatalf("failed to parse profile: %v", err)
   954  		}
   955  		t.Logf("parsed proto: %s", p)
   956  		if err := p.CheckValid(); err != nil {
   957  			t.Fatalf("invalid profile: %v", err)
   958  		}
   959  
   960  		stks := stacks(p)
   961  		for _, want := range [][]string{
   962  			{"sync.(*Mutex).Unlock", "runtime/pprof.blockMutex.func1"},
   963  		} {
   964  			if !containsStack(stks, want) {
   965  				t.Errorf("No matching stack entry for %+v", want)
   966  			}
   967  		}
   968  	})
   969  }
   970  
   971  func func1(c chan int) { <-c }
   972  func func2(c chan int) { <-c }
   973  func func3(c chan int) { <-c }
   974  func func4(c chan int) { <-c }
   975  
   976  func TestGoroutineCounts(t *testing.T) {
   977  	// Setting GOMAXPROCS to 1 ensures we can force all goroutines to the
   978  	// desired blocking point.
   979  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
   980  
   981  	c := make(chan int)
   982  	for i := 0; i < 100; i++ {
   983  		switch {
   984  		case i%10 == 0:
   985  			go func1(c)
   986  		case i%2 == 0:
   987  			go func2(c)
   988  		default:
   989  			go func3(c)
   990  		}
   991  		// Let goroutines block on channel
   992  		for j := 0; j < 5; j++ {
   993  			runtime.Gosched()
   994  		}
   995  	}
   996  	ctx := context.Background()
   997  
   998  	// ... and again, with labels this time (just with fewer iterations to keep
   999  	// sorting deterministic).
  1000  	Do(ctx, Labels("label", "value"), func(context.Context) {
  1001  		for i := 0; i < 89; i++ {
  1002  			switch {
  1003  			case i%10 == 0:
  1004  				go func1(c)
  1005  			case i%2 == 0:
  1006  				go func2(c)
  1007  			default:
  1008  				go func3(c)
  1009  			}
  1010  			// Let goroutines block on channel
  1011  			for j := 0; j < 5; j++ {
  1012  				runtime.Gosched()
  1013  			}
  1014  		}
  1015  	})
  1016  
  1017  	var w bytes.Buffer
  1018  	goroutineProf := Lookup("goroutine")
  1019  
  1020  	// Check debug profile
  1021  	goroutineProf.WriteTo(&w, 1)
  1022  	prof := w.String()
  1023  
  1024  	labels := labelMap{"label": "value"}
  1025  	labelStr := "\n# labels: " + labels.String()
  1026  	if !containsInOrder(prof, "\n50 @ ", "\n44 @", labelStr,
  1027  		"\n40 @", "\n36 @", labelStr, "\n10 @", "\n9 @", labelStr, "\n1 @") {
  1028  		t.Errorf("expected sorted goroutine counts with Labels:\n%s", prof)
  1029  	}
  1030  
  1031  	// Check proto profile
  1032  	w.Reset()
  1033  	goroutineProf.WriteTo(&w, 0)
  1034  	p, err := profile.Parse(&w)
  1035  	if err != nil {
  1036  		t.Errorf("error parsing protobuf profile: %v", err)
  1037  	}
  1038  	if err := p.CheckValid(); err != nil {
  1039  		t.Errorf("protobuf profile is invalid: %v", err)
  1040  	}
  1041  	expectedLabels := map[int64]map[string]string{
  1042  		50: map[string]string{},
  1043  		44: map[string]string{"label": "value"},
  1044  		40: map[string]string{},
  1045  		36: map[string]string{"label": "value"},
  1046  		10: map[string]string{},
  1047  		9:  map[string]string{"label": "value"},
  1048  		1:  map[string]string{},
  1049  	}
  1050  	if !containsCountsLabels(p, expectedLabels) {
  1051  		t.Errorf("expected count profile to contain goroutines with counts and labels %v, got %v",
  1052  			expectedLabels, p)
  1053  	}
  1054  
  1055  	close(c)
  1056  
  1057  	time.Sleep(10 * time.Millisecond) // let goroutines exit
  1058  }
  1059  
  1060  func containsInOrder(s string, all ...string) bool {
  1061  	for _, t := range all {
  1062  		i := strings.Index(s, t)
  1063  		if i < 0 {
  1064  			return false
  1065  		}
  1066  		s = s[i+len(t):]
  1067  	}
  1068  	return true
  1069  }
  1070  
  1071  func containsCountsLabels(prof *profile.Profile, countLabels map[int64]map[string]string) bool {
  1072  	m := make(map[int64]int)
  1073  	type nkey struct {
  1074  		count    int64
  1075  		key, val string
  1076  	}
  1077  	n := make(map[nkey]int)
  1078  	for c, kv := range countLabels {
  1079  		m[c]++
  1080  		for k, v := range kv {
  1081  			n[nkey{
  1082  				count: c,
  1083  				key:   k,
  1084  				val:   v,
  1085  			}]++
  1086  
  1087  		}
  1088  	}
  1089  	for _, s := range prof.Sample {
  1090  		// The count is the single value in the sample
  1091  		if len(s.Value) != 1 {
  1092  			return false
  1093  		}
  1094  		m[s.Value[0]]--
  1095  		for k, vs := range s.Label {
  1096  			for _, v := range vs {
  1097  				n[nkey{
  1098  					count: s.Value[0],
  1099  					key:   k,
  1100  					val:   v,
  1101  				}]--
  1102  			}
  1103  		}
  1104  	}
  1105  	for _, n := range m {
  1106  		if n > 0 {
  1107  			return false
  1108  		}
  1109  	}
  1110  	for _, ncnt := range n {
  1111  		if ncnt != 0 {
  1112  			return false
  1113  		}
  1114  	}
  1115  	return true
  1116  }
  1117  
  1118  var emptyCallStackTestRun int64
  1119  
  1120  // Issue 18836.
  1121  func TestEmptyCallStack(t *testing.T) {
  1122  	name := fmt.Sprintf("test18836_%d", emptyCallStackTestRun)
  1123  	emptyCallStackTestRun++
  1124  
  1125  	t.Parallel()
  1126  	var buf bytes.Buffer
  1127  	p := NewProfile(name)
  1128  
  1129  	p.Add("foo", 47674)
  1130  	p.WriteTo(&buf, 1)
  1131  	p.Remove("foo")
  1132  	got := buf.String()
  1133  	prefix := name + " profile: total 1\n"
  1134  	if !strings.HasPrefix(got, prefix) {
  1135  		t.Fatalf("got:\n\t%q\nwant prefix:\n\t%q\n", got, prefix)
  1136  	}
  1137  	lostevent := "lostProfileEvent"
  1138  	if !strings.Contains(got, lostevent) {
  1139  		t.Fatalf("got:\n\t%q\ndoes not contain:\n\t%q\n", got, lostevent)
  1140  	}
  1141  }
  1142  
  1143  // stackContainsLabeled takes a spec like funcname;key=value and matches if the stack has that key
  1144  // and value and has funcname somewhere in the stack.
  1145  func stackContainsLabeled(spec string, count uintptr, stk []*profile.Location, labels map[string][]string) bool {
  1146  	semi := strings.Index(spec, ";")
  1147  	if semi == -1 {
  1148  		panic("no semicolon in key/value spec")
  1149  	}
  1150  	kv := strings.SplitN(spec[semi+1:], "=", 2)
  1151  	if len(kv) != 2 {
  1152  		panic("missing = in key/value spec")
  1153  	}
  1154  	if !contains(labels[kv[0]], kv[1]) {
  1155  		return false
  1156  	}
  1157  	return stackContains(spec[:semi], count, stk, labels)
  1158  }
  1159  
  1160  func TestCPUProfileLabel(t *testing.T) {
  1161  	testCPUProfile(t, stackContainsLabeled, []string{"runtime/pprof.cpuHogger;key=value"}, avoidFunctions(), func(dur time.Duration) {
  1162  		Do(context.Background(), Labels("key", "value"), func(context.Context) {
  1163  			cpuHogger(cpuHog1, &salt1, dur)
  1164  		})
  1165  	})
  1166  }
  1167  
  1168  func TestLabelRace(t *testing.T) {
  1169  	// Test the race detector annotations for synchronization
  1170  	// between settings labels and consuming them from the
  1171  	// profile.
  1172  	testCPUProfile(t, stackContainsLabeled, []string{"runtime/pprof.cpuHogger;key=value"}, nil, func(dur time.Duration) {
  1173  		start := time.Now()
  1174  		var wg sync.WaitGroup
  1175  		for time.Since(start) < dur {
  1176  			var salts [10]int
  1177  			for i := 0; i < 10; i++ {
  1178  				wg.Add(1)
  1179  				go func(j int) {
  1180  					Do(context.Background(), Labels("key", "value"), func(context.Context) {
  1181  						cpuHogger(cpuHog1, &salts[j], time.Millisecond)
  1182  					})
  1183  					wg.Done()
  1184  				}(i)
  1185  			}
  1186  			wg.Wait()
  1187  		}
  1188  	})
  1189  }
  1190  
  1191  // Check that there is no deadlock when the program receives SIGPROF while in
  1192  // 64bit atomics' critical section. Used to happen on mips{,le}. See #20146.
  1193  func TestAtomicLoadStore64(t *testing.T) {
  1194  	f, err := os.CreateTemp("", "profatomic")
  1195  	if err != nil {
  1196  		t.Fatalf("TempFile: %v", err)
  1197  	}
  1198  	defer os.Remove(f.Name())
  1199  	defer f.Close()
  1200  
  1201  	if err := StartCPUProfile(f); err != nil {
  1202  		t.Fatal(err)
  1203  	}
  1204  	defer StopCPUProfile()
  1205  
  1206  	var flag uint64
  1207  	done := make(chan bool, 1)
  1208  
  1209  	go func() {
  1210  		for atomic.LoadUint64(&flag) == 0 {
  1211  			runtime.Gosched()
  1212  		}
  1213  		done <- true
  1214  	}()
  1215  	time.Sleep(50 * time.Millisecond)
  1216  	atomic.StoreUint64(&flag, 1)
  1217  	<-done
  1218  }
  1219  
  1220  func TestTracebackAll(t *testing.T) {
  1221  	// With gccgo, if a profiling signal arrives at the wrong time
  1222  	// during traceback, it may crash or hang. See issue #29448.
  1223  	f, err := os.CreateTemp("", "proftraceback")
  1224  	if err != nil {
  1225  		t.Fatalf("TempFile: %v", err)
  1226  	}
  1227  	defer os.Remove(f.Name())
  1228  	defer f.Close()
  1229  
  1230  	if err := StartCPUProfile(f); err != nil {
  1231  		t.Fatal(err)
  1232  	}
  1233  	defer StopCPUProfile()
  1234  
  1235  	ch := make(chan int)
  1236  	defer close(ch)
  1237  
  1238  	count := 10
  1239  	for i := 0; i < count; i++ {
  1240  		go func() {
  1241  			<-ch // block
  1242  		}()
  1243  	}
  1244  
  1245  	N := 10000
  1246  	if testing.Short() {
  1247  		N = 500
  1248  	}
  1249  	buf := make([]byte, 10*1024)
  1250  	for i := 0; i < N; i++ {
  1251  		runtime.Stack(buf, true)
  1252  	}
  1253  }
  1254  
  1255  // TestTryAdd tests the cases that are hard to test with real program execution.
  1256  //
  1257  // For example, the current go compilers may not always inline functions
  1258  // involved in recursion but that may not be true in the future compilers. This
  1259  // tests such cases by using fake call sequences and forcing the profile build
  1260  // utilizing translateCPUProfile defined in proto_test.go
  1261  func TestTryAdd(t *testing.T) {
  1262  	if _, found := findInlinedCall(inlinedCallerDump, 4<<10); !found {
  1263  		t.Skip("Can't determine whether anything was inlined into inlinedCallerDump.")
  1264  	}
  1265  
  1266  	// inlinedCallerDump
  1267  	//   inlinedCalleeDump
  1268  	pcs := make([]uintptr, 2)
  1269  	inlinedCallerDump(pcs)
  1270  	inlinedCallerStack := make([]uint64, 2)
  1271  	for i := range pcs {
  1272  		inlinedCallerStack[i] = uint64(pcs[i])
  1273  	}
  1274  
  1275  	if _, found := findInlinedCall(recursionChainBottom, 4<<10); !found {
  1276  		t.Skip("Can't determine whether anything was inlined into recursionChainBottom.")
  1277  	}
  1278  
  1279  	// recursionChainTop
  1280  	//   recursionChainMiddle
  1281  	//     recursionChainBottom
  1282  	//       recursionChainTop
  1283  	//         recursionChainMiddle
  1284  	//           recursionChainBottom
  1285  	pcs = make([]uintptr, 6)
  1286  	recursionChainTop(1, pcs)
  1287  	recursionStack := make([]uint64, len(pcs))
  1288  	for i := range pcs {
  1289  		recursionStack[i] = uint64(pcs[i])
  1290  	}
  1291  
  1292  	period := int64(2000 * 1000) // 1/500*1e9 nanosec.
  1293  
  1294  	testCases := []struct {
  1295  		name        string
  1296  		input       []uint64          // following the input format assumed by profileBuilder.addCPUData.
  1297  		wantLocs    [][]string        // ordered location entries with function names.
  1298  		wantSamples []*profile.Sample // ordered samples, we care only about Value and the profile location IDs.
  1299  	}{{
  1300  		// Sanity test for a normal, complete stack trace.
  1301  		name: "full_stack_trace",
  1302  		input: []uint64{
  1303  			3, 0, 500, // hz = 500. Must match the period.
  1304  			5, 0, 50, inlinedCallerStack[0], inlinedCallerStack[1],
  1305  		},
  1306  		wantLocs: [][]string{
  1307  			{"runtime/pprof.inlinedCalleeDump", "runtime/pprof.inlinedCallerDump"},
  1308  		},
  1309  		wantSamples: []*profile.Sample{
  1310  			{Value: []int64{50, 50 * period}, Location: []*profile.Location{{ID: 1}}},
  1311  		},
  1312  	}, {
  1313  		name: "bug35538",
  1314  		input: []uint64{
  1315  			3, 0, 500, // hz = 500. Must match the period.
  1316  			// Fake frame: tryAdd will have inlinedCallerDump
  1317  			// (stack[1]) on the deck when it encounters the next
  1318  			// inline function. It should accept this.
  1319  			7, 0, 10, inlinedCallerStack[0], inlinedCallerStack[1], inlinedCallerStack[0], inlinedCallerStack[1],
  1320  			5, 0, 20, inlinedCallerStack[0], inlinedCallerStack[1],
  1321  		},
  1322  		wantLocs: [][]string{{"runtime/pprof.inlinedCalleeDump", "runtime/pprof.inlinedCallerDump"}},
  1323  		wantSamples: []*profile.Sample{
  1324  			{Value: []int64{10, 10 * period}, Location: []*profile.Location{{ID: 1}, {ID: 1}}},
  1325  			{Value: []int64{20, 20 * period}, Location: []*profile.Location{{ID: 1}}},
  1326  		},
  1327  	}, {
  1328  		name: "bug38096",
  1329  		input: []uint64{
  1330  			3, 0, 500, // hz = 500. Must match the period.
  1331  			// count (data[2]) == 0 && len(stk) == 1 is an overflow
  1332  			// entry. The "stk" entry is actually the count.
  1333  			4, 0, 0, 4242,
  1334  		},
  1335  		wantLocs: [][]string{{"runtime/pprof.lostProfileEvent"}},
  1336  		wantSamples: []*profile.Sample{
  1337  			{Value: []int64{4242, 4242 * period}, Location: []*profile.Location{{ID: 1}}},
  1338  		},
  1339  	}, {
  1340  		// If a function is directly called recursively then it must
  1341  		// not be inlined in the caller.
  1342  		//
  1343  		// N.B. We're generating an impossible profile here, with a
  1344  		// recursive inlineCalleeDump call. This is simulating a non-Go
  1345  		// function that looks like an inlined Go function other than
  1346  		// its recursive property. See pcDeck.tryAdd.
  1347  		name: "directly_recursive_func_is_not_inlined",
  1348  		input: []uint64{
  1349  			3, 0, 500, // hz = 500. Must match the period.
  1350  			5, 0, 30, inlinedCallerStack[0], inlinedCallerStack[0],
  1351  			4, 0, 40, inlinedCallerStack[0],
  1352  		},
  1353  		// inlinedCallerDump shows up here because
  1354  		// runtime_expandFinalInlineFrame adds it to the stack frame.
  1355  		wantLocs: [][]string{{"runtime/pprof.inlinedCalleeDump"}, {"runtime/pprof.inlinedCallerDump"}},
  1356  		wantSamples: []*profile.Sample{
  1357  			{Value: []int64{30, 30 * period}, Location: []*profile.Location{{ID: 1}, {ID: 1}, {ID: 2}}},
  1358  			{Value: []int64{40, 40 * period}, Location: []*profile.Location{{ID: 1}, {ID: 2}}},
  1359  		},
  1360  	}, {
  1361  		name: "recursion_chain_inline",
  1362  		input: []uint64{
  1363  			3, 0, 500, // hz = 500. Must match the period.
  1364  			9, 0, 10, recursionStack[0], recursionStack[1], recursionStack[2], recursionStack[3], recursionStack[4], recursionStack[5],
  1365  		},
  1366  		wantLocs: [][]string{
  1367  			{"runtime/pprof.recursionChainBottom"},
  1368  			{
  1369  				"runtime/pprof.recursionChainMiddle",
  1370  				"runtime/pprof.recursionChainTop",
  1371  				"runtime/pprof.recursionChainBottom",
  1372  			},
  1373  			{
  1374  				"runtime/pprof.recursionChainMiddle",
  1375  				"runtime/pprof.recursionChainTop",
  1376  				"runtime/pprof.TestTryAdd", // inlined into the test.
  1377  			},
  1378  		},
  1379  		wantSamples: []*profile.Sample{
  1380  			{Value: []int64{10, 10 * period}, Location: []*profile.Location{{ID: 1}, {ID: 2}, {ID: 3}}},
  1381  		},
  1382  	}, {
  1383  		name: "truncated_stack_trace_later",
  1384  		input: []uint64{
  1385  			3, 0, 500, // hz = 500. Must match the period.
  1386  			5, 0, 50, inlinedCallerStack[0], inlinedCallerStack[1],
  1387  			4, 0, 60, inlinedCallerStack[0],
  1388  		},
  1389  		wantLocs: [][]string{{"runtime/pprof.inlinedCalleeDump", "runtime/pprof.inlinedCallerDump"}},
  1390  		wantSamples: []*profile.Sample{
  1391  			{Value: []int64{50, 50 * period}, Location: []*profile.Location{{ID: 1}}},
  1392  			{Value: []int64{60, 60 * period}, Location: []*profile.Location{{ID: 1}}},
  1393  		},
  1394  	}, {
  1395  		name: "truncated_stack_trace_first",
  1396  		input: []uint64{
  1397  			3, 0, 500, // hz = 500. Must match the period.
  1398  			4, 0, 70, inlinedCallerStack[0],
  1399  			5, 0, 80, inlinedCallerStack[0], inlinedCallerStack[1],
  1400  		},
  1401  		wantLocs: [][]string{{"runtime/pprof.inlinedCalleeDump", "runtime/pprof.inlinedCallerDump"}},
  1402  		wantSamples: []*profile.Sample{
  1403  			{Value: []int64{70, 70 * period}, Location: []*profile.Location{{ID: 1}}},
  1404  			{Value: []int64{80, 80 * period}, Location: []*profile.Location{{ID: 1}}},
  1405  		},
  1406  	}, {
  1407  		// We can recover the inlined caller from a truncated stack.
  1408  		name: "truncated_stack_trace_only",
  1409  		input: []uint64{
  1410  			3, 0, 500, // hz = 500. Must match the period.
  1411  			4, 0, 70, inlinedCallerStack[0],
  1412  		},
  1413  		wantLocs: [][]string{{"runtime/pprof.inlinedCalleeDump", "runtime/pprof.inlinedCallerDump"}},
  1414  		wantSamples: []*profile.Sample{
  1415  			{Value: []int64{70, 70 * period}, Location: []*profile.Location{{ID: 1}}},
  1416  		},
  1417  	}, {
  1418  		// The same location is used for duplicated stacks.
  1419  		name: "truncated_stack_trace_twice",
  1420  		input: []uint64{
  1421  			3, 0, 500, // hz = 500. Must match the period.
  1422  			4, 0, 70, inlinedCallerStack[0],
  1423  			// Fake frame: add a fake call to
  1424  			// inlinedCallerDump to prevent this sample
  1425  			// from getting merged into above.
  1426  			5, 0, 80, inlinedCallerStack[1], inlinedCallerStack[0],
  1427  		},
  1428  		wantLocs: [][]string{
  1429  			{"runtime/pprof.inlinedCalleeDump", "runtime/pprof.inlinedCallerDump"},
  1430  			{"runtime/pprof.inlinedCallerDump"},
  1431  		},
  1432  		wantSamples: []*profile.Sample{
  1433  			{Value: []int64{70, 70 * period}, Location: []*profile.Location{{ID: 1}}},
  1434  			{Value: []int64{80, 80 * period}, Location: []*profile.Location{{ID: 2}, {ID: 1}}},
  1435  		},
  1436  	}}
  1437  
  1438  	for _, tc := range testCases {
  1439  		t.Run(tc.name, func(t *testing.T) {
  1440  			p, err := translateCPUProfile(tc.input)
  1441  			if err != nil {
  1442  				t.Fatalf("translating profile: %v", err)
  1443  			}
  1444  			t.Logf("Profile: %v\n", p)
  1445  
  1446  			// One location entry with all inlined functions.
  1447  			var gotLoc [][]string
  1448  			for _, loc := range p.Location {
  1449  				var names []string
  1450  				for _, line := range loc.Line {
  1451  					names = append(names, line.Function.Name)
  1452  				}
  1453  				gotLoc = append(gotLoc, names)
  1454  			}
  1455  			if got, want := fmtJSON(gotLoc), fmtJSON(tc.wantLocs); got != want {
  1456  				t.Errorf("Got Location = %+v\n\twant %+v", got, want)
  1457  			}
  1458  			// All samples should point to one location.
  1459  			var gotSamples []*profile.Sample
  1460  			for _, sample := range p.Sample {
  1461  				var locs []*profile.Location
  1462  				for _, loc := range sample.Location {
  1463  					locs = append(locs, &profile.Location{ID: loc.ID})
  1464  				}
  1465  				gotSamples = append(gotSamples, &profile.Sample{Value: sample.Value, Location: locs})
  1466  			}
  1467  			if got, want := fmtJSON(gotSamples), fmtJSON(tc.wantSamples); got != want {
  1468  				t.Errorf("Got Samples = %+v\n\twant %+v", got, want)
  1469  			}
  1470  		})
  1471  	}
  1472  }