Source file src/runtime/pprof/internal/profile/profile.go
1
2
3
4
5
6
7
8
9
10 package profile
11
12 import (
13 "bytes"
14 "compress/gzip"
15 "fmt"
16 "io"
17 "io/ioutil"
18 "regexp"
19 "strings"
20 "time"
21 )
22
23
24 type Profile struct {
25 SampleType []*ValueType
26 DefaultSampleType string
27 Sample []*Sample
28 Mapping []*Mapping
29 Location []*Location
30 Function []*Function
31 Comments []string
32
33 DropFrames string
34 KeepFrames string
35
36 TimeNanos int64
37 DurationNanos int64
38 PeriodType *ValueType
39 Period int64
40
41 commentX []int64
42 dropFramesX int64
43 keepFramesX int64
44 stringTable []string
45 defaultSampleTypeX int64
46 }
47
48
49 type ValueType struct {
50 Type string
51 Unit string
52
53 typeX int64
54 unitX int64
55 }
56
57
58 type Sample struct {
59 Location []*Location
60 Value []int64
61 Label map[string][]string
62 NumLabel map[string][]int64
63
64 locationIDX []uint64
65 labelX []Label
66 }
67
68
69 type Label struct {
70 keyX int64
71
72 strX int64
73 numX int64
74 }
75
76
77 type Mapping struct {
78 ID uint64
79 Start uint64
80 Limit uint64
81 Offset uint64
82 File string
83 BuildID string
84 HasFunctions bool
85 HasFilenames bool
86 HasLineNumbers bool
87 HasInlineFrames bool
88
89 fileX int64
90 buildIDX int64
91 }
92
93
94 type Location struct {
95 ID uint64
96 Mapping *Mapping
97 Address uint64
98 Line []Line
99
100 mappingIDX uint64
101 }
102
103
104 type Line struct {
105 Function *Function
106 Line int64
107
108 functionIDX uint64
109 }
110
111
112 type Function struct {
113 ID uint64
114 Name string
115 SystemName string
116 Filename string
117 StartLine int64
118
119 nameX int64
120 systemNameX int64
121 filenameX int64
122 }
123
124
125
126
127 func Parse(r io.Reader) (*Profile, error) {
128 orig, err := ioutil.ReadAll(r)
129 if err != nil {
130 return nil, err
131 }
132
133 var p *Profile
134 if len(orig) >= 2 && orig[0] == 0x1f && orig[1] == 0x8b {
135 gz, err := gzip.NewReader(bytes.NewBuffer(orig))
136 if err != nil {
137 return nil, fmt.Errorf("decompressing profile: %v", err)
138 }
139 data, err := ioutil.ReadAll(gz)
140 if err != nil {
141 return nil, fmt.Errorf("decompressing profile: %v", err)
142 }
143 orig = data
144 }
145 if p, err = parseUncompressed(orig); err != nil {
146 if p, err = parseLegacy(orig); err != nil {
147 return nil, fmt.Errorf("parsing profile: %v", err)
148 }
149 }
150
151 if err := p.CheckValid(); err != nil {
152 return nil, fmt.Errorf("malformed profile: %v", err)
153 }
154 return p, nil
155 }
156
157 var errUnrecognized = fmt.Errorf("unrecognized profile format")
158 var errMalformed = fmt.Errorf("malformed profile format")
159
160 func parseLegacy(data []byte) (*Profile, error) {
161 parsers := []func([]byte) (*Profile, error){
162 parseCPU,
163 parseHeap,
164 parseGoCount,
165 parseThread,
166 parseContention,
167 }
168
169 for _, parser := range parsers {
170 p, err := parser(data)
171 if err == nil {
172 p.setMain()
173 p.addLegacyFrameInfo()
174 return p, nil
175 }
176 if err != errUnrecognized {
177 return nil, err
178 }
179 }
180 return nil, errUnrecognized
181 }
182
183 func parseUncompressed(data []byte) (*Profile, error) {
184 p := &Profile{}
185 if err := unmarshal(data, p); err != nil {
186 return nil, err
187 }
188
189 if err := p.postDecode(); err != nil {
190 return nil, err
191 }
192
193 return p, nil
194 }
195
196 var libRx = regexp.MustCompile(`([.]so$|[.]so[._][0-9]+)`)
197
198
199
200
201 func (p *Profile) setMain() {
202 for i := 0; i < len(p.Mapping); i++ {
203 file := strings.TrimSpace(strings.ReplaceAll(p.Mapping[i].File, "(deleted)", ""))
204 if len(file) == 0 {
205 continue
206 }
207 if len(libRx.FindStringSubmatch(file)) > 0 {
208 continue
209 }
210 if strings.HasPrefix(file, "[") {
211 continue
212 }
213
214 p.Mapping[i], p.Mapping[0] = p.Mapping[0], p.Mapping[i]
215 break
216 }
217 }
218
219
220 func (p *Profile) Write(w io.Writer) error {
221 p.preEncode()
222 b := marshal(p)
223 zw := gzip.NewWriter(w)
224 defer zw.Close()
225 _, err := zw.Write(b)
226 return err
227 }
228
229
230
231
232
233 func (p *Profile) CheckValid() error {
234
235 sampleLen := len(p.SampleType)
236 if sampleLen == 0 && len(p.Sample) != 0 {
237 return fmt.Errorf("missing sample type information")
238 }
239 for _, s := range p.Sample {
240 if len(s.Value) != sampleLen {
241 return fmt.Errorf("mismatch: sample has: %d values vs. %d types", len(s.Value), len(p.SampleType))
242 }
243 }
244
245
246
247 mappings := make(map[uint64]*Mapping, len(p.Mapping))
248 for _, m := range p.Mapping {
249 if m.ID == 0 {
250 return fmt.Errorf("found mapping with reserved ID=0")
251 }
252 if mappings[m.ID] != nil {
253 return fmt.Errorf("multiple mappings with same id: %d", m.ID)
254 }
255 mappings[m.ID] = m
256 }
257 functions := make(map[uint64]*Function, len(p.Function))
258 for _, f := range p.Function {
259 if f.ID == 0 {
260 return fmt.Errorf("found function with reserved ID=0")
261 }
262 if functions[f.ID] != nil {
263 return fmt.Errorf("multiple functions with same id: %d", f.ID)
264 }
265 functions[f.ID] = f
266 }
267 locations := make(map[uint64]*Location, len(p.Location))
268 for _, l := range p.Location {
269 if l.ID == 0 {
270 return fmt.Errorf("found location with reserved id=0")
271 }
272 if locations[l.ID] != nil {
273 return fmt.Errorf("multiple locations with same id: %d", l.ID)
274 }
275 locations[l.ID] = l
276 if m := l.Mapping; m != nil {
277 if m.ID == 0 || mappings[m.ID] != m {
278 return fmt.Errorf("inconsistent mapping %p: %d", m, m.ID)
279 }
280 }
281 for _, ln := range l.Line {
282 if f := ln.Function; f != nil {
283 if f.ID == 0 || functions[f.ID] != f {
284 return fmt.Errorf("inconsistent function %p: %d", f, f.ID)
285 }
286 }
287 }
288 }
289 return nil
290 }
291
292
293
294
295 func (p *Profile) Aggregate(inlineFrame, function, filename, linenumber, address bool) error {
296 for _, m := range p.Mapping {
297 m.HasInlineFrames = m.HasInlineFrames && inlineFrame
298 m.HasFunctions = m.HasFunctions && function
299 m.HasFilenames = m.HasFilenames && filename
300 m.HasLineNumbers = m.HasLineNumbers && linenumber
301 }
302
303
304 if !function || !filename {
305 for _, f := range p.Function {
306 if !function {
307 f.Name = ""
308 f.SystemName = ""
309 }
310 if !filename {
311 f.Filename = ""
312 }
313 }
314 }
315
316
317 if !inlineFrame || !address || !linenumber {
318 for _, l := range p.Location {
319 if !inlineFrame && len(l.Line) > 1 {
320 l.Line = l.Line[len(l.Line)-1:]
321 }
322 if !linenumber {
323 for i := range l.Line {
324 l.Line[i].Line = 0
325 }
326 }
327 if !address {
328 l.Address = 0
329 }
330 }
331 }
332
333 return p.CheckValid()
334 }
335
336
337
338 func (p *Profile) String() string {
339
340 ss := make([]string, 0, len(p.Sample)+len(p.Mapping)+len(p.Location))
341 if pt := p.PeriodType; pt != nil {
342 ss = append(ss, fmt.Sprintf("PeriodType: %s %s", pt.Type, pt.Unit))
343 }
344 ss = append(ss, fmt.Sprintf("Period: %d", p.Period))
345 if p.TimeNanos != 0 {
346 ss = append(ss, fmt.Sprintf("Time: %v", time.Unix(0, p.TimeNanos)))
347 }
348 if p.DurationNanos != 0 {
349 ss = append(ss, fmt.Sprintf("Duration: %v", time.Duration(p.DurationNanos)))
350 }
351
352 ss = append(ss, "Samples:")
353 var sh1 string
354 for _, s := range p.SampleType {
355 sh1 = sh1 + fmt.Sprintf("%s/%s ", s.Type, s.Unit)
356 }
357 ss = append(ss, strings.TrimSpace(sh1))
358 for _, s := range p.Sample {
359 var sv string
360 for _, v := range s.Value {
361 sv = fmt.Sprintf("%s %10d", sv, v)
362 }
363 sv = sv + ": "
364 for _, l := range s.Location {
365 sv = sv + fmt.Sprintf("%d ", l.ID)
366 }
367 ss = append(ss, sv)
368 const labelHeader = " "
369 if len(s.Label) > 0 {
370 ls := labelHeader
371 for k, v := range s.Label {
372 ls = ls + fmt.Sprintf("%s:%v ", k, v)
373 }
374 ss = append(ss, ls)
375 }
376 if len(s.NumLabel) > 0 {
377 ls := labelHeader
378 for k, v := range s.NumLabel {
379 ls = ls + fmt.Sprintf("%s:%v ", k, v)
380 }
381 ss = append(ss, ls)
382 }
383 }
384
385 ss = append(ss, "Locations")
386 for _, l := range p.Location {
387 locStr := fmt.Sprintf("%6d: %#x ", l.ID, l.Address)
388 if m := l.Mapping; m != nil {
389 locStr = locStr + fmt.Sprintf("M=%d ", m.ID)
390 }
391 if len(l.Line) == 0 {
392 ss = append(ss, locStr)
393 }
394 for li := range l.Line {
395 lnStr := "??"
396 if fn := l.Line[li].Function; fn != nil {
397 lnStr = fmt.Sprintf("%s %s:%d s=%d",
398 fn.Name,
399 fn.Filename,
400 l.Line[li].Line,
401 fn.StartLine)
402 if fn.Name != fn.SystemName {
403 lnStr = lnStr + "(" + fn.SystemName + ")"
404 }
405 }
406 ss = append(ss, locStr+lnStr)
407
408 locStr = " "
409 }
410 }
411
412 ss = append(ss, "Mappings")
413 for _, m := range p.Mapping {
414 bits := ""
415 if m.HasFunctions {
416 bits += "[FN]"
417 }
418 if m.HasFilenames {
419 bits += "[FL]"
420 }
421 if m.HasLineNumbers {
422 bits += "[LN]"
423 }
424 if m.HasInlineFrames {
425 bits += "[IN]"
426 }
427 ss = append(ss, fmt.Sprintf("%d: %#x/%#x/%#x %s %s %s",
428 m.ID,
429 m.Start, m.Limit, m.Offset,
430 m.File,
431 m.BuildID,
432 bits))
433 }
434
435 return strings.Join(ss, "\n") + "\n"
436 }
437
438
439
440
441
442 func (p *Profile) Merge(pb *Profile, r float64) error {
443 if err := p.Compatible(pb); err != nil {
444 return err
445 }
446
447 pb = pb.Copy()
448
449
450 if pb.Period > p.Period {
451 p.Period = pb.Period
452 }
453
454 p.DurationNanos += pb.DurationNanos
455
456 p.Mapping = append(p.Mapping, pb.Mapping...)
457 for i, m := range p.Mapping {
458 m.ID = uint64(i + 1)
459 }
460 p.Location = append(p.Location, pb.Location...)
461 for i, l := range p.Location {
462 l.ID = uint64(i + 1)
463 }
464 p.Function = append(p.Function, pb.Function...)
465 for i, f := range p.Function {
466 f.ID = uint64(i + 1)
467 }
468
469 if r != 1.0 {
470 for _, s := range pb.Sample {
471 for i, v := range s.Value {
472 s.Value[i] = int64((float64(v) * r))
473 }
474 }
475 }
476 p.Sample = append(p.Sample, pb.Sample...)
477 return p.CheckValid()
478 }
479
480
481
482
483 func (p *Profile) Compatible(pb *Profile) error {
484 if !compatibleValueTypes(p.PeriodType, pb.PeriodType) {
485 return fmt.Errorf("incompatible period types %v and %v", p.PeriodType, pb.PeriodType)
486 }
487
488 if len(p.SampleType) != len(pb.SampleType) {
489 return fmt.Errorf("incompatible sample types %v and %v", p.SampleType, pb.SampleType)
490 }
491
492 for i := range p.SampleType {
493 if !compatibleValueTypes(p.SampleType[i], pb.SampleType[i]) {
494 return fmt.Errorf("incompatible sample types %v and %v", p.SampleType, pb.SampleType)
495 }
496 }
497
498 return nil
499 }
500
501
502
503 func (p *Profile) HasFunctions() bool {
504 for _, l := range p.Location {
505 if l.Mapping == nil || !l.Mapping.HasFunctions {
506 return false
507 }
508 }
509 return true
510 }
511
512
513
514 func (p *Profile) HasFileLines() bool {
515 for _, l := range p.Location {
516 if l.Mapping == nil || (!l.Mapping.HasFilenames || !l.Mapping.HasLineNumbers) {
517 return false
518 }
519 }
520 return true
521 }
522
523 func compatibleValueTypes(v1, v2 *ValueType) bool {
524 if v1 == nil || v2 == nil {
525 return true
526 }
527 return v1.Type == v2.Type && v1.Unit == v2.Unit
528 }
529
530
531 func (p *Profile) Copy() *Profile {
532 p.preEncode()
533 b := marshal(p)
534
535 pp := &Profile{}
536 if err := unmarshal(b, pp); err != nil {
537 panic(err)
538 }
539 if err := pp.postDecode(); err != nil {
540 panic(err)
541 }
542
543 return pp
544 }
545
546
547
548
549 type Demangler func(name []string) (map[string]string, error)
550
551
552
553
554 func (p *Profile) Demangle(d Demangler) error {
555
556 var names []string
557 for _, fn := range p.Function {
558 names = append(names, fn.SystemName)
559 }
560
561
562 demangled, err := d(names)
563 if err != nil {
564 return err
565 }
566 for _, fn := range p.Function {
567 if dd, ok := demangled[fn.SystemName]; ok {
568 fn.Name = dd
569 }
570 }
571 return nil
572 }
573
574
575 func (p *Profile) Empty() bool {
576 return len(p.Sample) == 0
577 }
578
View as plain text