diff --git a/embed/etcd.go b/embed/etcd.go index 43be1a3bd..8eb96239a 100644 --- a/embed/etcd.go +++ b/embed/etcd.go @@ -25,6 +25,7 @@ import ( "github.com/coreos/etcd/etcdserver" "github.com/coreos/etcd/etcdserver/api/v2http" "github.com/coreos/etcd/pkg/cors" + "github.com/coreos/etcd/pkg/debugutil" runtimeutil "github.com/coreos/etcd/pkg/runtime" "github.com/coreos/etcd/pkg/transport" "github.com/coreos/etcd/pkg/types" @@ -237,7 +238,7 @@ func startClientListeners(cfg *Config) (sctxs map[string]*serveCtx, err error) { } if cfg.EnablePprof { - plog.Infof("pprof is enabled under %s", pprofPrefix) + plog.Infof("pprof is enabled under %s", debugutil.HTTPPrefixPProf) } sctxs = make(map[string]*serveCtx) diff --git a/embed/serve.go b/embed/serve.go index fb05df730..158aa3797 100644 --- a/embed/serve.go +++ b/embed/serve.go @@ -20,7 +20,6 @@ import ( defaultLog "log" "net" "net/http" - "net/http/pprof" "strings" "time" @@ -30,6 +29,7 @@ import ( "github.com/coreos/etcd/etcdserver/api/v3lock/v3lockpb" "github.com/coreos/etcd/etcdserver/api/v3rpc" pb "github.com/coreos/etcd/etcdserver/etcdserverpb" + "github.com/coreos/etcd/pkg/debugutil" "github.com/coreos/etcd/pkg/transport" "github.com/cockroachdb/cmux" @@ -40,8 +40,6 @@ import ( "google.golang.org/grpc/credentials" ) -const pprofPrefix = "/debug/pprof" - type serveCtx struct { l net.Listener secure bool @@ -214,16 +212,9 @@ func (sctx *serveCtx) registerUserHandler(s string, h http.Handler) { } func (sctx *serveCtx) registerPprof() { - sctx.registerUserHandler(pprofPrefix+"/", http.HandlerFunc(pprof.Index)) - sctx.registerUserHandler(pprofPrefix+"/profile", http.HandlerFunc(pprof.Profile)) - sctx.registerUserHandler(pprofPrefix+"/symbol", http.HandlerFunc(pprof.Symbol)) - sctx.registerUserHandler(pprofPrefix+"/cmdline", http.HandlerFunc(pprof.Cmdline)) - sctx.registerUserHandler(pprofPrefix+"/trace", http.HandlerFunc(pprof.Trace)) - - sctx.registerUserHandler(pprofPrefix+"/heap", pprof.Handler("heap")) - sctx.registerUserHandler(pprofPrefix+"/goroutine", pprof.Handler("goroutine")) - sctx.registerUserHandler(pprofPrefix+"/threadcreate", pprof.Handler("threadcreate")) - sctx.registerUserHandler(pprofPrefix+"/block", pprof.Handler("block")) + for p, h := range debugutil.PProfHandlers() { + sctx.registerUserHandler(p, h) + } } func (sctx *serveCtx) registerTrace() { diff --git a/etcdmain/grpc_proxy.go b/etcdmain/grpc_proxy.go index a830dcdc4..068da2315 100644 --- a/etcdmain/grpc_proxy.go +++ b/etcdmain/grpc_proxy.go @@ -25,6 +25,7 @@ import ( "github.com/coreos/etcd/clientv3" "github.com/coreos/etcd/clientv3/namespace" pb "github.com/coreos/etcd/etcdserver/etcdserverpb" + "github.com/coreos/etcd/pkg/debugutil" "github.com/coreos/etcd/pkg/transport" "github.com/coreos/etcd/proxy/grpcproxy" @@ -47,6 +48,8 @@ var ( grpcProxyResolverTTL int grpcProxyNamespace string + + grpcProxyEnablePprof bool ) func init() { @@ -80,6 +83,7 @@ func newGRPCProxyStartCommand() *cobra.Command { cmd.Flags().StringVar(&grpcProxyResolverPrefix, "resolver-prefix", "", "prefix to use for registering proxy (must be shared with other grpc-proxy members)") cmd.Flags().IntVar(&grpcProxyResolverTTL, "resolver-ttl", 0, "specify TTL, in seconds, when registering proxy endpoints") cmd.Flags().StringVar(&grpcProxyNamespace, "namespace", "", "string to prefix to all keys for namespacing requests") + cmd.Flags().BoolVar(&grpcProxyEnablePprof, "enable-pprof", false, `Enable runtime profiling data via HTTP server. Address is at client URL + "/debug/pprof/"`) return &cmd } @@ -161,6 +165,13 @@ func startGRPCProxy(cmd *cobra.Command, args []string) { httpmux := http.NewServeMux() httpmux.HandleFunc("/", http.NotFound) httpmux.Handle("/metrics", prometheus.Handler()) + if grpcProxyEnablePprof { + for p, h := range debugutil.PProfHandlers() { + httpmux.Handle(p, h) + } + plog.Infof("pprof is enabled under %s", debugutil.HTTPPrefixPProf) + } + srvhttp := &http.Server{ Handler: httpmux, } diff --git a/pkg/debugutil/doc.go b/pkg/debugutil/doc.go new file mode 100644 index 000000000..74499eb27 --- /dev/null +++ b/pkg/debugutil/doc.go @@ -0,0 +1,16 @@ +// Copyright 2017 The etcd Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package debugutil includes utility functions for debugging. +package debugutil diff --git a/pkg/debugutil/pprof.go b/pkg/debugutil/pprof.go new file mode 100644 index 000000000..2e87124b8 --- /dev/null +++ b/pkg/debugutil/pprof.go @@ -0,0 +1,39 @@ +// Copyright 2017 The etcd Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package debugutil + +import ( + "net/http" + "net/http/pprof" +) + +const HTTPPrefixPProf = "/debug/pprof" + +// PProfHandlers returns a map of pprof handlers keyed by the HTTP path. +func PProfHandlers() map[string]http.Handler { + m := make(map[string]http.Handler) + + m[HTTPPrefixPProf+"/"] = http.HandlerFunc(pprof.Index) + m[HTTPPrefixPProf+"/profile"] = http.HandlerFunc(pprof.Profile) + m[HTTPPrefixPProf+"/symbol"] = http.HandlerFunc(pprof.Symbol) + m[HTTPPrefixPProf+"/cmdline"] = http.HandlerFunc(pprof.Cmdline) + m[HTTPPrefixPProf+"/trace "] = http.HandlerFunc(pprof.Trace) + m[HTTPPrefixPProf+"/heap"] = pprof.Handler("heap") + m[HTTPPrefixPProf+"/goroutine"] = pprof.Handler("goroutine") + m[HTTPPrefixPProf+"/threadcreate"] = pprof.Handler("threadcreate") + m[HTTPPrefixPProf+"/block"] = pprof.Handler("block") + + return m +}