github.com/riscv/riscv-go@v0.0.0-20200123204226-124ebd6fcc8e/src/runtime/pprof/internal/protopprof/protopprof.go (about) 1 // Copyright 2016 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package protopprof converts the runtime's raw profile logs 6 // to Profile structs containing a representation of the pprof 7 // protocol buffer profile format. 8 package protopprof 9 10 import ( 11 "fmt" 12 "os" 13 "runtime" 14 "time" 15 "unsafe" 16 17 "internal/pprof/profile" 18 ) 19 20 // TranslateCPUProfile parses binary CPU profiling stack trace data 21 // generated by runtime.CPUProfile() into a profile struct. 22 func TranslateCPUProfile(b []byte, startTime time.Time) (*profile.Profile, error) { 23 const wordSize = unsafe.Sizeof(uintptr(0)) 24 const minRawProfile = 5 * wordSize // Need a minimum of 5 words. 25 if uintptr(len(b)) < minRawProfile { 26 return nil, fmt.Errorf("truncated profile") 27 } 28 n := int(uintptr(len(b)) / wordSize) 29 data := ((*[1 << 28]uintptr)(unsafe.Pointer(&b[0])))[:n:n] 30 period := data[3] 31 data = data[5:] // skip header 32 33 // profile initialization taken from pprof tool 34 p := &profile.Profile{ 35 Period: int64(period) * 1000, 36 PeriodType: &profile.ValueType{Type: "cpu", Unit: "nanoseconds"}, 37 SampleType: []*profile.ValueType{ 38 {Type: "samples", Unit: "count"}, 39 {Type: "cpu", Unit: "nanoseconds"}, 40 }, 41 TimeNanos: int64(startTime.UnixNano()), 42 DurationNanos: time.Since(startTime).Nanoseconds(), 43 } 44 // Parse CPU samples from the profile. 45 locs := make(map[uint64]*profile.Location) 46 for len(b) > 0 { 47 if len(data) < 2 || uintptr(len(data)) < 2+data[1] { 48 return nil, fmt.Errorf("truncated profile") 49 } 50 count := data[0] 51 nstk := data[1] 52 if uintptr(len(data)) < 2+nstk { 53 return nil, fmt.Errorf("truncated profile") 54 } 55 stk := data[2 : 2+nstk] 56 data = data[2+nstk:] 57 58 if count == 0 && nstk == 1 && stk[0] == 0 { 59 // end of data marker 60 break 61 } 62 63 sloc := make([]*profile.Location, len(stk)) 64 for i, addr := range stk { 65 addr := uint64(addr) 66 // Addresses from stack traces point to the next instruction after 67 // each call. Adjust by -1 to land somewhere on the actual call 68 // (except for the leaf, which is not a call). 69 if i > 0 { 70 addr-- 71 } 72 loc := locs[addr] 73 if loc == nil { 74 loc = &profile.Location{ 75 ID: uint64(len(p.Location) + 1), 76 Address: addr, 77 } 78 locs[addr] = loc 79 p.Location = append(p.Location, loc) 80 } 81 sloc[i] = loc 82 } 83 p.Sample = append(p.Sample, &profile.Sample{ 84 Value: []int64{int64(count), int64(count) * int64(p.Period)}, 85 Location: sloc, 86 }) 87 } 88 89 if runtime.GOOS == "linux" { 90 if err := addMappings(p); err != nil { 91 return nil, err 92 } 93 } 94 return p, nil 95 } 96 97 func addMappings(p *profile.Profile) error { 98 // Parse memory map from /proc/self/maps 99 f, err := os.Open("/proc/self/maps") 100 if err != nil { 101 return err 102 } 103 defer f.Close() 104 return p.ParseMemoryMap(f) 105 }