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