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