Source file src/pkg/net/http/pprof/pprof.go
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 // Then use the pprof tool to look at the heap profile: 18 // 19 // go tool pprof http://localhost:6060/debug/pprof/heap 20 // 21 // Or to look at a 30-second CPU profile: 22 // 23 // go tool pprof http://localhost:6060/debug/pprof/profile 24 // 25 // Or to view all available profiles: 26 // 27 // go tool pprof http://localhost:6060/debug/pprof/ 28 // 29 // For a study of the facility in action, visit 30 // 31 // http://blog.golang.org/2011/06/profiling-go-programs.html 32 // 33 package pprof 34 35 import ( 36 "bufio" 37 "bytes" 38 "fmt" 39 "html/template" 40 "io" 41 "log" 42 "net/http" 43 "os" 44 "runtime" 45 "runtime/pprof" 46 "strconv" 47 "strings" 48 "time" 49 ) 50 51 func init() { 52 http.Handle("/debug/pprof/", http.HandlerFunc(Index)) 53 http.Handle("/debug/pprof/cmdline", http.HandlerFunc(Cmdline)) 54 http.Handle("/debug/pprof/profile", http.HandlerFunc(Profile)) 55 http.Handle("/debug/pprof/symbol", http.HandlerFunc(Symbol)) 56 } 57 58 // Cmdline responds with the running program's 59 // command line, with arguments separated by NUL bytes. 60 // The package initialization registers it as /debug/pprof/cmdline. 61 func Cmdline(w http.ResponseWriter, r *http.Request) { 62 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 63 fmt.Fprintf(w, strings.Join(os.Args, "\x00")) 64 } 65 66 // Profile responds with the pprof-formatted cpu profile. 67 // The package initialization registers it as /debug/pprof/profile. 68 func Profile(w http.ResponseWriter, r *http.Request) { 69 sec, _ := strconv.ParseInt(r.FormValue("seconds"), 10, 64) 70 if sec == 0 { 71 sec = 30 72 } 73 74 // Set Content Type assuming StartCPUProfile will work, 75 // because if it does it starts writing. 76 w.Header().Set("Content-Type", "application/octet-stream") 77 if err := pprof.StartCPUProfile(w); err != nil { 78 // StartCPUProfile failed, so no writes yet. 79 // Can change header back to text content 80 // and send error code. 81 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 82 w.WriteHeader(http.StatusInternalServerError) 83 fmt.Fprintf(w, "Could not enable CPU profiling: %s\n", err) 84 return 85 } 86 time.Sleep(time.Duration(sec) * time.Second) 87 pprof.StopCPUProfile() 88 } 89 90 // Symbol looks up the program counters listed in the request, 91 // responding with a table mapping program counters to function names. 92 // The package initialization registers it as /debug/pprof/symbol. 93 func Symbol(w http.ResponseWriter, r *http.Request) { 94 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 95 96 // We have to read the whole POST body before 97 // writing any output. Buffer the output here. 98 var buf bytes.Buffer 99 100 // We don't know how many symbols we have, but we 101 // do have symbol information. Pprof only cares whether 102 // this number is 0 (no symbols available) or > 0. 103 fmt.Fprintf(&buf, "num_symbols: 1\n") 104 105 var b *bufio.Reader 106 if r.Method == "POST" { 107 b = bufio.NewReader(r.Body) 108 } else { 109 b = bufio.NewReader(strings.NewReader(r.URL.RawQuery)) 110 } 111 112 for { 113 word, err := b.ReadSlice('+') 114 if err == nil { 115 word = word[0 : len(word)-1] // trim + 116 } 117 pc, _ := strconv.ParseUint(string(word), 0, 64) 118 if pc != 0 { 119 f := runtime.FuncForPC(uintptr(pc)) 120 if f != nil { 121 fmt.Fprintf(&buf, "%#x %s\n", pc, f.Name()) 122 } 123 } 124 125 // Wait until here to check for err; the last 126 // symbol will have an err because it doesn't end in +. 127 if err != nil { 128 if err != io.EOF { 129 fmt.Fprintf(&buf, "reading request: %v\n", err) 130 } 131 break 132 } 133 } 134 135 w.Write(buf.Bytes()) 136 } 137 138 // Handler returns an HTTP handler that serves the named profile. 139 func Handler(name string) http.Handler { 140 return handler(name) 141 } 142 143 type handler string 144 145 func (name handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 146 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 147 debug, _ := strconv.Atoi(r.FormValue("debug")) 148 p := pprof.Lookup(string(name)) 149 if p == nil { 150 w.WriteHeader(404) 151 fmt.Fprintf(w, "Unknown profile: %s\n", name) 152 return 153 } 154 p.WriteTo(w, debug) 155 return 156 } 157 158 // Index responds with the pprof-formatted profile named by the request. 159 // For example, "/debug/pprof/heap" serves the "heap" profile. 160 // Index responds to a request for "/debug/pprof/" with an HTML page 161 // listing the available profiles. 162 func Index(w http.ResponseWriter, r *http.Request) { 163 if strings.HasPrefix(r.URL.Path, "/debug/pprof/") { 164 name := r.URL.Path[len("/debug/pprof/"):] 165 if name != "" { 166 handler(name).ServeHTTP(w, r) 167 return 168 } 169 } 170 171 profiles := pprof.Profiles() 172 if err := indexTmpl.Execute(w, profiles); err != nil { 173 log.Print(err) 174 } 175 } 176 177 var indexTmpl = template.Must(template.New("index").Parse(`<html> 178 <head> 179 <title>/debug/pprof/</title> 180 </head> 181 /debug/pprof/<br> 182 <br> 183 <body> 184 profiles:<br> 185 <table> 186 {{range .}} 187 <tr><td align=right>{{.Count}}<td><a href="/debug/pprof/{{.Name}}?debug=1">{{.Name}}</a> 188 {{end}} 189 </table> 190 <br> 191 <a href="/debug/pprof/goroutine?debug=2">full goroutine stack dump</a><br> 192 </body> 193 </html> 194 `))