From 4df98808da895be94a1612e5b3ee7ab409e9c761 Mon Sep 17 00:00:00 2001 From: Tom DNetto Date: Wed, 7 Jun 2023 12:58:01 -0700 Subject: [PATCH] cmd/derper: add default-off debug endpoint for profiling Updates https://github.com/tailscale/corp/issues/11492 Signed-off-by: Tom DNetto --- cmd/derper/derper.go | 89 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/cmd/derper/derper.go b/cmd/derper/derper.go index 02736b6be..373faa8b5 100644 --- a/cmd/derper/derper.go +++ b/cmd/derper/derper.go @@ -12,15 +12,19 @@ import ( "expvar" "flag" "fmt" + "html" "io" "log" "math" "net" "net/http" + "net/http/pprof" "net/netip" "os" "path/filepath" "regexp" + "runtime" + "strconv" "strings" "time" @@ -33,6 +37,7 @@ import ( "tailscale.com/net/stun" "tailscale.com/tsweb" "tailscale.com/types/key" + "tailscale.com/version" ) var ( @@ -55,6 +60,8 @@ var ( acceptConnLimit = flag.Float64("accept-connection-limit", math.Inf(+1), "rate limit for accepting new connection") acceptConnBurst = flag.Int("accept-connection-burst", math.MaxInt, "burst limit for accepting new connection") + + debugAddr = flag.String("debug_addr", "", "listening address for debug server") ) var ( @@ -136,6 +143,7 @@ func main() { if *dev { *addr = ":3340" // above the keys DERP + *debugAddr = "localhost:8383" log.Printf("Running in dev mode.") tsweb.DevMode = true } @@ -222,6 +230,11 @@ func main() { go serveSTUN(listenHost, *stunPort) } + if *debugAddr != "" { + log.Printf("running debug server on %s", *debugAddr) + go runDebugServer(*debugAddr, s) + } + quietLogger := log.New(logFilter{}, "", 0) httpsrv := &http.Server{ Addr: *addr, @@ -513,3 +526,79 @@ func (logFilter) Write(p []byte) (int, error) { log.Printf("%s", p) return len(p), nil } + +func runDebugServer(addr string, s *derp.Server) { + mux := http.NewServeMux() + mux.Handle("/debug/vars", http.DefaultServeMux) // serve out expvar + mux.HandleFunc("/debug/varz", tsweb.VarzHandler) // expvar but in prometheus format + mux.HandleFunc("/debug/pprof/", 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("/debug/pprofrate", http.HandlerFunc(handlePprofRate)) + + mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html; charset=utf-8") + w.WriteHeader(200) + fmt.Fprintf(w, ` +

DERP

+

+ This is a Tailscale DERP debug server. +

+ +`, html.EscapeString(version.Long())) + })) + + srv := &http.Server{ + Addr: addr, + Handler: mux, + } + if err := srv.ListenAndServe(); err != nil { + if err != http.ErrServerClosed { + log.Fatal(err) + } + } +} + +// handlePprofRate adjusts the pprof rate. +// +// $ curl -d block=0 http://localhost:8383/debug/pprofrate +// $ curl -d mutex=0 http://localhost:8383/debug/pprofrate +// +// See: +// +// - https://pkg.go.dev/runtime#SetMutexProfileFraction +// - https://pkg.go.dev/runtime#SetBlockProfileRate +// +// And corp#5277. +func handlePprofRate(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + http.Error(w, "method not allowed; want POST", http.StatusMethodNotAllowed) + return + } + if s := r.FormValue("block"); s != "" { + v, err := strconv.Atoi(s) + if err != nil { + http.Error(w, "bad block value", http.StatusBadRequest) + return + } + runtime.SetBlockProfileRate(v) + fmt.Fprintf(w, "block set to %v\n", v) + } + if s := r.FormValue("mutex"); s != "" { + v, err := strconv.Atoi(s) + if err != nil { + http.Error(w, "bad mutex value", http.StatusBadRequest) + return + } + old := runtime.SetMutexProfileFraction(v) + fmt.Fprintf(w, "mutex changed from %v to %v\n", old, v) + } +}