github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/src/net/http/pprof/pprof.go (about) 1 // Copyright 2010 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 // Package pprof serves via its HTTP server runtime profiling data 6 // in the format expected by the pprof visualization tool. 7 // For more information about pprof, see 8 // http://code.google.com/p/google-perftools/. 9 // 10 // The package is typically only imported for the side effect of 11 // registering its HTTP handlers. 12 // The handled paths all begin with /debug/pprof/. 13 // 14 // To use pprof, link this package into your program: 15 // import _ "net/http/pprof" 16 // 17 // If your application is not already running an http server, you 18 // need to start one. Add "net/http" and "log" to your imports and 19 // the following code to your main function: 20 // 21 // go func() { 22 // log.Println(http.ListenAndServe("localhost:6060", nil)) 23 // }() 24 // 25 // Then use the pprof tool to look at the heap profile: 26 // 27 // go tool pprof http://localhost:6060/debug/pprof/heap 28 // 29 // Or to look at a 30-second CPU profile: 30 // 31 // go tool pprof http://localhost:6060/debug/pprof/profile 32 // 33 // Or to look at the goroutine blocking profile, after calling 34 // runtime.SetBlockProfileRate in your program: 35 // 36 // go tool pprof http://localhost:6060/debug/pprof/block 37 // 38 // Or to collect a 5-second execution trace: 39 // 40 // wget http://localhost:6060/debug/pprof/trace?seconds=5 41 // 42 // To view all available profiles, open http://localhost:6060/debug/pprof/ 43 // in your browser. 44 // 45 // For a study of the facility in action, visit 46 // 47 // https://blog.golang.org/2011/06/profiling-go-programs.html 48 // 49 package pprof 50 51 import ( 52 "bufio" 53 "bytes" 54 "fmt" 55 "html/template" 56 "io" 57 "log" 58 "net/http" 59 "os" 60 "runtime" 61 "runtime/pprof" 62 "runtime/trace" 63 "strconv" 64 "strings" 65 "time" 66 ) 67 68 func init() { 69 http.Handle("/debug/pprof/", http.HandlerFunc(Index)) 70 http.Handle("/debug/pprof/cmdline", http.HandlerFunc(Cmdline)) 71 http.Handle("/debug/pprof/profile", http.HandlerFunc(Profile)) 72 http.Handle("/debug/pprof/symbol", http.HandlerFunc(Symbol)) 73 http.Handle("/debug/pprof/trace", http.HandlerFunc(Trace)) 74 } 75 76 // Cmdline responds with the running program's 77 // command line, with arguments separated by NUL bytes. 78 // The package initialization registers it as /debug/pprof/cmdline. 79 func Cmdline(w http.ResponseWriter, r *http.Request) { 80 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 81 fmt.Fprintf(w, strings.Join(os.Args, "\x00")) 82 } 83 84 func sleep(w http.ResponseWriter, d time.Duration) { 85 var clientGone <-chan bool 86 if cn, ok := w.(http.CloseNotifier); ok { 87 clientGone = cn.CloseNotify() 88 } 89 select { 90 case <-time.After(d): 91 case <-clientGone: 92 } 93 } 94 95 // Profile responds with the pprof-formatted cpu profile. 96 // The package initialization registers it as /debug/pprof/profile. 97 func Profile(w http.ResponseWriter, r *http.Request) { 98 sec, _ := strconv.ParseInt(r.FormValue("seconds"), 10, 64) 99 if sec == 0 { 100 sec = 30 101 } 102 103 // Set Content Type assuming StartCPUProfile will work, 104 // because if it does it starts writing. 105 w.Header().Set("Content-Type", "application/octet-stream") 106 if err := pprof.StartCPUProfile(w); err != nil { 107 // StartCPUProfile failed, so no writes yet. 108 // Can change header back to text content 109 // and send error code. 110 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 111 w.WriteHeader(http.StatusInternalServerError) 112 fmt.Fprintf(w, "Could not enable CPU profiling: %s\n", err) 113 return 114 } 115 sleep(w, time.Duration(sec)*time.Second) 116 pprof.StopCPUProfile() 117 } 118 119 // Trace responds with the execution trace in binary form. 120 // Tracing lasts for duration specified in seconds GET parameter, or for 1 second if not specified. 121 // The package initialization registers it as /debug/pprof/trace. 122 func Trace(w http.ResponseWriter, r *http.Request) { 123 sec, err := strconv.ParseFloat(r.FormValue("seconds"), 64) 124 if sec <= 0 || err != nil { 125 sec = 1 126 } 127 128 // Set Content Type assuming trace.Start will work, 129 // because if it does it starts writing. 130 w.Header().Set("Content-Type", "application/octet-stream") 131 if err := trace.Start(w); err != nil { 132 // trace.Start failed, so no writes yet. 133 // Can change header back to text content and send error code. 134 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 135 w.WriteHeader(http.StatusInternalServerError) 136 fmt.Fprintf(w, "Could not enable tracing: %s\n", err) 137 return 138 } 139 sleep(w, time.Duration(sec*float64(time.Second))) 140 trace.Stop() 141 } 142 143 // Symbol looks up the program counters listed in the request, 144 // responding with a table mapping program counters to function names. 145 // The package initialization registers it as /debug/pprof/symbol. 146 func Symbol(w http.ResponseWriter, r *http.Request) { 147 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 148 149 // We have to read the whole POST body before 150 // writing any output. Buffer the output here. 151 var buf bytes.Buffer 152 153 // We don't know how many symbols we have, but we 154 // do have symbol information. Pprof only cares whether 155 // this number is 0 (no symbols available) or > 0. 156 fmt.Fprintf(&buf, "num_symbols: 1\n") 157 158 var b *bufio.Reader 159 if r.Method == "POST" { 160 b = bufio.NewReader(r.Body) 161 } else { 162 b = bufio.NewReader(strings.NewReader(r.URL.RawQuery)) 163 } 164 165 for { 166 word, err := b.ReadSlice('+') 167 if err == nil { 168 word = word[0 : len(word)-1] // trim + 169 } 170 pc, _ := strconv.ParseUint(string(word), 0, 64) 171 if pc != 0 { 172 f := runtime.FuncForPC(uintptr(pc)) 173 if f != nil { 174 fmt.Fprintf(&buf, "%#x %s\n", pc, f.Name()) 175 } 176 } 177 178 // Wait until here to check for err; the last 179 // symbol will have an err because it doesn't end in +. 180 if err != nil { 181 if err != io.EOF { 182 fmt.Fprintf(&buf, "reading request: %v\n", err) 183 } 184 break 185 } 186 } 187 188 w.Write(buf.Bytes()) 189 } 190 191 // Handler returns an HTTP handler that serves the named profile. 192 func Handler(name string) http.Handler { 193 return handler(name) 194 } 195 196 type handler string 197 198 func (name handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 199 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 200 debug, _ := strconv.Atoi(r.FormValue("debug")) 201 p := pprof.Lookup(string(name)) 202 if p == nil { 203 w.WriteHeader(404) 204 fmt.Fprintf(w, "Unknown profile: %s\n", name) 205 return 206 } 207 gc, _ := strconv.Atoi(r.FormValue("gc")) 208 if name == "heap" && gc > 0 { 209 runtime.GC() 210 } 211 p.WriteTo(w, debug) 212 return 213 } 214 215 // Index responds with the pprof-formatted profile named by the request. 216 // For example, "/debug/pprof/heap" serves the "heap" profile. 217 // Index responds to a request for "/debug/pprof/" with an HTML page 218 // listing the available profiles. 219 func Index(w http.ResponseWriter, r *http.Request) { 220 if strings.HasPrefix(r.URL.Path, "/debug/pprof/") { 221 name := strings.TrimPrefix(r.URL.Path, "/debug/pprof/") 222 if name != "" { 223 handler(name).ServeHTTP(w, r) 224 return 225 } 226 } 227 228 profiles := pprof.Profiles() 229 if err := indexTmpl.Execute(w, profiles); err != nil { 230 log.Print(err) 231 } 232 } 233 234 var indexTmpl = template.Must(template.New("index").Parse(`<html> 235 <head> 236 <title>/debug/pprof/</title> 237 </head> 238 <body> 239 /debug/pprof/<br> 240 <br> 241 profiles:<br> 242 <table> 243 {{range .}} 244 <tr><td align=right>{{.Count}}<td><a href="{{.Name}}?debug=1">{{.Name}}</a> 245 {{end}} 246 </table> 247 <br> 248 <a href="goroutine?debug=2">full goroutine stack dump</a><br> 249 </body> 250 </html> 251 `))