aboutsummaryrefslogtreecommitdiff
path: root/plugin/pprof
diff options
context:
space:
mode:
Diffstat (limited to 'plugin/pprof')
-rw-r--r--plugin/pprof/README.md41
-rw-r--r--plugin/pprof/pprof.go49
-rw-r--r--plugin/pprof/setup.go53
-rw-r--r--plugin/pprof/setup_test.go34
4 files changed, 177 insertions, 0 deletions
diff --git a/plugin/pprof/README.md b/plugin/pprof/README.md
new file mode 100644
index 000000000..06a36e442
--- /dev/null
+++ b/plugin/pprof/README.md
@@ -0,0 +1,41 @@
+# pprof
+
+*pprof* publishes runtime profiling data at endpoints under /debug/pprof.
+
+You can visit `/debug/pprof` on your site for an index of the available endpoints. By default it
+will listen on localhost:6053.
+
+> This is a debugging tool. Certain requests (such as collecting execution traces) can be slow. If
+> you use pprof on a live site, consider restricting access or enabling it only temporarily.
+
+For more information, please see [Go's pprof
+documentation](https://golang.org/pkg/net/http/pprof/) and read
+[Profiling Go Programs](https://blog.golang.org/profiling-go-programs).
+
+## Syntax
+
+~~~
+pprof [ADDRESS]
+~~~
+
+If not specified, ADDRESS defaults to localhost:6053.
+
+## Examples
+
+Enable pprof endpoints:
+
+~~~
+pprof
+~~~
+
+Listen on an alternate address:
+
+~~~
+pprof 10.9.8.7:6060
+~~~
+
+Listen on an all addresses on port 6060:
+
+~~~
+pprof :6060
+~~~
diff --git a/plugin/pprof/pprof.go b/plugin/pprof/pprof.go
new file mode 100644
index 000000000..020776ecf
--- /dev/null
+++ b/plugin/pprof/pprof.go
@@ -0,0 +1,49 @@
+// Package pprof implement a debug endpoint for getting profiles using the
+// go pprof tooling.
+package pprof
+
+import (
+ "log"
+ "net"
+ "net/http"
+ pp "net/http/pprof"
+)
+
+type handler struct {
+ addr string
+ ln net.Listener
+ mux *http.ServeMux
+}
+
+func (h *handler) Startup() error {
+ ln, err := net.Listen("tcp", h.addr)
+ if err != nil {
+ log.Printf("[ERROR] Failed to start pprof handler: %s", err)
+ return err
+ }
+
+ h.ln = ln
+
+ h.mux = http.NewServeMux()
+ h.mux.HandleFunc(path+"/", pp.Index)
+ h.mux.HandleFunc(path+"/cmdline", pp.Cmdline)
+ h.mux.HandleFunc(path+"/profile", pp.Profile)
+ h.mux.HandleFunc(path+"/symbol", pp.Symbol)
+ h.mux.HandleFunc(path+"/trace", pp.Trace)
+
+ go func() {
+ http.Serve(h.ln, h.mux)
+ }()
+ return nil
+}
+
+func (h *handler) Shutdown() error {
+ if h.ln != nil {
+ return h.ln.Close()
+ }
+ return nil
+}
+
+const (
+ path = "/debug/pprof"
+)
diff --git a/plugin/pprof/setup.go b/plugin/pprof/setup.go
new file mode 100644
index 000000000..22b82e94b
--- /dev/null
+++ b/plugin/pprof/setup.go
@@ -0,0 +1,53 @@
+package pprof
+
+import (
+ "net"
+ "sync"
+
+ "github.com/coredns/coredns/plugin"
+
+ "github.com/mholt/caddy"
+)
+
+const defaultAddr = "localhost:6053"
+
+func init() {
+ caddy.RegisterPlugin("pprof", caddy.Plugin{
+ ServerType: "dns",
+ Action: setup,
+ })
+}
+
+func setup(c *caddy.Controller) error {
+ found := false
+ h := &handler{addr: defaultAddr}
+ for c.Next() {
+ if found {
+ return plugin.Error("pprof", c.Err("pprof can only be specified once"))
+ }
+ args := c.RemainingArgs()
+ if len(args) == 1 {
+ h.addr = args[0]
+ _, _, e := net.SplitHostPort(h.addr)
+ if e != nil {
+ return e
+ }
+ }
+ if len(args) > 1 {
+ return plugin.Error("pprof", c.ArgErr())
+ }
+ if c.NextBlock() {
+ return plugin.Error("pprof", c.ArgErr())
+ }
+ found = true
+ }
+
+ pprofOnce.Do(func() {
+ c.OnStartup(h.Startup)
+ c.OnShutdown(h.Shutdown)
+ })
+
+ return nil
+}
+
+var pprofOnce sync.Once
diff --git a/plugin/pprof/setup_test.go b/plugin/pprof/setup_test.go
new file mode 100644
index 000000000..eaa4cb37e
--- /dev/null
+++ b/plugin/pprof/setup_test.go
@@ -0,0 +1,34 @@
+package pprof
+
+import (
+ "testing"
+
+ "github.com/mholt/caddy"
+)
+
+func TestPProf(t *testing.T) {
+ tests := []struct {
+ input string
+ shouldErr bool
+ }{
+ {`pprof`, false},
+ {`pprof 1.2.3.4:1234`, false},
+ {`pprof :1234`, false},
+ {`pprof {}`, true},
+ {`pprof /foo`, true},
+ {`pprof {
+ a b
+ }`, true},
+ {`pprof
+ pprof`, true},
+ }
+ for i, test := range tests {
+ c := caddy.NewTestController("dns", test.input)
+ err := setup(c)
+ if test.shouldErr && err == nil {
+ t.Errorf("Test %v: Expected error but found nil", i)
+ } else if !test.shouldErr && err != nil {
+ t.Errorf("Test %v: Expected no error but found error: %v", i, err)
+ }
+ }
+}