Home | History | Annotate | Download | only in pprof
      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:
     34 //
     35 //	go tool pprof http://localhost:6060/debug/pprof/block
     36 //
     37 // Or to collect a 5-second execution trace:
     38 //
     39 //	wget http://localhost:6060/debug/pprof/trace?seconds=5
     40 //
     41 // To view all available profiles, open http://localhost:6060/debug/pprof/
     42 // in your browser.
     43 //
     44 // For a study of the facility in action, visit
     45 //
     46 //	https://blog.golang.org/2011/06/profiling-go-programs.html
     47 //
     48 package pprof
     49 
     50 import (
     51 	"bufio"
     52 	"bytes"
     53 	"fmt"
     54 	"html/template"
     55 	"io"
     56 	"log"
     57 	"net/http"
     58 	"os"
     59 	"runtime"
     60 	"runtime/pprof"
     61 	"runtime/trace"
     62 	"strconv"
     63 	"strings"
     64 	"time"
     65 )
     66 
     67 func init() {
     68 	http.Handle("/debug/pprof/", http.HandlerFunc(Index))
     69 	http.Handle("/debug/pprof/cmdline", http.HandlerFunc(Cmdline))
     70 	http.Handle("/debug/pprof/profile", http.HandlerFunc(Profile))
     71 	http.Handle("/debug/pprof/symbol", http.HandlerFunc(Symbol))
     72 	http.Handle("/debug/pprof/trace", http.HandlerFunc(Trace))
     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 // Profile responds with the pprof-formatted cpu profile.
     84 // The package initialization registers it as /debug/pprof/profile.
     85 func Profile(w http.ResponseWriter, r *http.Request) {
     86 	sec, _ := strconv.ParseInt(r.FormValue("seconds"), 10, 64)
     87 	if sec == 0 {
     88 		sec = 30
     89 	}
     90 
     91 	// Set Content Type assuming StartCPUProfile will work,
     92 	// because if it does it starts writing.
     93 	w.Header().Set("Content-Type", "application/octet-stream")
     94 	if err := pprof.StartCPUProfile(w); err != nil {
     95 		// StartCPUProfile failed, so no writes yet.
     96 		// Can change header back to text content
     97 		// and send error code.
     98 		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
     99 		w.WriteHeader(http.StatusInternalServerError)
    100 		fmt.Fprintf(w, "Could not enable CPU profiling: %s\n", err)
    101 		return
    102 	}
    103 	time.Sleep(time.Duration(sec) * time.Second)
    104 	pprof.StopCPUProfile()
    105 }
    106 
    107 // Trace responds with the execution trace in binary form.
    108 // Tracing lasts for duration specified in seconds GET parameter, or for 1 second if not specified.
    109 // The package initialization registers it as /debug/pprof/trace.
    110 func Trace(w http.ResponseWriter, r *http.Request) {
    111 	sec, _ := strconv.ParseInt(r.FormValue("seconds"), 10, 64)
    112 	if sec == 0 {
    113 		sec = 1
    114 	}
    115 
    116 	// Set Content Type assuming trace.Start will work,
    117 	// because if it does it starts writing.
    118 	w.Header().Set("Content-Type", "application/octet-stream")
    119 	if err := trace.Start(w); err != nil {
    120 		// trace.Start failed, so no writes yet.
    121 		// Can change header back to text content and send error code.
    122 		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
    123 		w.WriteHeader(http.StatusInternalServerError)
    124 		fmt.Fprintf(w, "Could not enable tracing: %s\n", err)
    125 		return
    126 	}
    127 	time.Sleep(time.Duration(sec) * time.Second)
    128 	trace.Stop()
    129 }
    130 
    131 // Symbol looks up the program counters listed in the request,
    132 // responding with a table mapping program counters to function names.
    133 // The package initialization registers it as /debug/pprof/symbol.
    134 func Symbol(w http.ResponseWriter, r *http.Request) {
    135 	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
    136 
    137 	// We have to read the whole POST body before
    138 	// writing any output.  Buffer the output here.
    139 	var buf bytes.Buffer
    140 
    141 	// We don't know how many symbols we have, but we
    142 	// do have symbol information.  Pprof only cares whether
    143 	// this number is 0 (no symbols available) or > 0.
    144 	fmt.Fprintf(&buf, "num_symbols: 1\n")
    145 
    146 	var b *bufio.Reader
    147 	if r.Method == "POST" {
    148 		b = bufio.NewReader(r.Body)
    149 	} else {
    150 		b = bufio.NewReader(strings.NewReader(r.URL.RawQuery))
    151 	}
    152 
    153 	for {
    154 		word, err := b.ReadSlice('+')
    155 		if err == nil {
    156 			word = word[0 : len(word)-1] // trim +
    157 		}
    158 		pc, _ := strconv.ParseUint(string(word), 0, 64)
    159 		if pc != 0 {
    160 			f := runtime.FuncForPC(uintptr(pc))
    161 			if f != nil {
    162 				fmt.Fprintf(&buf, "%#x %s\n", pc, f.Name())
    163 			}
    164 		}
    165 
    166 		// Wait until here to check for err; the last
    167 		// symbol will have an err because it doesn't end in +.
    168 		if err != nil {
    169 			if err != io.EOF {
    170 				fmt.Fprintf(&buf, "reading request: %v\n", err)
    171 			}
    172 			break
    173 		}
    174 	}
    175 
    176 	w.Write(buf.Bytes())
    177 }
    178 
    179 // Handler returns an HTTP handler that serves the named profile.
    180 func Handler(name string) http.Handler {
    181 	return handler(name)
    182 }
    183 
    184 type handler string
    185 
    186 func (name handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    187 	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
    188 	debug, _ := strconv.Atoi(r.FormValue("debug"))
    189 	p := pprof.Lookup(string(name))
    190 	if p == nil {
    191 		w.WriteHeader(404)
    192 		fmt.Fprintf(w, "Unknown profile: %s\n", name)
    193 		return
    194 	}
    195 	gc, _ := strconv.Atoi(r.FormValue("gc"))
    196 	if name == "heap" && gc > 0 {
    197 		runtime.GC()
    198 	}
    199 	p.WriteTo(w, debug)
    200 	return
    201 }
    202 
    203 // Index responds with the pprof-formatted profile named by the request.
    204 // For example, "/debug/pprof/heap" serves the "heap" profile.
    205 // Index responds to a request for "/debug/pprof/" with an HTML page
    206 // listing the available profiles.
    207 func Index(w http.ResponseWriter, r *http.Request) {
    208 	if strings.HasPrefix(r.URL.Path, "/debug/pprof/") {
    209 		name := strings.TrimPrefix(r.URL.Path, "/debug/pprof/")
    210 		if name != "" {
    211 			handler(name).ServeHTTP(w, r)
    212 			return
    213 		}
    214 	}
    215 
    216 	profiles := pprof.Profiles()
    217 	if err := indexTmpl.Execute(w, profiles); err != nil {
    218 		log.Print(err)
    219 	}
    220 }
    221 
    222 var indexTmpl = template.Must(template.New("index").Parse(`<html>
    223 <head>
    224 <title>/debug/pprof/</title>
    225 </head>
    226 <body>
    227 /debug/pprof/<br>
    228 <br>
    229 profiles:<br>
    230 <table>
    231 {{range .}}
    232 <tr><td align=right>{{.Count}}<td><a href="{{.Name}}?debug=1">{{.Name}}</a>
    233 {{end}}
    234 </table>
    235 <br>
    236 <a href="goroutine?debug=2">full goroutine stack dump</a><br>
    237 </body>
    238 </html>
    239 `))
    240