From adce6fee5c080451c03b3cb585da6fa406ed9dfc Mon Sep 17 00:00:00 2001 From: Kleissner Date: Thu, 6 Jan 2022 00:05:27 +0100 Subject: [PATCH] Add debug API. This can be used for profiling. --- API.go | 4 +++ Debug empty.go | 11 ++++++ Debug.go | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++ Main.go | 3 +- README.md | 18 ++++++++++ go.mod | 2 +- go.sum | 4 +-- 7 files changed, 136 insertions(+), 4 deletions(-) create mode 100644 Debug empty.go create mode 100644 Debug.go diff --git a/API.go b/API.go index 1e2e5d6..6da9df7 100644 --- a/API.go +++ b/API.go @@ -44,6 +44,10 @@ func startAPI(backend *core.Backend, apiListen []string, apiKey uuid.UUID) { api.AllowKeyInParam = append(api.AllowKeyInParam, "/console") api.Router.HandleFunc("/console", apiConsole(backend)).Methods("GET") api.Router.HandleFunc("/shutdown", apiShutdown(backend)).Methods("GET") + + if config.DebugAPI { + attachDebugAPI(api) + } } // parseCmdParams parses the "-webapi", "-apikey", and "-watchpid" command line parameters. diff --git a/Debug empty.go b/Debug empty.go new file mode 100644 index 0000000..f9e0b1a --- /dev/null +++ b/Debug empty.go @@ -0,0 +1,11 @@ +//go:build !debug +// +build !debug + +package main + +import ( + "github.com/PeernetOfficial/core/webapi" +) + +func attachDebugAPI(api *webapi.WebapiInstance) { +} diff --git a/Debug.go b/Debug.go new file mode 100644 index 0000000..4544a0c --- /dev/null +++ b/Debug.go @@ -0,0 +1,98 @@ +//go:build debug +// +build debug + +/* +File Name: Debug.go +Copyright: 2017 Kleissner Investments s.r.o. +Author: Peter Kleissner + +Debug runtime functionality. The functions only work if the config setting DebugAPI is enabled. +*/ + +package main + +import ( + "net/http" + "net/http/pprof" // Warning: If the default HTTP handler is used, this installs handlers! + "runtime" + + "github.com/PeernetOfficial/core/webapi" +) + +// apiDebugBugcheck handles /debug/bugcheck +func apiDebugBugcheck(w http.ResponseWriter, r *http.Request) { + + if !config.DebugAPI { + http.Error(w, "", http.StatusOK) + return + } + + http.Error(w, "Executing immediate bugcheck", http.StatusOK) + + panic("via /debug/bugcheck") +} + +// apiDebugStack handles /debug/stack +func apiDebugStack(w http.ResponseWriter, r *http.Request) { + + if !config.DebugAPI { + http.Error(w, "", http.StatusOK) + return + } + + buffer := make([]byte, 1*1024*1024) + size := runtime.Stack(buffer, true) + + http.Error(w, string(buffer[:size]), http.StatusOK) +} + +func attachDebugAPI(api *webapi.WebapiInstance) { + api.AllowKeyInParam = append(api.AllowKeyInParam, []string{ + "/debug/bugcheck", + "/debug/stack", + "/debug/pprof", + "/debug/pprof/cmdline", + "/debug/pprof/profile", + "/debug/pprof/symbol", + "/debug/pprof/trace", + "/debug/pprof/goroutine", + "/debug/pprof/heap", + "/debug/pprof/threadcreate", + "/debug/pprof/block", + "/debug/pprof/allocs", + "/debug/pprof/mutex", + }...) + + api.Router.HandleFunc("/debug/bugcheck", apiDebugBugcheck) + api.Router.HandleFunc("/debug/stack", apiDebugStack) + + api.Router.HandleFunc("/debug/pprof", pprof.Index) + api.Router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) + api.Router.HandleFunc("/debug/pprof/profile", pprof.Profile) + api.Router.HandleFunc("/debug/pprof/symbol", pprof.Symbol) + api.Router.HandleFunc("/debug/pprof/trace", pprof.Trace) + + // Manually add support for paths linked to by index page at /debug/pprof/ + api.Router.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine")) + api.Router.Handle("/debug/pprof/heap", pprof.Handler("heap")) + api.Router.Handle("/debug/pprof/threadcreate", pprof.Handler("threadcreate")) + api.Router.Handle("/debug/pprof/block", pprof.Handler("block")) + api.Router.Handle("/debug/pprof/allocs", pprof.Handler("allocs")) + api.Router.Handle("/debug/pprof/mutex", pprof.Handler("mutex")) +} + +/* +To use the pprof functionality set DebugAPI in the config to true and then use the right endpoints. + +For heap profiling: +* While running, download the file http://[IP:Port]/debug/pprof/heap +* Run "go tool pprof -http=127.0.0.1:80 [service].exe heap". The http parameter will open an interactive web-server. +* pprof supports the following options. Default is inuse_space. + -inuse_space Display in-use memory size + -inuse_objects Display in-use object counts + -alloc_space Display allocated memory size + -alloc_objects Display allocated object counts +* Instead of download the file in step 2, you can directly provide the full URL in step 3! + +Enabling DebugAPI shall not have any performance impact; it just installs the handlers. Only once a profiler is actually used, it may impact the performance. +*/ diff --git a/Main.go b/Main.go index 98411c6..ab3b570 100644 --- a/Main.go +++ b/Main.go @@ -19,7 +19,8 @@ const appName = "Peernet Cmd" var config struct { // Log settings - ErrorOutput int `yaml:"ErrorOutput"` // 0 = Log file (default), 1 = Command line, 2 = Log file + command line, 3 = None + ErrorOutput int `yaml:"ErrorOutput"` // 0 = Log file (default), 1 = Command line, 2 = Log file + command line, 3 = None + DebugAPI bool `yaml:"DebugAPI"` // Enables the debug API which allows profiling. Do not enable in production. Only available if compiled with debug tag. // API settings APIListen []string `yaml:"APIListen"` // WebListen is in format IP:Port and declares where the web-interface should listen on. IP can also be ommitted to listen on any. diff --git a/README.md b/README.md index 405021b..b2fb230 100644 --- a/README.md +++ b/README.md @@ -231,3 +231,21 @@ Use the parameter `-watchpid=[PID]` to specify a process ID to monitor for exit ``` Cmd -watchpid=1234 ``` + +## Debug + +### Compile Debug Version + +A debug version that exposes multiple endpoints via `/debug` for profiling can be compiled by using the debug tag: + +``` +go build -tags debug +``` + +### Use the Profiler + +Once started attach the profiler `go tool pprof`. Note that `/debug` endpoints are not excluded from API authentication. If an API key is used, it can be provided via the `&k=` parameter. + +``` +go tool pprof -http=127.0.0.1:80 Cmd.exe http://127.0.0.1:1337/debug/pprof/heap?k=a30c01eb-856c-4b79-bdde-3c56a248f71b +``` diff --git a/go.mod b/go.mod index 6adf0e0..5852549 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/PeernetOfficial/Cmd go 1.16 require ( - github.com/PeernetOfficial/core v0.0.0-20211231003743-0689543d432f + github.com/PeernetOfficial/core v0.0.0-20220103002105-a19bac19163f github.com/google/uuid v1.3.0 github.com/gorilla/websocket v1.4.3-0.20210424162022-e8629af678b7 ) diff --git a/go.sum b/go.sum index e9102ac..90d04c8 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/IncSW/geoip2 v0.1.1 h1:afzzYF7n9JbdcPy8aiBSgBJuXi4mTWXZ3z6V3o6Vg34= github.com/IncSW/geoip2 v0.1.1/go.mod h1:adcasR40vXiUBjtzdaTTKL/6wSf+fgO4M8Gve/XzPUk= -github.com/PeernetOfficial/core v0.0.0-20211231003743-0689543d432f h1:jlHQnoboaX8vEX6PQdt86SUeLM77qR8WN0EyeH8dv5Q= -github.com/PeernetOfficial/core v0.0.0-20211231003743-0689543d432f/go.mod h1:VNhfwAZqya5JDZTS/ffa8Shof0OmQCx025CQMieGB7o= +github.com/PeernetOfficial/core v0.0.0-20220103002105-a19bac19163f h1:nkMU/dzSA61anIeBJGfCsq1+Ek6VR+BNXi4FnFkVu8k= +github.com/PeernetOfficial/core v0.0.0-20220103002105-a19bac19163f/go.mod h1:VNhfwAZqya5JDZTS/ffa8Shof0OmQCx025CQMieGB7o= github.com/akrylysov/pogreb v0.10.1 h1:FqlR8VR7uCbJdfUob916tPM+idpKgeESDXOA1K0DK4w= github.com/akrylysov/pogreb v0.10.1/go.mod h1:pNs6QmpQ1UlTJKDezuRWmaqkgUE2TuU0YTWyqJZ7+lI= github.com/enfipy/locker v1.1.0 h1:2zVJ0ky7cS1Vjs0x6OQWFiT2dSEiHrI5/O2KCz1fgGc=