github.com/goproxy0/go@v0.0.0-20171111080102-49cc0c489d2c/src/cmd/trace/pprof.go (about) 1 // Copyright 2014 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 // Serving of pprof-like profiles. 6 7 package main 8 9 import ( 10 "bufio" 11 "fmt" 12 "internal/trace" 13 "io" 14 "io/ioutil" 15 "net/http" 16 "os" 17 "os/exec" 18 "path/filepath" 19 "runtime" 20 21 "github.com/google/pprof/profile" 22 ) 23 24 func goCmd() string { 25 var exeSuffix string 26 if runtime.GOOS == "windows" { 27 exeSuffix = ".exe" 28 } 29 path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix) 30 if _, err := os.Stat(path); err == nil { 31 return path 32 } 33 return "go" 34 } 35 36 func init() { 37 http.HandleFunc("/io", serveSVGProfile(pprofIO)) 38 http.HandleFunc("/block", serveSVGProfile(pprofBlock)) 39 http.HandleFunc("/syscall", serveSVGProfile(pprofSyscall)) 40 http.HandleFunc("/sched", serveSVGProfile(pprofSched)) 41 } 42 43 // Record represents one entry in pprof-like profiles. 44 type Record struct { 45 stk []*trace.Frame 46 n uint64 47 time int64 48 } 49 50 // pprofIO generates IO pprof-like profile (time spent in IO wait). 51 func pprofIO(w io.Writer) error { 52 events, err := parseEvents() 53 if err != nil { 54 return err 55 } 56 prof := make(map[uint64]Record) 57 for _, ev := range events { 58 if ev.Type != trace.EvGoBlockNet || ev.Link == nil || ev.StkID == 0 || len(ev.Stk) == 0 { 59 continue 60 } 61 rec := prof[ev.StkID] 62 rec.stk = ev.Stk 63 rec.n++ 64 rec.time += ev.Link.Ts - ev.Ts 65 prof[ev.StkID] = rec 66 } 67 return buildProfile(prof).Write(w) 68 } 69 70 // pprofBlock generates blocking pprof-like profile (time spent blocked on synchronization primitives). 71 func pprofBlock(w io.Writer) error { 72 events, err := parseEvents() 73 if err != nil { 74 return err 75 } 76 prof := make(map[uint64]Record) 77 for _, ev := range events { 78 switch ev.Type { 79 case trace.EvGoBlockSend, trace.EvGoBlockRecv, trace.EvGoBlockSelect, 80 trace.EvGoBlockSync, trace.EvGoBlockCond, trace.EvGoBlockGC: 81 default: 82 continue 83 } 84 if ev.Link == nil || ev.StkID == 0 || len(ev.Stk) == 0 { 85 continue 86 } 87 rec := prof[ev.StkID] 88 rec.stk = ev.Stk 89 rec.n++ 90 rec.time += ev.Link.Ts - ev.Ts 91 prof[ev.StkID] = rec 92 } 93 return buildProfile(prof).Write(w) 94 } 95 96 // pprofSyscall generates syscall pprof-like profile (time spent blocked in syscalls). 97 func pprofSyscall(w io.Writer) error { 98 events, err := parseEvents() 99 if err != nil { 100 return err 101 } 102 prof := make(map[uint64]Record) 103 for _, ev := range events { 104 if ev.Type != trace.EvGoSysCall || ev.Link == nil || ev.StkID == 0 || len(ev.Stk) == 0 { 105 continue 106 } 107 rec := prof[ev.StkID] 108 rec.stk = ev.Stk 109 rec.n++ 110 rec.time += ev.Link.Ts - ev.Ts 111 prof[ev.StkID] = rec 112 } 113 return buildProfile(prof).Write(w) 114 } 115 116 // pprofSched generates scheduler latency pprof-like profile 117 // (time between a goroutine become runnable and actually scheduled for execution). 118 func pprofSched(w io.Writer) error { 119 events, err := parseEvents() 120 if err != nil { 121 return err 122 } 123 prof := make(map[uint64]Record) 124 for _, ev := range events { 125 if (ev.Type != trace.EvGoUnblock && ev.Type != trace.EvGoCreate) || 126 ev.Link == nil || ev.StkID == 0 || len(ev.Stk) == 0 { 127 continue 128 } 129 rec := prof[ev.StkID] 130 rec.stk = ev.Stk 131 rec.n++ 132 rec.time += ev.Link.Ts - ev.Ts 133 prof[ev.StkID] = rec 134 } 135 return buildProfile(prof).Write(w) 136 } 137 138 // serveSVGProfile serves pprof-like profile generated by prof as svg. 139 func serveSVGProfile(prof func(w io.Writer) error) http.HandlerFunc { 140 return func(w http.ResponseWriter, r *http.Request) { 141 blockf, err := ioutil.TempFile("", "block") 142 if err != nil { 143 http.Error(w, fmt.Sprintf("failed to create temp file: %v", err), http.StatusInternalServerError) 144 return 145 } 146 defer func() { 147 blockf.Close() 148 os.Remove(blockf.Name()) 149 }() 150 blockb := bufio.NewWriter(blockf) 151 if err := prof(blockb); err != nil { 152 http.Error(w, fmt.Sprintf("failed to generate profile: %v", err), http.StatusInternalServerError) 153 return 154 } 155 if err := blockb.Flush(); err != nil { 156 http.Error(w, fmt.Sprintf("failed to flush temp file: %v", err), http.StatusInternalServerError) 157 return 158 } 159 if err := blockf.Close(); err != nil { 160 http.Error(w, fmt.Sprintf("failed to close temp file: %v", err), http.StatusInternalServerError) 161 return 162 } 163 svgFilename := blockf.Name() + ".svg" 164 if output, err := exec.Command(goCmd(), "tool", "pprof", "-svg", "-output", svgFilename, blockf.Name()).CombinedOutput(); err != nil { 165 http.Error(w, fmt.Sprintf("failed to execute go tool pprof: %v\n%s", err, output), http.StatusInternalServerError) 166 return 167 } 168 defer os.Remove(svgFilename) 169 w.Header().Set("Content-Type", "image/svg+xml") 170 http.ServeFile(w, r, svgFilename) 171 } 172 } 173 174 func buildProfile(prof map[uint64]Record) *profile.Profile { 175 p := &profile.Profile{ 176 PeriodType: &profile.ValueType{Type: "trace", Unit: "count"}, 177 Period: 1, 178 SampleType: []*profile.ValueType{ 179 {Type: "contentions", Unit: "count"}, 180 {Type: "delay", Unit: "nanoseconds"}, 181 }, 182 } 183 locs := make(map[uint64]*profile.Location) 184 funcs := make(map[string]*profile.Function) 185 for _, rec := range prof { 186 var sloc []*profile.Location 187 for _, frame := range rec.stk { 188 loc := locs[frame.PC] 189 if loc == nil { 190 fn := funcs[frame.File+frame.Fn] 191 if fn == nil { 192 fn = &profile.Function{ 193 ID: uint64(len(p.Function) + 1), 194 Name: frame.Fn, 195 SystemName: frame.Fn, 196 Filename: frame.File, 197 } 198 p.Function = append(p.Function, fn) 199 funcs[frame.File+frame.Fn] = fn 200 } 201 loc = &profile.Location{ 202 ID: uint64(len(p.Location) + 1), 203 Address: frame.PC, 204 Line: []profile.Line{ 205 profile.Line{ 206 Function: fn, 207 Line: int64(frame.Line), 208 }, 209 }, 210 } 211 p.Location = append(p.Location, loc) 212 locs[frame.PC] = loc 213 } 214 sloc = append(sloc, loc) 215 } 216 p.Sample = append(p.Sample, &profile.Sample{ 217 Value: []int64{int64(rec.n), rec.time}, 218 Location: sloc, 219 }) 220 } 221 return p 222 }