diff --git a/cmd/tailscale/cli/cli.go b/cmd/tailscale/cli/cli.go index f8d5e0880..623b4a3bd 100644 --- a/cmd/tailscale/cli/cli.go +++ b/cmd/tailscale/cli/cli.go @@ -68,6 +68,11 @@ func Run(args []string) error { Exec: func(context.Context, []string) error { return flag.ErrHelp }, } + // Don't advertise the debug command, but it exists. + if strSliceContains(args, "debug") { + rootCmd.Subcommands = append(rootCmd.Subcommands, debugCmd) + } + if err := rootCmd.Parse(args); err != nil { return err } @@ -129,3 +134,12 @@ func pump(ctx context.Context, bc *ipn.BackendClient, conn net.Conn) { bc.GotNotifyMsg(msg) } } + +func strSliceContains(ss []string, s string) bool { + for _, v := range ss { + if v == s { + return true + } + } + return false +} diff --git a/cmd/tailscale/cli/debug.go b/cmd/tailscale/cli/debug.go new file mode 100644 index 000000000..50e9b9c23 --- /dev/null +++ b/cmd/tailscale/cli/debug.go @@ -0,0 +1,43 @@ +// 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. + +package cli + +import ( + "context" + "errors" + "flag" + "os" + + "github.com/peterbourgon/ff/v2/ffcli" + "tailscale.com/client/tailscale" +) + +var debugCmd = &ffcli.Command{ + Name: "debug", + Exec: runDebug, + FlagSet: (func() *flag.FlagSet { + fs := flag.NewFlagSet("debug", flag.ExitOnError) + fs.BoolVar(&debugArgs.goroutines, "daemon-goroutines", false, "If true, dump the tailscaled daemon's goroutines") + return fs + })(), +} + +var debugArgs struct { + goroutines bool +} + +func runDebug(ctx context.Context, args []string) error { + if len(args) > 0 { + return errors.New("unknown arguments") + } + if debugArgs.goroutines { + goroutines, err := tailscale.Goroutines(ctx) + if err != nil { + return err + } + os.Stdout.Write(goroutines) + } + return nil +}