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