From 38de6eeb22ffc9576e39a5003d4b910420cd356d Mon Sep 17 00:00:00 2001 From: Oleg Kozliuk Date: Thu, 2 Oct 2025 13:10:52 +0000 Subject: [PATCH 1/2] Speculative improvement of handling of unsymbolized profiles --- pkg/ingester/otlp/convert.go | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/pkg/ingester/otlp/convert.go b/pkg/ingester/otlp/convert.go index 0b80ae4cce..213ae41a93 100644 --- a/pkg/ingester/otlp/convert.go +++ b/pkg/ingester/otlp/convert.go @@ -243,7 +243,7 @@ func (p *profileBuilder) convertLocationBack(ol *otelProfile.Location, dictionar return 0, fmt.Errorf("could not access mapping: %w", err) } - mappingId, err := p.convertMappingBack(om, dictionary) + mappingId, err := p.convertMappingBack(ol, om, dictionary) if err != nil { return 0, err } @@ -404,11 +404,21 @@ func (p *profileBuilder) convertSampleAttributesToLabelsBack(os *otelProfile.Sam } // convertMappingsBack converts a slice of OpenTelemetry Mapping entries to Google Mapping entries. -func (p *profileBuilder) convertMappingBack(om *otelProfile.Mapping, dictionary *otelProfile.ProfilesDictionary) (uint64, error) { +func (p *profileBuilder) convertMappingBack(ol *otelProfile.Location, om *otelProfile.Mapping, dictionary *otelProfile.ProfilesDictionary) (uint64, error) { if i, ok := p.mappingMap[om]; ok { return i, nil } + hasLines := false + hasFunctions := false + hasInlineFrames := false + for i, line := range ol.Line { + hasFunctions = hasFunctions || line.FunctionIndex > 0 + hasLines = hasLines || line.Line > 0 + hasInlineFrames = hasInlineFrames || i >= 1 + } + hasFilenames := hasLines + buildID, _ := getAttributeValueByKeyOrEmpty(om.AttributeIndices, dictionary, "process.executable.build_id.gnu") filenameLabel, err := at(dictionary.StringTable, om.FilenameStrindex) if err != nil { @@ -420,10 +430,10 @@ func (p *profileBuilder) convertMappingBack(om *otelProfile.Mapping, dictionary FileOffset: om.FileOffset, Filename: p.addstr(filenameLabel), BuildId: p.addstr(buildID), - HasFunctions: true, - HasFilenames: true, - HasLineNumbers: true, - HasInlineFrames: true, + HasFunctions: hasFunctions, + HasFilenames: hasFilenames, + HasLineNumbers: hasLines, + HasInlineFrames: hasInlineFrames, } p.dst.Mapping = append(p.dst.Mapping, gm) gm.Id = uint64(len(p.dst.Mapping)) From 7aba8d4c720bca089a5d310c6074443ecf941b2c Mon Sep 17 00:00:00 2001 From: Oleg Kozliuk Date: Fri, 10 Oct 2025 10:19:42 +0200 Subject: [PATCH 2/2] Improved handling of mixed symbolization cases --- pkg/ingester/otlp/convert.go | 71 +++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 17 deletions(-) diff --git a/pkg/ingester/otlp/convert.go b/pkg/ingester/otlp/convert.go index 213ae41a93..fb137acf94 100644 --- a/pkg/ingester/otlp/convert.go +++ b/pkg/ingester/otlp/convert.go @@ -243,9 +243,9 @@ func (p *profileBuilder) convertLocationBack(ol *otelProfile.Location, dictionar return 0, fmt.Errorf("could not access mapping: %w", err) } - mappingId, err := p.convertMappingBack(ol, om, dictionary) - if err != nil { - return 0, err + mappingId, ok := p.mappingMap[om] + if !ok { + return 0, fmt.Errorf("mapping not found in mappingMap") } gl := &googleProfile.Location{ MappingId: mappingId, @@ -350,6 +350,30 @@ func (p *profileBuilder) convertSampleBack(os *otelProfile.Sample, dictionary *o return nil, fmt.Errorf("invalid stack index: %d", stackIndex) } stack := dictionary.StackTable[stackIndex] + + // First, gather map of locations pointing to each mapping + locationMap := make(map[*otelProfile.Mapping][]*otelProfile.Location) + + for _, locIdx := range stack.LocationIndices { + loc, err := at(dictionary.LocationTable, locIdx) + if err != nil { + return nil, fmt.Errorf("could not access location at index %d: %w", locIdx, err) + } + mapping, err := at(dictionary.MappingTable, loc.GetMappingIndex()) + if err != nil { + return nil, fmt.Errorf("could not access mapping at index %d: %w", loc.GetMappingIndex(), err) + } + locationMap[mapping] = append(locationMap[mapping], loc) + } + + // Now, convert each mapping, based on information from all locations that point to it + for mapping, locs := range locationMap { + _, err := p.convertMappingBack(locs, mapping, dictionary) + if err != nil { + return nil, err + } + } + for _, olocIdx := range stack.LocationIndices { oloc, err := at(dictionary.LocationTable, olocIdx) if err != nil { @@ -403,21 +427,34 @@ func (p *profileBuilder) convertSampleAttributesToLabelsBack(os *otelProfile.Sam return nil } -// convertMappingsBack converts a slice of OpenTelemetry Mapping entries to Google Mapping entries. -func (p *profileBuilder) convertMappingBack(ol *otelProfile.Location, om *otelProfile.Mapping, dictionary *otelProfile.ProfilesDictionary) (uint64, error) { - if i, ok := p.mappingMap[om]; ok { - return i, nil - } - - hasLines := false - hasFunctions := false - hasInlineFrames := false - for i, line := range ol.Line { - hasFunctions = hasFunctions || line.FunctionIndex > 0 - hasLines = hasLines || line.Line > 0 - hasInlineFrames = hasInlineFrames || i >= 1 +// convertMappingBack converts an OpenTelemetry Mapping to a Google Mapping taking into account availability +// of symbol data in all locations that point to this mapping. +func (p *profileBuilder) convertMappingBack(ols []*otelProfile.Location, om *otelProfile.Mapping, dictionary *otelProfile.ProfilesDictionary) (uint64, error) { + hasLines := true + hasFunctions := true + hasInlineFrames := true + hasFilenames := true + + /* + We survey all locations that point to this mapping to determine + whether the mapping has functions, filenames, line numbers, inline frames. + Note that even if a single location does not have symbol information, + the mapping is marked as not having that information. + */ + for _, ol := range ols { + for i, line := range ol.Line { + hasFunctions = hasFunctions && line.FunctionIndex > 0 + hasLines = hasLines && line.Line > 0 + hasInlineFrames = hasInlineFrames && i >= 1 + + if line.FunctionIndex > 0 { + function, _ := at(dictionary.FunctionTable, line.FunctionIndex) + if function != nil { + hasFilenames = hasFilenames && function.FilenameStrindex > 0 + } + } + } } - hasFilenames := hasLines buildID, _ := getAttributeValueByKeyOrEmpty(om.AttributeIndices, dictionary, "process.executable.build_id.gnu") filenameLabel, err := at(dictionary.StringTable, om.FilenameStrindex)