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