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