github.com/grafana/pyroscope@v1.18.0/pkg/model/sampletype/relabel_test.go (about) 1 package sampletype 2 3 import ( 4 "testing" 5 6 "github.com/stretchr/testify/require" 7 8 typesv1 "github.com/grafana/pyroscope/api/gen/proto/go/types/v1" 9 phlaremodel "github.com/grafana/pyroscope/pkg/model" 10 11 "github.com/prometheus/common/model" 12 "github.com/prometheus/prometheus/model/relabel" 13 "github.com/stretchr/testify/assert" 14 15 googlev1 "github.com/grafana/pyroscope/api/gen/proto/go/google/v1" 16 "github.com/grafana/pyroscope/pkg/pprof" 17 "github.com/grafana/pyroscope/pkg/validation" 18 ) 19 20 func TestRelabelProfile(t *testing.T) { 21 tests := []struct { 22 name string 23 profile *googlev1.Profile 24 rules []*relabel.Config 25 expectedTypes []string 26 expectedValues [][]int64 27 }{ 28 { 29 name: "drop alloc_objects and alloc_space from memory profile", 30 profile: &googlev1.Profile{ 31 StringTable: []string{"", "alloc_objects", "count", "alloc_space", "bytes", "inuse_objects", "inuse_space"}, 32 SampleType: []*googlev1.ValueType{ 33 {Type: 1, Unit: 2}, // alloc_objects, count 34 {Type: 3, Unit: 4}, // alloc_space, bytes 35 {Type: 5, Unit: 2}, // inuse_objects, count 36 {Type: 6, Unit: 4}, // inuse_space, bytes 37 }, 38 Sample: []*googlev1.Sample{ 39 {LocationId: []uint64{1}, Value: []int64{100, 2048, 50, 1024}}, 40 {LocationId: []uint64{2}, Value: []int64{200, 4096, 150, 3072}}, 41 }, 42 Location: []*googlev1.Location{ 43 {Id: 1, MappingId: 1, Address: 0xef}, 44 {Id: 2, MappingId: 1, Address: 0xcafe000}, 45 }, 46 Mapping: []*googlev1.Mapping{{Id: 1}}, 47 }, 48 rules: []*relabel.Config{ 49 { 50 SourceLabels: []model.LabelName{"__type__"}, 51 Regex: relabel.MustNewRegexp("alloc_.*"), 52 Action: relabel.Drop, 53 }, 54 }, 55 expectedTypes: []string{"inuse_objects", "inuse_space"}, 56 expectedValues: [][]int64{ 57 {50, 1024}, 58 {150, 3072}, 59 }, 60 }, 61 { 62 name: "keep only inuse_space", 63 profile: &googlev1.Profile{ 64 StringTable: []string{"", "alloc_objects", "count", "alloc_space", "bytes", "inuse_objects", "inuse_space"}, 65 SampleType: []*googlev1.ValueType{ 66 {Type: 1, Unit: 2}, // alloc_objects, count 67 {Type: 3, Unit: 4}, // alloc_space, bytes 68 {Type: 5, Unit: 2}, // inuse_objects, count 69 {Type: 6, Unit: 4}, // inuse_space, bytes 70 }, 71 Sample: []*googlev1.Sample{ 72 {LocationId: []uint64{1}, Value: []int64{100, 2048, 50, 1024}}, 73 {LocationId: []uint64{2}, Value: []int64{200, 4096, 150, 3072}}, 74 }, 75 Location: []*googlev1.Location{ 76 {Id: 1, MappingId: 1, Address: 0xef}, 77 {Id: 2, MappingId: 1, Address: 0xcafe000}, 78 }, 79 Mapping: []*googlev1.Mapping{{Id: 1}}, 80 }, 81 rules: []*relabel.Config{ 82 { 83 SourceLabels: []model.LabelName{"__type__"}, 84 Regex: relabel.MustNewRegexp("inuse_space"), 85 Action: relabel.Keep, 86 }, 87 }, 88 expectedTypes: []string{"inuse_space"}, 89 expectedValues: [][]int64{ 90 {1024}, 91 {3072}, 92 }, 93 }, 94 { 95 name: "drop by unit - drop count types", 96 profile: &googlev1.Profile{ 97 StringTable: []string{"", "alloc_objects", "count", "alloc_space", "bytes", "inuse_objects", "inuse_space"}, 98 SampleType: []*googlev1.ValueType{ 99 {Type: 1, Unit: 2}, // alloc_objects, count 100 {Type: 3, Unit: 4}, // alloc_space, bytes 101 {Type: 5, Unit: 2}, // inuse_objects, count 102 {Type: 6, Unit: 4}, // inuse_space, bytes 103 }, 104 Sample: []*googlev1.Sample{ 105 {LocationId: []uint64{1}, Value: []int64{100, 2048, 50, 1024}}, 106 }, 107 Location: []*googlev1.Location{ 108 {Id: 1, MappingId: 1, Address: 0xef}, 109 }, 110 Mapping: []*googlev1.Mapping{{Id: 1}}, 111 }, 112 rules: []*relabel.Config{ 113 { 114 SourceLabels: []model.LabelName{"__unit__"}, 115 Regex: relabel.MustNewRegexp("count"), 116 Action: relabel.Drop, 117 }, 118 }, 119 expectedTypes: []string{"alloc_space", "inuse_space"}, 120 expectedValues: [][]int64{ 121 {2048, 1024}, 122 }, 123 }, 124 { 125 name: "drop all sample types", 126 profile: &googlev1.Profile{ 127 StringTable: []string{"", "cpu", "nanoseconds"}, 128 SampleType: []*googlev1.ValueType{ 129 {Type: 1, Unit: 2}, // cpu, nanoseconds 130 }, 131 Sample: []*googlev1.Sample{ 132 {LocationId: []uint64{1}, Value: []int64{1000}}, 133 }, 134 Location: []*googlev1.Location{ 135 {Id: 1, MappingId: 1, Address: 0xef}, 136 }, 137 Mapping: []*googlev1.Mapping{{Id: 1}}, 138 }, 139 rules: []*relabel.Config{ 140 { 141 SourceLabels: []model.LabelName{"__type__"}, 142 Regex: relabel.MustNewRegexp(".*"), 143 Action: relabel.Drop, 144 }, 145 }, 146 expectedTypes: []string{}, 147 expectedValues: [][]int64{}, 148 }, 149 { 150 name: "no rules - no changes", 151 profile: &googlev1.Profile{ 152 StringTable: []string{"", "cpu", "nanoseconds"}, 153 SampleType: []*googlev1.ValueType{ 154 {Type: 1, Unit: 2}, 155 }, 156 Sample: []*googlev1.Sample{ 157 {LocationId: []uint64{1}, Value: []int64{1000}}, 158 }, 159 Location: []*googlev1.Location{ 160 {Id: 1, MappingId: 1, Address: 0xef}, 161 }, 162 Mapping: []*googlev1.Mapping{{Id: 1}}, 163 }, 164 rules: []*relabel.Config{}, 165 expectedTypes: []string{"cpu"}, 166 expectedValues: [][]int64{ 167 {1000}, 168 }, 169 }, 170 { 171 name: "complex relabeling with multiple rules", 172 profile: &googlev1.Profile{ 173 StringTable: []string{"", "samples", "count", "cpu", "nanoseconds", "wall", "goroutines"}, 174 SampleType: []*googlev1.ValueType{ 175 {Type: 1, Unit: 2}, // samples, count 176 {Type: 3, Unit: 4}, // cpu, nanoseconds 177 {Type: 5, Unit: 4}, // wall, nanoseconds 178 {Type: 6, Unit: 2}, // goroutines, count 179 }, 180 Sample: []*googlev1.Sample{ 181 {LocationId: []uint64{1}, Value: []int64{10, 1000000, 2000000, 5}}, 182 {LocationId: []uint64{2}, Value: []int64{20, 3000000, 4000000, 8}}, 183 }, 184 Location: []*googlev1.Location{ 185 {Id: 1, MappingId: 1, Address: 0xef}, 186 {Id: 2, MappingId: 1, Address: 0xcafe000}, 187 }, 188 Mapping: []*googlev1.Mapping{{Id: 1}}, 189 }, 190 rules: []*relabel.Config{ 191 { 192 SourceLabels: []model.LabelName{"__type__"}, 193 Regex: relabel.MustNewRegexp("samples"), 194 Action: relabel.Drop, 195 }, 196 { 197 SourceLabels: []model.LabelName{"__type__", "__unit__"}, 198 Separator: "/", 199 Regex: relabel.MustNewRegexp("goroutines/count"), 200 Action: relabel.Drop, 201 }, 202 }, 203 expectedTypes: []string{"cpu", "wall"}, 204 expectedValues: [][]int64{ 205 {1000000, 2000000}, 206 {3000000, 4000000}, 207 }, 208 }, 209 { 210 name: "keep rule with no matches drops everything", 211 profile: &googlev1.Profile{ 212 StringTable: []string{"", "cpu", "nanoseconds", "wall"}, 213 SampleType: []*googlev1.ValueType{ 214 {Type: 1, Unit: 2}, // cpu, nanoseconds 215 {Type: 3, Unit: 2}, // wall, nanoseconds 216 }, 217 Sample: []*googlev1.Sample{ 218 {LocationId: []uint64{1}, Value: []int64{1000, 2000}}, 219 }, 220 Location: []*googlev1.Location{ 221 {Id: 1, MappingId: 1, Address: 0xef}, 222 }, 223 Mapping: []*googlev1.Mapping{{Id: 1}}, 224 }, 225 rules: []*relabel.Config{ 226 { 227 SourceLabels: []model.LabelName{"__type__"}, 228 Regex: relabel.MustNewRegexp("memory"), 229 Action: relabel.Keep, 230 }, 231 }, 232 expectedTypes: []string{}, 233 expectedValues: [][]int64{}, 234 }, 235 } 236 237 for _, tt := range tests { 238 t.Run(tt.name, func(t *testing.T) { 239 check := func(t testing.TB) { 240 assert.Equal(t, len(tt.expectedTypes), len(tt.profile.SampleType), "sample type count mismatch") 241 for i, expectedType := range tt.expectedTypes { 242 actualType := tt.profile.StringTable[tt.profile.SampleType[i].Type] 243 assert.Equal(t, expectedType, actualType, "sample type at index %d", i) 244 } 245 if tt.expectedValues != nil { 246 assert.Equal(t, len(tt.expectedValues), len(tt.profile.Sample), "sample count mismatch") 247 for i, sample := range tt.profile.Sample { 248 if i < len(tt.expectedValues) { 249 assert.Equal(t, tt.expectedValues[i], sample.Value, "sample values at index %d", i) 250 } 251 } 252 } 253 } 254 255 p := validation.ValidatedProfile{Profile: pprof.RawFromProto(tt.profile)} 256 257 Relabel(p, tt.rules, nil) 258 259 p.Normalize() 260 check(t) 261 }) 262 } 263 } 264 265 func TestTestdata(t *testing.T) { 266 tests := []struct { 267 f string 268 rules []*relabel.Config 269 series phlaremodel.Labels 270 expectedSize int 271 expectedNormalizedSize int 272 }{ 273 { 274 f: "../../../pkg/pprof/testdata/heap", 275 rules: []*relabel.Config{ 276 { 277 SourceLabels: []model.LabelName{"__type__", "service_name"}, 278 Separator: ";", 279 Regex: relabel.MustNewRegexp("inuse_space;test_service_name"), 280 Action: relabel.Keep, 281 }, 282 }, 283 series: []*typesv1.LabelPair{{ 284 Name: "service_name", 285 Value: "test_service_name", 286 }}, 287 expectedSize: 847138, 288 expectedNormalizedSize: 46178, 289 }, 290 } 291 for _, td := range tests { 292 t.Run(td.f, func(t *testing.T) { 293 f, err := pprof.OpenFile(td.f) 294 require.NoError(t, err) 295 require.Equal(t, td.expectedSize, f.SizeVT()) 296 Relabel(validation.ValidatedProfile{Profile: f}, td.rules, td.series) 297 f.Normalize() 298 require.Equal(t, td.expectedNormalizedSize, f.SizeVT()) 299 }) 300 } 301 }