From 0493265b1d9210d8a2ee86b2a96df271e37c916c Mon Sep 17 00:00:00 2001 From: Fred Heinecke Date: Fri, 2 May 2025 04:46:25 +0000 Subject: [PATCH] Added Go pprof handlers Signed-off-by: Fred Heinecke --- transcoder/.env.example | 6 ++++ transcoder/main.go | 3 ++ transcoder/src/api/pprof.go | 60 +++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 transcoder/src/api/pprof.go diff --git a/transcoder/.env.example b/transcoder/.env.example index d70c9a35..d33d1b9d 100644 --- a/transcoder/.env.example +++ b/transcoder/.env.example @@ -20,6 +20,12 @@ GOCODER_VAAPI_RENDERER="/dev/dri/renderD128" # the qsv device path (only used with GOCODER_HWACCEL=qsv) GOCODER_QSV_RENDERER="/dev/dri/renderD128" +# Performance tuning +# Set to true to enable pprof endpoints for profiling (/debug/pprof/). It is not recommended to expose +# this to users or the Internet, as this could be used to leak information via a side-channel attack. +# It is recommended to use a reverse proxy to restrict access to this endpoint, if enabled. +ENABLE_PPROF_ENDPOINT="false" + # Database things # Setting this ignores the below connection variables and overrides any default values # POSTGRES_URL=postgres://user:password@host:port/dbname?sslmode=disable diff --git a/transcoder/main.go b/transcoder/main.go index 6965d91b..acd8844a 100644 --- a/transcoder/main.go +++ b/transcoder/main.go @@ -6,6 +6,7 @@ import ( "strconv" "github.com/zoriya/kyoo/transcoder/src" + "github.com/zoriya/kyoo/transcoder/src/api" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" @@ -322,5 +323,7 @@ func main() { g.GET("/:path/attachment/:name", h.GetAttachment) g.GET("/:path/subtitle/:name", h.GetSubtitle) + api.RegisterPProfHandlers(e) + e.Logger.Fatal(e.Start(":7666")) } diff --git a/transcoder/src/api/pprof.go b/transcoder/src/api/pprof.go new file mode 100644 index 00000000..60c7fd9e --- /dev/null +++ b/transcoder/src/api/pprof.go @@ -0,0 +1,60 @@ +package api + +import ( + // Important: simply import the pprof package to register its with a default HTTP mux, if one is defined. + // This is done in the init function of the pprof package, and is unavoidable. + // This package should not use the default HTTP mux to prevent accidentially enabling these endpoints. + "net/http" + "net/http/pprof" + "os" + runtimepprof "runtime/pprof" + "strconv" + + "github.com/labstack/echo/v4" +) + +// This is similar to https://github.com/sevennt/echo-pprof/blob/master/pprof.go. +// Unfortunately, this library is not maintained anymore and doesn't support echo v4. +// It also hard-codes codes all pprof handlers, which sometimes change when new profiles are added. + +func RegisterPProfHandlers(e *echo.Echo) { + enablePProf := false + if enablePProfVar, ok := os.LookupEnv("ENABLE_PPROF_ENDPOINT"); ok { + enablePProf, _ = strconv.ParseBool(enablePProfVar) + } + + if !enablePProf { + return + } + + prefix := "/debug/pprof" // Standard prefix for pprof + g := e.Group(prefix) + + routers := map[string]http.HandlerFunc{ + "": pprof.Index, + "/": pprof.Index, + "/cmdline": pprof.Cmdline, + "/profile": pprof.Profile, + "/symbol": pprof.Symbol, + "/trace": pprof.Trace, + } + + // Handle all profiles supported by the Go runtime + // These are not hard-coded so that this function does not need to be updated + // when new profiles are added in the future. + for _, profile := range runtimepprof.Profiles() { + profileName := profile.Name() + path := "/" + profileName + routers[path] = pprof.Handler(profileName).ServeHTTP + } + + for path, handler := range routers { + handler := func(ctx echo.Context) error { + handler(ctx.Response().Writer, ctx.Request()) + return nil + } + + // The pprof handlers will accept/reject specific methods if needed. + g.Any(path, handler) + } +}