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 `))