Source file src/pkg/cmd/vendor/github.com/google/pprof/profile/profile.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package profile
18
19 import (
20 "bytes"
21 "compress/gzip"
22 "fmt"
23 "io"
24 "io/ioutil"
25 "path/filepath"
26 "regexp"
27 "sort"
28 "strings"
29 "sync"
30 "time"
31 )
32
33
34 type Profile struct {
35 SampleType []*ValueType
36 DefaultSampleType string
37 Sample []*Sample
38 Mapping []*Mapping
39 Location []*Location
40 Function []*Function
41 Comments []string
42
43 DropFrames string
44 KeepFrames string
45
46 TimeNanos int64
47 DurationNanos int64
48 PeriodType *ValueType
49 Period int64
50
51
52
53 encodeMu sync.Mutex
54
55 commentX []int64
56 dropFramesX int64
57 keepFramesX int64
58 stringTable []string
59 defaultSampleTypeX int64
60 }
61
62
63 type ValueType struct {
64 Type string
65 Unit string
66
67 typeX int64
68 unitX int64
69 }
70
71
72 type Sample struct {
73 Location []*Location
74 Value []int64
75 Label map[string][]string
76 NumLabel map[string][]int64
77 NumUnit map[string][]string
78
79 locationIDX []uint64
80 labelX []label
81 }
82
83
84 type label struct {
85 keyX int64
86
87 strX int64
88 numX int64
89
90 unitX int64
91 }
92
93
94 type Mapping struct {
95 ID uint64
96 Start uint64
97 Limit uint64
98 Offset uint64
99 File string
100 BuildID string
101 HasFunctions bool
102 HasFilenames bool
103 HasLineNumbers bool
104 HasInlineFrames bool
105
106 fileX int64
107 buildIDX int64
108 }
109
110
111 type Location struct {
112 ID uint64
113 Mapping *Mapping
114 Address uint64
115 Line []Line
116 IsFolded bool
117
118 mappingIDX uint64
119 }
120
121
122 type Line struct {
123 Function *Function
124 Line int64
125
126 functionIDX uint64
127 }
128
129
130 type Function struct {
131 ID uint64
132 Name string
133 SystemName string
134 Filename string
135 StartLine int64
136
137 nameX int64
138 systemNameX int64
139 filenameX int64
140 }
141
142
143
144
145 func Parse(r io.Reader) (*Profile, error) {
146 data, err := ioutil.ReadAll(r)
147 if err != nil {
148 return nil, err
149 }
150 return ParseData(data)
151 }
152
153
154
155 func ParseData(data []byte) (*Profile, error) {
156 var p *Profile
157 var err error
158 if len(data) >= 2 && data[0] == 0x1f && data[1] == 0x8b {
159 gz, err := gzip.NewReader(bytes.NewBuffer(data))
160 if err == nil {
161 data, err = ioutil.ReadAll(gz)
162 }
163 if err != nil {
164 return nil, fmt.Errorf("decompressing profile: %v", err)
165 }
166 }
167 if p, err = ParseUncompressed(data); err != nil && err != errNoData && err != errConcatProfile {
168 p, err = parseLegacy(data)
169 }
170
171 if err != nil {
172 return nil, fmt.Errorf("parsing profile: %v", err)
173 }
174
175 if err := p.CheckValid(); err != nil {
176 return nil, fmt.Errorf("malformed profile: %v", err)
177 }
178 return p, nil
179 }
180
181 var errUnrecognized = fmt.Errorf("unrecognized profile format")
182 var errMalformed = fmt.Errorf("malformed profile format")
183 var errNoData = fmt.Errorf("empty input file")
184 var errConcatProfile = fmt.Errorf("concatenated profiles detected")
185
186 func parseLegacy(data []byte) (*Profile, error) {
187 parsers := []func([]byte) (*Profile, error){
188 parseCPU,
189 parseHeap,
190 parseGoCount,
191 parseThread,
192 parseContention,
193 parseJavaProfile,
194 }
195
196 for _, parser := range parsers {
197 p, err := parser(data)
198 if err == nil {
199 p.addLegacyFrameInfo()
200 return p, nil
201 }
202 if err != errUnrecognized {
203 return nil, err
204 }
205 }
206 return nil, errUnrecognized
207 }
208
209
210 func ParseUncompressed(data []byte) (*Profile, error) {
211 if len(data) == 0 {
212 return nil, errNoData
213 }
214 p := &Profile{}
215 if err := unmarshal(data, p); err != nil {
216 return nil, err
217 }
218
219 if err := p.postDecode(); err != nil {
220 return nil, err
221 }
222
223 return p, nil
224 }
225
226 var libRx = regexp.MustCompile(`([.]so$|[.]so[._][0-9]+)`)
227
228
229
230 func (p *Profile) massageMappings() {
231
232 if len(p.Mapping) > 1 {
233 mappings := []*Mapping{p.Mapping[0]}
234 for _, m := range p.Mapping[1:] {
235 lm := mappings[len(mappings)-1]
236 if adjacent(lm, m) {
237 lm.Limit = m.Limit
238 if m.File != "" {
239 lm.File = m.File
240 }
241 if m.BuildID != "" {
242 lm.BuildID = m.BuildID
243 }
244 p.updateLocationMapping(m, lm)
245 continue
246 }
247 mappings = append(mappings, m)
248 }
249 p.Mapping = mappings
250 }
251
252
253 for i, m := range p.Mapping {
254 file := strings.TrimSpace(strings.Replace(m.File, "(deleted)", "", -1))
255 if len(file) == 0 {
256 continue
257 }
258 if len(libRx.FindStringSubmatch(file)) > 0 {
259 continue
260 }
261 if file[0] == '[' {
262 continue
263 }
264
265 p.Mapping[0], p.Mapping[i] = p.Mapping[i], p.Mapping[0]
266 break
267 }
268
269
270 for i, m := range p.Mapping {
271 m.ID = uint64(i + 1)
272 }
273 }
274
275
276
277
278 func adjacent(m1, m2 *Mapping) bool {
279 if m1.File != "" && m2.File != "" {
280 if m1.File != m2.File {
281 return false
282 }
283 }
284 if m1.BuildID != "" && m2.BuildID != "" {
285 if m1.BuildID != m2.BuildID {
286 return false
287 }
288 }
289 if m1.Limit != m2.Start {
290 return false
291 }
292 if m1.Offset != 0 && m2.Offset != 0 {
293 offset := m1.Offset + (m1.Limit - m1.Start)
294 if offset != m2.Offset {
295 return false
296 }
297 }
298 return true
299 }
300
301 func (p *Profile) updateLocationMapping(from, to *Mapping) {
302 for _, l := range p.Location {
303 if l.Mapping == from {
304 l.Mapping = to
305 }
306 }
307 }
308
309 func serialize(p *Profile) []byte {
310 p.encodeMu.Lock()
311 p.preEncode()
312 b := marshal(p)
313 p.encodeMu.Unlock()
314 return b
315 }
316
317
318 func (p *Profile) Write(w io.Writer) error {
319 zw := gzip.NewWriter(w)
320 defer zw.Close()
321 _, err := zw.Write(serialize(p))
322 return err
323 }
324
325
326 func (p *Profile) WriteUncompressed(w io.Writer) error {
327 _, err := w.Write(serialize(p))
328 return err
329 }
330
331
332
333
334
335 func (p *Profile) CheckValid() error {
336
337 sampleLen := len(p.SampleType)
338 if sampleLen == 0 && len(p.Sample) != 0 {
339 return fmt.Errorf("missing sample type information")
340 }
341 for _, s := range p.Sample {
342 if s == nil {
343 return fmt.Errorf("profile has nil sample")
344 }
345 if len(s.Value) != sampleLen {
346 return fmt.Errorf("mismatch: sample has %d values vs. %d types", len(s.Value), len(p.SampleType))
347 }
348 for _, l := range s.Location {
349 if l == nil {
350 return fmt.Errorf("sample has nil location")
351 }
352 }
353 }
354
355
356
357 mappings := make(map[uint64]*Mapping, len(p.Mapping))
358 for _, m := range p.Mapping {
359 if m == nil {
360 return fmt.Errorf("profile has nil mapping")
361 }
362 if m.ID == 0 {
363 return fmt.Errorf("found mapping with reserved ID=0")
364 }
365 if mappings[m.ID] != nil {
366 return fmt.Errorf("multiple mappings with same id: %d", m.ID)
367 }
368 mappings[m.ID] = m
369 }
370 functions := make(map[uint64]*Function, len(p.Function))
371 for _, f := range p.Function {
372 if f == nil {
373 return fmt.Errorf("profile has nil function")
374 }
375 if f.ID == 0 {
376 return fmt.Errorf("found function with reserved ID=0")
377 }
378 if functions[f.ID] != nil {
379 return fmt.Errorf("multiple functions with same id: %d", f.ID)
380 }
381 functions[f.ID] = f
382 }
383 locations := make(map[uint64]*Location, len(p.Location))
384 for _, l := range p.Location {
385 if l == nil {
386 return fmt.Errorf("profile has nil location")
387 }
388 if l.ID == 0 {
389 return fmt.Errorf("found location with reserved id=0")
390 }
391 if locations[l.ID] != nil {
392 return fmt.Errorf("multiple locations with same id: %d", l.ID)
393 }
394 locations[l.ID] = l
395 if m := l.Mapping; m != nil {
396 if m.ID == 0 || mappings[m.ID] != m {
397 return fmt.Errorf("inconsistent mapping %p: %d", m, m.ID)
398 }
399 }
400 for _, ln := range l.Line {
401 if f := ln.Function; f != nil {
402 if f.ID == 0 || functions[f.ID] != f {
403 return fmt.Errorf("inconsistent function %p: %d", f, f.ID)
404 }
405 }
406 }
407 }
408 return nil
409 }
410
411
412
413
414 func (p *Profile) Aggregate(inlineFrame, function, filename, linenumber, address bool) error {
415 for _, m := range p.Mapping {
416 m.HasInlineFrames = m.HasInlineFrames && inlineFrame
417 m.HasFunctions = m.HasFunctions && function
418 m.HasFilenames = m.HasFilenames && filename
419 m.HasLineNumbers = m.HasLineNumbers && linenumber
420 }
421
422
423 if !function || !filename {
424 for _, f := range p.Function {
425 if !function {
426 f.Name = ""
427 f.SystemName = ""
428 }
429 if !filename {
430 f.Filename = ""
431 }
432 }
433 }
434
435
436 if !inlineFrame || !address || !linenumber {
437 for _, l := range p.Location {
438 if !inlineFrame && len(l.Line) > 1 {
439 l.Line = l.Line[len(l.Line)-1:]
440 }
441 if !linenumber {
442 for i := range l.Line {
443 l.Line[i].Line = 0
444 }
445 }
446 if !address {
447 l.Address = 0
448 }
449 }
450 }
451
452 return p.CheckValid()
453 }
454
455
456
457
458
459
460
461
462
463
464 func (p *Profile) NumLabelUnits() (map[string]string, map[string][]string) {
465 numLabelUnits := map[string]string{}
466 ignoredUnits := map[string]map[string]bool{}
467 encounteredKeys := map[string]bool{}
468
469
470 for _, s := range p.Sample {
471 for k := range s.NumLabel {
472 encounteredKeys[k] = true
473 for _, unit := range s.NumUnit[k] {
474 if unit == "" {
475 continue
476 }
477 if wantUnit, ok := numLabelUnits[k]; !ok {
478 numLabelUnits[k] = unit
479 } else if wantUnit != unit {
480 if v, ok := ignoredUnits[k]; ok {
481 v[unit] = true
482 } else {
483 ignoredUnits[k] = map[string]bool{unit: true}
484 }
485 }
486 }
487 }
488 }
489
490
491 for key := range encounteredKeys {
492 unit := numLabelUnits[key]
493 if unit == "" {
494 switch key {
495 case "alignment", "request":
496 numLabelUnits[key] = "bytes"
497 default:
498 numLabelUnits[key] = key
499 }
500 }
501 }
502
503
504 unitsIgnored := make(map[string][]string, len(ignoredUnits))
505 for key, values := range ignoredUnits {
506 units := make([]string, len(values))
507 i := 0
508 for unit := range values {
509 units[i] = unit
510 i++
511 }
512 sort.Strings(units)
513 unitsIgnored[key] = units
514 }
515
516 return numLabelUnits, unitsIgnored
517 }
518
519
520
521 func (p *Profile) String() string {
522 ss := make([]string, 0, len(p.Comments)+len(p.Sample)+len(p.Mapping)+len(p.Location))
523 for _, c := range p.Comments {
524 ss = append(ss, "Comment: "+c)
525 }
526 if pt := p.PeriodType; pt != nil {
527 ss = append(ss, fmt.Sprintf("PeriodType: %s %s", pt.Type, pt.Unit))
528 }
529 ss = append(ss, fmt.Sprintf("Period: %d", p.Period))
530 if p.TimeNanos != 0 {
531 ss = append(ss, fmt.Sprintf("Time: %v", time.Unix(0, p.TimeNanos)))
532 }
533 if p.DurationNanos != 0 {
534 ss = append(ss, fmt.Sprintf("Duration: %.4v", time.Duration(p.DurationNanos)))
535 }
536
537 ss = append(ss, "Samples:")
538 var sh1 string
539 for _, s := range p.SampleType {
540 dflt := ""
541 if s.Type == p.DefaultSampleType {
542 dflt = "[dflt]"
543 }
544 sh1 = sh1 + fmt.Sprintf("%s/%s%s ", s.Type, s.Unit, dflt)
545 }
546 ss = append(ss, strings.TrimSpace(sh1))
547 for _, s := range p.Sample {
548 ss = append(ss, s.string())
549 }
550
551 ss = append(ss, "Locations")
552 for _, l := range p.Location {
553 ss = append(ss, l.string())
554 }
555
556 ss = append(ss, "Mappings")
557 for _, m := range p.Mapping {
558 ss = append(ss, m.string())
559 }
560
561 return strings.Join(ss, "\n") + "\n"
562 }
563
564
565
566 func (m *Mapping) string() string {
567 bits := ""
568 if m.HasFunctions {
569 bits = bits + "[FN]"
570 }
571 if m.HasFilenames {
572 bits = bits + "[FL]"
573 }
574 if m.HasLineNumbers {
575 bits = bits + "[LN]"
576 }
577 if m.HasInlineFrames {
578 bits = bits + "[IN]"
579 }
580 return fmt.Sprintf("%d: %#x/%#x/%#x %s %s %s",
581 m.ID,
582 m.Start, m.Limit, m.Offset,
583 m.File,
584 m.BuildID,
585 bits)
586 }
587
588
589
590 func (l *Location) string() string {
591 ss := []string{}
592 locStr := fmt.Sprintf("%6d: %#x ", l.ID, l.Address)
593 if m := l.Mapping; m != nil {
594 locStr = locStr + fmt.Sprintf("M=%d ", m.ID)
595 }
596 if l.IsFolded {
597 locStr = locStr + "[F] "
598 }
599 if len(l.Line) == 0 {
600 ss = append(ss, locStr)
601 }
602 for li := range l.Line {
603 lnStr := "??"
604 if fn := l.Line[li].Function; fn != nil {
605 lnStr = fmt.Sprintf("%s %s:%d s=%d",
606 fn.Name,
607 fn.Filename,
608 l.Line[li].Line,
609 fn.StartLine)
610 if fn.Name != fn.SystemName {
611 lnStr = lnStr + "(" + fn.SystemName + ")"
612 }
613 }
614 ss = append(ss, locStr+lnStr)
615
616 locStr = " "
617 }
618 return strings.Join(ss, "\n")
619 }
620
621
622
623 func (s *Sample) string() string {
624 ss := []string{}
625 var sv string
626 for _, v := range s.Value {
627 sv = fmt.Sprintf("%s %10d", sv, v)
628 }
629 sv = sv + ": "
630 for _, l := range s.Location {
631 sv = sv + fmt.Sprintf("%d ", l.ID)
632 }
633 ss = append(ss, sv)
634 const labelHeader = " "
635 if len(s.Label) > 0 {
636 ss = append(ss, labelHeader+labelsToString(s.Label))
637 }
638 if len(s.NumLabel) > 0 {
639 ss = append(ss, labelHeader+numLabelsToString(s.NumLabel, s.NumUnit))
640 }
641 return strings.Join(ss, "\n")
642 }
643
644
645
646 func labelsToString(labels map[string][]string) string {
647 ls := []string{}
648 for k, v := range labels {
649 ls = append(ls, fmt.Sprintf("%s:%v", k, v))
650 }
651 sort.Strings(ls)
652 return strings.Join(ls, " ")
653 }
654
655
656
657 func numLabelsToString(numLabels map[string][]int64, numUnits map[string][]string) string {
658 ls := []string{}
659 for k, v := range numLabels {
660 units := numUnits[k]
661 var labelString string
662 if len(units) == len(v) {
663 values := make([]string, len(v))
664 for i, vv := range v {
665 values[i] = fmt.Sprintf("%d %s", vv, units[i])
666 }
667 labelString = fmt.Sprintf("%s:%v", k, values)
668 } else {
669 labelString = fmt.Sprintf("%s:%v", k, v)
670 }
671 ls = append(ls, labelString)
672 }
673 sort.Strings(ls)
674 return strings.Join(ls, " ")
675 }
676
677
678
679 func (p *Profile) SetLabel(key string, value []string) {
680 for _, sample := range p.Sample {
681 if sample.Label == nil {
682 sample.Label = map[string][]string{key: value}
683 } else {
684 sample.Label[key] = value
685 }
686 }
687 }
688
689
690
691 func (p *Profile) RemoveLabel(key string) {
692 for _, sample := range p.Sample {
693 delete(sample.Label, key)
694 }
695 }
696
697
698 func (s *Sample) HasLabel(key, value string) bool {
699 for _, v := range s.Label[key] {
700 if v == value {
701 return true
702 }
703 }
704 return false
705 }
706
707
708
709 func (s *Sample) DiffBaseSample() bool {
710 return s.HasLabel("pprof::base", "true")
711 }
712
713
714 func (p *Profile) Scale(ratio float64) {
715 if ratio == 1 {
716 return
717 }
718 ratios := make([]float64, len(p.SampleType))
719 for i := range p.SampleType {
720 ratios[i] = ratio
721 }
722 p.ScaleN(ratios)
723 }
724
725
726 func (p *Profile) ScaleN(ratios []float64) error {
727 if len(p.SampleType) != len(ratios) {
728 return fmt.Errorf("mismatched scale ratios, got %d, want %d", len(ratios), len(p.SampleType))
729 }
730 allOnes := true
731 for _, r := range ratios {
732 if r != 1 {
733 allOnes = false
734 break
735 }
736 }
737 if allOnes {
738 return nil
739 }
740 for _, s := range p.Sample {
741 for i, v := range s.Value {
742 if ratios[i] != 1 {
743 s.Value[i] = int64(float64(v) * ratios[i])
744 }
745 }
746 }
747 return nil
748 }
749
750
751
752 func (p *Profile) HasFunctions() bool {
753 for _, l := range p.Location {
754 if l.Mapping != nil && !l.Mapping.HasFunctions {
755 return false
756 }
757 }
758 return true
759 }
760
761
762
763 func (p *Profile) HasFileLines() bool {
764 for _, l := range p.Location {
765 if l.Mapping != nil && (!l.Mapping.HasFilenames || !l.Mapping.HasLineNumbers) {
766 return false
767 }
768 }
769 return true
770 }
771
772
773
774
775 func (m *Mapping) Unsymbolizable() bool {
776 name := filepath.Base(m.File)
777 return strings.HasPrefix(name, "[") || strings.HasPrefix(name, "linux-vdso") || strings.HasPrefix(m.File, "/dev/dri/")
778 }
779
780
781 func (p *Profile) Copy() *Profile {
782 pp := &Profile{}
783 if err := unmarshal(serialize(p), pp); err != nil {
784 panic(err)
785 }
786 if err := pp.postDecode(); err != nil {
787 panic(err)
788 }
789
790 return pp
791 }
792
View as plain text