~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/worker/introspection/pprof/pprof.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
`))