Skip to content

Commit 113bdf3

Browse files
committed
[pkg/testdata] Add function for profiles validation
1 parent da63825 commit 113bdf3

File tree

4 files changed

+474
-2
lines changed

4 files changed

+474
-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: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
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+
stLen := pp.StringTable().Len()
15+
if stLen < 1 {
16+
return fmt.Errorf("empty string table, must at least contain the empty string")
17+
}
18+
if pp.StringTable().At(0) != "" {
19+
return fmt.Errorf("string table must start with the empty string")
20+
}
21+
22+
if pp.SampleType().Len() < 1 {
23+
// Since the proto field 'default_sample_type_index' is always valid, there must be at least
24+
// one sample type in the profile.
25+
return fmt.Errorf("missing sample type, need at least a default")
26+
}
27+
28+
if err := validateSampleType(pp); err != nil {
29+
return err
30+
}
31+
32+
if err := validateSample(pp); err != nil {
33+
return err
34+
}
35+
36+
if err := validateValueType(stLen, pp.PeriodType()); err != nil {
37+
return fmt.Errorf("period_type: %w", err)
38+
}
39+
40+
if err := validateIndex(stLen, pp.DefaultSampleTypeStrindex()); err != nil {
41+
return fmt.Errorf("default_sample_type_strindex: %w", err)
42+
}
43+
44+
if err := validateIndices(stLen, pp.CommentStrindices()); err != nil {
45+
return fmt.Errorf("comment_strindices: %w", err)
46+
}
47+
48+
if err := validateIndices(pp.AttributeTable().Len(), pp.AttributeIndices()); err != nil {
49+
return fmt.Errorf("attribute_indices: %w", err)
50+
}
51+
52+
if err := validateAttributeUnits(pp); err != nil {
53+
return err
54+
}
55+
56+
return nil
57+
}
58+
59+
func validateIndices(length int, indices pcommon.Int32Slice) error {
60+
for i := range indices.Len() {
61+
if err := validateIndex(length, indices.At(i)); err != nil {
62+
return fmt.Errorf("[%d]: %w", i, err)
63+
}
64+
}
65+
return nil
66+
}
67+
68+
func validateIndex(length int, idx int32) error {
69+
if idx < 0 || int(idx) >= length {
70+
return fmt.Errorf("index %d is out of range [0..%d)",
71+
idx, length)
72+
}
73+
return nil
74+
}
75+
76+
func validateSampleType(pp pprofile.Profile) error {
77+
stLen := pp.StringTable().Len()
78+
for i := range pp.SampleType().Len() {
79+
if err := validateValueType(stLen, pp.SampleType().At(i)); err != nil {
80+
return fmt.Errorf("sample_type[%d]: %w", i, err)
81+
}
82+
}
83+
return nil
84+
}
85+
86+
func validateValueType(stLen int, pvt pprofile.ValueType) error {
87+
if err := validateIndex(stLen, pvt.TypeStrindex()); err != nil {
88+
return fmt.Errorf("type_strindex: %w", err)
89+
}
90+
if err := validateIndex(stLen, pvt.UnitStrindex()); err != nil {
91+
return fmt.Errorf("unit_strindex: %w", err)
92+
}
93+
if pvt.AggregationTemporality() != pprofile.AggregationTemporalityDelta &&
94+
pvt.AggregationTemporality() != pprofile.AggregationTemporalityCumulative {
95+
return fmt.Errorf("aggregation_temporality %d is invalid",
96+
pvt.AggregationTemporality())
97+
}
98+
return nil
99+
}
100+
101+
func validateSample(pp pprofile.Profile) error {
102+
for i := range pp.Sample().Len() {
103+
if err := validateSampleAt(pp, i); err != nil {
104+
return fmt.Errorf("sample[%d]: %w", i, err)
105+
}
106+
}
107+
return nil
108+
}
109+
110+
func validateSampleAt(pp pprofile.Profile, idx int) error {
111+
sample := pp.Sample().At(idx)
112+
113+
numValues := pp.SampleType().Len()
114+
if sample.Value().Len() != numValues {
115+
return fmt.Errorf("value length %d does not match sample_type length=%d",
116+
sample.Value().Len(), numValues)
117+
}
118+
119+
length := sample.LocationsLength()
120+
if length < 0 {
121+
return fmt.Errorf("locations_length %d is negative", length)
122+
}
123+
124+
start := sample.LocationsStartIndex()
125+
if err := validateIndex(pp.LocationIndices().Len(), start); err != nil {
126+
return fmt.Errorf("locations_start_index: %w", err)
127+
}
128+
129+
end := start + length
130+
if err := validateIndex(pp.LocationIndices().Len(), end); err != nil {
131+
return fmt.Errorf("locations end (%d+%d): %w", start, length, err)
132+
}
133+
134+
for i := start; i < end; i++ {
135+
locIdx := pp.LocationIndices().At(int(i))
136+
if err := validateIndex(pp.LocationTable().Len(), locIdx); err != nil {
137+
return fmt.Errorf("location_indices[%d]: %w", i, err)
138+
}
139+
if err := validateLocation(pp, pp.LocationTable().At(int(locIdx))); err != nil {
140+
return fmt.Errorf("locations[%d]: %w", i, err)
141+
}
142+
}
143+
144+
if err := validateIndices(pp.AttributeTable().Len(), sample.AttributeIndices()); err != nil {
145+
return fmt.Errorf("attribute_indices: %w", err)
146+
}
147+
148+
startTime := uint64(pp.Time().AsTime().UnixNano())
149+
endTime := startTime + uint64(pp.Duration().AsTime().UnixNano())
150+
for i := range sample.TimestampsUnixNano().Len() {
151+
ts := sample.TimestampsUnixNano().At(i)
152+
if ts < startTime || ts >= endTime {
153+
return fmt.Errorf("timestamps_unix_nano[%d] %d is out of range [%d..%d)",
154+
i, ts, startTime, endTime)
155+
}
156+
}
157+
158+
if sample.HasLinkIndex() {
159+
if err := validateIndex(pp.LinkTable().Len(), sample.LinkIndex()); err != nil {
160+
return fmt.Errorf("link_index: %w", err)
161+
}
162+
}
163+
164+
return nil
165+
}
166+
167+
func validateLocation(pp pprofile.Profile, loc pprofile.Location) error {
168+
var errs error
169+
170+
if err := validateIndex(pp.MappingTable().Len(), loc.MappingIndex()); err != nil {
171+
// Continuing would run into a panic.
172+
return fmt.Errorf("mapping_index: %w", err)
173+
}
174+
175+
if err := validateMapping(pp, pp.MappingTable().At(int(loc.MappingIndex()))); err != nil {
176+
errs = errors.Join(errs, fmt.Errorf("mapping: %w", err))
177+
}
178+
179+
for i := range loc.Line().Len() {
180+
if err := validateLine(pp, loc.Line().At(i)); err != nil {
181+
errs = errors.Join(errs, fmt.Errorf("line[%d]: %w", i, err))
182+
}
183+
}
184+
185+
if err := validateIndices(pp.AttributeTable().Len(), loc.AttributeIndices()); err != nil {
186+
errs = errors.Join(errs, fmt.Errorf("attribute_indices: %w", err))
187+
}
188+
189+
return errs
190+
}
191+
192+
func validateLine(pp pprofile.Profile, line pprofile.Line) error {
193+
if err := validateIndex(pp.FunctionTable().Len(), line.FunctionIndex()); err != nil {
194+
return fmt.Errorf("function_index: %w", err)
195+
}
196+
197+
return nil
198+
}
199+
200+
func validateMapping(pp pprofile.Profile, mapping pprofile.Mapping) error {
201+
var errs error
202+
203+
if err := validateIndex(pp.StringTable().Len(), mapping.FilenameStrindex()); err != nil {
204+
errs = errors.Join(errs, fmt.Errorf("filename_strindex: %w", err))
205+
}
206+
207+
if err := validateIndices(pp.AttributeTable().Len(), mapping.AttributeIndices()); err != nil {
208+
errs = errors.Join(errs, fmt.Errorf("attribute_indices: %w", err))
209+
}
210+
211+
return errs
212+
}
213+
214+
func validateAttributeUnits(pp pprofile.Profile) error {
215+
var errs error
216+
217+
for i := range pp.AttributeUnits().Len() {
218+
if err := validateAttributeUnit(pp, pp.AttributeUnits().At(i)); err != nil {
219+
errs = errors.Join(errs, fmt.Errorf("attribute_units[%d]: %w", i, err))
220+
}
221+
}
222+
223+
return errs
224+
}
225+
226+
func validateAttributeUnit(pp pprofile.Profile, au pprofile.AttributeUnit) error {
227+
var errs error
228+
229+
if err := validateIndex(pp.StringTable().Len(), au.AttributeKeyStrindex()); err != nil {
230+
errs = errors.Join(errs, fmt.Errorf("attribute_key: %w", err))
231+
}
232+
233+
if err := validateIndex(pp.StringTable().Len(), au.UnitStrindex()); err != nil {
234+
errs = errors.Join(errs, fmt.Errorf("unit: %w", err))
235+
}
236+
237+
return errs
238+
}

0 commit comments

Comments
 (0)