encode.go raw
1 // Copyright 2014 Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package profile
16
17 import (
18 "errors"
19 "sort"
20 "strings"
21 )
22
23 func (p *Profile) decoder() []decoder {
24 return profileDecoder
25 }
26
27 // preEncode populates the unexported fields to be used by encode
28 // (with suffix X) from the corresponding exported fields. The
29 // exported fields are cleared up to facilitate testing.
30 func (p *Profile) preEncode() {
31 strings := make(map[string]int)
32 addString(strings, "")
33
34 for _, st := range p.SampleType {
35 st.typeX = addString(strings, st.Type)
36 st.unitX = addString(strings, st.Unit)
37 }
38
39 for _, s := range p.Sample {
40 s.labelX = nil
41 var keys []string
42 for k := range s.Label {
43 keys = append(keys, k)
44 }
45 sort.Strings(keys)
46 for _, k := range keys {
47 vs := s.Label[k]
48 for _, v := range vs {
49 s.labelX = append(s.labelX,
50 label{
51 keyX: addString(strings, k),
52 strX: addString(strings, v),
53 },
54 )
55 }
56 }
57 var numKeys []string
58 for k := range s.NumLabel {
59 numKeys = append(numKeys, k)
60 }
61 sort.Strings(numKeys)
62 for _, k := range numKeys {
63 keyX := addString(strings, k)
64 vs := s.NumLabel[k]
65 units := s.NumUnit[k]
66 for i, v := range vs {
67 var unitX int64
68 if len(units) != 0 {
69 unitX = addString(strings, units[i])
70 }
71 s.labelX = append(s.labelX,
72 label{
73 keyX: keyX,
74 numX: v,
75 unitX: unitX,
76 },
77 )
78 }
79 }
80 s.locationIDX = make([]uint64, len(s.Location))
81 for i, loc := range s.Location {
82 s.locationIDX[i] = loc.ID
83 }
84 }
85
86 for _, m := range p.Mapping {
87 m.fileX = addString(strings, m.File)
88 m.buildIDX = addString(strings, m.BuildID)
89 }
90
91 for _, l := range p.Location {
92 for i, ln := range l.Line {
93 if ln.Function != nil {
94 l.Line[i].functionIDX = ln.Function.ID
95 } else {
96 l.Line[i].functionIDX = 0
97 }
98 }
99 if l.Mapping != nil {
100 l.mappingIDX = l.Mapping.ID
101 } else {
102 l.mappingIDX = 0
103 }
104 }
105 for _, f := range p.Function {
106 f.nameX = addString(strings, f.Name)
107 f.systemNameX = addString(strings, f.SystemName)
108 f.filenameX = addString(strings, f.Filename)
109 }
110
111 p.dropFramesX = addString(strings, p.DropFrames)
112 p.keepFramesX = addString(strings, p.KeepFrames)
113
114 if pt := p.PeriodType; pt != nil {
115 pt.typeX = addString(strings, pt.Type)
116 pt.unitX = addString(strings, pt.Unit)
117 }
118
119 p.commentX = nil
120 for _, c := range p.Comments {
121 p.commentX = append(p.commentX, addString(strings, c))
122 }
123
124 p.defaultSampleTypeX = addString(strings, p.DefaultSampleType)
125 p.docURLX = addString(strings, p.DocURL)
126
127 p.stringTable = make([]string, len(strings))
128 for s, i := range strings {
129 p.stringTable[i] = s
130 }
131 }
132
133 func (p *Profile) encode(b *buffer) {
134 for _, x := range p.SampleType {
135 encodeMessage(b, 1, x)
136 }
137 for _, x := range p.Sample {
138 encodeMessage(b, 2, x)
139 }
140 for _, x := range p.Mapping {
141 encodeMessage(b, 3, x)
142 }
143 for _, x := range p.Location {
144 encodeMessage(b, 4, x)
145 }
146 for _, x := range p.Function {
147 encodeMessage(b, 5, x)
148 }
149 encodeStrings(b, 6, p.stringTable)
150 encodeInt64Opt(b, 7, p.dropFramesX)
151 encodeInt64Opt(b, 8, p.keepFramesX)
152 encodeInt64Opt(b, 9, p.TimeNanos)
153 encodeInt64Opt(b, 10, p.DurationNanos)
154 if pt := p.PeriodType; pt != nil && (pt.typeX != 0 || pt.unitX != 0) {
155 encodeMessage(b, 11, p.PeriodType)
156 }
157 encodeInt64Opt(b, 12, p.Period)
158 encodeInt64s(b, 13, p.commentX)
159 encodeInt64(b, 14, p.defaultSampleTypeX)
160 encodeInt64Opt(b, 15, p.docURLX)
161 }
162
163 var profileDecoder = []decoder{
164 nil, // 0
165 // repeated ValueType sample_type = 1
166 func(b *buffer, m message) error {
167 x := new(ValueType)
168 pp := m.(*Profile)
169 pp.SampleType = append(pp.SampleType, x)
170 return decodeMessage(b, x)
171 },
172 // repeated Sample sample = 2
173 func(b *buffer, m message) error {
174 x := new(Sample)
175 pp := m.(*Profile)
176 pp.Sample = append(pp.Sample, x)
177 return decodeMessage(b, x)
178 },
179 // repeated Mapping mapping = 3
180 func(b *buffer, m message) error {
181 x := new(Mapping)
182 pp := m.(*Profile)
183 pp.Mapping = append(pp.Mapping, x)
184 return decodeMessage(b, x)
185 },
186 // repeated Location location = 4
187 func(b *buffer, m message) error {
188 x := new(Location)
189 x.Line = b.tmpLines[:0] // Use shared space temporarily
190 pp := m.(*Profile)
191 pp.Location = append(pp.Location, x)
192 err := decodeMessage(b, x)
193 b.tmpLines = x.Line[:0]
194 // Copy to shrink size and detach from shared space.
195 x.Line = append([]Line(nil), x.Line...)
196 return err
197 },
198 // repeated Function function = 5
199 func(b *buffer, m message) error {
200 x := new(Function)
201 pp := m.(*Profile)
202 pp.Function = append(pp.Function, x)
203 return decodeMessage(b, x)
204 },
205 // repeated string string_table = 6
206 func(b *buffer, m message) error {
207 err := decodeStrings(b, &m.(*Profile).stringTable)
208 if err != nil {
209 return err
210 }
211 if m.(*Profile).stringTable[0] != "" {
212 return errors.New("string_table[0] must be ''")
213 }
214 return nil
215 },
216 // int64 drop_frames = 7
217 func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).dropFramesX) },
218 // int64 keep_frames = 8
219 func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).keepFramesX) },
220 // int64 time_nanos = 9
221 func(b *buffer, m message) error {
222 if m.(*Profile).TimeNanos != 0 {
223 return errConcatProfile
224 }
225 return decodeInt64(b, &m.(*Profile).TimeNanos)
226 },
227 // int64 duration_nanos = 10
228 func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).DurationNanos) },
229 // ValueType period_type = 11
230 func(b *buffer, m message) error {
231 x := new(ValueType)
232 pp := m.(*Profile)
233 pp.PeriodType = x
234 return decodeMessage(b, x)
235 },
236 // int64 period = 12
237 func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).Period) },
238 // repeated int64 comment = 13
239 func(b *buffer, m message) error { return decodeInt64s(b, &m.(*Profile).commentX) },
240 // int64 defaultSampleType = 14
241 func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).defaultSampleTypeX) },
242 // string doc_link = 15;
243 func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).docURLX) },
244 }
245
246 // postDecode takes the unexported fields populated by decode (with
247 // suffix X) and populates the corresponding exported fields.
248 // The unexported fields are cleared up to facilitate testing.
249 func (p *Profile) postDecode() error {
250 var err error
251 mappings := make(map[uint64]*Mapping, len(p.Mapping))
252 mappingIds := make([]*Mapping, len(p.Mapping)+1)
253 for _, m := range p.Mapping {
254 m.File, err = getString(p.stringTable, &m.fileX, err)
255 m.BuildID, err = getString(p.stringTable, &m.buildIDX, err)
256 if m.ID < uint64(len(mappingIds)) {
257 mappingIds[m.ID] = m
258 } else {
259 mappings[m.ID] = m
260 }
261
262 // If this a main linux kernel mapping with a relocation symbol suffix
263 // ("[kernel.kallsyms]_text"), extract said suffix.
264 // It is fairly hacky to handle at this level, but the alternatives appear even worse.
265 const prefix = "[kernel.kallsyms]"
266 if strings.HasPrefix(m.File, prefix) {
267 m.KernelRelocationSymbol = m.File[len(prefix):]
268 }
269 }
270
271 functions := make(map[uint64]*Function, len(p.Function))
272 functionIds := make([]*Function, len(p.Function)+1)
273 for _, f := range p.Function {
274 f.Name, err = getString(p.stringTable, &f.nameX, err)
275 f.SystemName, err = getString(p.stringTable, &f.systemNameX, err)
276 f.Filename, err = getString(p.stringTable, &f.filenameX, err)
277 if f.ID < uint64(len(functionIds)) {
278 functionIds[f.ID] = f
279 } else {
280 functions[f.ID] = f
281 }
282 }
283
284 locations := make(map[uint64]*Location, len(p.Location))
285 locationIds := make([]*Location, len(p.Location)+1)
286 for _, l := range p.Location {
287 if id := l.mappingIDX; id < uint64(len(mappingIds)) {
288 l.Mapping = mappingIds[id]
289 } else {
290 l.Mapping = mappings[id]
291 }
292 l.mappingIDX = 0
293 for i, ln := range l.Line {
294 if id := ln.functionIDX; id != 0 {
295 l.Line[i].functionIDX = 0
296 if id < uint64(len(functionIds)) {
297 l.Line[i].Function = functionIds[id]
298 } else {
299 l.Line[i].Function = functions[id]
300 }
301 }
302 }
303 if l.ID < uint64(len(locationIds)) {
304 locationIds[l.ID] = l
305 } else {
306 locations[l.ID] = l
307 }
308 }
309
310 for _, st := range p.SampleType {
311 st.Type, err = getString(p.stringTable, &st.typeX, err)
312 st.Unit, err = getString(p.stringTable, &st.unitX, err)
313 }
314
315 // Pre-allocate space for all locations.
316 numLocations := 0
317 for _, s := range p.Sample {
318 numLocations += len(s.locationIDX)
319 }
320 locBuffer := make([]*Location, numLocations)
321
322 for _, s := range p.Sample {
323 if len(s.labelX) > 0 {
324 labels := make(map[string][]string, len(s.labelX))
325 numLabels := make(map[string][]int64, len(s.labelX))
326 numUnits := make(map[string][]string, len(s.labelX))
327 for _, l := range s.labelX {
328 var key, value string
329 key, err = getString(p.stringTable, &l.keyX, err)
330 if l.strX != 0 {
331 value, err = getString(p.stringTable, &l.strX, err)
332 labels[key] = append(labels[key], value)
333 } else if l.numX != 0 || l.unitX != 0 {
334 numValues := numLabels[key]
335 units := numUnits[key]
336 if l.unitX != 0 {
337 var unit string
338 unit, err = getString(p.stringTable, &l.unitX, err)
339 units = padStringArray(units, len(numValues))
340 numUnits[key] = append(units, unit)
341 }
342 numLabels[key] = append(numLabels[key], l.numX)
343 }
344 }
345 if len(labels) > 0 {
346 s.Label = labels
347 }
348 if len(numLabels) > 0 {
349 s.NumLabel = numLabels
350 for key, units := range numUnits {
351 if len(units) > 0 {
352 numUnits[key] = padStringArray(units, len(numLabels[key]))
353 }
354 }
355 s.NumUnit = numUnits
356 }
357 }
358
359 s.Location = locBuffer[:len(s.locationIDX)]
360 locBuffer = locBuffer[len(s.locationIDX):]
361 for i, lid := range s.locationIDX {
362 if lid < uint64(len(locationIds)) {
363 s.Location[i] = locationIds[lid]
364 } else {
365 s.Location[i] = locations[lid]
366 }
367 }
368 s.locationIDX = nil
369 }
370
371 p.DropFrames, err = getString(p.stringTable, &p.dropFramesX, err)
372 p.KeepFrames, err = getString(p.stringTable, &p.keepFramesX, err)
373
374 if pt := p.PeriodType; pt == nil {
375 p.PeriodType = &ValueType{}
376 }
377
378 if pt := p.PeriodType; pt != nil {
379 pt.Type, err = getString(p.stringTable, &pt.typeX, err)
380 pt.Unit, err = getString(p.stringTable, &pt.unitX, err)
381 }
382
383 for _, i := range p.commentX {
384 var c string
385 c, err = getString(p.stringTable, &i, err)
386 p.Comments = append(p.Comments, c)
387 }
388
389 p.commentX = nil
390 p.DefaultSampleType, err = getString(p.stringTable, &p.defaultSampleTypeX, err)
391 p.DocURL, err = getString(p.stringTable, &p.docURLX, err)
392 p.stringTable = nil
393 return err
394 }
395
396 // padStringArray pads arr with enough empty strings to make arr
397 // length l when arr's length is less than l.
398 func padStringArray(arr []string, l int) []string {
399 if l <= len(arr) {
400 return arr
401 }
402 return append(arr, make([]string, l-len(arr))...)
403 }
404
405 func (p *ValueType) decoder() []decoder {
406 return valueTypeDecoder
407 }
408
409 func (p *ValueType) encode(b *buffer) {
410 encodeInt64Opt(b, 1, p.typeX)
411 encodeInt64Opt(b, 2, p.unitX)
412 }
413
414 var valueTypeDecoder = []decoder{
415 nil, // 0
416 // optional int64 type = 1
417 func(b *buffer, m message) error { return decodeInt64(b, &m.(*ValueType).typeX) },
418 // optional int64 unit = 2
419 func(b *buffer, m message) error { return decodeInt64(b, &m.(*ValueType).unitX) },
420 }
421
422 func (p *Sample) decoder() []decoder {
423 return sampleDecoder
424 }
425
426 func (p *Sample) encode(b *buffer) {
427 encodeUint64s(b, 1, p.locationIDX)
428 encodeInt64s(b, 2, p.Value)
429 for _, x := range p.labelX {
430 encodeMessage(b, 3, x)
431 }
432 }
433
434 var sampleDecoder = []decoder{
435 nil, // 0
436 // repeated uint64 location = 1
437 func(b *buffer, m message) error { return decodeUint64s(b, &m.(*Sample).locationIDX) },
438 // repeated int64 value = 2
439 func(b *buffer, m message) error { return decodeInt64s(b, &m.(*Sample).Value) },
440 // repeated Label label = 3
441 func(b *buffer, m message) error {
442 s := m.(*Sample)
443 n := len(s.labelX)
444 s.labelX = append(s.labelX, label{})
445 return decodeMessage(b, &s.labelX[n])
446 },
447 }
448
449 func (p label) decoder() []decoder {
450 return labelDecoder
451 }
452
453 func (p label) encode(b *buffer) {
454 encodeInt64Opt(b, 1, p.keyX)
455 encodeInt64Opt(b, 2, p.strX)
456 encodeInt64Opt(b, 3, p.numX)
457 encodeInt64Opt(b, 4, p.unitX)
458 }
459
460 var labelDecoder = []decoder{
461 nil, // 0
462 // optional int64 key = 1
463 func(b *buffer, m message) error { return decodeInt64(b, &m.(*label).keyX) },
464 // optional int64 str = 2
465 func(b *buffer, m message) error { return decodeInt64(b, &m.(*label).strX) },
466 // optional int64 num = 3
467 func(b *buffer, m message) error { return decodeInt64(b, &m.(*label).numX) },
468 // optional int64 num = 4
469 func(b *buffer, m message) error { return decodeInt64(b, &m.(*label).unitX) },
470 }
471
472 func (p *Mapping) decoder() []decoder {
473 return mappingDecoder
474 }
475
476 func (p *Mapping) encode(b *buffer) {
477 encodeUint64Opt(b, 1, p.ID)
478 encodeUint64Opt(b, 2, p.Start)
479 encodeUint64Opt(b, 3, p.Limit)
480 encodeUint64Opt(b, 4, p.Offset)
481 encodeInt64Opt(b, 5, p.fileX)
482 encodeInt64Opt(b, 6, p.buildIDX)
483 encodeBoolOpt(b, 7, p.HasFunctions)
484 encodeBoolOpt(b, 8, p.HasFilenames)
485 encodeBoolOpt(b, 9, p.HasLineNumbers)
486 encodeBoolOpt(b, 10, p.HasInlineFrames)
487 }
488
489 var mappingDecoder = []decoder{
490 nil, // 0
491 func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).ID) }, // optional uint64 id = 1
492 func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).Start) }, // optional uint64 memory_offset = 2
493 func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).Limit) }, // optional uint64 memory_limit = 3
494 func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).Offset) }, // optional uint64 file_offset = 4
495 func(b *buffer, m message) error { return decodeInt64(b, &m.(*Mapping).fileX) }, // optional int64 filename = 5
496 func(b *buffer, m message) error { return decodeInt64(b, &m.(*Mapping).buildIDX) }, // optional int64 build_id = 6
497 func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasFunctions) }, // optional bool has_functions = 7
498 func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasFilenames) }, // optional bool has_filenames = 8
499 func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasLineNumbers) }, // optional bool has_line_numbers = 9
500 func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasInlineFrames) }, // optional bool has_inline_frames = 10
501 }
502
503 func (p *Location) decoder() []decoder {
504 return locationDecoder
505 }
506
507 func (p *Location) encode(b *buffer) {
508 encodeUint64Opt(b, 1, p.ID)
509 encodeUint64Opt(b, 2, p.mappingIDX)
510 encodeUint64Opt(b, 3, p.Address)
511 for i := range p.Line {
512 encodeMessage(b, 4, &p.Line[i])
513 }
514 encodeBoolOpt(b, 5, p.IsFolded)
515 }
516
517 var locationDecoder = []decoder{
518 nil, // 0
519 func(b *buffer, m message) error { return decodeUint64(b, &m.(*Location).ID) }, // optional uint64 id = 1;
520 func(b *buffer, m message) error { return decodeUint64(b, &m.(*Location).mappingIDX) }, // optional uint64 mapping_id = 2;
521 func(b *buffer, m message) error { return decodeUint64(b, &m.(*Location).Address) }, // optional uint64 address = 3;
522 func(b *buffer, m message) error { // repeated Line line = 4
523 pp := m.(*Location)
524 n := len(pp.Line)
525 pp.Line = append(pp.Line, Line{})
526 return decodeMessage(b, &pp.Line[n])
527 },
528 func(b *buffer, m message) error { return decodeBool(b, &m.(*Location).IsFolded) }, // optional bool is_folded = 5;
529 }
530
531 func (p *Line) decoder() []decoder {
532 return lineDecoder
533 }
534
535 func (p *Line) encode(b *buffer) {
536 encodeUint64Opt(b, 1, p.functionIDX)
537 encodeInt64Opt(b, 2, p.Line)
538 encodeInt64Opt(b, 3, p.Column)
539 }
540
541 var lineDecoder = []decoder{
542 nil, // 0
543 // optional uint64 function_id = 1
544 func(b *buffer, m message) error { return decodeUint64(b, &m.(*Line).functionIDX) },
545 // optional int64 line = 2
546 func(b *buffer, m message) error { return decodeInt64(b, &m.(*Line).Line) },
547 // optional int64 column = 3
548 func(b *buffer, m message) error { return decodeInt64(b, &m.(*Line).Column) },
549 }
550
551 func (p *Function) decoder() []decoder {
552 return functionDecoder
553 }
554
555 func (p *Function) encode(b *buffer) {
556 encodeUint64Opt(b, 1, p.ID)
557 encodeInt64Opt(b, 2, p.nameX)
558 encodeInt64Opt(b, 3, p.systemNameX)
559 encodeInt64Opt(b, 4, p.filenameX)
560 encodeInt64Opt(b, 5, p.StartLine)
561 }
562
563 var functionDecoder = []decoder{
564 nil, // 0
565 // optional uint64 id = 1
566 func(b *buffer, m message) error { return decodeUint64(b, &m.(*Function).ID) },
567 // optional int64 function_name = 2
568 func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).nameX) },
569 // optional int64 function_system_name = 3
570 func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).systemNameX) },
571 // repeated int64 filename = 4
572 func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).filenameX) },
573 // optional int64 start_line = 5
574 func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).StartLine) },
575 }
576
577 func addString(strings map[string]int, s string) int64 {
578 i, ok := strings[s]
579 if !ok {
580 i = len(strings)
581 strings[s] = i
582 }
583 return int64(i)
584 }
585
586 func getString(strings []string, strng *int64, err error) (string, error) {
587 if err != nil {
588 return "", err
589 }
590 s := int(*strng)
591 if s < 0 || s >= len(strings) {
592 return "", errMalformed
593 }
594 *strng = 0
595 return strings[s], nil
596 }
597