From 33d4df747b89279cf7a57e4e60394cacab80a947 Mon Sep 17 00:00:00 2001 From: Anton Kosyakov Date: Fri, 19 Mar 2021 09:14:06 +0000 Subject: [PATCH] [supervisor] allow to enable pprof --- .werft/values.dev.yaml | 2 ++ components/common-go/pprof/pprof.go | 23 +++++++++++++------ components/supervisor/go.mod | 1 + components/supervisor/go.sum | 1 + .../supervisor/pkg/supervisor/config.go | 3 +++ .../supervisor/pkg/supervisor/supervisor.go | 4 ++++ 6 files changed, 27 insertions(+), 7 deletions(-) diff --git a/.werft/values.dev.yaml b/.werft/values.dev.yaml index afb7bfe152a077..5f26587a5d42c5 100644 --- a/.werft/values.dev.yaml +++ b/.werft/values.dev.yaml @@ -71,6 +71,8 @@ components: env: - name: THEIA_RATELIMIT_LOG value: "50" + - name: SUPERVISOR_DEBUG_ENABLE + value: "true" prebuild: spec: containers: diff --git a/components/common-go/pprof/pprof.go b/components/common-go/pprof/pprof.go index 884b6a51171c76..c3db5c06d8497c 100644 --- a/components/common-go/pprof/pprof.go +++ b/components/common-go/pprof/pprof.go @@ -15,14 +15,12 @@ import ( "github.com/gitpod-io/gitpod/common-go/log" ) +const Path = "/debug/pprof/" + // Serve starts a new HTTP server serving pprof endpoints on the given addr func Serve(addr string) { mux := http.NewServeMux() - mux.HandleFunc("/debug/pprof/", index) - mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) - mux.HandleFunc("/debug/pprof/profile", pprof.Profile) - mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) - mux.HandleFunc("/debug/pprof/trace", pprof.Trace) + mux.Handle(Path, Handler()) log.WithField("addr", addr).Info("serving pprof service") err := http.ListenAndServe(addr, mux) @@ -31,8 +29,19 @@ func Serve(addr string) { } } +// Handler produces the pprof endpoint handler +func Handler() http.Handler { + mux := http.NewServeMux() + mux.HandleFunc("/", index) + mux.HandleFunc("/cmdline", pprof.Cmdline) + mux.HandleFunc("/profile", pprof.Profile) + mux.HandleFunc("/symbol", pprof.Symbol) + mux.HandleFunc("/trace", pprof.Trace) + return mux +} + func index(w http.ResponseWriter, r *http.Request) { - if strings.HasPrefix(r.URL.Path, "/debug/pprof/") { + if strings.HasPrefix(r.URL.Path, Path) { // according to Ian Lance Taylor it's ok to turn on mutex and block profiling // when asking for the actual profile [1]. This handler implements this idea, as // discussed in [2] @@ -41,7 +50,7 @@ func index(w http.ResponseWriter, r *http.Request) { // [2] /~https://github.com/golang/go/issues/23401 var ( - name = strings.TrimPrefix(r.URL.Path, "/debug/pprof/") + name = strings.TrimPrefix(r.URL.Path, Path) seconds, serr = strconv.ParseInt(r.URL.Query().Get("seconds"), 10, 64) ) if name == "mutex" { diff --git a/components/supervisor/go.mod b/components/supervisor/go.mod index 2dc71680d42feb..9e9e9e923cc99f 100644 --- a/components/supervisor/go.mod +++ b/components/supervisor/go.mod @@ -20,6 +20,7 @@ require ( github.com/golang/protobuf v1.4.3 github.com/google/go-cmp v0.5.4 github.com/google/uuid v1.1.4 + github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 github.com/grpc-ecosystem/grpc-gateway/v2 v2.2.0 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/prometheus/procfs v0.0.8 // indirect diff --git a/components/supervisor/go.sum b/components/supervisor/go.sum index 542be88fed237d..81a1fb96c67217 100644 --- a/components/supervisor/go.sum +++ b/components/supervisor/go.sum @@ -193,6 +193,7 @@ github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 h1:FlFbCRLd5Jr4iYXZufAvgWN6Ao0JrI5chLINnUXDDr0= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0 h1:bM6ZAFZmc/wPFaRDi0d5L7hGEZEx/2u+Tmr2evNHDiI= diff --git a/components/supervisor/pkg/supervisor/config.go b/components/supervisor/pkg/supervisor/config.go index 0ce907553274e5..689f5558facab5 100644 --- a/components/supervisor/pkg/supervisor/config.go +++ b/components/supervisor/pkg/supervisor/config.go @@ -192,6 +192,9 @@ type WorkspaceConfig struct { // GitpodHeadless controls whether the workspace is running headless GitpodHeadless string `env:"GITPOD_HEADLESS"` + + // DebugEnabled controls whether the supervisor debugging facilities (pprof, grpc tracing) shoudl be enabled + DebugEnable bool `env:"SUPERVISOR_DEBUG_ENABLE"` } // WorkspaceGitpodToken is a list of tokens that should be added to supervisor's token service diff --git a/components/supervisor/pkg/supervisor/supervisor.go b/components/supervisor/pkg/supervisor/supervisor.go index 5f4c75060e804d..5a88f2bc8f4f1f 100644 --- a/components/supervisor/pkg/supervisor/supervisor.go +++ b/components/supervisor/pkg/supervisor/supervisor.go @@ -28,6 +28,7 @@ import ( "google.golang.org/grpc" "github.com/gitpod-io/gitpod/common-go/log" + "github.com/gitpod-io/gitpod/common-go/pprof" csapi "github.com/gitpod-io/gitpod/content-service/api" "github.com/gitpod-io/gitpod/content-service/pkg/executor" "github.com/gitpod-io/gitpod/content-service/pkg/initializer" @@ -596,6 +597,9 @@ func startAPIEndpoint(ctx context.Context, cfg *Config, wg *sync.WaitGroup, serv routes := http.NewServeMux() routes.Handle("/_supervisor/v1/", http.StripPrefix("/_supervisor", restMux)) routes.Handle("/_supervisor/frontend", http.FileServer(http.Dir(cfg.FrontendLocation))) + if cfg.DebugEnable { + routes.Handle("/_supervisor"+pprof.Path, http.StripPrefix("/_supervisor", pprof.Handler())) + } go http.Serve(httpMux, routes) go m.Serve()