pkg/pdatatest/pprofiletest/validate.go (194 lines of code) (raw):

// Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 package pprofiletest // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/pprofiletest" import ( "errors" "fmt" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pprofile" ) func ValidateProfile(pp pprofile.Profile) error { var errs error stLen := pp.StringTable().Len() if stLen < 1 { // Return here to avoid panicking when accessing the string table. return fmt.Errorf("empty string table, must at least contain the empty string") } if pp.StringTable().At(0) != "" { errs = errors.Join(errs, fmt.Errorf("string table must start with the empty string")) } if pp.SampleType().Len() < 1 { // Since the proto field 'default_sample_type_index' is always valid, there must be at least // one sample type in the profile. errs = errors.Join(errs, fmt.Errorf("missing sample type, need at least a default")) } errs = errors.Join(errs, validateSampleType(pp)) errs = errors.Join(errs, validateSamples(pp)) if err := validateValueType(stLen, pp.PeriodType()); err != nil { errs = errors.Join(errs, fmt.Errorf("period_type: %w", err)) } if err := validateIndex(stLen, pp.DefaultSampleTypeStrindex()); err != nil { errs = errors.Join(errs, fmt.Errorf("default_sample_type_strindex: %w", err)) } if err := validateIndices(stLen, pp.CommentStrindices()); err != nil { errs = errors.Join(errs, fmt.Errorf("comment_strindices: %w", err)) } if err := validateIndices(pp.AttributeTable().Len(), pp.AttributeIndices()); err != nil { errs = errors.Join(errs, fmt.Errorf("attribute_indices: %w", err)) } errs = errors.Join(errs, validateAttributeUnits(pp)) return errs } func validateIndices(length int, indices pcommon.Int32Slice) error { var errs error for i := range indices.Len() { if err := validateIndex(length, indices.At(i)); err != nil { errs = errors.Join(errs, fmt.Errorf("[%d]: %w", i, err)) } } return errs } func validateIndex(length int, idx int32) error { if idx < 0 || int(idx) >= length { return fmt.Errorf("index %d is out of range [0..%d)", idx, length) } return nil } func validateSampleType(pp pprofile.Profile) error { var errs error stLen := pp.StringTable().Len() for i := range pp.SampleType().Len() { if err := validateValueType(stLen, pp.SampleType().At(i)); err != nil { errs = errors.Join(errs, fmt.Errorf("sample_type[%d]: %w", i, err)) } } return errs } func validateValueType(stLen int, pvt pprofile.ValueType) error { var errs error if err := validateIndex(stLen, pvt.TypeStrindex()); err != nil { errs = errors.Join(errs, fmt.Errorf("type_strindex: %w", err)) } if err := validateIndex(stLen, pvt.UnitStrindex()); err != nil { errs = errors.Join(errs, fmt.Errorf("unit_strindex: %w", err)) } if pvt.AggregationTemporality() != pprofile.AggregationTemporalityDelta && pvt.AggregationTemporality() != pprofile.AggregationTemporalityCumulative { errs = errors.Join(errs, fmt.Errorf("aggregation_temporality %d is invalid", pvt.AggregationTemporality())) } return errs } func validateSamples(pp pprofile.Profile) error { var errs error for i := range pp.Sample().Len() { if err := validateSample(pp, pp.Sample().At(i)); err != nil { errs = errors.Join(errs, fmt.Errorf("sample[%d]: %w", i, err)) } } return errs } func validateSample(pp pprofile.Profile, sample pprofile.Sample) error { var errs error length := sample.LocationsLength() if length < 0 { errs = errors.Join(errs, fmt.Errorf("locations_length %d is negative", length)) } if length > 0 { start := sample.LocationsStartIndex() if err := validateIndex(pp.LocationIndices().Len(), start); err != nil { errs = errors.Join(errs, fmt.Errorf("locations_start_index: %w", err)) } end := start + length if err := validateIndex(pp.LocationIndices().Len(), end-1); err != nil { errs = errors.Join(errs, fmt.Errorf("locations end (%d+%d): %w", start, length, err)) } if errs != nil { // Return here to avoid panicking when accessing the location indices. return errs } for i := start; i < end; i++ { locIdx := pp.LocationIndices().At(int(i)) if err := validateIndex(pp.LocationTable().Len(), locIdx); err != nil { errs = errors.Join(errs, fmt.Errorf("location_indices[%d]: %w", i, err)) continue } if err := validateLocation(pp, pp.LocationTable().At(int(locIdx))); err != nil { errs = errors.Join(errs, fmt.Errorf("locations[%d]: %w", i, err)) } } } numValues := pp.SampleType().Len() if sample.Value().Len() != numValues { errs = errors.Join(errs, fmt.Errorf("value length %d does not match sample_type length=%d", sample.Value().Len(), numValues)) } if err := validateIndices(pp.AttributeTable().Len(), sample.AttributeIndices()); err != nil { errs = errors.Join(errs, fmt.Errorf("attribute_indices: %w", err)) } startTime := uint64(pp.Time().AsTime().UnixNano()) endTime := startTime + uint64(pp.Duration().AsTime().UnixNano()) for i := range sample.TimestampsUnixNano().Len() { ts := sample.TimestampsUnixNano().At(i) if ts < startTime || ts >= endTime { errs = errors.Join(errs, fmt.Errorf("timestamps_unix_nano[%d] %d is out of range [%d..%d)", i, ts, startTime, endTime)) } } if sample.HasLinkIndex() { if err := validateIndex(pp.LinkTable().Len(), sample.LinkIndex()); err != nil { errs = errors.Join(errs, fmt.Errorf("link_index: %w", err)) } } return errs } func validateLocation(pp pprofile.Profile, loc pprofile.Location) error { var errs error if loc.HasMappingIndex() { if err := validateIndex(pp.MappingTable().Len(), loc.MappingIndex()); err != nil { // Continuing would run into a panic. return fmt.Errorf("mapping_index: %w", err) } if err := validateMapping(pp, pp.MappingTable().At(int(loc.MappingIndex()))); err != nil { errs = errors.Join(errs, fmt.Errorf("mapping: %w", err)) } } for i := range loc.Line().Len() { if err := validateLine(pp, loc.Line().At(i)); err != nil { errs = errors.Join(errs, fmt.Errorf("line[%d]: %w", i, err)) } } if err := validateIndices(pp.AttributeTable().Len(), loc.AttributeIndices()); err != nil { errs = errors.Join(errs, fmt.Errorf("attribute_indices: %w", err)) } return errs } func validateLine(pp pprofile.Profile, line pprofile.Line) error { if err := validateIndex(pp.FunctionTable().Len(), line.FunctionIndex()); err != nil { return fmt.Errorf("function_index: %w", err) } return nil } func validateMapping(pp pprofile.Profile, mapping pprofile.Mapping) error { var errs error if err := validateIndex(pp.StringTable().Len(), mapping.FilenameStrindex()); err != nil { errs = errors.Join(errs, fmt.Errorf("filename_strindex: %w", err)) } if err := validateIndices(pp.AttributeTable().Len(), mapping.AttributeIndices()); err != nil { errs = errors.Join(errs, fmt.Errorf("attribute_indices: %w", err)) } return errs } func validateAttributeUnits(pp pprofile.Profile) error { var errs error for i := range pp.AttributeUnits().Len() { if err := validateAttributeUnit(pp, pp.AttributeUnits().At(i)); err != nil { errs = errors.Join(errs, fmt.Errorf("attribute_units[%d]: %w", i, err)) } } return errs } func validateAttributeUnit(pp pprofile.Profile, au pprofile.AttributeUnit) error { var errs error if err := validateIndex(pp.StringTable().Len(), au.AttributeKeyStrindex()); err != nil { errs = errors.Join(errs, fmt.Errorf("attribute_key: %w", err)) } if err := validateIndex(pp.StringTable().Len(), au.UnitStrindex()); err != nil { errs = errors.Join(errs, fmt.Errorf("unit: %w", err)) } return errs }