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