ipn/localapi, cmd/tailscale: add CPU & memory profile support, debug command

This was already possible on Linux if you ran tailscaled with --debug
(which runs net/http/pprof), but it requires the user have the Go
toolchain around.

Also, it wasn't possible on macOS, as there's no way to run the IPNExtension
with a debug server (it doesn't run tailscaled).

And on Windows it's super tedious: beyond what users want to do or
what we want to explain.

Instead, put it in "tailscale debug" so it works and works the same on
all platforms. Then we can ask users to run it when we're debugging something
and they can email us the output files.

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2021-09-23 09:20:14 -07:00
committed by Brad Fitzpatrick
parent 27bc4e744c
commit efb84ca60d
5 changed files with 91 additions and 1 deletions

View File

@@ -94,6 +94,8 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.serveWhoIs(w, r)
case "/localapi/v0/goroutines":
h.serveGoroutines(w, r)
case "/localapi/v0/profile":
h.serveProfile(w, r)
case "/localapi/v0/status":
h.serveStatus(w, r)
case "/localapi/v0/logout":
@@ -181,6 +183,24 @@ func (h *Handler) serveGoroutines(w http.ResponseWriter, r *http.Request) {
w.Write(buf)
}
// serveProfileFunc is the implementation of Handler.serveProfile, after auth,
// for platforms where we want to link it in.
var serveProfileFunc func(http.ResponseWriter, *http.Request)
func (h *Handler) serveProfile(w http.ResponseWriter, r *http.Request) {
// Require write access out of paranoia that the profile dump
// might contain something sensitive.
if !h.PermitWrite {
http.Error(w, "profile access denied", http.StatusForbidden)
return
}
if serveProfileFunc == nil {
http.Error(w, "not implemented on this platform", http.StatusServiceUnavailable)
return
}
serveProfileFunc(w, r)
}
func (h *Handler) serveCheckIPForwarding(w http.ResponseWriter, r *http.Request) {
if !h.PermitRead {
http.Error(w, "IP forwarding check access denied", http.StatusForbidden)

30
ipn/localapi/profile.go Normal file
View File

@@ -0,0 +1,30 @@
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !ios && !android
// +build !ios,!android
// We don't include it on mobile where we're more memory constrained and
// there's no CLI to get at the results anyway.
package localapi
import (
"net/http"
"net/http/pprof"
)
func init() {
serveProfileFunc = serveProfile
}
func serveProfile(w http.ResponseWriter, r *http.Request) {
name := r.FormValue("name")
switch name {
case "profile":
pprof.Profile(w, r)
default:
pprof.Handler(name).ServeHTTP(w, r)
}
}