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