diff options
Diffstat (limited to 'plugin/pprof')
-rw-r--r-- | plugin/pprof/README.md | 41 | ||||
-rw-r--r-- | plugin/pprof/pprof.go | 49 | ||||
-rw-r--r-- | plugin/pprof/setup.go | 53 | ||||
-rw-r--r-- | plugin/pprof/setup_test.go | 34 |
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) + } + } +} |