github.com/grafana/pyroscope@v1.18.0/pkg/pprof/pprof_test.go (about)

     1  package pprof
     2  
     3  import (
     4  	"io/fs"
     5  	"math/rand"
     6  	"os"
     7  	"path/filepath"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/google/pprof/profile"
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  	"google.golang.org/protobuf/proto"
    15  
    16  	profilev1 "github.com/grafana/pyroscope/api/gen/proto/go/google/v1"
    17  	typesv1 "github.com/grafana/pyroscope/api/gen/proto/go/types/v1"
    18  	"github.com/grafana/pyroscope/pkg/pprof/testhelper"
    19  )
    20  
    21  func TestNormalizeProfile(t *testing.T) {
    22  	currentTime = func() time.Time {
    23  		t, _ := time.Parse(time.RFC3339, "2020-01-01T00:00:00Z")
    24  		return t
    25  	}
    26  	defer func() {
    27  		currentTime = time.Now
    28  	}()
    29  
    30  	p := &profilev1.Profile{
    31  		SampleType: []*profilev1.ValueType{
    32  			{Type: 2, Unit: 1},
    33  			{Type: 3, Unit: 4},
    34  		},
    35  		Sample: []*profilev1.Sample{
    36  			{LocationId: []uint64{2, 3}, Value: []int64{0, 1}, Label: []*profilev1.Label{{Num: 10, Key: 1}, {Num: 11, Key: 1}}},
    37  			// Those samples should be dropped.
    38  			{LocationId: []uint64{1, 2, 3}, Value: []int64{0, 0}, Label: []*profilev1.Label{{Num: 10, Key: 1}}},
    39  			{LocationId: []uint64{4}, Value: []int64{0, 0}, Label: []*profilev1.Label{{Num: 10, Key: 1}}},
    40  		},
    41  		Mapping: []*profilev1.Mapping{{Id: 1, HasFunctions: true, MemoryStart: 100, MemoryLimit: 200, FileOffset: 200}},
    42  		Location: []*profilev1.Location{
    43  			{Id: 1, MappingId: 1, Address: 5, Line: []*profilev1.Line{{FunctionId: 1, Line: 1}, {FunctionId: 2, Line: 3}}},
    44  			{Id: 2, MappingId: 1, Address: 2, Line: []*profilev1.Line{{FunctionId: 2, Line: 1}, {FunctionId: 3, Line: 3}}},
    45  			{Id: 3, MappingId: 1, Address: 1, Line: []*profilev1.Line{{FunctionId: 3, Line: 1}, {FunctionId: 4, Line: 3}}},
    46  			{Id: 4, MappingId: 1, Address: 0, Line: []*profilev1.Line{{FunctionId: 5, Line: 1}}},
    47  		},
    48  		Function: []*profilev1.Function{
    49  			{Id: 1, Name: 5, SystemName: 6, Filename: 7, StartLine: 1},
    50  			{Id: 2, Name: 8, SystemName: 9, Filename: 10, StartLine: 1},
    51  			{Id: 3, Name: 11, SystemName: 12, Filename: 13, StartLine: 1},
    52  			{Id: 4, Name: 14, SystemName: 15, Filename: 7, StartLine: 1},
    53  			{Id: 5, Name: 16, SystemName: 17, Filename: 18, StartLine: 1},
    54  		},
    55  		StringTable: []string{
    56  			"",
    57  			"bytes", "in_used", "allocs", "count",
    58  			"main", "runtime.main", "main.go", // fn1
    59  			"foo", "runtime.foo", "foo.go", // fn2
    60  			"bar", "runtime.bar", "bar.go", // fn3
    61  			"buzz", "runtime.buzz", // fn4
    62  			"bla", "runtime.bla", "bla.go", // fn5
    63  		},
    64  		PeriodType:        &profilev1.ValueType{Type: 2, Unit: 1},
    65  		Comment:           []int64{},
    66  		DefaultSampleType: 0,
    67  	}
    68  
    69  	pf := &Profile{Profile: p}
    70  	pf.Normalize()
    71  	require.Equal(t, &profilev1.Profile{
    72  		SampleType: []*profilev1.ValueType{
    73  			{Type: 2, Unit: 1},
    74  			{Type: 3, Unit: 4},
    75  		},
    76  		Sample: []*profilev1.Sample{
    77  			{LocationId: []uint64{1, 2}, Value: []int64{0, 1}, Label: []*profilev1.Label{}},
    78  		},
    79  		Mapping: []*profilev1.Mapping{{
    80  			Id:           1,
    81  			HasFunctions: true,
    82  		}},
    83  		Location: []*profilev1.Location{
    84  			{Id: 1, MappingId: 1, Line: []*profilev1.Line{{FunctionId: 1, Line: 1}, {FunctionId: 2, Line: 3}}},
    85  			{Id: 2, MappingId: 1, Line: []*profilev1.Line{{FunctionId: 2, Line: 1}, {FunctionId: 3, Line: 3}}},
    86  		},
    87  		Function: []*profilev1.Function{
    88  			{Id: 1, Name: 6, SystemName: 7, Filename: 8, StartLine: 1},
    89  			{Id: 2, Name: 9, SystemName: 10, Filename: 11, StartLine: 1},
    90  			{Id: 3, Name: 12, SystemName: 13, Filename: 5, StartLine: 1},
    91  		},
    92  		StringTable: []string{
    93  			"",
    94  			"bytes", "in_used", "allocs", "count",
    95  			"main.go",
    96  			"foo", "runtime.foo", "foo.go",
    97  			"bar", "runtime.bar", "bar.go",
    98  			"buzz", "runtime.buzz",
    99  		},
   100  		PeriodType:        &profilev1.ValueType{Type: 2, Unit: 1},
   101  		Comment:           []int64{},
   102  		TimeNanos:         1577836800000000000,
   103  		DefaultSampleType: 0,
   104  	}, pf.Profile)
   105  }
   106  
   107  func TestNormalizeProfile_NegativeSample(t *testing.T) {
   108  	currentTime = func() time.Time {
   109  		t, _ := time.Parse(time.RFC3339, "2020-01-01T00:00:00Z")
   110  		return t
   111  	}
   112  	defer func() {
   113  		currentTime = time.Now
   114  	}()
   115  
   116  	p := &profilev1.Profile{
   117  		SampleType: []*profilev1.ValueType{
   118  			{Type: 1, Unit: 2},
   119  		},
   120  		Sample: []*profilev1.Sample{
   121  			{LocationId: []uint64{2, 1}, Value: []int64{10}, Label: []*profilev1.Label{{Str: 5, Key: 5}, {Str: 5, Key: 6}}},
   122  			{LocationId: []uint64{2, 1}, Value: []int64{10}, Label: []*profilev1.Label{{Str: 5, Key: 5}, {Str: 5, Key: 7}}},
   123  			{LocationId: []uint64{2, 1}, Value: []int64{10}, Label: []*profilev1.Label{{Str: 5, Key: 5}, {Str: 5, Key: 7}}},
   124  			{LocationId: []uint64{2, 1}, Value: []int64{-10}, Label: []*profilev1.Label{{Str: 5, Key: 5}, {Str: 5, Key: 7}}},
   125  			{LocationId: []uint64{2, 1}, Value: []int64{0}, Label: []*profilev1.Label{{Str: 5, Key: 5}, {Str: 5, Key: 7}}},
   126  		},
   127  		Mapping: []*profilev1.Mapping{{Id: 1, HasFunctions: true, MemoryStart: 100, MemoryLimit: 200, FileOffset: 200}},
   128  		Location: []*profilev1.Location{
   129  			{Id: 1, MappingId: 1, Address: 5, Line: []*profilev1.Line{{FunctionId: 1, Line: 1}}},
   130  			{Id: 2, MappingId: 1, Address: 2, Line: []*profilev1.Line{{FunctionId: 2, Line: 1}}},
   131  		},
   132  		Function: []*profilev1.Function{
   133  			{Id: 1, Name: 3, SystemName: 3, Filename: 4, StartLine: 1},
   134  			{Id: 2, Name: 5, SystemName: 5, Filename: 4, StartLine: 1},
   135  		},
   136  		StringTable: []string{
   137  			"",
   138  			"cpu", "nanoseconds",
   139  			"main", "main.go",
   140  			"foo", "bar", "baz",
   141  		},
   142  		PeriodType: &profilev1.ValueType{Type: 1, Unit: 2},
   143  	}
   144  
   145  	pf := &Profile{Profile: p}
   146  	pf.Normalize()
   147  	require.Equal(t, pf.Profile, &profilev1.Profile{
   148  		SampleType: []*profilev1.ValueType{
   149  			{Type: 1, Unit: 2},
   150  		},
   151  		Sample: []*profilev1.Sample{
   152  			{LocationId: []uint64{2, 1}, Value: []int64{10}, Label: []*profilev1.Label{{Str: 5, Key: 5}, {Str: 5, Key: 6}}},
   153  			{LocationId: []uint64{2, 1}, Value: []int64{20}, Label: []*profilev1.Label{{Str: 5, Key: 5}, {Str: 5, Key: 7}}},
   154  		},
   155  		Mapping: []*profilev1.Mapping{{Id: 1, HasFunctions: true}},
   156  		Location: []*profilev1.Location{
   157  			{Id: 1, MappingId: 1, Line: []*profilev1.Line{{FunctionId: 1, Line: 1}}},
   158  			{Id: 2, MappingId: 1, Line: []*profilev1.Line{{FunctionId: 2, Line: 1}}},
   159  		},
   160  		Function: []*profilev1.Function{
   161  			{Id: 1, Name: 3, SystemName: 3, Filename: 4, StartLine: 1},
   162  			{Id: 2, Name: 5, SystemName: 5, Filename: 4, StartLine: 1},
   163  		},
   164  		StringTable: []string{
   165  			"",
   166  			"cpu", "nanoseconds",
   167  			"main", "main.go",
   168  			"foo", "bar", "baz",
   169  		},
   170  		PeriodType: &profilev1.ValueType{Type: 1, Unit: 2},
   171  		TimeNanos:  1577836800000000000,
   172  	})
   173  }
   174  
   175  func TestNormalizeProfile_SampleLabels(t *testing.T) {
   176  	currentTime = func() time.Time {
   177  		t, _ := time.Parse(time.RFC3339, "2020-01-01T00:00:00Z")
   178  		return t
   179  	}
   180  	defer func() {
   181  		currentTime = time.Now
   182  	}()
   183  
   184  	p := &profilev1.Profile{
   185  		SampleType: []*profilev1.ValueType{
   186  			{Type: 1, Unit: 2},
   187  		},
   188  		Sample: []*profilev1.Sample{
   189  			{LocationId: []uint64{2, 1}, Value: []int64{10}, Label: []*profilev1.Label{{Str: 5, Key: 5}, {Str: 5, Key: 6}}},
   190  			{LocationId: []uint64{2, 1}, Value: []int64{10}, Label: []*profilev1.Label{{Str: 5, Key: 5}, {Str: 5, Key: 7}}},
   191  			{LocationId: []uint64{2, 1}, Value: []int64{10}, Label: []*profilev1.Label{{Str: 5, Key: 5}, {Str: 5, Key: 7}}},
   192  		},
   193  		Mapping: []*profilev1.Mapping{{Id: 1, HasFunctions: true, MemoryStart: 100, MemoryLimit: 200, FileOffset: 200}},
   194  		Location: []*profilev1.Location{
   195  			{Id: 1, MappingId: 1, Address: 5, Line: []*profilev1.Line{{FunctionId: 1, Line: 1}}},
   196  			{Id: 2, MappingId: 1, Address: 2, Line: []*profilev1.Line{{FunctionId: 2, Line: 1}}},
   197  		},
   198  		Function: []*profilev1.Function{
   199  			{Id: 1, Name: 3, SystemName: 3, Filename: 4, StartLine: 1},
   200  			{Id: 2, Name: 5, SystemName: 5, Filename: 4, StartLine: 1},
   201  		},
   202  		StringTable: []string{
   203  			"",
   204  			"cpu", "nanoseconds",
   205  			"main", "main.go",
   206  			"foo", "bar", "baz",
   207  		},
   208  		PeriodType: &profilev1.ValueType{Type: 1, Unit: 2},
   209  	}
   210  
   211  	pf := &Profile{Profile: p}
   212  	pf.Normalize()
   213  	require.Equal(t, pf.Profile, &profilev1.Profile{
   214  		SampleType: []*profilev1.ValueType{
   215  			{Type: 1, Unit: 2},
   216  		},
   217  		Sample: []*profilev1.Sample{
   218  			{LocationId: []uint64{2, 1}, Value: []int64{10}, Label: []*profilev1.Label{{Str: 5, Key: 5}, {Str: 5, Key: 6}}},
   219  			{LocationId: []uint64{2, 1}, Value: []int64{20}, Label: []*profilev1.Label{{Str: 5, Key: 5}, {Str: 5, Key: 7}}},
   220  		},
   221  		Mapping: []*profilev1.Mapping{{Id: 1, HasFunctions: true}},
   222  		Location: []*profilev1.Location{
   223  			{Id: 1, MappingId: 1, Line: []*profilev1.Line{{FunctionId: 1, Line: 1}}},
   224  			{Id: 2, MappingId: 1, Line: []*profilev1.Line{{FunctionId: 2, Line: 1}}},
   225  		},
   226  		Function: []*profilev1.Function{
   227  			{Id: 1, Name: 3, SystemName: 3, Filename: 4, StartLine: 1},
   228  			{Id: 2, Name: 5, SystemName: 5, Filename: 4, StartLine: 1},
   229  		},
   230  		StringTable: []string{
   231  			"",
   232  			"cpu", "nanoseconds",
   233  			"main", "main.go",
   234  			"foo", "bar", "baz",
   235  		},
   236  		PeriodType: &profilev1.ValueType{Type: 1, Unit: 2},
   237  		TimeNanos:  1577836800000000000,
   238  	})
   239  }
   240  
   241  func Test_sanitizeReferences(t *testing.T) {
   242  	type testCase struct {
   243  		name     string
   244  		profile  *profilev1.Profile
   245  		expected *profilev1.Profile
   246  	}
   247  
   248  	testCases := []testCase{
   249  		{
   250  			name: "string_reference",
   251  			profile: &profilev1.Profile{
   252  				SampleType:        []*profilev1.ValueType{{Type: 10, Unit: 10}},
   253  				Sample:            []*profilev1.Sample{{Label: []*profilev1.Label{{Key: 10, Str: 10, NumUnit: 10}}}},
   254  				Mapping:           []*profilev1.Mapping{{Filename: 10, BuildId: 10}},
   255  				Function:          []*profilev1.Function{{Name: 10, SystemName: 10, Filename: 10}},
   256  				DropFrames:        10,
   257  				KeepFrames:        10,
   258  				PeriodType:        &profilev1.ValueType{Type: 10, Unit: 10},
   259  				DefaultSampleType: 10,
   260  				Comment:           []int64{0, 10},
   261  				StringTable:       []string{},
   262  			},
   263  			expected: &profilev1.Profile{
   264  				SampleType:  []*profilev1.ValueType{{}},
   265  				Sample:      []*profilev1.Sample{},
   266  				Mapping:     []*profilev1.Mapping{{Id: 1}},
   267  				Function:    []*profilev1.Function{{Id: 1}},
   268  				PeriodType:  &profilev1.ValueType{},
   269  				Comment:     []int64{0, 0},
   270  				StringTable: []string{""},
   271  			},
   272  		},
   273  		{
   274  			name: "zero_string_non_0",
   275  			profile: &profilev1.Profile{
   276  				SampleType:        []*profilev1.ValueType{{Type: 10, Unit: 10}},
   277  				Sample:            []*profilev1.Sample{{Label: []*profilev1.Label{{Key: 10, Str: 10, NumUnit: 10}}}},
   278  				Mapping:           []*profilev1.Mapping{{Filename: 10, BuildId: 10}},
   279  				Function:          []*profilev1.Function{{Name: 10, Filename: 0, SystemName: 2}},
   280  				DropFrames:        10,
   281  				KeepFrames:        10,
   282  				PeriodType:        &profilev1.ValueType{Type: 10, Unit: 10},
   283  				DefaultSampleType: 10,
   284  				Comment:           []int64{1, 10},
   285  				StringTable:       []string{"foo", "", "bar"},
   286  			},
   287  			expected: &profilev1.Profile{
   288  				SampleType:  []*profilev1.ValueType{{}},
   289  				Sample:      []*profilev1.Sample{},
   290  				Mapping:     []*profilev1.Mapping{{Id: 1}},
   291  				Function:    []*profilev1.Function{{Id: 1, Filename: 1, SystemName: 2}},
   292  				PeriodType:  &profilev1.ValueType{},
   293  				Comment:     []int64{0, 0},
   294  				StringTable: []string{"", "foo", "bar"},
   295  			},
   296  		},
   297  		{
   298  			name: "zero_string_missing",
   299  			profile: &profilev1.Profile{
   300  				SampleType:        []*profilev1.ValueType{{Type: 10, Unit: 10}},
   301  				Sample:            []*profilev1.Sample{{Label: []*profilev1.Label{{Key: 10, Str: 10, NumUnit: 10}}}},
   302  				Mapping:           []*profilev1.Mapping{{Filename: 10, BuildId: 10}},
   303  				Function:          []*profilev1.Function{{Name: 0, SystemName: 0, Filename: 1}},
   304  				DropFrames:        10,
   305  				KeepFrames:        10,
   306  				PeriodType:        &profilev1.ValueType{Type: 10, Unit: 10},
   307  				DefaultSampleType: 10,
   308  				StringTable:       []string{"foo", "bar"},
   309  			},
   310  			expected: &profilev1.Profile{
   311  				SampleType:  []*profilev1.ValueType{{}},
   312  				Sample:      []*profilev1.Sample{},
   313  				Mapping:     []*profilev1.Mapping{{Id: 1}},
   314  				Function:    []*profilev1.Function{{Id: 1, Name: 2, SystemName: 2, Filename: 1}},
   315  				PeriodType:  &profilev1.ValueType{},
   316  				StringTable: []string{"", "bar", "foo"},
   317  			},
   318  		},
   319  		{
   320  			name: "multiple_zero_strings",
   321  			profile: &profilev1.Profile{
   322  				SampleType: []*profilev1.ValueType{{}},
   323  				Sample: []*profilev1.Sample{
   324  					{LocationId: []uint64{1}, Value: []int64{1}, Label: []*profilev1.Label{{Key: 1, Str: 5}}},
   325  				},
   326  				Location:    []*profilev1.Location{{Id: 1, MappingId: 1, Line: []*profilev1.Line{{FunctionId: 1}}}},
   327  				Function:    []*profilev1.Function{{Id: 1, Name: 1, SystemName: 1, Filename: 2}},
   328  				Mapping:     []*profilev1.Mapping{{Id: 1, Filename: 1}},
   329  				StringTable: []string{"", "foo", "", "", "", "bar"},
   330  			},
   331  			expected: &profilev1.Profile{
   332  				SampleType: []*profilev1.ValueType{{}},
   333  				Sample: []*profilev1.Sample{
   334  					{LocationId: []uint64{1}, Value: []int64{1}, Label: []*profilev1.Label{{Key: 1, Str: 5}}},
   335  				},
   336  				Location:    []*profilev1.Location{{Id: 1, MappingId: 1, Line: []*profilev1.Line{{FunctionId: 1}}}},
   337  				Function:    []*profilev1.Function{{Id: 1, Name: 1, SystemName: 1, Filename: 2}},
   338  				Mapping:     []*profilev1.Mapping{{Id: 1, Filename: 1}},
   339  				StringTable: []string{"", "foo", "", "", "", "bar"},
   340  			},
   341  		},
   342  		{
   343  			name: "mapping_reference",
   344  			profile: &profilev1.Profile{
   345  				Sample: []*profilev1.Sample{
   346  					{LocationId: []uint64{2, 1}},
   347  					{LocationId: []uint64{3, 2, 1}},
   348  				},
   349  				Location: []*profilev1.Location{
   350  					{Id: 1, MappingId: 1, Address: 1},
   351  					{Id: 3, MappingId: 5, Address: 2},
   352  					{Id: 2, MappingId: 0, Address: 3},
   353  				},
   354  				Mapping: []*profilev1.Mapping{
   355  					{Id: 1},
   356  				},
   357  			},
   358  			expected: &profilev1.Profile{
   359  				Sample: []*profilev1.Sample{
   360  					{LocationId: []uint64{2, 1}},
   361  				},
   362  				Location: []*profilev1.Location{
   363  					{Id: 1, MappingId: 1, Address: 1},
   364  					{Id: 2, MappingId: 2, Address: 3},
   365  				},
   366  				Mapping: []*profilev1.Mapping{
   367  					{Id: 1},
   368  					{Id: 2},
   369  				},
   370  				StringTable: []string{""},
   371  			},
   372  		},
   373  		{
   374  			name: "location_reference",
   375  			profile: &profilev1.Profile{
   376  				Sample: []*profilev1.Sample{
   377  					{LocationId: []uint64{1, 0}},
   378  					{LocationId: []uint64{5}},
   379  				},
   380  				Location: []*profilev1.Location{
   381  					{Id: 1, MappingId: 1, Address: 0xa},
   382  					{Id: 0, MappingId: 0, Address: 0xa},
   383  				},
   384  			},
   385  			expected: &profilev1.Profile{
   386  				Sample: []*profilev1.Sample{},
   387  				Location: []*profilev1.Location{
   388  					{Id: 1, MappingId: 1, Address: 0xa},
   389  				},
   390  				Mapping: []*profilev1.Mapping{
   391  					{Id: 1},
   392  				},
   393  				StringTable: []string{""},
   394  			},
   395  		},
   396  		{
   397  			name: "function_reference",
   398  			profile: &profilev1.Profile{
   399  				Sample: []*profilev1.Sample{
   400  					{LocationId: []uint64{2, 1}},
   401  				},
   402  				Location: []*profilev1.Location{
   403  					{Id: 2, MappingId: 1, Line: []*profilev1.Line{{FunctionId: 5}}},
   404  					{Id: 3, MappingId: 1, Line: []*profilev1.Line{{FunctionId: 10}}},
   405  					{Id: 1, MappingId: 1, Line: []*profilev1.Line{{}}},
   406  				},
   407  				Function: []*profilev1.Function{
   408  					{Id: 10},
   409  				},
   410  			},
   411  			expected: &profilev1.Profile{
   412  				Sample:   []*profilev1.Sample{},
   413  				Location: []*profilev1.Location{},
   414  				Function: []*profilev1.Function{
   415  					{Id: 1},
   416  				},
   417  				StringTable: []string{""},
   418  			},
   419  		},
   420  		{
   421  			name:     "nil_profile",
   422  			profile:  nil,
   423  			expected: nil,
   424  		},
   425  		{
   426  			name: "nil_sample_type",
   427  			profile: &profilev1.Profile{
   428  				SampleType: []*profilev1.ValueType{nil},
   429  			},
   430  			expected: &profilev1.Profile{
   431  				SampleType:  []*profilev1.ValueType{},
   432  				StringTable: []string{""},
   433  			},
   434  		},
   435  		{
   436  			name: "nil_sample",
   437  			profile: &profilev1.Profile{
   438  				SampleType: []*profilev1.ValueType{{}},
   439  				Sample:     []*profilev1.Sample{nil},
   440  			},
   441  			expected: &profilev1.Profile{
   442  				SampleType:  []*profilev1.ValueType{{}},
   443  				Sample:      []*profilev1.Sample{},
   444  				StringTable: []string{""},
   445  			},
   446  		},
   447  		{
   448  			name: "nil_location",
   449  			profile: &profilev1.Profile{
   450  				SampleType: []*profilev1.ValueType{{}},
   451  				Sample:     []*profilev1.Sample{{LocationId: []uint64{1}, Value: []int64{1}}},
   452  				Location:   []*profilev1.Location{nil},
   453  			},
   454  			expected: &profilev1.Profile{
   455  				SampleType:  []*profilev1.ValueType{{}},
   456  				Sample:      []*profilev1.Sample{},
   457  				Location:    []*profilev1.Location{},
   458  				StringTable: []string{""},
   459  			},
   460  		},
   461  		{
   462  			name: "nil_function",
   463  			profile: &profilev1.Profile{
   464  				SampleType: []*profilev1.ValueType{{}},
   465  				Sample:     []*profilev1.Sample{{LocationId: []uint64{1}, Value: []int64{1}}},
   466  				Location:   []*profilev1.Location{{Line: []*profilev1.Line{{FunctionId: 1}}}},
   467  				Function:   []*profilev1.Function{nil},
   468  			},
   469  			expected: &profilev1.Profile{
   470  				SampleType:  []*profilev1.ValueType{{}},
   471  				Sample:      []*profilev1.Sample{},
   472  				Location:    []*profilev1.Location{},
   473  				Function:    []*profilev1.Function{},
   474  				Mapping:     []*profilev1.Mapping{{Id: 1}},
   475  				StringTable: []string{""},
   476  			},
   477  		},
   478  		{
   479  			name: "nil_mapping",
   480  			profile: &profilev1.Profile{
   481  				SampleType: []*profilev1.ValueType{{}},
   482  				Sample:     []*profilev1.Sample{{LocationId: []uint64{1}, Value: []int64{1}}},
   483  				Location:   []*profilev1.Location{{MappingId: 1, Line: []*profilev1.Line{{FunctionId: 1}}}},
   484  				Function:   []*profilev1.Function{nil},
   485  				Mapping:    []*profilev1.Mapping{nil},
   486  			},
   487  			expected: &profilev1.Profile{
   488  				SampleType:  []*profilev1.ValueType{{}},
   489  				Sample:      []*profilev1.Sample{},
   490  				Location:    []*profilev1.Location{},
   491  				Function:    []*profilev1.Function{},
   492  				Mapping:     []*profilev1.Mapping{},
   493  				StringTable: []string{""},
   494  			},
   495  		},
   496  	}
   497  
   498  	for _, tc := range testCases {
   499  		tc := tc
   500  		t.Run(tc.name, func(t *testing.T) {
   501  			sanitizeProfile(tc.profile, new(sanitizeStats))
   502  			assert.Equal(t, tc.expected, tc.profile)
   503  		})
   504  	}
   505  }
   506  
   507  func Test_sanitize_fixtures(t *testing.T) {
   508  	require.NoError(t, filepath.WalkDir("testdata", func(path string, d fs.DirEntry, err error) error {
   509  		switch {
   510  		case err != nil:
   511  			return err
   512  		case filepath.Ext(path) == ".txt":
   513  			return nil
   514  		case d.IsDir():
   515  			switch d.Name() {
   516  			case "fuzz":
   517  			case "malformed":
   518  				return fs.SkipDir
   519  			default:
   520  				return nil
   521  			}
   522  		}
   523  
   524  		t.Run(path, func(t *testing.T) {
   525  			f, err := OpenFile(path)
   526  			require.NoError(t, err)
   527  			c := f.CloneVT()
   528  			sanitizeProfile(f.Profile, new(sanitizeStats))
   529  			assert.Equal(t, len(c.Sample), len(f.Sample))
   530  			assert.Equal(t, len(c.Location), len(f.Location))
   531  			assert.Equal(t, len(c.Function), len(f.Function))
   532  			assert.Equal(t, len(c.StringTable), len(f.StringTable))
   533  			if len(c.Mapping) != 0 {
   534  				assert.Equal(t, len(c.Mapping), len(f.Mapping))
   535  			} else {
   536  				assert.Equal(t, 1, len(f.Mapping))
   537  			}
   538  		})
   539  		return nil
   540  	}))
   541  }
   542  
   543  func TestFromProfile(t *testing.T) {
   544  	out, err := FromProfile(testhelper.FooBarProfile)
   545  	require.NoError(t, err)
   546  	data, err := proto.Marshal(out)
   547  	require.NoError(t, err)
   548  	outProfile, err := profile.ParseUncompressed(data)
   549  	require.NoError(t, err)
   550  
   551  	require.Equal(t, testhelper.FooBarProfile, outProfile)
   552  }
   553  
   554  const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
   555  
   556  func RandStringBytes(n int) string {
   557  	b := make([]byte, n)
   558  	for i := range b {
   559  		b[i] = letterBytes[rand.Intn(len(letterBytes))]
   560  	}
   561  	return string(b)
   562  }
   563  
   564  func BenchmarkNormalize(b *testing.B) {
   565  	profiles := make([]*Profile, b.N)
   566  	for i := 0; i < b.N; i++ {
   567  		builder := testhelper.NewProfileBuilder(0).CPUProfile()
   568  		// 10% of samples should be dropped.
   569  		for i := 0; i < 1000; i++ {
   570  			builder.ForStacktraceString(RandStringBytes(3), RandStringBytes(3)).AddSamples(0)
   571  		}
   572  		for i := 0; i < 10000; i++ {
   573  			builder.ForStacktraceString(RandStringBytes(3), RandStringBytes(3)).AddSamples(1)
   574  		}
   575  		profiles[i] = &Profile{Profile: builder.Profile}
   576  	}
   577  
   578  	b.ResetTimer()
   579  	b.ReportAllocs()
   580  	for i := 0; i < b.N; i++ {
   581  		profiles[i].Normalize()
   582  	}
   583  }
   584  
   585  func TestRemoveDuplicateSampleStacktraces(t *testing.T) {
   586  	p, err := OpenFile("testdata/heap")
   587  	require.NoError(t, err)
   588  	duplicate := countSampleDuplicates(p)
   589  	total := len(p.Sample)
   590  	t.Log("total dupe", duplicate)
   591  	t.Log("total samples", total)
   592  
   593  	p.Normalize()
   594  
   595  	require.Equal(t, 0, countSampleDuplicates(p), "duplicates should be removed")
   596  	require.Equal(t, total-duplicate, len(p.Sample), "unexpected total samples")
   597  }
   598  
   599  func TestEmptyMappingJava(t *testing.T) {
   600  	p, err := OpenFile("testdata/profile_java")
   601  	require.NoError(t, err)
   602  	require.Len(t, p.Mapping, 0)
   603  
   604  	p.Normalize()
   605  	require.Len(t, p.Mapping, 1)
   606  
   607  	for _, loc := range p.Location {
   608  		require.Equal(t, loc.MappingId, uint64(1))
   609  	}
   610  }
   611  
   612  func countSampleDuplicates(p *Profile) int {
   613  	hashes := p.hasher.Hashes(p.Sample)
   614  	uniq := map[uint64][]*profilev1.Sample{}
   615  	for i, s := range p.Sample {
   616  
   617  		if _, ok := uniq[hashes[i]]; !ok {
   618  			uniq[hashes[i]] = []*profilev1.Sample{s}
   619  			continue
   620  		}
   621  		uniq[hashes[i]] = append(uniq[hashes[i]], s)
   622  	}
   623  	totalDupe := 0
   624  	for _, v := range uniq {
   625  		totalDupe += len(v) - 1
   626  	}
   627  	return totalDupe
   628  }
   629  
   630  var prof *profilev1.Profile
   631  
   632  func BenchmarkFromRawBytes(b *testing.B) {
   633  	data, err := os.ReadFile("testdata/heap")
   634  	require.NoError(b, err)
   635  	b.ResetTimer()
   636  	b.ReportAllocs()
   637  	for i := 0; i < b.N; i++ {
   638  		err := FromBytes(data, func(p *profilev1.Profile, i int) error {
   639  			prof = p
   640  			return nil
   641  		})
   642  		if err != nil {
   643  			b.Fatal(err)
   644  		}
   645  	}
   646  }
   647  
   648  func Test_GroupSamplesByLabels(t *testing.T) {
   649  	type testCase struct {
   650  		description string
   651  		input       *profilev1.Profile
   652  		expected    []SampleGroup
   653  	}
   654  
   655  	testCases := []*testCase{
   656  		{
   657  			description: "no samples",
   658  			input:       new(profilev1.Profile),
   659  			expected:    nil,
   660  		},
   661  		{
   662  			description: "single label set",
   663  			input: &profilev1.Profile{
   664  				Sample: []*profilev1.Sample{
   665  					{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}}},
   666  				},
   667  			},
   668  			expected: []SampleGroup{
   669  				{
   670  					Labels: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}},
   671  					Samples: []*profilev1.Sample{
   672  						{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}}},
   673  					},
   674  				},
   675  			},
   676  		},
   677  		{
   678  			description: "all sets are unique",
   679  			input: &profilev1.Profile{
   680  				Sample: []*profilev1.Sample{
   681  					{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}}},
   682  					{Label: []*profilev1.Label{{Key: 1, Str: 3}, {Key: 2, Str: 4}}},
   683  				},
   684  			},
   685  			expected: []SampleGroup{
   686  				{
   687  					Labels: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}},
   688  					Samples: []*profilev1.Sample{
   689  						{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}}},
   690  					},
   691  				},
   692  				{
   693  					Labels: []*profilev1.Label{{Key: 1, Str: 3}, {Key: 2, Str: 4}},
   694  					Samples: []*profilev1.Sample{
   695  						{Label: []*profilev1.Label{{Key: 1, Str: 3}, {Key: 2, Str: 4}}},
   696  					},
   697  				},
   698  			},
   699  		},
   700  		{
   701  			description: "ends with unique label set",
   702  			input: &profilev1.Profile{
   703  				Sample: []*profilev1.Sample{
   704  					{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}}},
   705  					{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}}},
   706  					{Label: []*profilev1.Label{{Key: 1, Str: 3}, {Key: 2, Str: 4}}},
   707  				},
   708  			},
   709  			expected: []SampleGroup{
   710  				{
   711  					Labels: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}},
   712  					Samples: []*profilev1.Sample{
   713  						{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}}},
   714  						{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}}},
   715  					},
   716  				},
   717  				{
   718  					Labels: []*profilev1.Label{{Key: 1, Str: 3}, {Key: 2, Str: 4}},
   719  					Samples: []*profilev1.Sample{
   720  						{Label: []*profilev1.Label{{Key: 1, Str: 3}, {Key: 2, Str: 4}}},
   721  					},
   722  				},
   723  			},
   724  		},
   725  		{
   726  			description: "starts with unique label set",
   727  			input: &profilev1.Profile{
   728  				Sample: []*profilev1.Sample{
   729  					{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}}},
   730  					{Label: []*profilev1.Label{{Key: 1, Str: 3}, {Key: 2, Str: 4}}},
   731  					{Label: []*profilev1.Label{{Key: 1, Str: 3}, {Key: 2, Str: 4}}},
   732  				},
   733  			},
   734  			expected: []SampleGroup{
   735  				{
   736  					Labels: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}},
   737  					Samples: []*profilev1.Sample{
   738  						{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}}},
   739  					},
   740  				},
   741  				{
   742  					Labels: []*profilev1.Label{{Key: 1, Str: 3}, {Key: 2, Str: 4}},
   743  					Samples: []*profilev1.Sample{
   744  						{Label: []*profilev1.Label{{Key: 1, Str: 3}, {Key: 2, Str: 4}}},
   745  						{Label: []*profilev1.Label{{Key: 1, Str: 3}, {Key: 2, Str: 4}}},
   746  					},
   747  				},
   748  			},
   749  		},
   750  		{
   751  			description: "no unique sets",
   752  			input: &profilev1.Profile{
   753  				Sample: []*profilev1.Sample{
   754  					{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}}},
   755  					{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}}},
   756  					{Label: []*profilev1.Label{{Key: 1, Str: 3}, {Key: 2, Str: 4}}},
   757  					{Label: []*profilev1.Label{{Key: 1, Str: 3}, {Key: 2, Str: 4}}},
   758  				},
   759  			},
   760  			expected: []SampleGroup{
   761  				{
   762  					Labels: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}},
   763  					Samples: []*profilev1.Sample{
   764  						{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}}},
   765  						{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}}},
   766  					},
   767  				},
   768  				{
   769  					Labels: []*profilev1.Label{{Key: 1, Str: 3}, {Key: 2, Str: 4}},
   770  					Samples: []*profilev1.Sample{
   771  						{Label: []*profilev1.Label{{Key: 1, Str: 3}, {Key: 2, Str: 4}}},
   772  						{Label: []*profilev1.Label{{Key: 1, Str: 3}, {Key: 2, Str: 4}}},
   773  					},
   774  				},
   775  			},
   776  		},
   777  	}
   778  
   779  	for _, tc := range testCases {
   780  		tc := tc
   781  		t.Run(tc.description, func(t *testing.T) {
   782  			require.Equal(t, tc.expected, GroupSamplesByLabels(tc.input))
   783  		})
   784  	}
   785  }
   786  
   787  func Test_FilterLabelsInPlace(t *testing.T) {
   788  	type testCase struct {
   789  		labels        []*profilev1.Label
   790  		keys          []int64
   791  		expectedOrder []*profilev1.Label
   792  		expectedIndex int
   793  	}
   794  
   795  	testCases := []testCase{
   796  		{
   797  			labels: []*profilev1.Label{
   798  				{Key: 1, Str: 100},
   799  				{Key: 2, Str: 200},
   800  				{Key: 3, Str: 300},
   801  				{Key: 4, Str: 400},
   802  				{Key: 5, Str: 500},
   803  			},
   804  			keys: []int64{2, 4},
   805  			expectedOrder: []*profilev1.Label{
   806  				{Key: 2, Str: 200},
   807  				{Key: 4, Str: 400},
   808  				{Key: 3, Str: 300},
   809  				{Key: 1, Str: 100},
   810  				{Key: 5, Str: 500},
   811  			},
   812  			expectedIndex: 2,
   813  		},
   814  		{
   815  			labels: []*profilev1.Label{
   816  				{Key: 1, Str: 100},
   817  				{Key: 2, Str: 200},
   818  				{Key: 3, Str: 300},
   819  				{Key: 4, Str: 400},
   820  				{Key: 5, Str: 500},
   821  			},
   822  			keys: []int64{1, 3, 5},
   823  			expectedOrder: []*profilev1.Label{
   824  				{Key: 1, Str: 100},
   825  				{Key: 3, Str: 300},
   826  				{Key: 5, Str: 500},
   827  				{Key: 4, Str: 400},
   828  				{Key: 2, Str: 200},
   829  			},
   830  			expectedIndex: 3,
   831  		},
   832  		{
   833  			labels: []*profilev1.Label{
   834  				{Key: 1, Str: 100},
   835  				{Key: 2, Str: 200},
   836  				{Key: 3, Str: 300},
   837  				{Key: 4, Str: 400},
   838  				{Key: 5, Str: 500},
   839  			},
   840  			keys: []int64{6, 7},
   841  			expectedOrder: []*profilev1.Label{
   842  				{Key: 1, Str: 100},
   843  				{Key: 2, Str: 200},
   844  				{Key: 3, Str: 300},
   845  				{Key: 4, Str: 400},
   846  				{Key: 5, Str: 500},
   847  			},
   848  			expectedIndex: 0,
   849  		},
   850  		{
   851  			labels: []*profilev1.Label{
   852  				{Key: 3, Str: 300},
   853  				{Key: 4, Str: 400},
   854  				{Key: 5, Str: 500},
   855  			},
   856  			keys: []int64{1, 2},
   857  			expectedOrder: []*profilev1.Label{
   858  				{Key: 3, Str: 300},
   859  				{Key: 4, Str: 400},
   860  				{Key: 5, Str: 500},
   861  			},
   862  			expectedIndex: 0,
   863  		},
   864  		{
   865  			labels: []*profilev1.Label{
   866  				{Key: 3, Str: 300},
   867  				{Key: 4, Str: 400},
   868  				{Key: 5, Str: 500},
   869  			},
   870  			keys: []int64{4},
   871  			expectedOrder: []*profilev1.Label{
   872  				{Key: 4, Str: 400},
   873  				{Key: 3, Str: 300},
   874  				{Key: 5, Str: 500},
   875  			},
   876  			expectedIndex: 1,
   877  		},
   878  		{
   879  			labels: []*profilev1.Label{
   880  				{Key: 3, Str: 300},
   881  				{Key: 4, Str: 400},
   882  				{Key: 5, Str: 500},
   883  			},
   884  			keys: []int64{3},
   885  			expectedOrder: []*profilev1.Label{
   886  				{Key: 3, Str: 300},
   887  				{Key: 4, Str: 400},
   888  				{Key: 5, Str: 500},
   889  			},
   890  			expectedIndex: 1,
   891  		},
   892  		{
   893  			labels: []*profilev1.Label{
   894  				{Key: 3, Str: 300},
   895  				{Key: 4, Str: 400},
   896  				{Key: 5, Str: 500},
   897  			},
   898  			keys: []int64{5},
   899  			expectedOrder: []*profilev1.Label{
   900  				{Key: 5, Str: 500},
   901  				{Key: 4, Str: 400},
   902  				{Key: 3, Str: 300},
   903  			},
   904  			expectedIndex: 1,
   905  		},
   906  		{
   907  			labels: []*profilev1.Label{
   908  				{Key: 3, Str: 300},
   909  				{Key: 4, Str: 400},
   910  				{Key: 5, Str: 500},
   911  			},
   912  			expectedOrder: []*profilev1.Label{
   913  				{Key: 3, Str: 300},
   914  				{Key: 4, Str: 400},
   915  				{Key: 5, Str: 500},
   916  			},
   917  			expectedIndex: 0,
   918  		},
   919  	}
   920  
   921  	for _, tc := range testCases {
   922  		tc := tc
   923  		t.Run("", func(t *testing.T) {
   924  			boundaryIdx := FilterLabelsInPlace(tc.labels, tc.keys)
   925  			require.Equal(t, tc.expectedOrder, tc.labels)
   926  			require.Equal(t, tc.expectedIndex, boundaryIdx)
   927  		})
   928  	}
   929  }
   930  
   931  func Test_GroupSamplesWithout(t *testing.T) {
   932  	type testCase struct {
   933  		description string
   934  		input       *profilev1.Profile
   935  		expected    []SampleGroup
   936  		without     []int64
   937  	}
   938  
   939  	testCases := []*testCase{
   940  		{
   941  			description: "no samples",
   942  			input:       new(profilev1.Profile),
   943  			expected:    nil,
   944  		},
   945  		{
   946  			description: "no sample labels",
   947  			input: &profilev1.Profile{
   948  				Sample: []*profilev1.Sample{{}, {}},
   949  			},
   950  			expected: []SampleGroup{
   951  				{
   952  					Samples: []*profilev1.Sample{{}, {}},
   953  				},
   954  			},
   955  		},
   956  		{
   957  			description: "without all, single label set",
   958  			input: &profilev1.Profile{
   959  				Sample: []*profilev1.Sample{
   960  					{Label: []*profilev1.Label{{Key: 2, Str: 2}, {Key: 1, Str: 1}}},
   961  				},
   962  			},
   963  			without: []int64{1, 2},
   964  			expected: []SampleGroup{
   965  				{
   966  					Labels: []*profilev1.Label{},
   967  					Samples: []*profilev1.Sample{
   968  						{Label: []*profilev1.Label{{Key: 2, Str: 2}, {Key: 1, Str: 1}}},
   969  					},
   970  				},
   971  			},
   972  		},
   973  		{
   974  			description: "without all, many label sets",
   975  			input: &profilev1.Profile{
   976  				Sample: []*profilev1.Sample{
   977  					{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}}},
   978  					{Label: []*profilev1.Label{{Key: 1, Str: 3}, {Key: 2, Str: 4}}},
   979  					{Label: []*profilev1.Label{{Key: 1, Str: 3}}},
   980  					{Label: []*profilev1.Label{}},
   981  				},
   982  			},
   983  			without: []int64{1, 2},
   984  			expected: []SampleGroup{
   985  				{
   986  					Labels: []*profilev1.Label{},
   987  					Samples: []*profilev1.Sample{
   988  						{Label: []*profilev1.Label{{Key: 2, Str: 2}, {Key: 1, Str: 1}}},
   989  						{Label: []*profilev1.Label{{Key: 2, Str: 4}, {Key: 1, Str: 3}}},
   990  						{Label: []*profilev1.Label{{Key: 1, Str: 3}}},
   991  						{Label: []*profilev1.Label{}},
   992  					},
   993  				},
   994  			},
   995  		},
   996  		{
   997  			description: "without none",
   998  			input: &profilev1.Profile{
   999  				Sample: []*profilev1.Sample{
  1000  					{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}}},
  1001  					{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}}},
  1002  					{Label: []*profilev1.Label{{Key: 1, Str: 3}}},
  1003  					{Label: []*profilev1.Label{}},
  1004  				},
  1005  			},
  1006  			without: []int64{},
  1007  			expected: []SampleGroup{
  1008  				{
  1009  					Labels: []*profilev1.Label{},
  1010  					Samples: []*profilev1.Sample{
  1011  						{Label: []*profilev1.Label{}},
  1012  					},
  1013  				},
  1014  				{
  1015  					Labels: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}},
  1016  					Samples: []*profilev1.Sample{
  1017  						{Label: []*profilev1.Label{}},
  1018  						{Label: []*profilev1.Label{}},
  1019  					},
  1020  				},
  1021  				{
  1022  					Labels: []*profilev1.Label{{Key: 1, Str: 3}},
  1023  					Samples: []*profilev1.Sample{
  1024  						{Label: []*profilev1.Label{}},
  1025  					},
  1026  				},
  1027  			},
  1028  		},
  1029  		{
  1030  			description: "without single, multiple groups",
  1031  			input: &profilev1.Profile{
  1032  				Sample: []*profilev1.Sample{
  1033  					{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 100}, {Key: 3, Str: 3}}},
  1034  					{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 101}, {Key: 3, Str: 3}}},
  1035  					{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 102}, {Key: 3, Str: 4}}},
  1036  					{Label: []*profilev1.Label{{Key: 1, Str: 1}}},
  1037  					{Label: []*profilev1.Label{}},
  1038  				},
  1039  			},
  1040  			without: []int64{2},
  1041  			expected: []SampleGroup{
  1042  				{
  1043  					Labels: []*profilev1.Label{},
  1044  					Samples: []*profilev1.Sample{
  1045  						{Label: []*profilev1.Label{}},
  1046  					},
  1047  				},
  1048  				{
  1049  					Labels: []*profilev1.Label{{Key: 1, Str: 1}},
  1050  					Samples: []*profilev1.Sample{
  1051  						{Label: []*profilev1.Label{}},
  1052  					},
  1053  				},
  1054  				{
  1055  					Labels: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 3, Str: 3}},
  1056  					Samples: []*profilev1.Sample{
  1057  						{Label: []*profilev1.Label{{Key: 2, Str: 100}}},
  1058  						{Label: []*profilev1.Label{{Key: 2, Str: 101}}},
  1059  					},
  1060  				},
  1061  				{
  1062  					Labels: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 3, Str: 4}},
  1063  					Samples: []*profilev1.Sample{
  1064  						{Label: []*profilev1.Label{{Key: 2, Str: 102}}},
  1065  					},
  1066  				},
  1067  			},
  1068  		},
  1069  		{
  1070  			description: "without single, non-existent",
  1071  			input: &profilev1.Profile{
  1072  				Sample: []*profilev1.Sample{
  1073  					{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}, {Key: 3, Str: 3}}},
  1074  					{Label: []*profilev1.Label{{Key: 1, Str: 1}}},
  1075  					{Label: []*profilev1.Label{}},
  1076  				},
  1077  			},
  1078  			without: []int64{7},
  1079  			expected: []SampleGroup{
  1080  				{
  1081  					Labels: []*profilev1.Label{},
  1082  					Samples: []*profilev1.Sample{
  1083  						{Label: []*profilev1.Label{}},
  1084  					},
  1085  				},
  1086  				{
  1087  					Labels: []*profilev1.Label{{Key: 1, Str: 1}},
  1088  					Samples: []*profilev1.Sample{
  1089  						{Label: []*profilev1.Label{}},
  1090  					},
  1091  				},
  1092  				{
  1093  					Labels: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}, {Key: 3, Str: 3}},
  1094  					Samples: []*profilev1.Sample{
  1095  						{Label: []*profilev1.Label{}},
  1096  					},
  1097  				},
  1098  			},
  1099  		},
  1100  		{
  1101  			description: "without multiple, non-existent mixed",
  1102  			input: &profilev1.Profile{
  1103  				Sample: []*profilev1.Sample{
  1104  					{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}, {Key: 3, Str: 3}}},
  1105  					{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}, {Key: 3, Str: 13}}},
  1106  					{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}, {Key: 3, Str: 3}, {Key: 5, Str: 5}}},
  1107  					{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 2}, {Key: 3, Str: 13}, {Key: 5, Str: 15}}},
  1108  					{Label: []*profilev1.Label{{Key: 1, Str: 1}}},
  1109  					{Label: []*profilev1.Label{}},
  1110  				},
  1111  			},
  1112  			without: []int64{2, 3, 5},
  1113  			expected: []SampleGroup{
  1114  				{
  1115  					Labels: []*profilev1.Label{},
  1116  					Samples: []*profilev1.Sample{
  1117  						{Label: []*profilev1.Label{}},
  1118  					},
  1119  				},
  1120  				{
  1121  					Labels: []*profilev1.Label{{Key: 1, Str: 1}},
  1122  					Samples: []*profilev1.Sample{
  1123  						{Label: []*profilev1.Label{{Key: 3, Str: 3}, {Key: 2, Str: 2}}},
  1124  						{Label: []*profilev1.Label{{Key: 3, Str: 13}, {Key: 2, Str: 2}}},
  1125  						{Label: []*profilev1.Label{{Key: 5, Str: 5}, {Key: 3, Str: 3}, {Key: 2, Str: 2}}},
  1126  						{Label: []*profilev1.Label{{Key: 5, Str: 15}, {Key: 3, Str: 13}, {Key: 2, Str: 2}}},
  1127  						{Label: []*profilev1.Label{}},
  1128  					},
  1129  				},
  1130  			},
  1131  		},
  1132  		{
  1133  			description: "without single existent, single group",
  1134  			input: &profilev1.Profile{
  1135  				Sample: []*profilev1.Sample{
  1136  					{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 100}, {Key: 3, Str: 3}}},
  1137  					{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 3, Str: 3}}},
  1138  				},
  1139  			},
  1140  			without: []int64{2},
  1141  			expected: []SampleGroup{
  1142  				{
  1143  					Labels: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 3, Str: 3}},
  1144  					Samples: []*profilev1.Sample{
  1145  						{Label: []*profilev1.Label{{Key: 2, Str: 100}}},
  1146  						{Label: []*profilev1.Label{}},
  1147  					},
  1148  				},
  1149  			},
  1150  		},
  1151  		{
  1152  			description: "Testcase for extra labels capacity (restoreRemovedLabels nil check)",
  1153  			input: &profilev1.Profile{
  1154  				Sample: []*profilev1.Sample{
  1155  					{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 2, Str: 100}, {Key: 3, Str: 3}, nil, nil}[:3]},
  1156  					{Label: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 3, Str: 3}}},
  1157  				}[:2],
  1158  			},
  1159  			without: []int64{2},
  1160  			expected: []SampleGroup{
  1161  				{
  1162  					Labels: []*profilev1.Label{{Key: 1, Str: 1}, {Key: 3, Str: 3}},
  1163  					Samples: []*profilev1.Sample{
  1164  						{Label: []*profilev1.Label{{Key: 2, Str: 100}}},
  1165  						{Label: []*profilev1.Label{}},
  1166  					},
  1167  				},
  1168  			},
  1169  		},
  1170  	}
  1171  
  1172  	for _, tc := range testCases {
  1173  		tc := tc
  1174  		t.Run(tc.description, func(t *testing.T) {
  1175  			require.Equal(t, tc.expected, GroupSamplesWithoutLabelsByKey(tc.input, tc.without))
  1176  			for _, g := range tc.expected {
  1177  				for _, sample := range g.Samples {
  1178  					for _, label := range sample.Label {
  1179  						assert.NotNil(t, label)
  1180  					}
  1181  				}
  1182  			}
  1183  		})
  1184  	}
  1185  }
  1186  
  1187  func Test_SampleExporter_WholeProfile(t *testing.T) {
  1188  	p, err := OpenFile("testdata/heap")
  1189  	require.NoError(t, err)
  1190  	e := NewSampleExporter(p.Profile)
  1191  	n := new(profilev1.Profile)
  1192  	e.ExportSamples(n, p.Sample)
  1193  
  1194  	// Samples are modified in-place, therefore
  1195  	// we have to re-read the profile.
  1196  	p, err = OpenFile("testdata/heap")
  1197  	require.NoError(t, err)
  1198  	requireProfilesEqual(t, p.Profile, n)
  1199  }
  1200  
  1201  func requireProfilesEqual(t *testing.T, expected, actual *profilev1.Profile) {
  1202  	require.Equal(t, expected.SampleType, actual.SampleType)
  1203  	require.Equal(t, expected.PeriodType, actual.PeriodType)
  1204  	require.Equal(t, expected.Period, actual.Period)
  1205  	require.Equal(t, expected.Comment, actual.Comment)
  1206  	require.Equal(t, expected.DropFrames, actual.DropFrames)
  1207  	require.Equal(t, expected.KeepFrames, actual.KeepFrames)
  1208  	require.Equal(t, expected.DefaultSampleType, actual.DefaultSampleType)
  1209  	require.Equal(t, expected.TimeNanos, actual.TimeNanos)
  1210  	require.Equal(t, expected.DurationNanos, actual.DurationNanos)
  1211  	require.Equal(t, expected.Sample, actual.Sample)
  1212  	require.Equal(t, expected.Location, actual.Location)
  1213  	require.Equal(t, expected.Function, actual.Function)
  1214  	require.Equal(t, expected.Mapping, actual.Mapping)
  1215  	require.Equal(t, expected.StringTable, actual.StringTable)
  1216  }
  1217  
  1218  func Test_SampleExporter_Partial(t *testing.T) {
  1219  	p, err := OpenFile("testdata/go.cpu.labels.pprof")
  1220  	require.NoError(t, err)
  1221  	e := NewSampleExporter(p.Profile)
  1222  	n := new(profilev1.Profile)
  1223  	e.ExportSamples(n, p.Sample[:2])
  1224  	expected := &profilev1.Profile{
  1225  		SampleType: []*profilev1.ValueType{
  1226  			{
  1227  				Type: 1,
  1228  				Unit: 2,
  1229  			},
  1230  			{
  1231  				Type: 3,
  1232  				Unit: 4,
  1233  			},
  1234  		},
  1235  		Sample: []*profilev1.Sample{
  1236  			{
  1237  				LocationId: []uint64{1, 2, 3, 4, 5, 6, 3, 7, 8, 9},
  1238  				Value:      []int64{1, 10000000},
  1239  				Label: []*profilev1.Label{
  1240  					{Key: 5, Str: 6},
  1241  					{Key: 7, Str: 8},
  1242  					{Key: 9, Str: 10},
  1243  				},
  1244  			},
  1245  			{
  1246  				LocationId: []uint64{1, 10, 6, 3, 7, 11, 12, 6, 3, 7, 8, 9},
  1247  				Value:      []int64{1, 10000000},
  1248  				Label: []*profilev1.Label{
  1249  					{Key: 5, Str: 6},
  1250  					{Key: 7, Str: 11},
  1251  					{Key: 9, Str: 12},
  1252  				},
  1253  			},
  1254  		},
  1255  		Mapping: []*profilev1.Mapping{
  1256  			{
  1257  				Id:           1,
  1258  				HasFunctions: true,
  1259  			},
  1260  		},
  1261  		Location: []*profilev1.Location{
  1262  			{
  1263  				Id:        1,
  1264  				MappingId: 1,
  1265  				Address:   19497668,
  1266  				Line:      []*profilev1.Line{{FunctionId: 1, Line: 19}},
  1267  			},
  1268  			{
  1269  				Id:        2,
  1270  				MappingId: 1,
  1271  				Address:   19498429,
  1272  				Line:      []*profilev1.Line{{FunctionId: 2, Line: 43}},
  1273  			},
  1274  			{
  1275  				Id:        3,
  1276  				MappingId: 1,
  1277  				Address:   19267106,
  1278  				Line:      []*profilev1.Line{{FunctionId: 3, Line: 40}},
  1279  			},
  1280  			{
  1281  				Id:        4,
  1282  				MappingId: 1,
  1283  				Address:   19499013,
  1284  				Line:      []*profilev1.Line{{FunctionId: 4, Line: 42}},
  1285  			},
  1286  			{
  1287  				Id:        5,
  1288  				MappingId: 1,
  1289  				Address:   19499251,
  1290  				Line:      []*profilev1.Line{{FunctionId: 5, Line: 68}},
  1291  			},
  1292  			{
  1293  				Id:        6,
  1294  				MappingId: 1,
  1295  				Address:   19285318,
  1296  				Line:      []*profilev1.Line{{FunctionId: 6, Line: 101}},
  1297  			},
  1298  			{
  1299  				Id:        7,
  1300  				MappingId: 1,
  1301  				Address:   19285188,
  1302  				Line:      []*profilev1.Line{{FunctionId: 7, Line: 101}},
  1303  			},
  1304  			{
  1305  				Id:        8,
  1306  				MappingId: 1,
  1307  				Address:   19499465,
  1308  				Line:      []*profilev1.Line{{FunctionId: 8, Line: 65}},
  1309  			},
  1310  			{
  1311  				Id:        9,
  1312  				MappingId: 1,
  1313  				Address:   17007057,
  1314  				Line:      []*profilev1.Line{{FunctionId: 9, Line: 250}},
  1315  			},
  1316  			{
  1317  				Id:        10,
  1318  				MappingId: 1,
  1319  				Address:   19497725,
  1320  				Line:      []*profilev1.Line{{FunctionId: 10, Line: 31}},
  1321  			},
  1322  			{
  1323  				Id:        11,
  1324  				MappingId: 1,
  1325  				Address:   19498309,
  1326  				Line:      []*profilev1.Line{{FunctionId: 11, Line: 30}},
  1327  			},
  1328  			{
  1329  				Id:        12,
  1330  				MappingId: 1,
  1331  				Address:   19499236,
  1332  				Line:      []*profilev1.Line{{FunctionId: 5, Line: 67}},
  1333  			},
  1334  		},
  1335  		Function: []*profilev1.Function{
  1336  			{
  1337  				Id:         1,
  1338  				Name:       13,
  1339  				SystemName: 13,
  1340  				Filename:   14,
  1341  			},
  1342  			{
  1343  				Id:         2,
  1344  				Name:       15,
  1345  				SystemName: 15,
  1346  				Filename:   14,
  1347  			},
  1348  			{
  1349  				Id:         3,
  1350  				Name:       16,
  1351  				SystemName: 16,
  1352  				Filename:   17,
  1353  			},
  1354  			{
  1355  				Id:         4,
  1356  				Name:       18,
  1357  				SystemName: 18,
  1358  				Filename:   14,
  1359  			},
  1360  			{
  1361  				Id:         5,
  1362  				Name:       19,
  1363  				SystemName: 19,
  1364  				Filename:   14,
  1365  			},
  1366  			{
  1367  				Id:         6,
  1368  				Name:       20,
  1369  				SystemName: 20,
  1370  				Filename:   21,
  1371  			},
  1372  			{
  1373  				Id:         7,
  1374  				Name:       22,
  1375  				SystemName: 22,
  1376  				Filename:   21,
  1377  			},
  1378  			{
  1379  				Id:         8,
  1380  				Name:       23,
  1381  				SystemName: 23,
  1382  				Filename:   14,
  1383  			},
  1384  			{
  1385  				Id:         9,
  1386  				Name:       24,
  1387  				SystemName: 24,
  1388  				Filename:   25,
  1389  			},
  1390  			{
  1391  				Id:         10,
  1392  				Name:       26,
  1393  				SystemName: 26,
  1394  				Filename:   14,
  1395  			},
  1396  			{
  1397  				Id:         11,
  1398  				Name:       27,
  1399  				SystemName: 27,
  1400  				Filename:   14,
  1401  			},
  1402  		},
  1403  		StringTable: []string{
  1404  			"",
  1405  			"samples",
  1406  			"count",
  1407  			"cpu",
  1408  			"nanoseconds",
  1409  			"foo",
  1410  			"bar",
  1411  			"profile_id",
  1412  			"c717c11b87121639",
  1413  			"function",
  1414  			"slow",
  1415  			"8c946fa4ae322f7f",
  1416  			"fast",
  1417  			"main.work",
  1418  			"/Users/kolesnikovae/Documents/src/pyroscope/examples/golang-push/simple/main.go",
  1419  			"main.slowFunction.func1",
  1420  			"runtime/pprof.Do",
  1421  			"/usr/local/go/src/runtime/pprof/runtime.go",
  1422  			"main.slowFunction",
  1423  			"main.main.func2",
  1424  			"github.com/pyroscope-io/client/pyroscope.TagWrapper.func1",
  1425  			"/Users/kolesnikovae/go/pkg/mod/github.com/pyroscope-io/client@v0.2.4-0.20220607180407-0ba26860ce5b/pyroscope/api.go",
  1426  			"github.com/pyroscope-io/client/pyroscope.TagWrapper",
  1427  			"main.main",
  1428  			"runtime.main",
  1429  			"/usr/local/go/src/runtime/proc.go",
  1430  			"main.fastFunction.func1",
  1431  			"main.fastFunction",
  1432  		},
  1433  		TimeNanos:     1654798932062349000,
  1434  		DurationNanos: 10123363553,
  1435  		PeriodType: &profilev1.ValueType{
  1436  			Type: 3,
  1437  			Unit: 4,
  1438  		},
  1439  		Period: 10000000,
  1440  	}
  1441  	requireProfilesEqual(t, expected, n)
  1442  }
  1443  
  1444  func Test_GroupSamplesWithout_Go_CPU_profile(t *testing.T) {
  1445  	p, err := OpenFile("testdata/go.cpu.labels.pprof")
  1446  	require.NoError(t, err)
  1447  
  1448  	groups := GroupSamplesWithoutLabels(p.Profile, ProfileIDLabelName)
  1449  	require.Len(t, groups, 3)
  1450  
  1451  	assert.Equal(t, groups[0].Labels, []*profilev1.Label{{Key: 18, Str: 19}})
  1452  	assert.Equal(t, len(groups[0].Samples), 5)
  1453  
  1454  	assert.Equal(t, groups[1].Labels, []*profilev1.Label{{Key: 18, Str: 19}, {Key: 22, Str: 23}})
  1455  	assert.Equal(t, len(groups[1].Samples), 325)
  1456  
  1457  	assert.Equal(t, groups[2].Labels, []*profilev1.Label{{Key: 18, Str: 19}, {Key: 22, Str: 27}})
  1458  	assert.Equal(t, len(groups[2].Samples), 150)
  1459  }
  1460  
  1461  func Test_GroupSamplesWithout_dotnet_profile(t *testing.T) {
  1462  	p, err := OpenFile("testdata/dotnet.labels.pprof")
  1463  	require.NoError(t, err)
  1464  
  1465  	groups := GroupSamplesWithoutLabels(p.Profile, ProfileIDLabelName)
  1466  	require.Len(t, groups, 1)
  1467  	assert.Equal(t, groups[0].Labels, []*profilev1.Label{{Key: 64, Str: 65}, {Key: 66, Str: 67}})
  1468  }
  1469  
  1470  func Test_GroupSamplesWithout_single_group_with_optional_span_id(t *testing.T) {
  1471  	// pprof.Do(context.Background(), pprof.Labels("function", "slow", "qwe", "asd", "asdasd", "zxczxc"), func(c context.Context) {
  1472  	//   work(40000)
  1473  	//   pprof.Do(c, pprof.Labels("span_id", "239"), func(c context.Context) {
  1474  	//     work(40000)
  1475  	//   })
  1476  	// })
  1477  	p, err := OpenFile("testdata/single_group_with_optional_span_id.pb.gz")
  1478  	require.NoError(t, err)
  1479  
  1480  	groups := GroupSamplesWithoutLabels(p.Profile, SpanIDLabelName)
  1481  	require.Len(t, groups, 1)
  1482  	assert.Equal(t, groups[0].Labels, []*profilev1.Label{{Key: 5, Str: 6}, {Key: 7, Str: 8}, {Key: 9, Str: 10}})
  1483  }
  1484  
  1485  func Test_GetProfileLanguage_go_cpu_profile(t *testing.T) {
  1486  	p, err := OpenFile("testdata/go.cpu.labels.pprof")
  1487  	require.NoError(t, err)
  1488  
  1489  	language := GetLanguage(p)
  1490  	assert.Equal(t, "go", language)
  1491  }
  1492  
  1493  func Test_GetProfileLanguage_go_heap_profile(t *testing.T) {
  1494  	p, err := OpenFile("testdata/heap")
  1495  	require.NoError(t, err)
  1496  
  1497  	language := GetLanguage(p)
  1498  	assert.Equal(t, "go", language)
  1499  }
  1500  
  1501  func Test_GetProfileLanguage_dotnet_profile(t *testing.T) {
  1502  	p, err := OpenFile("testdata/dotnet.labels.pprof")
  1503  	require.NoError(t, err)
  1504  
  1505  	language := GetLanguage(p)
  1506  	assert.Equal(t, "dotnet", language)
  1507  }
  1508  
  1509  func Test_GetProfileLanguage_java_profile(t *testing.T) {
  1510  	p, err := OpenFile("testdata/profile_java")
  1511  	require.NoError(t, err)
  1512  
  1513  	language := GetLanguage(p)
  1514  	assert.Equal(t, "java", language)
  1515  }
  1516  
  1517  func Test_GetProfileLanguage_python_profile(t *testing.T) {
  1518  	p, err := OpenFile("testdata/profile_python")
  1519  	require.NoError(t, err)
  1520  
  1521  	language := GetLanguage(p)
  1522  	assert.Equal(t, "python", language)
  1523  }
  1524  
  1525  func Test_GetProfileLanguage_ruby_profile(t *testing.T) {
  1526  	p, err := OpenFile("testdata/profile_ruby")
  1527  	require.NoError(t, err)
  1528  
  1529  	language := GetLanguage(p)
  1530  	assert.Equal(t, "ruby", language)
  1531  }
  1532  
  1533  func Test_GetProfileLanguage_nodejs_profile(t *testing.T) {
  1534  	p, err := OpenFile("testdata/profile_nodejs")
  1535  	require.NoError(t, err)
  1536  
  1537  	language := GetLanguage(p)
  1538  	assert.Equal(t, "nodejs", language)
  1539  }
  1540  
  1541  func Test_GetProfileLanguage_rust_profile(t *testing.T) {
  1542  	p, err := OpenFile("testdata/profile_rust")
  1543  	require.NoError(t, err)
  1544  
  1545  	language := GetLanguage(p)
  1546  	assert.Equal(t, "rust", language)
  1547  }
  1548  
  1549  func Benchmark_GetProfileLanguage(b *testing.B) {
  1550  	tests := []string{
  1551  		"testdata/go.cpu.labels.pprof",
  1552  		"testdata/heap",
  1553  		"testdata/dotnet.labels.pprof",
  1554  		"testdata/profile_java",
  1555  		"testdata/profile_nodejs",
  1556  		"testdata/profile_python",
  1557  		"testdata/profile_ruby",
  1558  		"testdata/profile_rust",
  1559  	}
  1560  
  1561  	for _, testdata := range tests {
  1562  		f := testdata
  1563  		b.Run(testdata, func(b *testing.B) {
  1564  			p, err := OpenFile(f)
  1565  			require.NoError(b, err)
  1566  			b.ResetTimer()
  1567  			b.ReportAllocs()
  1568  			for i := 0; i < b.N; i++ {
  1569  				language := GetLanguage(p)
  1570  				if language == "unknown" {
  1571  					b.Fatal()
  1572  				}
  1573  			}
  1574  		})
  1575  	}
  1576  }
  1577  
  1578  func Test_SetProfileMetadata(t *testing.T) {
  1579  	p := &profilev1.Profile{
  1580  		SampleType:  []*profilev1.ValueType{{}},
  1581  		StringTable: []string{"", "qux"},
  1582  		PeriodType:  &profilev1.ValueType{},
  1583  	}
  1584  	pt := &typesv1.ProfileType{
  1585  		ID:         "alfa",
  1586  		Name:       "bravo",
  1587  		SampleType: "foo",
  1588  		SampleUnit: "bar",
  1589  		PeriodType: "baz",
  1590  		PeriodUnit: "qux",
  1591  	}
  1592  	SetProfileMetadata(p, pt, 1, 2)
  1593  	expected := &profilev1.Profile{
  1594  		SampleType: []*profilev1.ValueType{{
  1595  			Type: 3, // foo
  1596  			Unit: 2, // bar
  1597  		}},
  1598  		StringTable: []string{"", "qux", "bar", "foo", "baz"},
  1599  		PeriodType: &profilev1.ValueType{
  1600  			Type: 4, // baz
  1601  			Unit: 1, // qux
  1602  		},
  1603  		TimeNanos:         1,
  1604  		Period:            1,
  1605  		DefaultSampleType: 3, // foo
  1606  	}
  1607  	require.Equal(t, expected.String(), p.String())
  1608  }
  1609  
  1610  func Test_pprof_zero_addr_no_line_locations(t *testing.T) {
  1611  	b, err := OpenFile("testdata/malformed/no_addr_no_line.pb.gz")
  1612  	require.NoError(t, err)
  1613  
  1614  	var found bool
  1615  	for _, loc := range b.Location {
  1616  		if len(loc.Line) == 0 && loc.Address == 0 {
  1617  			found = true
  1618  			break
  1619  		}
  1620  	}
  1621  	if !found {
  1622  		t.Fatal("invalid fixture")
  1623  	}
  1624  
  1625  	b.Normalize()
  1626  	for _, loc := range b.Location {
  1627  		if len(loc.Line) == 0 && loc.Address == 0 {
  1628  			t.Fatal("found location without lines and address")
  1629  		}
  1630  	}
  1631  
  1632  	expected := "samples_total=2 location_empty=1 sample_location_invalid=1"
  1633  	assert.Equal(t, expected, b.stats.pretty())
  1634  }
  1635  
  1636  func TestRawFromBytesWithLimit(t *testing.T) {
  1637  	// Create a simple profile
  1638  	p := &profilev1.Profile{
  1639  		SampleType: []*profilev1.ValueType{
  1640  			{Type: 1, Unit: 2},
  1641  		},
  1642  		Sample: []*profilev1.Sample{
  1643  			{LocationId: []uint64{1}, Value: []int64{100}},
  1644  			{LocationId: []uint64{2}, Value: []int64{200}},
  1645  		},
  1646  		Location: []*profilev1.Location{
  1647  			{Id: 1, MappingId: 1, Line: []*profilev1.Line{{FunctionId: 1, Line: 1}}},
  1648  			{Id: 2, MappingId: 1, Line: []*profilev1.Line{{FunctionId: 2, Line: 1}}},
  1649  		},
  1650  		Mapping: []*profilev1.Mapping{
  1651  			{Id: 1, Filename: 3},
  1652  		},
  1653  		Function: []*profilev1.Function{
  1654  			{Id: 1, Name: 4, SystemName: 4, Filename: 3},
  1655  			{Id: 2, Name: 5, SystemName: 5, Filename: 3},
  1656  		},
  1657  		StringTable: []string{
  1658  			"",
  1659  			"cpu", "nanoseconds",
  1660  			"main.go",
  1661  			"foo", "bar",
  1662  		},
  1663  		PeriodType: &profilev1.ValueType{Type: 1, Unit: 2},
  1664  		TimeNanos:  1,
  1665  		Period:     1,
  1666  	}
  1667  
  1668  	// Marshal the profile to bytes (compressed)
  1669  	data, err := Marshal(p, true)
  1670  	require.NoError(t, err)
  1671  	require.NotEmpty(t, data)
  1672  
  1673  	// Get the actual decompressed size
  1674  	normalProfile, err := RawFromBytesWithLimit(data, -1)
  1675  	require.NoError(t, err)
  1676  	require.NotNil(t, normalProfile)
  1677  	decompressedSize := normalProfile.rawSize
  1678  
  1679  	t.Logf("Compressed size: %d bytes, Decompressed size: %d bytes", len(data), decompressedSize)
  1680  
  1681  	t.Run("unlimited", func(t *testing.T) {
  1682  		// Test with -1 (no limit) - should succeed
  1683  		profile, err := RawFromBytesWithLimit(data, -1)
  1684  		require.NoError(t, err)
  1685  		require.NotNil(t, profile)
  1686  		require.Equal(t, 2, len(profile.Sample))
  1687  	})
  1688  
  1689  	t.Run("limit_exceeded", func(t *testing.T) {
  1690  		// Test with a limit smaller than decompressed size - should fail
  1691  		_, err := RawFromBytesWithLimit(data, int64(decompressedSize/2))
  1692  		require.Error(t, err)
  1693  		require.Contains(t, err.Error(), "decompressed size exceeds maximum allowed size")
  1694  	})
  1695  
  1696  	t.Run("limit_sufficient", func(t *testing.T) {
  1697  		// Test with a limit larger than decompressed size - should succeed
  1698  		profile, err := RawFromBytesWithLimit(data, int64(decompressedSize*2))
  1699  		require.NoError(t, err)
  1700  		require.NotNil(t, profile)
  1701  		require.Equal(t, 2, len(profile.Sample))
  1702  	})
  1703  
  1704  	t.Run("limit_exact", func(t *testing.T) {
  1705  		// Test with limit exactly equal to decompressed size - should succeed
  1706  		profile, err := RawFromBytesWithLimit(data, int64(decompressedSize))
  1707  		require.NoError(t, err)
  1708  		require.NotNil(t, profile)
  1709  		require.Equal(t, 2, len(profile.Sample))
  1710  	})
  1711  }
  1712  
  1713  func TestUnmarshalWithLimit(t *testing.T) {
  1714  	// Create a simple profile
  1715  	p := &profilev1.Profile{
  1716  		SampleType: []*profilev1.ValueType{
  1717  			{Type: 1, Unit: 2},
  1718  		},
  1719  		Sample: []*profilev1.Sample{
  1720  			{LocationId: []uint64{1}, Value: []int64{100}},
  1721  		},
  1722  		Location: []*profilev1.Location{
  1723  			{Id: 1, MappingId: 1, Line: []*profilev1.Line{{FunctionId: 1, Line: 1}}},
  1724  		},
  1725  		Mapping: []*profilev1.Mapping{
  1726  			{Id: 1, Filename: 3},
  1727  		},
  1728  		Function: []*profilev1.Function{
  1729  			{Id: 1, Name: 4, SystemName: 4, Filename: 3},
  1730  		},
  1731  		StringTable: []string{
  1732  			"",
  1733  			"cpu", "nanoseconds",
  1734  			"main.go",
  1735  			"foo",
  1736  		},
  1737  		PeriodType: &profilev1.ValueType{Type: 1, Unit: 2},
  1738  		TimeNanos:  1,
  1739  		Period:     1,
  1740  	}
  1741  
  1742  	// Marshal the profile to bytes (compressed)
  1743  	data, err := Marshal(p, true)
  1744  	require.NoError(t, err)
  1745  
  1746  	// Get the actual decompressed size
  1747  	testProfile := &profilev1.Profile{}
  1748  	err = UnmarshalWithLimit(data, testProfile, -1)
  1749  	require.NoError(t, err)
  1750  	decompressedSize, err := testProfile.MarshalVT()
  1751  	require.NoError(t, err)
  1752  
  1753  	t.Run("unlimited", func(t *testing.T) {
  1754  		result := &profilev1.Profile{}
  1755  		err := UnmarshalWithLimit(data, result, -1)
  1756  		require.NoError(t, err)
  1757  		require.Equal(t, 1, len(result.Sample))
  1758  	})
  1759  
  1760  	t.Run("limit_exceeded", func(t *testing.T) {
  1761  		result := &profilev1.Profile{}
  1762  		err := UnmarshalWithLimit(data, result, int64(len(decompressedSize)/2))
  1763  		require.Error(t, err)
  1764  		require.Contains(t, err.Error(), "decompressed size exceeds maximum allowed size")
  1765  	})
  1766  
  1767  	t.Run("limit_sufficient", func(t *testing.T) {
  1768  		result := &profilev1.Profile{}
  1769  		err := UnmarshalWithLimit(data, result, int64(len(decompressedSize)*2))
  1770  		require.NoError(t, err)
  1771  		require.Equal(t, 1, len(result.Sample))
  1772  	})
  1773  }