github.com/grafana/pyroscope@v1.18.0/pkg/model/profile_test.go (about)

     1  package model
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/assert"
     7  	"github.com/stretchr/testify/require"
     8  
     9  	profilev1 "github.com/grafana/pyroscope/api/gen/proto/go/google/v1"
    10  	typesv1 "github.com/grafana/pyroscope/api/gen/proto/go/types/v1"
    11  )
    12  
    13  func Test_extractMappingFilename(t *testing.T) {
    14  	assert.Equal(t, "app", extractMappingFilename(`app`))
    15  	assert.Equal(t, "app", extractMappingFilename(`./app`))
    16  	assert.Equal(t, "app", extractMappingFilename(`/usr/bin/app`))
    17  	assert.Equal(t, "app", extractMappingFilename(`../../../app`))
    18  	assert.Equal(t, "app", extractMappingFilename(`/usr/bin/app\`))
    19  	assert.Equal(t, "app", extractMappingFilename(`/usr/bin/app\\`))
    20  	assert.Equal(t, "my awesome app", extractMappingFilename(`/usr/bin/my awesome app`))
    21  	assert.Equal(t, "app", extractMappingFilename(`/usr/bin/my\ awesome\ app`))
    22  
    23  	assert.Equal(t, "app.exe", extractMappingFilename(`C:\\app.exe`))
    24  	assert.Equal(t, "app.exe", extractMappingFilename(`C:\\./app.exe`))
    25  	assert.Equal(t, "app.exe", extractMappingFilename(`./app.exe`))
    26  	assert.Equal(t, "app.exe", extractMappingFilename(`./../app.exe`))
    27  	assert.Equal(t, "app.exe", extractMappingFilename(`C:\\build\app.exe`))
    28  	assert.Equal(t, "My App.exe", extractMappingFilename(`C:\\build\My App.exe`))
    29  	assert.Equal(t, "Not My App.exe", extractMappingFilename(`C:\\build\Not My App.exe`))
    30  	assert.Equal(t, "app.exe", extractMappingFilename(`\\app.exe`))
    31  	assert.Equal(t, "app.exe", extractMappingFilename(`\\build\app.exe`))
    32  
    33  	assert.Equal(t, "bin", extractMappingFilename(`/usr/bin/`))
    34  	assert.Equal(t, "build", extractMappingFilename(`\\build\`))
    35  
    36  	assert.Equal(t, "", extractMappingFilename(""))
    37  	assert.Equal(t, "", extractMappingFilename(`[vdso]`))
    38  	assert.Equal(t, "", extractMappingFilename(`[vsyscall]`))
    39  	assert.Equal(t, "", extractMappingFilename(`//anon`))
    40  	assert.Equal(t, "not a path actually", extractMappingFilename(`not a path actually`))
    41  }
    42  
    43  func Test_symbolsPartitionKeyForProfile(t *testing.T) {
    44  	tests := []struct {
    45  		name           string
    46  		partitionLabel string
    47  		labels         Labels
    48  		profile        *profilev1.Profile
    49  		expected       string
    50  	}{
    51  		{
    52  			partitionLabel: "",
    53  			profile:        &profilev1.Profile{Mapping: []*profilev1.Mapping{}},
    54  			expected:       "unknown",
    55  		},
    56  		{
    57  			partitionLabel: "",
    58  			profile:        &profilev1.Profile{Mapping: []*profilev1.Mapping{}},
    59  			labels:         Labels{{Name: LabelNameServiceName, Value: "service_foo"}},
    60  			expected:       "service_foo",
    61  		},
    62  		{
    63  			partitionLabel: "",
    64  			profile: &profilev1.Profile{
    65  				Mapping:     []*profilev1.Mapping{{Filename: 1}},
    66  				StringTable: []string{"", "filename"},
    67  			},
    68  			expected: "filename",
    69  		},
    70  		{
    71  			partitionLabel: "partition",
    72  			profile:        &profilev1.Profile{},
    73  			labels:         Labels{{Name: "partition", Value: "partitionValue"}},
    74  			expected:       "partitionValue",
    75  		},
    76  		{ // partition label is specified but not found: mapping is ignored.
    77  			partitionLabel: "partition",
    78  			profile: &profilev1.Profile{
    79  				Mapping:     []*profilev1.Mapping{{Filename: 1}},
    80  				StringTable: []string{"", "valid_filename"},
    81  			},
    82  			expected: "unknown",
    83  		},
    84  		{
    85  			partitionLabel: "partition",
    86  			profile: &profilev1.Profile{
    87  				Mapping:     []*profilev1.Mapping{{Filename: 1}},
    88  				StringTable: []string{"", "valid_filename"},
    89  			},
    90  			expected: "partitionValue",
    91  			labels:   Labels{{Name: "partition", Value: "partitionValue"}},
    92  		},
    93  	}
    94  
    95  	for _, tt := range tests {
    96  		t.Run(tt.name, func(t *testing.T) {
    97  			actual := symbolsPartitionKeyForProfile(tt.labels, tt.partitionLabel, tt.profile)
    98  			assert.Equal(t, tt.expected, actual)
    99  		})
   100  	}
   101  }
   102  
   103  func Test_ParseProfileTypeSelector(t *testing.T) {
   104  	tests := []struct {
   105  		Name    string
   106  		ID      string
   107  		Want    *typesv1.ProfileType
   108  		WantErr string
   109  	}{
   110  		{
   111  			Name: "block_contentions_count",
   112  			ID:   "block:contentions:count:contentions:count",
   113  			Want: &typesv1.ProfileType{
   114  				Name:       "block",
   115  				ID:         "block:contentions:count:contentions:count",
   116  				SampleType: "contentions",
   117  				SampleUnit: "count",
   118  				PeriodType: "contentions",
   119  				PeriodUnit: "count",
   120  			},
   121  		},
   122  		{
   123  			Name: "mutex_delay_nanoseconds",
   124  			ID:   "mutex:delay:nanoseconds:contentions:count",
   125  			Want: &typesv1.ProfileType{
   126  				Name:       "mutex",
   127  				ID:         "mutex:delay:nanoseconds:contentions:count",
   128  				SampleType: "delay",
   129  				SampleUnit: "nanoseconds",
   130  				PeriodType: "contentions",
   131  				PeriodUnit: "count",
   132  			},
   133  		},
   134  		{
   135  			Name: "memory_alloc_space_bytes",
   136  			ID:   "memory:alloc_space:bytes:space:bytes",
   137  			Want: &typesv1.ProfileType{
   138  				Name:       "memory",
   139  				ID:         "memory:alloc_space:bytes:space:bytes",
   140  				SampleType: "alloc_space",
   141  				SampleUnit: "bytes",
   142  				PeriodType: "space",
   143  				PeriodUnit: "bytes",
   144  			},
   145  		},
   146  		{
   147  			Name:    "too_few_parts",
   148  			ID:      "memory:alloc_space:bytes:space",
   149  			WantErr: `profile-type selection must be of the form <name>:<sample-type>:<sample-unit>:<period-type>:<period-unit>(:delta), got(4): "memory:alloc_space:bytes:space"`,
   150  		},
   151  		{
   152  			Name:    "too_many_parts",
   153  			ID:      "memory:alloc_space:bytes:space:bytes:extra:part",
   154  			WantErr: `profile-type selection must be of the form <name>:<sample-type>:<sample-unit>:<period-type>:<period-unit>(:delta), got(7): "memory:alloc_space:bytes:space:bytes:extra:part"`,
   155  		},
   156  		{
   157  			Name:    "empty_string",
   158  			ID:      "",
   159  			WantErr: `profile-type selection must be of the form <name>:<sample-type>:<sample-unit>:<period-type>:<period-unit>(:delta), got(1): ""`,
   160  		},
   161  		{
   162  			Name: "valid_with_delta",
   163  			ID:   "cpu:samples:count:cpu:nanoseconds:delta",
   164  			Want: &typesv1.ProfileType{
   165  				Name:       "cpu",
   166  				ID:         "cpu:samples:count:cpu:nanoseconds:delta",
   167  				SampleType: "samples",
   168  				SampleUnit: "count",
   169  				PeriodType: "cpu",
   170  				PeriodUnit: "nanoseconds",
   171  			},
   172  		},
   173  	}
   174  
   175  	for _, tt := range tests {
   176  		t.Run(tt.Name, func(t *testing.T) {
   177  			got, err := ParseProfileTypeSelector(tt.ID)
   178  			if tt.WantErr != "" {
   179  				require.Error(t, err)
   180  				require.EqualError(t, err, tt.WantErr)
   181  			} else {
   182  				require.NoError(t, err)
   183  				require.Equal(t, tt.Want, got)
   184  			}
   185  		})
   186  	}
   187  }
   188  
   189  func Test_ParseProfileTypeSelector_ValidProfileTypes(t *testing.T) {
   190  	// Shamelessly copied from: https://github.com/grafana/profiles-drilldown/blob/4e261a8744034bddefdec757d5d2e1d8dc0ec2bb/src/shared/infrastructure/profile-metrics/profile-metrics.json#L93
   191  	var validProfileTypes = map[string]*typesv1.ProfileType{
   192  		"block:contentions:count:contentions:count": {
   193  			ID:         "block:contentions:count:contentions:count",
   194  			Name:       "block",
   195  			SampleType: "contentions",
   196  			SampleUnit: "count",
   197  			PeriodType: "contentions",
   198  			PeriodUnit: "count",
   199  		},
   200  		"block:delay:nanoseconds:contentions:count": {
   201  			ID:         "block:delay:nanoseconds:contentions:count",
   202  			Name:       "block",
   203  			SampleType: "delay",
   204  			SampleUnit: "nanoseconds",
   205  			PeriodType: "contentions",
   206  			PeriodUnit: "count",
   207  		},
   208  		"goroutine:goroutine:count:goroutine:count": {
   209  			ID:         "goroutine:goroutine:count:goroutine:count",
   210  			Name:       "goroutine",
   211  			SampleType: "goroutine",
   212  			SampleUnit: "count",
   213  			PeriodType: "goroutine",
   214  			PeriodUnit: "count",
   215  		},
   216  		"goroutines:goroutine:count:goroutine:count": {
   217  			ID:         "goroutines:goroutine:count:goroutine:count",
   218  			Name:       "goroutines",
   219  			SampleType: "goroutine",
   220  			SampleUnit: "count",
   221  			PeriodType: "goroutine",
   222  			PeriodUnit: "count",
   223  		},
   224  		"memory:alloc_in_new_tlab_bytes:bytes::": {
   225  			ID:         "memory:alloc_in_new_tlab_bytes:bytes::",
   226  			Name:       "memory",
   227  			SampleType: "alloc_in_new_tlab_bytes",
   228  			SampleUnit: "bytes",
   229  			PeriodType: "",
   230  			PeriodUnit: "",
   231  		},
   232  		"memory:alloc_in_new_tlab_objects:count::": {
   233  			ID:         "memory:alloc_in_new_tlab_objects:count::",
   234  			Name:       "memory",
   235  			SampleType: "alloc_in_new_tlab_objects",
   236  			SampleUnit: "count",
   237  			PeriodType: "",
   238  			PeriodUnit: "",
   239  		},
   240  		"memory:alloc_objects:count:space:bytes": {
   241  			ID:         "memory:alloc_objects:count:space:bytes",
   242  			Name:       "memory",
   243  			SampleType: "alloc_objects",
   244  			SampleUnit: "count",
   245  			PeriodType: "space",
   246  			PeriodUnit: "bytes",
   247  		},
   248  		"memory:alloc_space:bytes:space:bytes": {
   249  			ID:         "memory:alloc_space:bytes:space:bytes",
   250  			Name:       "memory",
   251  			SampleType: "alloc_space",
   252  			SampleUnit: "bytes",
   253  			PeriodType: "space",
   254  			PeriodUnit: "bytes",
   255  		},
   256  		"memory:inuse_objects:count:space:bytes": {
   257  			ID:         "memory:inuse_objects:count:space:bytes",
   258  			Name:       "memory",
   259  			SampleType: "inuse_objects",
   260  			SampleUnit: "count",
   261  			PeriodType: "space",
   262  			PeriodUnit: "bytes",
   263  		},
   264  		"memory:inuse_space:bytes:space:bytes": {
   265  			ID:         "memory:inuse_space:bytes:space:bytes",
   266  			Name:       "memory",
   267  			SampleType: "inuse_space",
   268  			SampleUnit: "bytes",
   269  			PeriodType: "space",
   270  			PeriodUnit: "bytes",
   271  		},
   272  		"mutex:contentions:count:contentions:count": {
   273  			ID:         "mutex:contentions:count:contentions:count",
   274  			Name:       "mutex",
   275  			SampleType: "contentions",
   276  			SampleUnit: "count",
   277  			PeriodType: "contentions",
   278  			PeriodUnit: "count",
   279  		},
   280  		"mutex:delay:nanoseconds:contentions:count": {
   281  			ID:         "mutex:delay:nanoseconds:contentions:count",
   282  			Name:       "mutex",
   283  			SampleType: "delay",
   284  			SampleUnit: "nanoseconds",
   285  			PeriodType: "contentions",
   286  			PeriodUnit: "count",
   287  		},
   288  		"process_cpu:alloc_samples:count:cpu:nanoseconds": {
   289  			ID:         "process_cpu:alloc_samples:count:cpu:nanoseconds",
   290  			Name:       "process_cpu",
   291  			SampleType: "alloc_samples",
   292  			SampleUnit: "count",
   293  			PeriodType: "cpu",
   294  			PeriodUnit: "nanoseconds",
   295  		},
   296  		"process_cpu:alloc_size:bytes:cpu:nanoseconds": {
   297  			ID:         "process_cpu:alloc_size:bytes:cpu:nanoseconds",
   298  			Name:       "process_cpu",
   299  			SampleType: "alloc_size",
   300  			SampleUnit: "bytes",
   301  			PeriodType: "cpu",
   302  			PeriodUnit: "nanoseconds",
   303  		},
   304  		"process_cpu:cpu:nanoseconds:cpu:nanoseconds": {
   305  			ID:         "process_cpu:cpu:nanoseconds:cpu:nanoseconds",
   306  			Name:       "process_cpu",
   307  			SampleType: "cpu",
   308  			SampleUnit: "nanoseconds",
   309  			PeriodType: "cpu",
   310  			PeriodUnit: "nanoseconds",
   311  		},
   312  		"process_cpu:exception:count:cpu:nanoseconds": {
   313  			ID:         "process_cpu:exception:count:cpu:nanoseconds",
   314  			Name:       "process_cpu",
   315  			SampleType: "exception",
   316  			SampleUnit: "count",
   317  			PeriodType: "cpu",
   318  			PeriodUnit: "nanoseconds",
   319  		},
   320  		"process_cpu:lock_count:count:cpu:nanoseconds": {
   321  			ID:         "process_cpu:lock_count:count:cpu:nanoseconds",
   322  			Name:       "process_cpu",
   323  			SampleType: "lock_count",
   324  			SampleUnit: "count",
   325  			PeriodType: "cpu",
   326  			PeriodUnit: "nanoseconds",
   327  		},
   328  		"process_cpu:lock_time:nanoseconds:cpu:nanoseconds": {
   329  			ID:         "process_cpu:lock_time:nanoseconds:cpu:nanoseconds",
   330  			Name:       "process_cpu",
   331  			SampleType: "lock_time",
   332  			SampleUnit: "nanoseconds",
   333  			PeriodType: "cpu",
   334  			PeriodUnit: "nanoseconds",
   335  		},
   336  		"process_cpu:samples:count::milliseconds": {
   337  			ID:         "process_cpu:samples:count::milliseconds",
   338  			Name:       "process_cpu",
   339  			SampleType: "samples",
   340  			SampleUnit: "count",
   341  			PeriodType: "",
   342  			PeriodUnit: "milliseconds",
   343  		},
   344  		"process_cpu:samples:count:cpu:nanoseconds": {
   345  			ID:         "process_cpu:samples:count:cpu:nanoseconds",
   346  			Name:       "process_cpu",
   347  			SampleType: "samples",
   348  			SampleUnit: "count",
   349  			PeriodType: "cpu",
   350  			PeriodUnit: "nanoseconds",
   351  		},
   352  	}
   353  
   354  	for id, want := range validProfileTypes {
   355  		t.Run(id, func(t *testing.T) {
   356  			got, err := ParseProfileTypeSelector(id)
   357  			require.NoError(t, err)
   358  			require.Equal(t, want, got)
   359  		})
   360  	}
   361  }