@@ -6,197 +6,238 @@ import (
6
6
7
7
googleProfile "github.com/grafana/pyroscope/api/gen/proto/go/google/v1"
8
8
otelProfile "github.com/grafana/pyroscope/api/otlp/profiles/v1experimental"
9
-
10
- "google.golang.org/protobuf/proto"
11
9
)
12
10
13
- func OprofToPprof (p * otelProfile.Profile ) ([]byte , error ) {
14
- dst := ConvertOtelToGoogle (p )
15
- return proto .Marshal (dst )
16
- }
11
+ const serviceNameKey = "service.name"
17
12
18
13
// ConvertOtelToGoogle converts an OpenTelemetry profile to a Google profile.
19
- func ConvertOtelToGoogle (src * otelProfile.Profile ) * googleProfile.Profile {
20
- dst := & googleProfile.Profile {
21
- SampleType : convertSampleTypesBack (src .SampleType ),
22
- StringTable : src .StringTable [:],
23
- TimeNanos : src .TimeNanos ,
24
- DurationNanos : src .DurationNanos ,
25
- PeriodType : convertValueTypeBack (src .PeriodType ),
26
- Period : src .Period ,
27
- DefaultSampleType : src .DefaultSampleType ,
28
- DropFrames : src .DropFrames ,
29
- KeepFrames : src .KeepFrames ,
30
- Comment : src .Comment ,
31
- Mapping : convertMappingsBack (src .Mapping ),
32
- }
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 )
14
+ func ConvertOtelToGoogle (src * otelProfile.Profile ) map [string ]* googleProfile.Profile {
15
+ svc2Profile := make (map [string ]* profileBuilder )
16
+ for _ , sample := range src .Sample {
17
+ svc := serviceNameFromSample (src , sample )
18
+ p , ok := svc2Profile [svc ]
19
+ if ! ok {
20
+ p = newProfileBuilder (src )
21
+ svc2Profile [svc ] = p
38
22
}
39
- return int64 ( stringmap [ s ] )
23
+ p . convertSampleBack ( sample )
40
24
}
41
- addstr ("" )
42
25
43
- if dst .TimeNanos == 0 {
44
- dst .TimeNanos = time .Now ().UnixNano ()
45
- }
46
- if dst .DurationNanos == 0 {
47
- dst .DurationNanos = (time .Second * 10 ).Nanoseconds ()
26
+ result := make (map [string ]* googleProfile.Profile )
27
+ for svc , p := range svc2Profile {
28
+ result [svc ] = p .dst
48
29
}
49
30
50
- // attribute_table
51
- // attribute_units
52
- // link_table
31
+ return result
32
+ }
33
+
34
+ type profileBuilder struct {
35
+ src * otelProfile.Profile
36
+ dst * googleProfile.Profile
37
+ stringMap map [string ]int64
38
+ functionMap map [* otelProfile.Function ]uint64
39
+ unsymbolziedFuncNameMap map [string ]uint64
40
+ locationMap map [* otelProfile.Location ]uint64
41
+ mappingMap map [* otelProfile.Mapping ]uint64
42
+ }
53
43
54
- dst .Function = []* googleProfile.Function {}
55
- for i , funcItem := range src .Function {
56
- gf := convertFunctionBack (funcItem )
57
- gf .Id = uint64 (i + 1 )
58
- dst .Function = append (dst .Function , gf )
44
+ func newProfileBuilder (src * otelProfile.Profile ) * profileBuilder {
45
+ res := & profileBuilder {
46
+ src : src ,
47
+ stringMap : make (map [string ]int64 ),
48
+ functionMap : make (map [* otelProfile.Function ]uint64 ),
49
+ locationMap : make (map [* otelProfile.Location ]uint64 ),
50
+ mappingMap : make (map [* otelProfile.Mapping ]uint64 ),
51
+ unsymbolziedFuncNameMap : make (map [string ]uint64 ),
52
+ dst : & googleProfile.Profile {
53
+ TimeNanos : src .TimeNanos ,
54
+ DurationNanos : src .DurationNanos ,
55
+ Period : src .Period ,
56
+ DefaultSampleType : src .DefaultSampleType ,
57
+ DropFrames : src .DropFrames ,
58
+ KeepFrames : src .KeepFrames ,
59
+ Comment : src .Comment ,
60
+ },
61
+ }
62
+ res .addstr ("" )
63
+ res .dst .SampleType = res .convertSampleTypesBack (src .SampleType )
64
+ res .dst .PeriodType = res .convertValueTypeBack (src .PeriodType )
65
+ if len (res .dst .SampleType ) == 0 {
66
+ res .dst .SampleType = []* googleProfile.ValueType {{
67
+ Type : res .addstr ("samples" ),
68
+ Unit : res .addstr ("ms" ),
69
+ }}
70
+ res .dst .DefaultSampleType = res .addstr ("samples" )
59
71
}
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
- }
71
-
72
- dst .Location = []* googleProfile.Location {}
73
- // Convert locations and mappings
74
- for i , loc := range src .Location {
75
- gl := convertLocationBack (loc )
76
- gl .Id = uint64 (i + 1 )
77
- if len (gl .Line ) == 0 {
78
- m := src .Mapping [loc .MappingIndex ]
79
- gl .Line = append (gl .Line , & googleProfile.Line {
80
- FunctionId : addfunc (fmt .Sprintf ("%s 0x%x" , src .StringTable [m .Filename ], loc .Address )),
81
- })
82
- }
83
- dst .Location = append (dst .Location , gl )
72
+ if res .dst .TimeNanos == 0 {
73
+ res .dst .TimeNanos = time .Now ().UnixNano ()
84
74
}
75
+ if res .dst .DurationNanos == 0 {
76
+ res .dst .DurationNanos = (time .Second * 10 ).Nanoseconds ()
77
+ }
78
+ return res
79
+ }
85
80
86
- // Convert samples
87
- for _ , sample := range src .Sample {
88
- gs := convertSampleBack (src , sample , src .LocationIndices , addstr )
89
- dst .Sample = append (dst .Sample , gs )
81
+ func (p * profileBuilder ) addstr (s string ) int64 {
82
+ if i , ok := p .stringMap [s ]; ok {
83
+ return i
90
84
}
85
+ idx := int64 (len (p .dst .StringTable ))
86
+ p .stringMap [s ] = idx
87
+ p .dst .StringTable = append (p .dst .StringTable , s )
88
+ return idx
89
+ }
91
90
92
- if len (dst .SampleType ) == 0 {
93
- dst .SampleType = []* googleProfile.ValueType {{
94
- Type : addstr ("samples" ),
95
- Unit : addstr ("ms" ),
96
- }}
97
- dst .DefaultSampleType = addstr ("samples" )
91
+ func (p * profileBuilder ) addfunc (s string ) uint64 {
92
+ if i , ok := p .unsymbolziedFuncNameMap [s ]; ok {
93
+ return i
94
+ }
95
+ idx := uint64 (len (p .dst .Function )) + 1
96
+ p .unsymbolziedFuncNameMap [s ] = idx
97
+ gf := & googleProfile.Function {
98
+ Id : idx ,
99
+ Name : p .addstr (s ),
98
100
}
101
+ p .dst .Function = append (p .dst .Function , gf )
102
+ return idx
103
+ }
99
104
100
- return dst
105
+ func serviceNameFromSample (p * otelProfile.Profile , sample * otelProfile.Sample ) string {
106
+ for _ , attributeIndex := range sample .Attributes {
107
+ attribute := p .AttributeTable [attributeIndex ]
108
+ if attribute .Key == serviceNameKey {
109
+ return attribute .Value .GetStringValue ()
110
+ }
111
+ }
112
+ return ""
101
113
}
102
114
103
- func convertSampleTypesBack (ost []* otelProfile.ValueType ) []* googleProfile.ValueType {
115
+ func ( p * profileBuilder ) convertSampleTypesBack (ost []* otelProfile.ValueType ) []* googleProfile.ValueType {
104
116
var gst []* googleProfile.ValueType
105
117
for _ , st := range ost {
106
118
gst = append (gst , & googleProfile.ValueType {
107
- Type : st .Type ,
108
- Unit : st .Unit ,
119
+ Type : p . addstr ( p . src . StringTable [ st .Type ]) ,
120
+ Unit : p . addstr ( p . src . StringTable [ st .Unit ]) ,
109
121
})
110
122
}
111
123
return gst
112
124
}
113
125
114
- func convertValueTypeBack (ovt * otelProfile.ValueType ) * googleProfile.ValueType {
126
+ func ( p * profileBuilder ) convertValueTypeBack (ovt * otelProfile.ValueType ) * googleProfile.ValueType {
115
127
if ovt == nil {
116
128
return nil
117
129
}
118
130
return & googleProfile.ValueType {
119
- Type : ovt .Type ,
120
- Unit : ovt .Unit ,
131
+ Type : p . addstr ( p . src . StringTable [ ovt .Type ]) ,
132
+ Unit : p . addstr ( p . src . StringTable [ ovt .Unit ]) ,
121
133
}
122
134
}
123
135
124
- func convertLocationBack (ol * otelProfile.Location ) * googleProfile.Location {
136
+ func (p * profileBuilder ) convertLocationBack (ol * otelProfile.Location ) uint64 {
137
+ if i , ok := p .locationMap [ol ]; ok {
138
+ return i
139
+ }
140
+ om := p .src .Mapping [ol .MappingIndex ]
125
141
gl := & googleProfile.Location {
126
- MappingId : ol . MappingIndex + 1 ,
142
+ MappingId : p . convertMappingBack ( om ) ,
127
143
Address : ol .Address ,
128
144
Line : make ([]* googleProfile.Line , len (ol .Line )),
129
145
IsFolded : ol .IsFolded ,
130
146
}
131
147
for i , line := range ol .Line {
132
- gl .Line [i ] = convertLineBack (line )
148
+ gl .Line [i ] = p . convertLineBack (line )
133
149
}
134
- return gl
150
+
151
+ if len (gl .Line ) == 0 {
152
+ gl .Line = append (gl .Line , & googleProfile.Line {
153
+ FunctionId : p .addfunc (fmt .Sprintf ("%s 0x%x" , p .src .StringTable [om .Filename ], ol .Address )),
154
+ })
155
+ }
156
+
157
+ p .dst .Location = append (p .dst .Location , gl )
158
+ gl .Id = uint64 (len (p .dst .Location ))
159
+ p .locationMap [ol ] = gl .Id
160
+ return gl .Id
135
161
}
136
162
137
163
// convertLineBack converts an OpenTelemetry Line to a Google Line.
138
- func convertLineBack (ol * otelProfile.Line ) * googleProfile.Line {
164
+ func ( p * profileBuilder ) convertLineBack (ol * otelProfile.Line ) * googleProfile.Line {
139
165
return & googleProfile.Line {
140
- FunctionId : ol .FunctionIndex + 1 ,
166
+ FunctionId : p . convertFunctionBack ( p . src . Function [ ol .FunctionIndex ]) ,
141
167
Line : ol .Line ,
142
168
}
143
169
}
144
170
145
- func convertFunctionBack (of * otelProfile.Function ) * googleProfile.Function {
146
- return & googleProfile.Function {
147
- Name : of .Name ,
148
- SystemName : of .SystemName ,
149
- Filename : of .Filename ,
171
+ func (p * profileBuilder ) convertFunctionBack (of * otelProfile.Function ) uint64 {
172
+ if i , ok := p .functionMap [of ]; ok {
173
+ return i
174
+ }
175
+ gf := & googleProfile.Function {
176
+ Name : p .addstr (p .src .StringTable [of .Name ]),
177
+ SystemName : p .addstr (p .src .StringTable [of .SystemName ]),
178
+ Filename : p .addstr (p .src .StringTable [of .Filename ]),
150
179
StartLine : of .StartLine ,
151
180
}
181
+ p .dst .Function = append (p .dst .Function , gf )
182
+ gf .Id = uint64 (len (p .dst .Function ))
183
+ p .functionMap [of ] = gf .Id
184
+ return gf .Id
152
185
}
153
186
154
- func convertSampleBack (p * otelProfile. Profile , os * otelProfile.Sample , locationIndexes [] int64 , addstr func ( s string ) int64 ) * googleProfile.Sample {
187
+ func (p * profileBuilder ) convertSampleBack ( os * otelProfile.Sample ) * googleProfile.Sample {
155
188
gs := & googleProfile.Sample {
156
189
Value : os .Value ,
157
190
}
158
191
159
192
if len (gs .Value ) == 0 {
160
193
gs .Value = []int64 {int64 (len (os .TimestampsUnixNano ))}
161
194
}
162
- convertSampleAttributesToLabelsBack (p , os , gs , addstr )
195
+ p . convertSampleAttributesToLabelsBack (os , gs )
163
196
164
197
for i := os .LocationsStartIndex ; i < os .LocationsStartIndex + os .LocationsLength ; i ++ {
165
- gs .LocationId = append (gs .LocationId , uint64 ( locationIndexes [ i ] + 1 ))
198
+ gs .LocationId = append (gs .LocationId , p . convertLocationBack ( p . src . Location [ p . src . LocationIndices [ i ]] ))
166
199
}
167
200
201
+ p .dst .Sample = append (p .dst .Sample , gs )
202
+
168
203
return gs
169
204
}
170
205
171
- func convertSampleAttributesToLabelsBack (p * otelProfile. Profile , os * otelProfile.Sample , gs * googleProfile.Sample , addstr func ( s string ) int64 ) {
206
+ func (p * profileBuilder ) convertSampleAttributesToLabelsBack ( os * otelProfile.Sample , gs * googleProfile.Sample ) {
172
207
gs .Label = make ([]* googleProfile.Label , 0 , len (os .Attributes ))
173
208
for _ , attribute := range os .Attributes {
174
- att := p .AttributeTable [attribute ]
209
+ att := p .src .AttributeTable [attribute ]
210
+ if att .Key == serviceNameKey {
211
+ continue
212
+ }
175
213
if att .Value .GetStringValue () != "" {
176
214
gs .Label = append (gs .Label , & googleProfile.Label {
177
- Key : addstr (att .Key ),
178
- Str : addstr (att .Value .GetStringValue ()),
215
+ Key : p . addstr (att .Key ),
216
+ Str : p . addstr (att .Value .GetStringValue ()),
179
217
})
180
218
}
181
219
}
182
220
}
183
221
184
222
// convertMappingsBack converts a slice of OpenTelemetry Mapping entries to Google Mapping entries.
185
- func convertMappingsBack (otelMappings []* otelProfile.Mapping ) []* googleProfile.Mapping {
186
- googleMappings := make ([]* googleProfile.Mapping , len (otelMappings ))
187
- for i , om := range otelMappings {
188
- googleMappings [i ] = & googleProfile.Mapping {
189
- Id : uint64 (i + 1 ), // Assuming direct mapping of IDs
190
- MemoryStart : om .MemoryStart ,
191
- MemoryLimit : om .MemoryLimit ,
192
- FileOffset : om .FileOffset ,
193
- Filename : om .Filename , // Assume direct use; may need conversion if using indices
194
- BuildId : om .BuildId ,
195
- HasFunctions : om .HasFunctions ,
196
- HasFilenames : om .HasFilenames ,
197
- HasLineNumbers : om .HasLineNumbers ,
198
- HasInlineFrames : om .HasInlineFrames ,
199
- }
200
- }
201
- return googleMappings
223
+ func (p * profileBuilder ) convertMappingBack (om * otelProfile.Mapping ) uint64 {
224
+ if i , ok := p .mappingMap [om ]; ok {
225
+ return i
226
+ }
227
+
228
+ gm := & googleProfile.Mapping {
229
+ MemoryStart : om .MemoryStart ,
230
+ MemoryLimit : om .MemoryLimit ,
231
+ FileOffset : om .FileOffset ,
232
+ Filename : p .addstr (p .src .StringTable [om .Filename ]),
233
+ BuildId : p .addstr (p .src .StringTable [om .BuildId ]),
234
+ HasFunctions : om .HasFunctions ,
235
+ HasFilenames : om .HasFilenames ,
236
+ HasLineNumbers : om .HasLineNumbers ,
237
+ HasInlineFrames : om .HasInlineFrames ,
238
+ }
239
+ p .dst .Mapping = append (p .dst .Mapping , gm )
240
+ gm .Id = uint64 (len (p .dst .Mapping ))
241
+ p .mappingMap [om ] = gm .Id
242
+ return gm .Id
202
243
}
0 commit comments