Files
cdb3d3cd0d [pdata/pprofile] copy data if read-only (#15082)
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue.
Ex. Adding a feature - Explain what this achieves.-->
#### Description
When multiple exporters are used in a profiles pipeline, the same data
object is shared and marked as read-only, causing a panic when mutation
is attempted.

Fix this by only creating a copy when the input is marked as read-only.
This preserves the in-place mutation optimization for mutable data while
preventing panics in shared data scenarios.


<!-- Issue number if applicable -->
#### Link to tracking issue
Fixes #15080

<!--Describe what testing was performed and which tests were added.-->
#### Testing

<!--Describe the documentation added.-->
#### Documentation

<!--Please delete paragraphs that you did not use before submitting.-->

---------

Signed-off-by: Florian Lehner <[email protected]>
Co-authored-by: Damien Mathieu <[email protected]>
2026-04-09 10:44:50 +00:00

62 lines
1.6 KiB
Go

// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package pprofile // import "go.opentelemetry.io/collector/pdata/pprofile"
var _ MarshalSizer = (*ProtoMarshaler)(nil)
type ProtoMarshaler struct{}
// MarshalProfiles marshals Profiles to gRPC format bytes.
// If the input data is read-only, it will be copied to a mutable
// instance before mutation.
func (e *ProtoMarshaler) MarshalProfiles(pd Profiles) ([]byte, error) {
// Only copy if data is shared/read-only to avoid unnecessary allocation
pdToUse := pd
if pd.IsReadOnly() {
pdCopy := NewProfiles()
pd.CopyTo(pdCopy)
pdToUse = pdCopy
}
// Convert strings to references for efficient transmission
convertProfilesToReferences(pdToUse)
size := pdToUse.getOrig().SizeProto()
buf := make([]byte, size)
_ = pdToUse.getOrig().MarshalProto(buf)
return buf, nil
}
func (e *ProtoMarshaler) ProfilesSize(pd Profiles) int {
return pd.getOrig().SizeProto()
}
func (e *ProtoMarshaler) ResourceProfilesSize(pd ResourceProfiles) int {
return pd.orig.SizeProto()
}
func (e *ProtoMarshaler) ScopeProfilesSize(pd ScopeProfiles) int {
return pd.orig.SizeProto()
}
func (e *ProtoMarshaler) ProfileSize(pd Profile) int {
return pd.orig.SizeProto()
}
type ProtoUnmarshaler struct{}
func (d *ProtoUnmarshaler) UnmarshalProfiles(buf []byte) (Profiles, error) {
pd := NewProfiles()
err := pd.getOrig().UnmarshalProto(buf)
if err != nil {
return Profiles{}, err
}
// Resolve all string_value_ref and key_ref to their actual strings
// so the pdata API works transparently
resolveProfilesReferences(pd)
return pd, nil
}