src/pkg/net/http/pprof/pprof.go - The Go Programming Language

Golang

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