github.com/lovishpuri/go-40569/src@v0.0.0-20230519171745-f8623e7c56cf/runtime/pprof/proto_test.go (about)

     1  // Copyright 2016 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
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/json"
    10  	"fmt"
    11  	"internal/abi"
    12  	"internal/profile"
    13  	"internal/testenv"
    14  	"os"
    15  	"os/exec"
    16  	"reflect"
    17  	"runtime"
    18  	"strings"
    19  	"testing"
    20  	"unsafe"
    21  )
    22  
    23  // translateCPUProfile parses binary CPU profiling stack trace data
    24  // generated by runtime.CPUProfile() into a profile struct.
    25  // This is only used for testing. Real conversions stream the
    26  // data into the profileBuilder as it becomes available.
    27  //
    28  // count is the number of records in data.
    29  func translateCPUProfile(data []uint64, count int) (*profile.Profile, error) {
    30  	var buf bytes.Buffer
    31  	b := newProfileBuilder(&buf)
    32  	tags := make([]unsafe.Pointer, count)
    33  	if err := b.addCPUData(data, tags); err != nil {
    34  		return nil, err
    35  	}
    36  	b.build()
    37  	return profile.Parse(&buf)
    38  }
    39  
    40  // fmtJSON returns a pretty-printed JSON form for x.
    41  // It works reasonably well for printing protocol-buffer
    42  // data structures like profile.Profile.
    43  func fmtJSON(x any) string {
    44  	js, _ := json.MarshalIndent(x, "", "\t")
    45  	return string(js)
    46  }
    47  
    48  func TestConvertCPUProfileEmpty(t *testing.T) {
    49  	// A test server with mock cpu profile data.
    50  	var buf bytes.Buffer
    51  
    52  	b := []uint64{3, 0, 500} // empty profile at 500 Hz (2ms sample period)
    53  	p, err := translateCPUProfile(b, 1)
    54  	if err != nil {
    55  		t.Fatalf("translateCPUProfile: %v", err)
    56  	}
    57  	if err := p.Write(&buf); err != nil {
    58  		t.Fatalf("writing profile: %v", err)
    59  	}
    60  
    61  	p, err = profile.Parse(&buf)
    62  	if err != nil {
    63  		t.Fatalf("profile.Parse: %v", err)
    64  	}
    65  
    66  	// Expected PeriodType and SampleType.
    67  	periodType := &profile.ValueType{Type: "cpu", Unit: "nanoseconds"}
    68  	sampleType := []*profile.ValueType{
    69  		{Type: "samples", Unit: "count"},
    70  		{Type: "cpu", Unit: "nanoseconds"},
    71  	}
    72  
    73  	checkProfile(t, p, 2000*1000, periodType, sampleType, nil, "")
    74  }
    75  
    76  func f1() { f1() }
    77  func f2() { f2() }
    78  
    79  // testPCs returns two PCs and two corresponding memory mappings
    80  // to use in test profiles.
    81  func testPCs(t *testing.T) (addr1, addr2 uint64, map1, map2 *profile.Mapping) {
    82  	switch runtime.GOOS {
    83  	case "linux", "android", "netbsd":
    84  		// Figure out two addresses from /proc/self/maps.
    85  		mmap, err := os.ReadFile("/proc/self/maps")
    86  		if err != nil {
    87  			t.Fatal(err)
    88  		}
    89  		mprof := &profile.Profile{}
    90  		if err = mprof.ParseMemoryMap(bytes.NewReader(mmap)); err != nil {
    91  			t.Fatalf("parsing /proc/self/maps: %v", err)
    92  		}
    93  		if len(mprof.Mapping) < 2 {
    94  			// It is possible for a binary to only have 1 executable
    95  			// region of memory.
    96  			t.Skipf("need 2 or more mappings, got %v", len(mprof.Mapping))
    97  		}
    98  		addr1 = mprof.Mapping[0].Start
    99  		map1 = mprof.Mapping[0]
   100  		map1.BuildID, _ = elfBuildID(map1.File)
   101  		addr2 = mprof.Mapping[1].Start
   102  		map2 = mprof.Mapping[1]
   103  		map2.BuildID, _ = elfBuildID(map2.File)
   104  	case "windows":
   105  		addr1 = uint64(abi.FuncPCABIInternal(f1))
   106  		addr2 = uint64(abi.FuncPCABIInternal(f2))
   107  
   108  		exe, err := os.Executable()
   109  		if err != nil {
   110  			t.Fatal(err)
   111  		}
   112  
   113  		start, end, err := readMainModuleMapping()
   114  		if err != nil {
   115  			t.Fatal(err)
   116  		}
   117  
   118  		map1 = &profile.Mapping{
   119  			ID:           1,
   120  			Start:        start,
   121  			Limit:        end,
   122  			File:         exe,
   123  			BuildID:      peBuildID(exe),
   124  			HasFunctions: true,
   125  		}
   126  		map2 = &profile.Mapping{
   127  			ID:           1,
   128  			Start:        start,
   129  			Limit:        end,
   130  			File:         exe,
   131  			BuildID:      peBuildID(exe),
   132  			HasFunctions: true,
   133  		}
   134  	case "js", "wasip1":
   135  		addr1 = uint64(abi.FuncPCABIInternal(f1))
   136  		addr2 = uint64(abi.FuncPCABIInternal(f2))
   137  	default:
   138  		addr1 = uint64(abi.FuncPCABIInternal(f1))
   139  		addr2 = uint64(abi.FuncPCABIInternal(f2))
   140  		// Fake mapping - HasFunctions will be true because two PCs from Go
   141  		// will be fully symbolized.
   142  		fake := &profile.Mapping{ID: 1, HasFunctions: true}
   143  		map1, map2 = fake, fake
   144  	}
   145  	return
   146  }
   147  
   148  func TestConvertCPUProfile(t *testing.T) {
   149  	addr1, addr2, map1, map2 := testPCs(t)
   150  
   151  	b := []uint64{
   152  		3, 0, 500, // hz = 500
   153  		5, 0, 10, uint64(addr1 + 1), uint64(addr1 + 2), // 10 samples in addr1
   154  		5, 0, 40, uint64(addr2 + 1), uint64(addr2 + 2), // 40 samples in addr2
   155  		5, 0, 10, uint64(addr1 + 1), uint64(addr1 + 2), // 10 samples in addr1
   156  	}
   157  	p, err := translateCPUProfile(b, 4)
   158  	if err != nil {
   159  		t.Fatalf("translating profile: %v", err)
   160  	}
   161  	period := int64(2000 * 1000)
   162  	periodType := &profile.ValueType{Type: "cpu", Unit: "nanoseconds"}
   163  	sampleType := []*profile.ValueType{
   164  		{Type: "samples", Unit: "count"},
   165  		{Type: "cpu", Unit: "nanoseconds"},
   166  	}
   167  	samples := []*profile.Sample{
   168  		{Value: []int64{20, 20 * 2000 * 1000}, Location: []*profile.Location{
   169  			{ID: 1, Mapping: map1, Address: addr1},
   170  			{ID: 2, Mapping: map1, Address: addr1 + 1},
   171  		}},
   172  		{Value: []int64{40, 40 * 2000 * 1000}, Location: []*profile.Location{
   173  			{ID: 3, Mapping: map2, Address: addr2},
   174  			{ID: 4, Mapping: map2, Address: addr2 + 1},
   175  		}},
   176  	}
   177  	checkProfile(t, p, period, periodType, sampleType, samples, "")
   178  }
   179  
   180  func checkProfile(t *testing.T, p *profile.Profile, period int64, periodType *profile.ValueType, sampleType []*profile.ValueType, samples []*profile.Sample, defaultSampleType string) {
   181  	t.Helper()
   182  
   183  	if p.Period != period {
   184  		t.Errorf("p.Period = %d, want %d", p.Period, period)
   185  	}
   186  	if !reflect.DeepEqual(p.PeriodType, periodType) {
   187  		t.Errorf("p.PeriodType = %v\nwant = %v", fmtJSON(p.PeriodType), fmtJSON(periodType))
   188  	}
   189  	if !reflect.DeepEqual(p.SampleType, sampleType) {
   190  		t.Errorf("p.SampleType = %v\nwant = %v", fmtJSON(p.SampleType), fmtJSON(sampleType))
   191  	}
   192  	if defaultSampleType != p.DefaultSampleType {
   193  		t.Errorf("p.DefaultSampleType = %v\nwant = %v", p.DefaultSampleType, defaultSampleType)
   194  	}
   195  	// Clear line info since it is not in the expected samples.
   196  	// If we used f1 and f2 above, then the samples will have line info.
   197  	for _, s := range p.Sample {
   198  		for _, l := range s.Location {
   199  			l.Line = nil
   200  		}
   201  	}
   202  	if fmtJSON(p.Sample) != fmtJSON(samples) { // ignore unexported fields
   203  		if len(p.Sample) == len(samples) {
   204  			for i := range p.Sample {
   205  				if !reflect.DeepEqual(p.Sample[i], samples[i]) {
   206  					t.Errorf("sample %d = %v\nwant = %v\n", i, fmtJSON(p.Sample[i]), fmtJSON(samples[i]))
   207  				}
   208  			}
   209  			if t.Failed() {
   210  				t.FailNow()
   211  			}
   212  		}
   213  		t.Fatalf("p.Sample = %v\nwant = %v", fmtJSON(p.Sample), fmtJSON(samples))
   214  	}
   215  }
   216  
   217  var profSelfMapsTests = `
   218  00400000-0040b000 r-xp 00000000 fc:01 787766                             /bin/cat
   219  0060a000-0060b000 r--p 0000a000 fc:01 787766                             /bin/cat
   220  0060b000-0060c000 rw-p 0000b000 fc:01 787766                             /bin/cat
   221  014ab000-014cc000 rw-p 00000000 00:00 0                                  [heap]
   222  7f7d76af8000-7f7d7797c000 r--p 00000000 fc:01 1318064                    /usr/lib/locale/locale-archive
   223  7f7d7797c000-7f7d77b36000 r-xp 00000000 fc:01 1180226                    /lib/x86_64-linux-gnu/libc-2.19.so
   224  7f7d77b36000-7f7d77d36000 ---p 001ba000 fc:01 1180226                    /lib/x86_64-linux-gnu/libc-2.19.so
   225  7f7d77d36000-7f7d77d3a000 r--p 001ba000 fc:01 1180226                    /lib/x86_64-linux-gnu/libc-2.19.so
   226  7f7d77d3a000-7f7d77d3c000 rw-p 001be000 fc:01 1180226                    /lib/x86_64-linux-gnu/libc-2.19.so
   227  7f7d77d3c000-7f7d77d41000 rw-p 00000000 00:00 0
   228  7f7d77d41000-7f7d77d64000 r-xp 00000000 fc:01 1180217                    /lib/x86_64-linux-gnu/ld-2.19.so
   229  7f7d77f3f000-7f7d77f42000 rw-p 00000000 00:00 0
   230  7f7d77f61000-7f7d77f63000 rw-p 00000000 00:00 0
   231  7f7d77f63000-7f7d77f64000 r--p 00022000 fc:01 1180217                    /lib/x86_64-linux-gnu/ld-2.19.so
   232  7f7d77f64000-7f7d77f65000 rw-p 00023000 fc:01 1180217                    /lib/x86_64-linux-gnu/ld-2.19.so
   233  7f7d77f65000-7f7d77f66000 rw-p 00000000 00:00 0
   234  7ffc342a2000-7ffc342c3000 rw-p 00000000 00:00 0                          [stack]
   235  7ffc34343000-7ffc34345000 r-xp 00000000 00:00 0                          [vdso]
   236  ffffffffff600000-ffffffffff601000 r-xp 00000090 00:00 0                  [vsyscall]
   237  ->
   238  00400000 0040b000 00000000 /bin/cat
   239  7f7d7797c000 7f7d77b36000 00000000 /lib/x86_64-linux-gnu/libc-2.19.so
   240  7f7d77d41000 7f7d77d64000 00000000 /lib/x86_64-linux-gnu/ld-2.19.so
   241  7ffc34343000 7ffc34345000 00000000 [vdso]
   242  ffffffffff600000 ffffffffff601000 00000090 [vsyscall]
   243  
   244  00400000-07000000 r-xp 00000000 00:00 0
   245  07000000-07093000 r-xp 06c00000 00:2e 536754                             /path/to/gobench_server_main
   246  07093000-0722d000 rw-p 06c92000 00:2e 536754                             /path/to/gobench_server_main
   247  0722d000-07b21000 rw-p 00000000 00:00 0
   248  c000000000-c000036000 rw-p 00000000 00:00 0
   249  ->
   250  07000000 07093000 06c00000 /path/to/gobench_server_main
   251  `
   252  
   253  var profSelfMapsTestsWithDeleted = `
   254  00400000-0040b000 r-xp 00000000 fc:01 787766                             /bin/cat (deleted)
   255  0060a000-0060b000 r--p 0000a000 fc:01 787766                             /bin/cat (deleted)
   256  0060b000-0060c000 rw-p 0000b000 fc:01 787766                             /bin/cat (deleted)
   257  014ab000-014cc000 rw-p 00000000 00:00 0                                  [heap]
   258  7f7d76af8000-7f7d7797c000 r--p 00000000 fc:01 1318064                    /usr/lib/locale/locale-archive
   259  7f7d7797c000-7f7d77b36000 r-xp 00000000 fc:01 1180226                    /lib/x86_64-linux-gnu/libc-2.19.so
   260  7f7d77b36000-7f7d77d36000 ---p 001ba000 fc:01 1180226                    /lib/x86_64-linux-gnu/libc-2.19.so
   261  7f7d77d36000-7f7d77d3a000 r--p 001ba000 fc:01 1180226                    /lib/x86_64-linux-gnu/libc-2.19.so
   262  7f7d77d3a000-7f7d77d3c000 rw-p 001be000 fc:01 1180226                    /lib/x86_64-linux-gnu/libc-2.19.so
   263  7f7d77d3c000-7f7d77d41000 rw-p 00000000 00:00 0
   264  7f7d77d41000-7f7d77d64000 r-xp 00000000 fc:01 1180217                    /lib/x86_64-linux-gnu/ld-2.19.so
   265  7f7d77f3f000-7f7d77f42000 rw-p 00000000 00:00 0
   266  7f7d77f61000-7f7d77f63000 rw-p 00000000 00:00 0
   267  7f7d77f63000-7f7d77f64000 r--p 00022000 fc:01 1180217                    /lib/x86_64-linux-gnu/ld-2.19.so
   268  7f7d77f64000-7f7d77f65000 rw-p 00023000 fc:01 1180217                    /lib/x86_64-linux-gnu/ld-2.19.so
   269  7f7d77f65000-7f7d77f66000 rw-p 00000000 00:00 0
   270  7ffc342a2000-7ffc342c3000 rw-p 00000000 00:00 0                          [stack]
   271  7ffc34343000-7ffc34345000 r-xp 00000000 00:00 0                          [vdso]
   272  ffffffffff600000-ffffffffff601000 r-xp 00000090 00:00 0                  [vsyscall]
   273  ->
   274  00400000 0040b000 00000000 /bin/cat
   275  7f7d7797c000 7f7d77b36000 00000000 /lib/x86_64-linux-gnu/libc-2.19.so
   276  7f7d77d41000 7f7d77d64000 00000000 /lib/x86_64-linux-gnu/ld-2.19.so
   277  7ffc34343000 7ffc34345000 00000000 [vdso]
   278  ffffffffff600000 ffffffffff601000 00000090 [vsyscall]
   279  
   280  00400000-0040b000 r-xp 00000000 fc:01 787766                             /bin/cat with space
   281  0060a000-0060b000 r--p 0000a000 fc:01 787766                             /bin/cat with space
   282  0060b000-0060c000 rw-p 0000b000 fc:01 787766                             /bin/cat with space
   283  014ab000-014cc000 rw-p 00000000 00:00 0                                  [heap]
   284  7f7d76af8000-7f7d7797c000 r--p 00000000 fc:01 1318064                    /usr/lib/locale/locale-archive
   285  7f7d7797c000-7f7d77b36000 r-xp 00000000 fc:01 1180226                    /lib/x86_64-linux-gnu/libc-2.19.so
   286  7f7d77b36000-7f7d77d36000 ---p 001ba000 fc:01 1180226                    /lib/x86_64-linux-gnu/libc-2.19.so
   287  7f7d77d36000-7f7d77d3a000 r--p 001ba000 fc:01 1180226                    /lib/x86_64-linux-gnu/libc-2.19.so
   288  7f7d77d3a000-7f7d77d3c000 rw-p 001be000 fc:01 1180226                    /lib/x86_64-linux-gnu/libc-2.19.so
   289  7f7d77d3c000-7f7d77d41000 rw-p 00000000 00:00 0
   290  7f7d77d41000-7f7d77d64000 r-xp 00000000 fc:01 1180217                    /lib/x86_64-linux-gnu/ld-2.19.so
   291  7f7d77f3f000-7f7d77f42000 rw-p 00000000 00:00 0
   292  7f7d77f61000-7f7d77f63000 rw-p 00000000 00:00 0
   293  7f7d77f63000-7f7d77f64000 r--p 00022000 fc:01 1180217                    /lib/x86_64-linux-gnu/ld-2.19.so
   294  7f7d77f64000-7f7d77f65000 rw-p 00023000 fc:01 1180217                    /lib/x86_64-linux-gnu/ld-2.19.so
   295  7f7d77f65000-7f7d77f66000 rw-p 00000000 00:00 0
   296  7ffc342a2000-7ffc342c3000 rw-p 00000000 00:00 0                          [stack]
   297  7ffc34343000-7ffc34345000 r-xp 00000000 00:00 0                          [vdso]
   298  ffffffffff600000-ffffffffff601000 r-xp 00000090 00:00 0                  [vsyscall]
   299  ->
   300  00400000 0040b000 00000000 /bin/cat with space
   301  7f7d7797c000 7f7d77b36000 00000000 /lib/x86_64-linux-gnu/libc-2.19.so
   302  7f7d77d41000 7f7d77d64000 00000000 /lib/x86_64-linux-gnu/ld-2.19.so
   303  7ffc34343000 7ffc34345000 00000000 [vdso]
   304  ffffffffff600000 ffffffffff601000 00000090 [vsyscall]
   305  `
   306  
   307  func TestProcSelfMaps(t *testing.T) {
   308  
   309  	f := func(t *testing.T, input string) {
   310  		for tx, tt := range strings.Split(input, "\n\n") {
   311  			in, out, ok := strings.Cut(tt, "->\n")
   312  			if !ok {
   313  				t.Fatal("malformed test case")
   314  			}
   315  			if len(out) > 0 && out[len(out)-1] != '\n' {
   316  				out += "\n"
   317  			}
   318  			var buf strings.Builder
   319  			parseProcSelfMaps([]byte(in), func(lo, hi, offset uint64, file, buildID string) {
   320  				fmt.Fprintf(&buf, "%08x %08x %08x %s\n", lo, hi, offset, file)
   321  			})
   322  			if buf.String() != out {
   323  				t.Errorf("#%d: have:\n%s\nwant:\n%s\n%q\n%q", tx, buf.String(), out, buf.String(), out)
   324  			}
   325  		}
   326  	}
   327  
   328  	t.Run("Normal", func(t *testing.T) {
   329  		f(t, profSelfMapsTests)
   330  	})
   331  
   332  	t.Run("WithDeletedFile", func(t *testing.T) {
   333  		f(t, profSelfMapsTestsWithDeleted)
   334  	})
   335  }
   336  
   337  // TestMapping checks the mapping section of CPU profiles
   338  // has the HasFunctions field set correctly. If all PCs included
   339  // in the samples are successfully symbolized, the corresponding
   340  // mapping entry (in this test case, only one entry) should have
   341  // its HasFunctions field set true.
   342  // The test generates a CPU profile that includes PCs from C side
   343  // that the runtime can't symbolize. See ./testdata/mappingtest.
   344  func TestMapping(t *testing.T) {
   345  	testenv.MustHaveGoRun(t)
   346  	testenv.MustHaveCGO(t)
   347  
   348  	prog := "./testdata/mappingtest/main.go"
   349  
   350  	// GoOnly includes only Go symbols that runtime will symbolize.
   351  	// Go+C includes C symbols that runtime will not symbolize.
   352  	for _, traceback := range []string{"GoOnly", "Go+C"} {
   353  		t.Run("traceback"+traceback, func(t *testing.T) {
   354  			cmd := exec.Command(testenv.GoToolPath(t), "run", prog)
   355  			if traceback != "GoOnly" {
   356  				cmd.Env = append(os.Environ(), "SETCGOTRACEBACK=1")
   357  			}
   358  			cmd.Stderr = new(bytes.Buffer)
   359  
   360  			out, err := cmd.Output()
   361  			if err != nil {
   362  				t.Fatalf("failed to run the test program %q: %v\n%v", prog, err, cmd.Stderr)
   363  			}
   364  
   365  			prof, err := profile.Parse(bytes.NewReader(out))
   366  			if err != nil {
   367  				t.Fatalf("failed to parse the generated profile data: %v", err)
   368  			}
   369  			t.Logf("Profile: %s", prof)
   370  
   371  			hit := make(map[*profile.Mapping]bool)
   372  			miss := make(map[*profile.Mapping]bool)
   373  			for _, loc := range prof.Location {
   374  				if symbolized(loc) {
   375  					hit[loc.Mapping] = true
   376  				} else {
   377  					miss[loc.Mapping] = true
   378  				}
   379  			}
   380  			if len(miss) == 0 {
   381  				t.Log("no location with missing symbol info was sampled")
   382  			}
   383  
   384  			for _, m := range prof.Mapping {
   385  				if miss[m] && m.HasFunctions {
   386  					t.Errorf("mapping %+v has HasFunctions=true, but contains locations with failed symbolization", m)
   387  					continue
   388  				}
   389  				if !miss[m] && hit[m] && !m.HasFunctions {
   390  					t.Errorf("mapping %+v has HasFunctions=false, but all referenced locations from this lapping were symbolized successfully", m)
   391  					continue
   392  				}
   393  			}
   394  
   395  			if traceback == "Go+C" {
   396  				// The test code was arranged to have PCs from C and
   397  				// they are not symbolized.
   398  				// Check no Location containing those unsymbolized PCs contains multiple lines.
   399  				for i, loc := range prof.Location {
   400  					if !symbolized(loc) && len(loc.Line) > 1 {
   401  						t.Errorf("Location[%d] contains unsymbolized PCs and multiple lines: %v", i, loc)
   402  					}
   403  				}
   404  			}
   405  		})
   406  	}
   407  }
   408  
   409  func symbolized(loc *profile.Location) bool {
   410  	if len(loc.Line) == 0 {
   411  		return false
   412  	}
   413  	l := loc.Line[0]
   414  	f := l.Function
   415  	if l.Line == 0 || f == nil || f.Name == "" || f.Filename == "" {
   416  		return false
   417  	}
   418  	return true
   419  }
   420  
   421  // TestFakeMapping tests if at least one mapping exists
   422  // (including a fake mapping), and their HasFunctions bits
   423  // are set correctly.
   424  func TestFakeMapping(t *testing.T) {
   425  	var buf bytes.Buffer
   426  	if err := Lookup("heap").WriteTo(&buf, 0); err != nil {
   427  		t.Fatalf("failed to write heap profile: %v", err)
   428  	}
   429  	prof, err := profile.Parse(&buf)
   430  	if err != nil {
   431  		t.Fatalf("failed to parse the generated profile data: %v", err)
   432  	}
   433  	t.Logf("Profile: %s", prof)
   434  	if len(prof.Mapping) == 0 {
   435  		t.Fatal("want profile with at least one mapping entry, got 0 mapping")
   436  	}
   437  
   438  	hit := make(map[*profile.Mapping]bool)
   439  	miss := make(map[*profile.Mapping]bool)
   440  	for _, loc := range prof.Location {
   441  		if symbolized(loc) {
   442  			hit[loc.Mapping] = true
   443  		} else {
   444  			miss[loc.Mapping] = true
   445  		}
   446  	}
   447  	for _, m := range prof.Mapping {
   448  		if miss[m] && m.HasFunctions {
   449  			t.Errorf("mapping %+v has HasFunctions=true, but contains locations with failed symbolization", m)
   450  			continue
   451  		}
   452  		if !miss[m] && hit[m] && !m.HasFunctions {
   453  			t.Errorf("mapping %+v has HasFunctions=false, but all referenced locations from this lapping were symbolized successfully", m)
   454  			continue
   455  		}
   456  	}
   457  }
   458  
   459  // Make sure the profiler can handle an empty stack trace.
   460  // See issue 37967.
   461  func TestEmptyStack(t *testing.T) {
   462  	b := []uint64{
   463  		3, 0, 500, // hz = 500
   464  		3, 0, 10, // 10 samples with an empty stack trace
   465  	}
   466  	_, err := translateCPUProfile(b, 2)
   467  	if err != nil {
   468  		t.Fatalf("translating profile: %v", err)
   469  	}
   470  }