github.com/muesli/go@v0.0.0-20170208044820-e410d2a81ef2/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 "strings" 15 "time" 16 "unsafe" 17 18 "internal/pprof/profile" 19 ) 20 21 // TranslateCPUProfile parses binary CPU profiling stack trace data 22 // generated by runtime.CPUProfile() into a profile struct. 23 func TranslateCPUProfile(b []byte, startTime time.Time) (*profile.Profile, error) { 24 const wordSize = unsafe.Sizeof(uintptr(0)) 25 const minRawProfile = 5 * wordSize // Need a minimum of 5 words. 26 if uintptr(len(b)) < minRawProfile { 27 return nil, fmt.Errorf("truncated profile") 28 } 29 n := int(uintptr(len(b)) / wordSize) 30 data := ((*[1 << 28]uintptr)(unsafe.Pointer(&b[0])))[:n:n] 31 period := data[3] 32 data = data[5:] // skip header 33 34 // profile initialization taken from pprof tool 35 p := &profile.Profile{ 36 Period: int64(period) * 1000, 37 PeriodType: &profile.ValueType{Type: "cpu", Unit: "nanoseconds"}, 38 SampleType: []*profile.ValueType{ 39 {Type: "samples", Unit: "count"}, 40 {Type: "cpu", Unit: "nanoseconds"}, 41 }, 42 TimeNanos: int64(startTime.UnixNano()), 43 DurationNanos: time.Since(startTime).Nanoseconds(), 44 } 45 // Parse CPU samples from the profile. 46 locs := make(map[uint64]*profile.Location) 47 for len(b) > 0 { 48 if len(data) < 2 || uintptr(len(data)) < 2+data[1] { 49 return nil, fmt.Errorf("truncated profile") 50 } 51 count := data[0] 52 nstk := data[1] 53 if uintptr(len(data)) < 2+nstk { 54 return nil, fmt.Errorf("truncated profile") 55 } 56 stk := data[2 : 2+nstk] 57 data = data[2+nstk:] 58 59 if count == 0 && nstk == 1 && stk[0] == 0 { 60 // end of data marker 61 break 62 } 63 64 sloc := make([]*profile.Location, len(stk)) 65 for i, addr := range stk { 66 addr := uint64(addr) 67 // Addresses from stack traces point to the next instruction after 68 // each call. Adjust by -1 to land somewhere on the actual call 69 // (except for the leaf, which is not a call). 70 if i > 0 { 71 addr-- 72 } 73 loc := locs[addr] 74 if loc == nil { 75 loc = &profile.Location{ 76 ID: uint64(len(p.Location) + 1), 77 Address: addr, 78 } 79 locs[addr] = loc 80 p.Location = append(p.Location, loc) 81 } 82 sloc[i] = loc 83 } 84 p.Sample = append(p.Sample, &profile.Sample{ 85 Value: []int64{int64(count), int64(count) * int64(p.Period)}, 86 Location: sloc, 87 }) 88 } 89 90 if runtime.GOOS == "linux" { 91 if err := addMappings(p); err != nil { 92 return nil, err 93 } 94 } 95 symbolize(p) 96 return p, nil 97 } 98 99 func addMappings(p *profile.Profile) error { 100 // Parse memory map from /proc/self/maps 101 f, err := os.Open("/proc/self/maps") 102 if err != nil { 103 return err 104 } 105 defer f.Close() 106 return p.ParseMemoryMap(f) 107 } 108 109 type function interface { 110 Name() string 111 FileLine(pc uintptr) (string, int) 112 } 113 114 // funcForPC is a wrapper for runtime.FuncForPC. Defined as var for testing. 115 var funcForPC = func(pc uintptr) function { 116 if f := runtime.FuncForPC(pc); f != nil { 117 return f 118 } 119 return nil 120 } 121 122 func symbolize(p *profile.Profile) { 123 fns := profileFunctionMap{} 124 for _, l := range p.Location { 125 pc := uintptr(l.Address) 126 f := funcForPC(pc) 127 if f == nil { 128 continue 129 } 130 file, lineno := f.FileLine(pc) 131 l.Line = []profile.Line{ 132 { 133 Function: fns.findOrAddFunction(f.Name(), file, p), 134 Line: int64(lineno), 135 }, 136 } 137 } 138 // Trim runtime functions. Always hide runtime.goexit. Other runtime 139 // functions are only hidden for heapz when they appear at the beginning. 140 isHeapz := p.PeriodType != nil && p.PeriodType.Type == "space" 141 for _, s := range p.Sample { 142 show := !isHeapz 143 var i int 144 for _, l := range s.Location { 145 if len(l.Line) > 0 && l.Line[0].Function != nil { 146 name := l.Line[0].Function.Name 147 if name == "runtime.goexit" || !show && strings.HasPrefix(name, "runtime.") { 148 continue 149 } 150 } 151 show = true 152 s.Location[i] = l 153 i++ 154 } 155 s.Location = s.Location[:i] 156 } 157 } 158 159 type profileFunctionMap map[profile.Function]*profile.Function 160 161 func (fns profileFunctionMap) findOrAddFunction(name, filename string, p *profile.Profile) *profile.Function { 162 f := profile.Function{ 163 Name: name, 164 SystemName: name, 165 Filename: filename, 166 } 167 if fp := fns[f]; fp != nil { 168 return fp 169 } 170 fp := new(profile.Function) 171 fns[f] = fp 172 173 *fp = f 174 fp.ID = uint64(len(p.Function) + 1) 175 p.Function = append(p.Function, fp) 176 return fp 177 }