Skip to content

Commit

Permalink
reporter: use semantic convention to report build_id
Browse files Browse the repository at this point in the history
open-telemetry/semantic-conventions#1329 got
merged, which introduces a semantic convention for build_ids. Use this
semantic convention in favor of BuildIdKind, that will be dropped with
open-telemetry/opentelemetry-proto#584.

Signed-off-by: Florian Lehner <[email protected]>
  • Loading branch information
florianl committed Sep 12, 2024
1 parent 6270dc8 commit 5216a42
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 32 deletions.
4 changes: 2 additions & 2 deletions processmanager/processinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,12 +300,12 @@ func (pm *ProcessManager) getELFInfo(pr process.Process, mapping *process.Mappin
baseName = "<anonymous-blob>"
}

buildID, _ := ef.GetBuildID()
gnuBuildID, _ := ef.GetBuildID()
mapping2 := *mapping // copy to avoid races if callee saves the closure
open := func() (process.ReadAtCloser, error) {
return pr.OpenMappingFile(&mapping2)
}
pm.reporter.ExecutableMetadata(fileID, baseName, buildID, libpf.Native, open)
pm.reporter.ExecutableMetadata(fileID, baseName, gnuBuildID, libpf.Native, open)

return info
}
Expand Down
2 changes: 1 addition & 1 deletion reporter/iface.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ type SymbolReporter interface {
// that don't support this may pass a `nil` function pointer. Implementations that
// wish to upload executables should NOT block this function to do so and instead just
// open the file and then enqueue the upload in the background.
ExecutableMetadata(fileID libpf.FileID, fileName, buildID string,
ExecutableMetadata(fileID libpf.FileID, fileName, gnuBuildID string,
interp libpf.InterpreterType, open ExecutableOpener)

// FrameMetadata accepts metadata associated with a frame and caches this information before
Expand Down
66 changes: 38 additions & 28 deletions reporter/otlp_reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ var _ Reporter = (*OTLPReporter)(nil)

// execInfo enriches an executable with additional metadata.
type execInfo struct {
fileName string
buildID string
fileName string
gnuBuildID string
}

// sourceInfo allows mapping a frame to its source origin.
Expand Down Expand Up @@ -87,6 +87,12 @@ type traceFramesCounts struct {
timestamps []uint64 // in nanoseconds
}

// attrKeyValue is a helper to populate Profile.attribute_table.
type attrKeyValue struct {
key string
value string
}

// OTLPReporter receives and transforms information to be OTLP/profiles compliant.
type OTLPReporter struct {
// name is the ScopeProfile's name.
Expand Down Expand Up @@ -207,10 +213,10 @@ func (r *OTLPReporter) ReportFallbackSymbol(frameID libpf.FrameID, symbol string
// ExecutableMetadata accepts a fileID with the corresponding filename
// and caches this information.
func (r *OTLPReporter) ExecutableMetadata(fileID libpf.FileID, fileName,
buildID string, _ libpf.InterpreterType, _ ExecutableOpener) {
gnuBuildID string, _ libpf.InterpreterType, _ ExecutableOpener) {
r.executables.Add(fileID, execInfo{
fileName: fileName,
buildID: buildID,
fileName: fileName,
gnuBuildID: gnuBuildID,
})
}

Expand Down Expand Up @@ -515,16 +521,10 @@ func (r *OTLPReporter) getProfile() (profile *profiles.Profile, startTS, endTS u
Unit: int64(getStringMapIndex(stringMap, "nanoseconds")),
},
Period: 1e9 / int64(r.samplesPerSecond),
// LocationIndices - Optional element we do not use.
// AttributeTable - Optional element we do not use.
// AttributeUnits - Optional element we do not use.
// LinkTable - Optional element we do not use.
// DropFrames - Optional element we do not use.
// KeepFrames - Optional element we do not use.
// TimeNanos - Optional element we do not use.
// DurationNanos - Optional element we do not use.
// PeriodType - Optional element we do not use.
// Period - Optional element we do not use.
// Comment - Optional element we do not use.
// DefaultSampleType - Optional element we do not use.
}
Expand Down Expand Up @@ -582,16 +582,21 @@ func (r *OTLPReporter) getProfile() (profile *profiles.Profile, startTS, endTS u
fileName = execInfo.fileName
}

mappingAttributes := addProfileAttributes(profile, []attrKeyValue{
// Once SemConv and its Go package is released with the new
// semantic convention for build_id, replace these hard coded
// strings.
{key: "process.executable.build_id.gnu", value: execInfo.gnuBuildID},
{key: "process.executable.build_id.profiling", value: traceInfo.files[i].StringNoQuotes()},
}, attributeMap)

profile.Mapping = append(profile.Mapping, &profiles.Mapping{
// Id - Optional element we do not use.
MemoryStart: uint64(traceInfo.mappingStarts[i]),
MemoryLimit: uint64(traceInfo.mappingEnds[i]),
FileOffset: traceInfo.mappingFileOffsets[i],
Filename: int64(getStringMapIndex(stringMap, fileName)),
BuildId: int64(getStringMapIndex(stringMap,
traceInfo.files[i].StringNoQuotes())),
BuildIdKind: *profiles.BuildIdKind_BUILD_ID_BINARY_HASH.Enum(),
// Attributes - Optional element we do not use.
Attributes: mappingAttributes,
// HasFunctions - Optional element we do not use.
// HasFilenames - Optional element we do not use.
// HasLineNumbers - Optional element we do not use.
Expand Down Expand Up @@ -667,7 +672,11 @@ func (r *OTLPReporter) getProfile() (profile *profiles.Profile, startTS, endTS u
profile.Location = append(profile.Location, loc)
}

sample.Attributes = getSampleAttributes(profile, traceKey, attributeMap)
sample.Attributes = addProfileAttributes(profile, []attrKeyValue{
{key: string(semconv.ContainerIDKey), value: traceKey.containerID},
{key: string(semconv.ThreadNameKey), value: traceKey.comm},
{key: string(semconv.ServiceNameKey), value: traceKey.apmServiceName},
}, attributeMap)
sample.LocationsLength = uint64(len(traceInfo.frameTypes))
locationIndex += sample.LocationsLength

Expand Down Expand Up @@ -736,32 +745,33 @@ func createFunctionEntry(funcMap map[funcInfo]uint64,
return idx
}

// getSampleAttributes builds a sample-specific list of attributes.
func getSampleAttributes(profile *profiles.Profile,
traceKey traceAndMetaKey, attributeMap map[string]uint64) []uint64 {
indices := make([]uint64, 0, 3)
// addProfileAttributes adds attributes to Profile.attribute_table and returns
// the indices to these attributes.
func addProfileAttributes(profile *profiles.Profile,
attributes []attrKeyValue, attributeMap map[string]uint64) []uint64 {
indices := make([]uint64, 0, len(attributes))

addAttr := func(k attribute.Key, v string) {
if v == "" {
addAttr := func(attr attrKeyValue) {
if attr.value == "" {
return
}
attributeCompositeKey := string(k) + "_" + v
attributeCompositeKey := attr.key + "_" + attr.value
if attributeIndex, exists := attributeMap[attributeCompositeKey]; exists {
indices = append(indices, attributeIndex)
return
}
newIndex := uint64(len(profile.AttributeTable))
indices = append(indices, newIndex)
profile.AttributeTable = append(profile.AttributeTable, &common.KeyValue{
Key: string(k),
Value: &common.AnyValue{Value: &common.AnyValue_StringValue{StringValue: v}},
Key: attr.key,
Value: &common.AnyValue{Value: &common.AnyValue_StringValue{StringValue: attr.value}},
})
attributeMap[attributeCompositeKey] = newIndex
}

addAttr(semconv.ContainerIDKey, traceKey.containerID)
addAttr(semconv.ThreadNameKey, traceKey.comm)
addAttr(semconv.ServiceNameKey, traceKey.apmServiceName)
for i := range attributes {
addAttr(attributes[i])
}

return indices
}
Expand Down
7 changes: 6 additions & 1 deletion reporter/otlp_reporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/open-telemetry/opentelemetry-ebpf-profiler/libpf"
"github.com/stretchr/testify/require"
semconv "go.opentelemetry.io/otel/semconv/v1.25.0"
common "go.opentelemetry.io/proto/otlp/common/v1"
profiles "go.opentelemetry.io/proto/otlp/profiles/v1experimental"
)
Expand Down Expand Up @@ -135,7 +136,11 @@ func TestGetSampleAttributes(t *testing.T) {
t.Run(name, func(t *testing.T) {
indices := make([][]uint64, 0)
for _, k := range tc.k {
indices = append(indices, getSampleAttributes(tc.profile, k, tc.attributeMap))
indices = append(indices, addProfileAttributes(tc.profile, []attrKeyValue{
{key: string(semconv.ContainerIDKey), value: k.containerID},
{key: string(semconv.ThreadNameKey), value: k.comm},
{key: string(semconv.ServiceNameKey), value: k.apmServiceName},
}, tc.attributeMap))
}
require.Equal(t, tc.expectedIndices, indices)
require.Equal(t, tc.expectedAttributeTable, tc.profile.AttributeTable)
Expand Down

0 comments on commit 5216a42

Please sign in to comment.