|
| 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