Skip to content

Conversation

@aalexand
Copy link
Member

@aalexand aalexand commented Oct 16, 2025

An initial version of the OpenTelemetry Profiles signal format conformance checker.

There are a few things remaining:

  • More unit tests. Some manual real data testing (e.g. against the eBPF collector output).
  • Add checks for sample values vs timestamps shape.
  • Add warning-level deeper dictionary checks: duplicated entries, unused entries.

@aalexand aalexand requested a review from a team as a code owner October 16, 2025 14:50

func checkStackTable(stackTable []*profiles.Stack, lenLocTable int) error {
if err := checkZeroVal(stackTable); err != nil {
return err
Copy link

@fandreuz fandreuz Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to treat this error differently than the others? Perhaps you could run run the for loop below regardless of what happens here, and prepend this error if it was found. It's valuable to see all errors in the table, even if I don't have the zero-entry for some reason.

Copy link
Member Author

@aalexand aalexand Oct 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, makes sense, I think it should just append and not bail out early. Basically it should behave like other checks. Will do.

strIdxs := map[string]int{}
for idx, s := range strTable {
if origIdx, ok := strIdxs[s]; ok {
errs = errors.Join(errs, fmt.Errorf("duplicate string at index %d, orig index %d: %s", idx, origIdx, s))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this part of the spec? I cannot find it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed in a recent SIG meeting or two that we want to add a couple of things at "should do, unless you know what you are doing":

  1. All dictionary tables should generally have no duplicate entries.
  2. All dictionary tables should generally have no orphaned entries (entries that are not referenced).

But we also said that these are not mandates. I need to figure out what's the best way to handle this in the conformance checker. I thought of switching from the error model to some kind of logging model with severity etc, but this will complicate things. Currently I'm thinking of simply having a flag for optional checks, off by default, and guard the two mentioned checks above with it.

And here specifically it's a bug that I need to fix - in that this should be an optional check.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thanks for the clarification.

return errors.New("resource profiles are empty")
}
for _, resourceProfiles := range data.ResourceProfiles {
// TODO: Check attributes?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a check to verify that a ResourceProfile holds at least one ScopeProfile.

for _, resourceProfiles := range data.ResourceProfiles {
// TODO: Check attributes?
for _, scopeProfiles := range resourceProfiles.ScopeProfiles {
// TODO: Check attributes?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a check to verify that a ScopeProfile holds at least one Profiles.

"google.golang.org/protobuf/proto"
)

func CheckConformance(data *profiles.ProfilesData) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If ebpf-profiler should make use of this, some additional work is needed to transform go.opentelemetry.io/proto/otlp/profiles/v1development to go.opentelemetry.io/collector/pdata/pprofile.

return errors.New("resource profiles are empty")
}
for _, resourceProfiles := range data.ResourceProfiles {
// TODO: Check attributes?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As Attributes in ResourceProfile are not part of the ProfilesDictionary, I think we don't need to check them.

for _, resourceProfiles := range data.ResourceProfiles {
// TODO: Check attributes?
for _, scopeProfiles := range resourceProfiles.ScopeProfiles {
// TODO: Check attributes?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As Attributes in ScopeProfiles are not part of the ProfilesDictionary, I think we don't need to check them.

Comment on lines +63 to +67
for i, strIdx := range prof.CommentStrindices {
if err := checkIndex(len(dict.StringTable), strIdx); err != nil {
errs = errors.Join(errs, prefixErrorf(err, "comment_strindices[%d]", i))
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the idea is to drop this field in the proto, we maybe can already drop the check here.

Suggested change
for i, strIdx := range prof.CommentStrindices {
if err := checkIndex(len(dict.StringTable), strIdx); err != nil {
errs = errors.Join(errs, prefixErrorf(err, "comment_strindices[%d]", i))
}
}

errs = errors.Join(errs, fmt.Errorf("timestamps_unix_nano[%d]=%d is outside profile time range [%d, %d]", i, tsUnixNano, startUnixNano, endUnixNano))
}
}
// TODO: Add a check for the value vs timestamp shapes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might also add a check for the number of values/timestamps.

// A Sample MUST have have at least one values or timestamps_unix_nano entry. If
// both fields are populated, they MUST contain the same number of elements

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants