Skip to content

Commit d2b03de

Browse files
committed
[pkg/testdata] Add function for profiles validation
1 parent e0a37b0 commit d2b03de

File tree

4 files changed

+1023
-2
lines changed

4 files changed

+1023
-2
lines changed

pkg/pdatatest/pprofiletest/profiles.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,11 +181,13 @@ func CompareScopeProfiles(expected, actual pprofile.ScopeProfiles) error {
181181
var outOfOrderErrs error
182182
for e := 0; e < numProfiles; e++ {
183183
elr := expected.Profiles().At(e)
184+
errs = multierr.Append(errs, ValidateProfile(elr))
184185
em := profileAttributesToMap(elr)
185186

186187
var foundMatch bool
187188
for a := 0; a < numProfiles; a++ {
188189
alr := actual.Profiles().At(a)
190+
errs = multierr.Append(errs, ValidateProfile(alr))
189191
if _, ok := matchingProfiles[alr]; ok {
190192
continue
191193
}
@@ -266,7 +268,7 @@ func CompareProfile(expected, actual pprofile.Profile) error {
266268
}
267269

268270
if !reflect.DeepEqual(expected.StringTable(), actual.StringTable()) {
269-
errs = multierr.Append(errs, fmt.Errorf("stringTable does not match expected"))
271+
errs = multierr.Append(errs, fmt.Errorf("stringTable '%v' does not match expected '%v'", actual.StringTable().AsRaw(), expected.StringTable().AsRaw()))
270272
}
271273

272274
if expected.OriginalPayloadFormat() != actual.OriginalPayloadFormat() {

pkg/pdatatest/pprofiletest/profiles_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -707,7 +707,7 @@ func TestCompareProfile(t *testing.T) {
707707
}(),
708708
err: multierr.Combine(
709709
errors.New(`attributes don't match expected: map[key:val], actual: map[key1:val1]`),
710-
errors.New(`stringTable does not match expected`),
710+
errors.New(`stringTable '[ cpu1 nanoseconds1 samples count samples1 count1 cpu2 nanoseconds2]' does not match expected '[ cpu nanoseconds samples count]'`),
711711
errors.New(`period does not match expected '1', actual '2'`),
712712
fmt.Errorf(`sampleType: %w`, fmt.Errorf(`missing expected valueType "unit: 4, type: 3, aggregationTemporality: 1"`)),
713713
fmt.Errorf(`sampleType: %w`, fmt.Errorf(`unexpected valueType "unit: 6, type: 5, aggregationTemporality: 1"`)),
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package pprofiletest // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/pprofiletest"
5+
import (
6+
"errors"
7+
"fmt"
8+
9+
"go.opentelemetry.io/collector/pdata/pcommon"
10+
"go.opentelemetry.io/collector/pdata/pprofile"
11+
)
12+
13+
func ValidateProfile(pp pprofile.Profile) error {
14+
var errs error
15+
16+
stLen := pp.StringTable().Len()
17+
if stLen < 1 {
18+
// Return here to avoid panicking when accessing the string table.
19+
return fmt.Errorf("empty string table, must at least contain the empty string")
20+
}
21+
22+
if pp.StringTable().At(0) != "" {
23+
errs = errors.Join(errs, fmt.Errorf("string table must start with the empty string"))
24+
}
25+
26+
if pp.SampleType().Len() < 1 {
27+
// Since the proto field 'default_sample_type_index' is always valid, there must be at least
28+
// one sample type in the profile.
29+
errs = errors.Join(errs, fmt.Errorf("missing sample type, need at least a default"))
30+
}
31+
32+
errs = errors.Join(errs, validateSampleType(pp))
33+
34+
errs = errors.Join(errs, validateSamples(pp))
35+
36+
if err := validateValueType(stLen, pp.PeriodType()); err != nil {
37+
errs = errors.Join(errs, fmt.Errorf("period_type: %w", err))
38+
}
39+
40+
if err := validateIndex(stLen, pp.DefaultSampleTypeStrindex()); err != nil {
41+
errs = errors.Join(errs, fmt.Errorf("default_sample_type_strindex: %w", err))
42+
}
43+
44+
if err := validateIndices(stLen, pp.CommentStrindices()); err != nil {
45+
errs = errors.Join(errs, fmt.Errorf("comment_strindices: %w", err))
46+
}
47+
48+
if err := validateIndices(pp.AttributeTable().Len(), pp.AttributeIndices()); err != nil {
49+
errs = errors.Join(errs, fmt.Errorf("attribute_indices: %w", err))
50+
}
51+
52+
errs = errors.Join(errs, validateAttributeUnits(pp))
53+
54+
return errs
55+
}
56+
57+
func validateIndices(length int, indices pcommon.Int32Slice) error {
58+
var errs error
59+
60+
for i := range indices.Len() {
61+
if err := validateIndex(length, indices.At(i)); err != nil {
62+
errs = errors.Join(errs, fmt.Errorf("[%d]: %w", i, err))
63+
}
64+
}
65+
66+
return errs
67+
}
68+
69+
func validateIndex(length int, idx int32) error {
70+
if idx < 0 || int(idx) >= length {
71+
return fmt.Errorf("index %d is out of range [0..%d)",
72+
idx, length)
73+
}
74+
return nil
75+
}
76+
77+
func validateSampleType(pp pprofile.Profile) error {
78+
var errs error
79+
80+
stLen := pp.StringTable().Len()
81+
for i := range pp.SampleType().Len() {
82+
if err := validateValueType(stLen, pp.SampleType().At(i)); err != nil {
83+
errs = errors.Join(errs, fmt.Errorf("sample_type[%d]: %w", i, err))
84+
}
85+
}
86+
87+
return errs
88+
}
89+
90+
func validateValueType(stLen int, pvt pprofile.ValueType) error {
91+
var errs error
92+
93+
if err := validateIndex(stLen, pvt.TypeStrindex()); err != nil {
94+
errs = errors.Join(errs, fmt.Errorf("type_strindex: %w", err))
95+
}
96+
97+
if err := validateIndex(stLen, pvt.UnitStrindex()); err != nil {
98+
errs = errors.Join(errs, fmt.Errorf("unit_strindex: %w", err))
99+
}
100+
101+
if pvt.AggregationTemporality() != pprofile.AggregationTemporalityDelta &&
102+
pvt.AggregationTemporality() != pprofile.AggregationTemporalityCumulative {
103+
errs = errors.Join(errs, fmt.Errorf("aggregation_temporality %d is invalid",
104+
pvt.AggregationTemporality()))
105+
}
106+
107+
return errs
108+
}
109+
110+
func validateSamples(pp pprofile.Profile) error {
111+
var errs error
112+
113+
for i := range pp.Sample().Len() {
114+
if err := validateSample(pp, pp.Sample().At(i)); err != nil {
115+
errs = errors.Join(errs, fmt.Errorf("sample[%d]: %w", i, err))
116+
}
117+
}
118+
119+
return errs
120+
}
121+
122+
func validateSample(pp pprofile.Profile, sample pprofile.Sample) error {
123+
var errs error
124+
125+
length := sample.LocationsLength()
126+
if length < 0 {
127+
errs = errors.Join(errs, fmt.Errorf("locations_length %d is negative", length))
128+
}
129+
130+
if length > 0 {
131+
start := sample.LocationsStartIndex()
132+
if err := validateIndex(pp.LocationIndices().Len(), start); err != nil {
133+
errs = errors.Join(errs, fmt.Errorf("locations_start_index: %w", err))
134+
}
135+
136+
end := start + length
137+
if err := validateIndex(pp.LocationIndices().Len(), end-1); err != nil {
138+
errs = errors.Join(errs, fmt.Errorf("locations end (%d+%d): %w", start, length, err))
139+
}
140+
141+
if errs != nil {
142+
// Return here to avoid panicking when accessing the location indices.
143+
return errs
144+
}
145+
146+
for i := start; i < end; i++ {
147+
locIdx := pp.LocationIndices().At(int(i))
148+
if err := validateIndex(pp.LocationTable().Len(), locIdx); err != nil {
149+
errs = errors.Join(errs, fmt.Errorf("location_indices[%d]: %w", i, err))
150+
continue
151+
}
152+
if err := validateLocation(pp, pp.LocationTable().At(int(locIdx))); err != nil {
153+
errs = errors.Join(errs, fmt.Errorf("locations[%d]: %w", i, err))
154+
}
155+
}
156+
}
157+
158+
numValues := pp.SampleType().Len()
159+
if sample.Value().Len() != numValues {
160+
errs = errors.Join(errs, fmt.Errorf("value length %d does not match sample_type length=%d",
161+
sample.Value().Len(), numValues))
162+
}
163+
164+
if err := validateIndices(pp.AttributeTable().Len(), sample.AttributeIndices()); err != nil {
165+
errs = errors.Join(errs, fmt.Errorf("attribute_indices: %w", err))
166+
}
167+
168+
startTime := uint64(pp.Time().AsTime().UnixNano())
169+
endTime := startTime + uint64(pp.Duration().AsTime().UnixNano())
170+
for i := range sample.TimestampsUnixNano().Len() {
171+
ts := sample.TimestampsUnixNano().At(i)
172+
if ts < startTime || ts >= endTime {
173+
errs = errors.Join(errs, fmt.Errorf("timestamps_unix_nano[%d] %d is out of range [%d..%d)",
174+
i, ts, startTime, endTime))
175+
}
176+
}
177+
178+
if sample.HasLinkIndex() {
179+
if err := validateIndex(pp.LinkTable().Len(), sample.LinkIndex()); err != nil {
180+
errs = errors.Join(errs, fmt.Errorf("link_index: %w", err))
181+
}
182+
}
183+
184+
return errs
185+
}
186+
187+
func validateLocation(pp pprofile.Profile, loc pprofile.Location) error {
188+
var errs error
189+
190+
if loc.HasMappingIndex() {
191+
if err := validateIndex(pp.MappingTable().Len(), loc.MappingIndex()); err != nil {
192+
// Continuing would run into a panic.
193+
return fmt.Errorf("mapping_index: %w", err)
194+
}
195+
196+
if err := validateMapping(pp, pp.MappingTable().At(int(loc.MappingIndex()))); err != nil {
197+
errs = errors.Join(errs, fmt.Errorf("mapping: %w", err))
198+
}
199+
}
200+
201+
for i := range loc.Line().Len() {
202+
if err := validateLine(pp, loc.Line().At(i)); err != nil {
203+
errs = errors.Join(errs, fmt.Errorf("line[%d]: %w", i, err))
204+
}
205+
}
206+
207+
if err := validateIndices(pp.AttributeTable().Len(), loc.AttributeIndices()); err != nil {
208+
errs = errors.Join(errs, fmt.Errorf("attribute_indices: %w", err))
209+
}
210+
211+
return errs
212+
}
213+
214+
func validateLine(pp pprofile.Profile, line pprofile.Line) error {
215+
if err := validateIndex(pp.FunctionTable().Len(), line.FunctionIndex()); err != nil {
216+
return fmt.Errorf("function_index: %w", err)
217+
}
218+
219+
return nil
220+
}
221+
222+
func validateMapping(pp pprofile.Profile, mapping pprofile.Mapping) error {
223+
var errs error
224+
225+
if err := validateIndex(pp.StringTable().Len(), mapping.FilenameStrindex()); err != nil {
226+
errs = errors.Join(errs, fmt.Errorf("filename_strindex: %w", err))
227+
}
228+
229+
if err := validateIndices(pp.AttributeTable().Len(), mapping.AttributeIndices()); err != nil {
230+
errs = errors.Join(errs, fmt.Errorf("attribute_indices: %w", err))
231+
}
232+
233+
return errs
234+
}
235+
236+
func validateAttributeUnits(pp pprofile.Profile) error {
237+
var errs error
238+
239+
for i := range pp.AttributeUnits().Len() {
240+
if err := validateAttributeUnit(pp, pp.AttributeUnits().At(i)); err != nil {
241+
errs = errors.Join(errs, fmt.Errorf("attribute_units[%d]: %w", i, err))
242+
}
243+
}
244+
245+
return errs
246+
}
247+
248+
func validateAttributeUnit(pp pprofile.Profile, au pprofile.AttributeUnit) error {
249+
var errs error
250+
251+
if err := validateIndex(pp.StringTable().Len(), au.AttributeKeyStrindex()); err != nil {
252+
errs = errors.Join(errs, fmt.Errorf("attribute_key: %w", err))
253+
}
254+
255+
if err := validateIndex(pp.StringTable().Len(), au.UnitStrindex()); err != nil {
256+
errs = errors.Join(errs, fmt.Errorf("unit: %w", err))
257+
}
258+
259+
return errs
260+
}

0 commit comments

Comments
 (0)