Skip to content

Commit 1886f2c

Browse files
authored
fix: otlp pprof fixes (#3741)
Do not append sample labels to series labels (imagine two samples with process=firefox and process=chrome, these attributes should not be appended to Push request series labels. Instead add them as sample labels. Fix function name for unsymbolized functions, previously it was incorrectly using only one function per mapping. Now we create multiple (a lot of them actually) functions with format libfoo.so 0x... Remove debugging pprof dump to fs
1 parent 749fbab commit 1886f2c

File tree

6 files changed

+342
-150
lines changed

6 files changed

+342
-150
lines changed

.mockery.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ packages:
2424
github.com/grafana/pyroscope/pkg/frontend:
2525
interfaces:
2626
Limits:
27+
github.com/grafana/pyroscope/pkg/ingester/otlp:
28+
interfaces:
29+
PushService:
2730
github.com/grafana/pyroscope/pkg/experiment/metastore/discovery:
2831
interfaces:
2932
Discovery:

pkg/ingester/otlp/convert.go

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@ func ConvertOtelToGoogle(src *otelProfile.Profile) *googleProfile.Profile {
3030
Comment: src.Comment,
3131
Mapping: convertMappingsBack(src.Mapping),
3232
}
33+
stringmap := make(map[string]int)
34+
addstr := func(s string) int64 {
35+
if _, ok := stringmap[s]; !ok {
36+
stringmap[s] = len(dst.StringTable)
37+
dst.StringTable = append(dst.StringTable, s)
38+
}
39+
return int64(stringmap[s])
40+
}
41+
addstr("")
3342

3443
if dst.TimeNanos == 0 {
3544
dst.TimeNanos = time.Now().UnixNano()
@@ -48,49 +57,44 @@ func ConvertOtelToGoogle(src *otelProfile.Profile) *googleProfile.Profile {
4857
gf.Id = uint64(i + 1)
4958
dst.Function = append(dst.Function, gf)
5059
}
60+
funcmap := map[string]uint64{}
61+
addfunc := func(s string) uint64 {
62+
if _, ok := funcmap[s]; !ok {
63+
funcmap[s] = uint64(len(dst.Function)) + 1
64+
dst.Function = append(dst.Function, &googleProfile.Function{
65+
Id: funcmap[s],
66+
Name: addstr(s),
67+
})
68+
}
69+
return funcmap[s]
70+
}
5171

52-
functionOffset := uint64(len(dst.Function)) + 1
5372
dst.Location = []*googleProfile.Location{}
54-
locationMappingIndexAddressMap := make(map[uint64]uint64)
5573
// Convert locations and mappings
5674
for i, loc := range src.Location {
5775
gl := convertLocationBack(loc)
5876
gl.Id = uint64(i + 1)
5977
if len(gl.Line) == 0 {
78+
m := src.Mapping[loc.MappingIndex]
6079
gl.Line = append(gl.Line, &googleProfile.Line{
61-
FunctionId: loc.MappingIndex + functionOffset,
80+
FunctionId: addfunc(fmt.Sprintf("%s 0x%x", src.StringTable[m.Filename], loc.Address)),
6281
})
6382
}
6483
dst.Location = append(dst.Location, gl)
65-
locationMappingIndexAddressMap[loc.MappingIndex] = loc.Address
66-
}
67-
68-
for _, m := range src.Mapping {
69-
address := locationMappingIndexAddressMap[m.Id]
70-
addressStr := fmt.Sprintf("%s 0x%x", dst.StringTable[m.Filename], address)
71-
dst.StringTable = append(dst.StringTable, addressStr)
72-
// i == 0 function_id = functionOffset
73-
id := uint64(len(dst.Function)) + 1
74-
dst.Function = append(dst.Function, &googleProfile.Function{
75-
Id: id,
76-
Name: int64(len(dst.StringTable) - 1),
77-
})
7884
}
7985

8086
// Convert samples
8187
for _, sample := range src.Sample {
82-
gs := convertSampleBack(sample, src.LocationIndices)
88+
gs := convertSampleBack(src, sample, src.LocationIndices, addstr)
8389
dst.Sample = append(dst.Sample, gs)
8490
}
8591

8692
if len(dst.SampleType) == 0 {
87-
dst.StringTable = append(dst.StringTable, "samples")
88-
dst.StringTable = append(dst.StringTable, "ms")
8993
dst.SampleType = []*googleProfile.ValueType{{
90-
Type: int64(len(dst.StringTable) - 2),
91-
Unit: int64(len(dst.StringTable) - 1),
94+
Type: addstr("samples"),
95+
Unit: addstr("ms"),
9296
}}
93-
dst.DefaultSampleType = int64(len(dst.StringTable) - 2)
97+
dst.DefaultSampleType = addstr("samples")
9498
}
9599

96100
return dst
@@ -147,14 +151,15 @@ func convertFunctionBack(of *otelProfile.Function) *googleProfile.Function {
147151
}
148152
}
149153

150-
func convertSampleBack(os *otelProfile.Sample, locationIndexes []int64) *googleProfile.Sample {
154+
func convertSampleBack(p *otelProfile.Profile, os *otelProfile.Sample, locationIndexes []int64, addstr func(s string) int64) *googleProfile.Sample {
151155
gs := &googleProfile.Sample{
152156
Value: os.Value,
153157
}
154158

155159
if len(gs.Value) == 0 {
156160
gs.Value = []int64{int64(len(os.TimestampsUnixNano))}
157161
}
162+
convertSampleAttributesToLabelsBack(p, os, gs, addstr)
158163

159164
for i := os.LocationsStartIndex; i < os.LocationsStartIndex+os.LocationsLength; i++ {
160165
gs.LocationId = append(gs.LocationId, uint64(locationIndexes[i]+1))
@@ -163,6 +168,19 @@ func convertSampleBack(os *otelProfile.Sample, locationIndexes []int64) *googleP
163168
return gs
164169
}
165170

171+
func convertSampleAttributesToLabelsBack(p *otelProfile.Profile, os *otelProfile.Sample, gs *googleProfile.Sample, addstr func(s string) int64) {
172+
gs.Label = make([]*googleProfile.Label, 0, len(os.Attributes))
173+
for _, attribute := range os.Attributes {
174+
att := p.AttributeTable[attribute]
175+
if att.Value.GetStringValue() != "" {
176+
gs.Label = append(gs.Label, &googleProfile.Label{
177+
Key: addstr(att.Key),
178+
Str: addstr(att.Value.GetStringValue()),
179+
})
180+
}
181+
}
182+
}
183+
166184
// convertMappingsBack converts a slice of OpenTelemetry Mapping entries to Google Mapping entries.
167185
func convertMappingsBack(otelMappings []*otelProfile.Mapping) []*googleProfile.Mapping {
168186
googleMappings := make([]*googleProfile.Mapping, len(otelMappings))

pkg/ingester/otlp/ingest_handler.go

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"fmt"
66
"net/http"
7-
"os"
87
"strings"
98

109
"connectrpc.com/connect"
@@ -18,7 +17,6 @@ import (
1817
typesv1 "github.com/grafana/pyroscope/api/gen/proto/go/types/v1"
1918
pprofileotlp "github.com/grafana/pyroscope/api/otlp/collector/profiles/v1experimental"
2019
v1 "github.com/grafana/pyroscope/api/otlp/common/v1"
21-
"github.com/grafana/pyroscope/api/otlp/profiles/v1experimental"
2220
"github.com/grafana/pyroscope/pkg/tenant"
2321
)
2422

@@ -111,16 +109,11 @@ func (h *ingestHandler) Export(ctx context.Context, er *pprofileotlp.ExportProfi
111109
// Add profile attributes
112110
labels = appendAttributesUnique(labels, p.GetAttributes(), processedKeys)
113111

114-
// Add profile-specific attributes from samples/attributetable
115-
labels = appendProfileLabels(labels, p.Profile, processedKeys)
116-
117112
pprofBytes, err := OprofToPprof(p.Profile)
118113
if err != nil {
119114
return &pprofileotlp.ExportProfilesServiceResponse{}, fmt.Errorf("failed to convert from OTLP to legacy pprof: %w", err)
120115
}
121116

122-
_ = os.WriteFile(".tmp/elastic.pprof", pprofBytes, 0644)
123-
124117
req := &pushv1.PushRequest{
125118
Series: []*pushv1.RawProfileSeries{
126119
{
@@ -199,41 +192,3 @@ func appendAttributesUnique(labels []*typesv1.LabelPair, attrs []v1.KeyValue, pr
199192
}
200193
return labels
201194
}
202-
203-
func appendProfileLabels(labels []*typesv1.LabelPair, profile *v1experimental.Profile, processedKeys map[string]bool) []*typesv1.LabelPair {
204-
if profile == nil {
205-
return labels
206-
}
207-
208-
// Create mapping of attribute indices to their values
209-
attrMap := make(map[uint64]v1.AnyValue)
210-
for i, attr := range profile.GetAttributeTable() {
211-
val := attr.GetValue()
212-
if val.GetValue() != nil {
213-
attrMap[uint64(i)] = val
214-
}
215-
}
216-
217-
// Process only attributes referenced in samples
218-
for _, sample := range profile.Sample {
219-
for _, attrIdx := range sample.GetAttributes() {
220-
attr := profile.AttributeTable[attrIdx]
221-
// Skip if we've already processed this key at any level
222-
if processedKeys[attr.Key] {
223-
continue
224-
}
225-
226-
if value, exists := attrMap[attrIdx]; exists {
227-
if sv := value.GetStringValue(); sv != "" {
228-
labels = append(labels, &typesv1.LabelPair{
229-
Name: attr.Key,
230-
Value: sv,
231-
})
232-
processedKeys[attr.Key] = true
233-
}
234-
}
235-
}
236-
}
237-
238-
return labels
239-
}

0 commit comments

Comments
 (0)