github.com/d4l3k/go@v0.0.0-20151015000803-65fc379daeda/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/ioutil" 14 "net/http" 15 "os" 16 "os/exec" 17 ) 18 19 func init() { 20 http.HandleFunc("/io", httpIO) 21 http.HandleFunc("/block", httpBlock) 22 http.HandleFunc("/syscall", httpSyscall) 23 http.HandleFunc("/sched", httpSched) 24 } 25 26 // Record represents one entry in pprof-like profiles. 27 type Record struct { 28 stk []*trace.Frame 29 n uint64 30 time int64 31 } 32 33 // httpIO serves IO pprof-like profile (time spent in IO wait). 34 func httpIO(w http.ResponseWriter, r *http.Request) { 35 events, err := parseEvents() 36 if err != nil { 37 http.Error(w, err.Error(), http.StatusInternalServerError) 38 return 39 } 40 prof := make(map[uint64]Record) 41 for _, ev := range events { 42 if ev.Type != trace.EvGoBlockNet || ev.Link == nil || ev.StkID == 0 || len(ev.Stk) == 0 { 43 continue 44 } 45 rec := prof[ev.StkID] 46 rec.stk = ev.Stk 47 rec.n++ 48 rec.time += ev.Link.Ts - ev.Ts 49 prof[ev.StkID] = rec 50 } 51 serveSVGProfile(w, r, prof) 52 } 53 54 // httpBlock serves blocking pprof-like profile (time spent blocked on synchronization primitives). 55 func httpBlock(w http.ResponseWriter, r *http.Request) { 56 events, err := parseEvents() 57 if err != nil { 58 http.Error(w, err.Error(), http.StatusInternalServerError) 59 return 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: 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 serveSVGProfile(w, r, prof) 79 } 80 81 // httpSyscall serves syscall pprof-like profile (time spent blocked in syscalls). 82 func httpSyscall(w http.ResponseWriter, r *http.Request) { 83 events, err := parseEvents() 84 if err != nil { 85 http.Error(w, err.Error(), http.StatusInternalServerError) 86 return 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 serveSVGProfile(w, r, prof) 100 } 101 102 // httpSched serves scheduler latency pprof-like profile 103 // (time between a goroutine become runnable and actually scheduled for execution). 104 func httpSched(w http.ResponseWriter, r *http.Request) { 105 events, err := parseEvents() 106 if err != nil { 107 http.Error(w, err.Error(), http.StatusInternalServerError) 108 return 109 } 110 prof := make(map[uint64]Record) 111 for _, ev := range events { 112 if (ev.Type != trace.EvGoUnblock && ev.Type != trace.EvGoCreate) || 113 ev.Link == nil || ev.StkID == 0 || len(ev.Stk) == 0 { 114 continue 115 } 116 rec := prof[ev.StkID] 117 rec.stk = ev.Stk 118 rec.n++ 119 rec.time += ev.Link.Ts - ev.Ts 120 prof[ev.StkID] = rec 121 } 122 serveSVGProfile(w, r, prof) 123 } 124 125 // generateSVGProfile generates pprof-like profile stored in prof and writes in to w. 126 func serveSVGProfile(w http.ResponseWriter, r *http.Request, prof map[uint64]Record) { 127 if len(prof) == 0 { 128 http.Error(w, "The profile is empty", http.StatusNotFound) 129 return 130 } 131 blockf, err := ioutil.TempFile("", "block") 132 if err != nil { 133 http.Error(w, fmt.Sprintf("failed to create temp file: %v", err), http.StatusInternalServerError) 134 return 135 } 136 defer os.Remove(blockf.Name()) 137 blockb := bufio.NewWriter(blockf) 138 fmt.Fprintf(blockb, "--- contention:\ncycles/second=1000000000\n") 139 for _, rec := range prof { 140 fmt.Fprintf(blockb, "%v %v @", rec.time, rec.n) 141 for _, f := range rec.stk { 142 fmt.Fprintf(blockb, " 0x%x", f.PC) 143 } 144 fmt.Fprintf(blockb, "\n") 145 } 146 err = blockb.Flush() 147 if err != nil { 148 http.Error(w, fmt.Sprintf("failed to flush temp file: %v", err), http.StatusInternalServerError) 149 return 150 } 151 err = blockf.Close() 152 if err != nil { 153 http.Error(w, fmt.Sprintf("failed to close temp file: %v", err), http.StatusInternalServerError) 154 return 155 } 156 157 svgFilename := blockf.Name() + ".svg" 158 _, err = exec.Command("go", "tool", "pprof", "-svg", "-output", svgFilename, programBinary, blockf.Name()).CombinedOutput() 159 if err != nil { 160 http.Error(w, fmt.Sprintf("failed to execute go tool pprof: %v", err), http.StatusInternalServerError) 161 return 162 } 163 defer os.Remove(svgFilename) 164 w.Header().Set("Content-Type", "image/svg+xml") 165 http.ServeFile(w, r, svgFilename) 166 }