github.com/neilgarb/delve@v1.9.2-nobreaks/cmd/dlv/cmds/commands.go (about)

     1  package cmds
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"net"
     8  	"os"
     9  	"os/exec"
    10  	"os/signal"
    11  	"path/filepath"
    12  	"reflect"
    13  	"runtime"
    14  	"strconv"
    15  	"strings"
    16  	"syscall"
    17  
    18  	"github.com/go-delve/delve/pkg/config"
    19  	"github.com/go-delve/delve/pkg/gobuild"
    20  	"github.com/go-delve/delve/pkg/goversion"
    21  	"github.com/go-delve/delve/pkg/logflags"
    22  	"github.com/go-delve/delve/pkg/terminal"
    23  	"github.com/go-delve/delve/pkg/version"
    24  	"github.com/go-delve/delve/service"
    25  	"github.com/go-delve/delve/service/api"
    26  	"github.com/go-delve/delve/service/dap"
    27  	"github.com/go-delve/delve/service/debugger"
    28  	"github.com/go-delve/delve/service/rpc2"
    29  	"github.com/go-delve/delve/service/rpccommon"
    30  	"github.com/mattn/go-isatty"
    31  	"github.com/spf13/cobra"
    32  )
    33  
    34  var (
    35  	// log is whether to log debug statements.
    36  	log bool
    37  	// logOutput is a comma separated list of components that should produce debug output.
    38  	logOutput string
    39  	// logDest is the file path or file descriptor where logs should go.
    40  	logDest string
    41  	// headless is whether to run without terminal.
    42  	headless bool
    43  	// continueOnStart is whether to continue the process on startup
    44  	continueOnStart bool
    45  	// apiVersion is the requested API version while running headless
    46  	apiVersion int
    47  	// acceptMulti allows multiple clients to connect to the same server
    48  	acceptMulti bool
    49  	// addr is the debugging server listen address.
    50  	addr string
    51  	// initFile is the path to initialization file.
    52  	initFile string
    53  	// buildFlags is the flags passed during compiler invocation.
    54  	buildFlags string
    55  	// workingDir is the working directory for running the program.
    56  	workingDir string
    57  	// checkLocalConnUser is true if the debugger should check that local
    58  	// connections come from the same user that started the headless server
    59  	checkLocalConnUser bool
    60  	// tty is used to provide an alternate TTY for the program you wish to debug.
    61  	tty string
    62  	// disableASLR is used to disable ASLR
    63  	disableASLR bool
    64  
    65  	// dapClientAddr is dap subcommand's flag that specifies the address of a DAP client.
    66  	// If it is specified, the dap server starts a debug session by dialing to the client.
    67  	// The dap server will serve only for the debug session.
    68  	dapClientAddr string
    69  
    70  	// backend selection
    71  	backend string
    72  
    73  	// checkGoVersion is true if the debugger should check the version of Go
    74  	// used to compile the executable and refuse to work on incompatible
    75  	// versions.
    76  	checkGoVersion bool
    77  
    78  	// rootCommand is the root of the command tree.
    79  	rootCommand *cobra.Command
    80  
    81  	traceAttachPid  int
    82  	traceExecFile   string
    83  	traceTestBinary bool
    84  	traceStackDepth int
    85  	traceUseEBPF    bool
    86  
    87  	// redirect specifications for target process
    88  	redirects []string
    89  
    90  	allowNonTerminalInteractive bool
    91  
    92  	conf        *config.Config
    93  	loadConfErr error
    94  )
    95  
    96  const dlvCommandLongDesc = `Delve is a source level debugger for Go programs.
    97  
    98  Delve enables you to interact with your program by controlling the execution of the process,
    99  evaluating variables, and providing information of thread / goroutine state, CPU register state and more.
   100  
   101  The goal of this tool is to provide a simple yet powerful interface for debugging Go programs.
   102  
   103  Pass flags to the program you are debugging using ` + "`--`" + `, for example:
   104  
   105  ` + "`dlv exec ./hello -- server --config conf/config.toml`"
   106  
   107  // New returns an initialized command tree.
   108  func New(docCall bool) *cobra.Command {
   109  	// Config setup and load.
   110  	conf, loadConfErr = config.LoadConfig()
   111  	// Delay reporting errors about configuration loading delayed until after the
   112  	// server is started so that the "server listening at" message is always
   113  	// the first thing emitted. Also logflags hasn't been setup yet at this point.
   114  	buildFlagsDefault := ""
   115  	if runtime.GOOS == "windows" {
   116  		ver, _ := goversion.Installed()
   117  		if ver.Major > 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 9, Rev: -1}) {
   118  			// Work-around for https://github.com/golang/go/issues/13154
   119  			buildFlagsDefault = "-ldflags='-linkmode internal'"
   120  		}
   121  	}
   122  
   123  	// Main dlv root command.
   124  	rootCommand = &cobra.Command{
   125  		Use:   "dlv",
   126  		Short: "Delve is a debugger for the Go programming language.",
   127  		Long:  dlvCommandLongDesc,
   128  	}
   129  
   130  	rootCommand.PersistentFlags().StringVarP(&addr, "listen", "l", "127.0.0.1:0", "Debugging server listen address.")
   131  
   132  	rootCommand.PersistentFlags().BoolVarP(&log, "log", "", false, "Enable debugging server logging.")
   133  	rootCommand.PersistentFlags().StringVarP(&logOutput, "log-output", "", "", `Comma separated list of components that should produce debug output (see 'dlv help log')`)
   134  	rootCommand.PersistentFlags().StringVarP(&logDest, "log-dest", "", "", "Writes logs to the specified file or file descriptor (see 'dlv help log').")
   135  
   136  	rootCommand.PersistentFlags().BoolVarP(&headless, "headless", "", false, "Run debug server only, in headless mode. Server will accept both JSON-RPC or DAP client connections.")
   137  	rootCommand.PersistentFlags().BoolVarP(&acceptMulti, "accept-multiclient", "", false, "Allows a headless server to accept multiple client connections via JSON-RPC or DAP.")
   138  	rootCommand.PersistentFlags().IntVar(&apiVersion, "api-version", 1, "Selects JSON-RPC API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md.")
   139  	rootCommand.PersistentFlags().StringVar(&initFile, "init", "", "Init file, executed by the terminal client.")
   140  	rootCommand.PersistentFlags().StringVar(&buildFlags, "build-flags", buildFlagsDefault, "Build flags, to be passed to the compiler. For example: --build-flags=\"-tags=integration -mod=vendor -cover -v\"")
   141  	rootCommand.PersistentFlags().StringVar(&workingDir, "wd", "", "Working directory for running the program.")
   142  	rootCommand.PersistentFlags().BoolVarP(&checkGoVersion, "check-go-version", "", true, "Exits if the version of Go in use is not compatible (too old or too new) with the version of Delve.")
   143  	rootCommand.PersistentFlags().BoolVarP(&checkLocalConnUser, "only-same-user", "", true, "Only connections from the same user that started this instance of Delve are allowed to connect.")
   144  	rootCommand.PersistentFlags().StringVar(&backend, "backend", "default", `Backend selection (see 'dlv help backend').`)
   145  	rootCommand.PersistentFlags().StringArrayVarP(&redirects, "redirect", "r", []string{}, "Specifies redirect rules for target process (see 'dlv help redirect')")
   146  	rootCommand.PersistentFlags().BoolVar(&allowNonTerminalInteractive, "allow-non-terminal-interactive", false, "Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr")
   147  	rootCommand.PersistentFlags().BoolVar(&disableASLR, "disable-aslr", false, "Disables address space randomization")
   148  
   149  	// 'attach' subcommand.
   150  	attachCommand := &cobra.Command{
   151  		Use:   "attach pid [executable]",
   152  		Short: "Attach to running process and begin debugging.",
   153  		Long: `Attach to an already running process and begin debugging it.
   154  
   155  This command will cause Delve to take control of an already running process, and
   156  begin a new debug session.  When exiting the debug session you will have the
   157  option to let the process continue or kill it.
   158  `,
   159  		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
   160  			if len(args) == 0 {
   161  				return errors.New("you must provide a PID")
   162  			}
   163  			return nil
   164  		},
   165  		Run: attachCmd,
   166  	}
   167  	attachCommand.Flags().BoolVar(&continueOnStart, "continue", false, "Continue the debugged process on start.")
   168  	rootCommand.AddCommand(attachCommand)
   169  
   170  	// 'connect' subcommand.
   171  	connectCommand := &cobra.Command{
   172  		Use:   "connect addr",
   173  		Short: "Connect to a headless debug server with a terminal client.",
   174  		Long:  "Connect to a running headless debug server with a terminal client.",
   175  		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
   176  			if len(args) == 0 {
   177  				return errors.New("you must provide an address as the first argument")
   178  			}
   179  			return nil
   180  		},
   181  		Run: connectCmd,
   182  	}
   183  	rootCommand.AddCommand(connectCommand)
   184  
   185  	// 'dap' subcommand.
   186  	dapCommand := &cobra.Command{
   187  		Use:   "dap",
   188  		Short: "Starts a headless TCP server communicating via Debug Adaptor Protocol (DAP).",
   189  		Long: `Starts a headless TCP server communicating via Debug Adaptor Protocol (DAP).
   190  
   191  The server is always headless and requires a DAP client like VS Code to connect and request a binary
   192  to be launched or a process to be attached to. The following modes can be specified via the client's launch config:
   193  - launch + exec   (executes precompiled binary, like 'dlv exec')
   194  - launch + debug  (builds and launches, like 'dlv debug')
   195  - launch + test   (builds and tests, like 'dlv test')
   196  - launch + replay (replays an rr trace, like 'dlv replay')
   197  - launch + core   (replays a core dump file, like 'dlv core')
   198  - attach + local  (attaches to a running process, like 'dlv attach')
   199  
   200  Program and output binary paths will be interpreted relative to dlv's working directory.
   201  
   202  This server does not accept multiple client connections (--accept-multiclient).
   203  Use 'dlv [command] --headless' instead and a DAP client with attach + remote config.
   204  While --continue is not supported, stopOnEntry launch/attach attribute can be used to control if
   205  execution is resumed at the start of the debug session.
   206  
   207  The --client-addr flag is a special flag that makes the server initiate a debug session
   208  by dialing in to the host:port where a DAP client is waiting. This server process
   209  will exit when the debug session ends.`,
   210  		Run: dapCmd,
   211  	}
   212  	dapCommand.Flags().StringVar(&dapClientAddr, "client-addr", "", "host:port where the DAP client is waiting for the DAP server to dial in")
   213  
   214  	// TODO(polina): support --tty when dlv dap allows to launch a program from command-line
   215  	rootCommand.AddCommand(dapCommand)
   216  
   217  	// 'debug' subcommand.
   218  	debugCommand := &cobra.Command{
   219  		Use:   "debug [package]",
   220  		Short: "Compile and begin debugging main package in current directory, or the package specified.",
   221  		Long: `Compiles your program with optimizations disabled, starts and attaches to it.
   222  
   223  By default, with no arguments, Delve will compile the 'main' package in the
   224  current directory, and begin to debug it. Alternatively you can specify a
   225  package name and Delve will compile that package instead, and begin a new debug
   226  session.`,
   227  		Run: debugCmd,
   228  	}
   229  	debugCommand.Flags().String("output", "./__debug_bin", "Output path for the binary.")
   230  	debugCommand.Flags().BoolVar(&continueOnStart, "continue", false, "Continue the debugged process on start.")
   231  	debugCommand.Flags().StringVar(&tty, "tty", "", "TTY to use for the target program")
   232  	rootCommand.AddCommand(debugCommand)
   233  
   234  	// 'exec' subcommand.
   235  	execCommand := &cobra.Command{
   236  		Use:   "exec <path/to/binary>",
   237  		Short: "Execute a precompiled binary, and begin a debug session.",
   238  		Long: `Execute a precompiled binary and begin a debug session.
   239  
   240  This command will cause Delve to exec the binary and immediately attach to it to
   241  begin a new debug session. Please note that if the binary was not compiled with
   242  optimizations disabled, it may be difficult to properly debug it. Please
   243  consider compiling debugging binaries with -gcflags="all=-N -l" on Go 1.10
   244  or later, -gcflags="-N -l" on earlier versions of Go.`,
   245  		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
   246  			if len(args) == 0 {
   247  				return errors.New("you must provide a path to a binary")
   248  			}
   249  			return nil
   250  		},
   251  		Run: func(cmd *cobra.Command, args []string) {
   252  			os.Exit(execute(0, args, conf, "", debugger.ExecutingExistingFile, args, buildFlags))
   253  		},
   254  	}
   255  	execCommand.Flags().StringVar(&tty, "tty", "", "TTY to use for the target program")
   256  	execCommand.Flags().BoolVar(&continueOnStart, "continue", false, "Continue the debugged process on start.")
   257  	rootCommand.AddCommand(execCommand)
   258  
   259  	// Deprecated 'run' subcommand.
   260  	runCommand := &cobra.Command{
   261  		Use:   "run",
   262  		Short: "Deprecated command. Use 'debug' instead.",
   263  		Run: func(cmd *cobra.Command, args []string) {
   264  			fmt.Println("This command is deprecated, please use 'debug' instead.")
   265  			os.Exit(0)
   266  		},
   267  	}
   268  	rootCommand.AddCommand(runCommand)
   269  
   270  	// 'test' subcommand.
   271  	testCommand := &cobra.Command{
   272  		Use:   "test [package]",
   273  		Short: "Compile test binary and begin debugging program.",
   274  		Long: `Compiles a test binary with optimizations disabled and begins a new debug session.
   275  
   276  The test command allows you to begin a new debug session in the context of your
   277  unit tests. By default Delve will debug the tests in the current directory.
   278  Alternatively you can specify a package name, and Delve will debug the tests in
   279  that package instead. Double-dashes ` + "`--`" + ` can be used to pass arguments to the test program:
   280  
   281  dlv test [package] -- -test.v -other-argument
   282  
   283  See also: 'go help testflag'.`,
   284  		Run: testCmd,
   285  	}
   286  	testCommand.Flags().String("output", "debug.test", "Output path for the binary.")
   287  	rootCommand.AddCommand(testCommand)
   288  
   289  	// 'trace' subcommand.
   290  	traceCommand := &cobra.Command{
   291  		Use:   "trace [package] regexp",
   292  		Short: "Compile and begin tracing program.",
   293  		Long: `Trace program execution.
   294  
   295  The trace sub command will set a tracepoint on every function matching the
   296  provided regular expression and output information when tracepoint is hit.  This
   297  is useful if you do not want to begin an entire debug session, but merely want
   298  to know what functions your process is executing.
   299  
   300  The output of the trace sub command is printed to stderr, so if you would like to
   301  only see the output of the trace operations you can redirect stdout.`,
   302  		Run: traceCmd,
   303  	}
   304  	traceCommand.Flags().IntVarP(&traceAttachPid, "pid", "p", 0, "Pid to attach to.")
   305  	traceCommand.Flags().StringVarP(&traceExecFile, "exec", "e", "", "Binary file to exec and trace.")
   306  	traceCommand.Flags().BoolVarP(&traceTestBinary, "test", "t", false, "Trace a test binary.")
   307  	traceCommand.Flags().BoolVarP(&traceUseEBPF, "ebpf", "", false, "Trace using eBPF (experimental).")
   308  	traceCommand.Flags().IntVarP(&traceStackDepth, "stack", "s", 0, "Show stack trace with given depth. (Ignored with -ebpf)")
   309  	traceCommand.Flags().String("output", "debug", "Output path for the binary.")
   310  	rootCommand.AddCommand(traceCommand)
   311  
   312  	coreCommand := &cobra.Command{
   313  		Use:   "core <executable> <core>",
   314  		Short: "Examine a core dump.",
   315  		Long: `Examine a core dump (only supports linux and windows core dumps).
   316  
   317  The core command will open the specified core file and the associated
   318  executable and let you examine the state of the process when the
   319  core dump was taken.
   320  
   321  Currently supports linux/amd64 and linux/arm64 core files, windows/amd64 minidumps and core files generated by Delve's 'dump' command.`,
   322  		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
   323  			if len(args) != 2 {
   324  				return errors.New("you must provide a core file and an executable")
   325  			}
   326  			return nil
   327  		},
   328  		Run: coreCmd,
   329  	}
   330  	rootCommand.AddCommand(coreCommand)
   331  
   332  	// 'version' subcommand.
   333  	var versionVerbose = false
   334  	versionCommand := &cobra.Command{
   335  		Use:   "version",
   336  		Short: "Prints version.",
   337  		Run: func(cmd *cobra.Command, args []string) {
   338  			fmt.Printf("Delve Debugger\n%s\n", version.DelveVersion)
   339  			if versionVerbose {
   340  				fmt.Printf("Build Details: %s\n", version.BuildInfo())
   341  			}
   342  		},
   343  	}
   344  	versionCommand.Flags().BoolVarP(&versionVerbose, "verbose", "v", false, "print verbose version info")
   345  	rootCommand.AddCommand(versionCommand)
   346  
   347  	if path, _ := exec.LookPath("rr"); path != "" || docCall {
   348  		replayCommand := &cobra.Command{
   349  			Use:   "replay [trace directory]",
   350  			Short: "Replays a rr trace.",
   351  			Long: `Replays a rr trace.
   352  
   353  The replay command will open a trace generated by mozilla rr. Mozilla rr must be installed:
   354  https://github.com/mozilla/rr
   355  			`,
   356  			PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
   357  				if len(args) == 0 {
   358  					return errors.New("you must provide a path to a binary")
   359  				}
   360  				return nil
   361  			},
   362  			Run: func(cmd *cobra.Command, args []string) {
   363  				backend = "rr"
   364  				os.Exit(execute(0, []string{}, conf, args[0], debugger.ExecutingOther, args, buildFlags))
   365  			},
   366  		}
   367  		rootCommand.AddCommand(replayCommand)
   368  	}
   369  
   370  	rootCommand.AddCommand(&cobra.Command{
   371  		Use:   "backend",
   372  		Short: "Help about the --backend flag.",
   373  		Long: `The --backend flag specifies which backend should be used, possible values
   374  are:
   375  
   376  	default		Uses lldb on macOS, native everywhere else.
   377  	native		Native backend.
   378  	lldb		Uses lldb-server or debugserver.
   379  	rr		Uses mozilla rr (https://github.com/mozilla/rr).
   380  
   381  `})
   382  
   383  	rootCommand.AddCommand(&cobra.Command{
   384  		Use:   "log",
   385  		Short: "Help about logging flags.",
   386  		Long: `Logging can be enabled by specifying the --log flag and using the
   387  --log-output flag to select which components should produce logs.
   388  
   389  The argument of --log-output must be a comma separated list of component
   390  names selected from this list:
   391  
   392  
   393  	debugger	Log debugger commands
   394  	gdbwire		Log connection to gdbserial backend
   395  	lldbout		Copy output from debugserver/lldb to standard output
   396  	debuglineerr	Log recoverable errors reading .debug_line
   397  	rpc		Log all RPC messages
   398  	dap		Log all DAP messages
   399  	fncall		Log function call protocol
   400  	minidump	Log minidump loading
   401  
   402  Additionally --log-dest can be used to specify where the logs should be
   403  written. 
   404  If the argument is a number it will be interpreted as a file descriptor,
   405  otherwise as a file path.
   406  This option will also redirect the "server listening at" message in headless
   407  and dap modes.
   408  
   409  `,
   410  	})
   411  
   412  	rootCommand.AddCommand(&cobra.Command{
   413  		Use:   "redirect",
   414  		Short: "Help about file redirection.",
   415  		Long: `The standard file descriptors of the target process can be controlled using the '-r' and '--tty' arguments. 
   416  
   417  The --tty argument allows redirecting all standard descriptors to a terminal, specified as an argument to --tty.
   418  
   419  The syntax for '-r' argument is:
   420  
   421  		-r [source:]destination
   422  
   423  Where source is one of 'stdin', 'stdout' or 'stderr' and destination is the path to a file. If the source is omitted stdin is used implicitly.
   424  
   425  File redirects can also be changed using the 'restart' command.
   426  `,
   427  	})
   428  
   429  	rootCommand.DisableAutoGenTag = true
   430  
   431  	return rootCommand
   432  }
   433  
   434  func dapCmd(cmd *cobra.Command, args []string) {
   435  	status := func() int {
   436  		if err := logflags.Setup(log, logOutput, logDest); err != nil {
   437  			fmt.Fprintf(os.Stderr, "%v\n", err)
   438  			return 1
   439  		}
   440  		defer logflags.Close()
   441  
   442  		if loadConfErr != nil {
   443  			logflags.DebuggerLogger().Errorf("%v", loadConfErr)
   444  		}
   445  
   446  		if cmd.Flag("headless").Changed {
   447  			fmt.Fprintf(os.Stderr, "Warning: dap mode is always headless\n")
   448  		}
   449  		if acceptMulti {
   450  			fmt.Fprintf(os.Stderr, "Warning: accept-multiclient mode not supported with dap\n")
   451  		}
   452  		if initFile != "" {
   453  			fmt.Fprint(os.Stderr, "Warning: init file ignored with dap\n")
   454  		}
   455  		if continueOnStart {
   456  			fmt.Fprintf(os.Stderr, "Warning: continue ignored with dap; specify via launch/attach request instead\n")
   457  		}
   458  		if backend != "default" {
   459  			fmt.Fprintf(os.Stderr, "Warning: backend ignored with dap; specify via launch/attach request instead\n")
   460  		}
   461  		if buildFlags != "" {
   462  			fmt.Fprintf(os.Stderr, "Warning: build flags ignored with dap; specify via launch/attach request instead\n")
   463  		}
   464  		if workingDir != "" {
   465  			fmt.Fprintf(os.Stderr, "Warning: working directory ignored with dap: specify via launch request instead\n")
   466  		}
   467  		dlvArgs, targetArgs := splitArgs(cmd, args)
   468  		if len(dlvArgs) > 0 {
   469  			fmt.Fprintf(os.Stderr, "Warning: debug arguments ignored with dap; specify via launch/attach request instead\n")
   470  		}
   471  		if len(targetArgs) > 0 {
   472  			fmt.Fprintf(os.Stderr, "Warning: program flags ignored with dap; specify via launch/attach request instead\n")
   473  		}
   474  
   475  		disconnectChan := make(chan struct{})
   476  		config := &service.Config{
   477  			DisconnectChan: disconnectChan,
   478  			Debugger: debugger.Config{
   479  				Backend:              backend,
   480  				Foreground:           true, // server always runs without terminal client
   481  				DebugInfoDirectories: conf.DebugInfoDirectories,
   482  				CheckGoVersion:       checkGoVersion,
   483  				DisableASLR:          disableASLR,
   484  			},
   485  			CheckLocalConnUser: checkLocalConnUser,
   486  		}
   487  		var conn net.Conn
   488  		if dapClientAddr == "" {
   489  			listener, err := net.Listen("tcp", addr)
   490  			if err != nil {
   491  				fmt.Printf("couldn't start listener: %s\n", err)
   492  				return 1
   493  			}
   494  			config.Listener = listener
   495  		} else { // with a predetermined client.
   496  			var err error
   497  			conn, err = net.Dial("tcp", dapClientAddr)
   498  			if err != nil {
   499  				fmt.Fprintf(os.Stderr, "Failed to connect to the DAP client: %v\n", err)
   500  				return 1
   501  			}
   502  		}
   503  
   504  		server := dap.NewServer(config)
   505  		defer server.Stop()
   506  		if conn == nil {
   507  			server.Run()
   508  		} else { // work with a predetermined client.
   509  			server.RunWithClient(conn)
   510  		}
   511  		waitForDisconnectSignal(disconnectChan)
   512  		return 0
   513  	}()
   514  	os.Exit(status)
   515  }
   516  
   517  func buildBinary(cmd *cobra.Command, args []string, isTest bool) (string, bool) {
   518  	debugname, err := filepath.Abs(cmd.Flag("output").Value.String())
   519  	if err != nil {
   520  		fmt.Fprintf(os.Stderr, "%v\n", err)
   521  		return "", false
   522  	}
   523  
   524  	if isTest {
   525  		err = gobuild.GoTestBuild(debugname, args, buildFlags)
   526  	} else {
   527  		err = gobuild.GoBuild(debugname, args, buildFlags)
   528  	}
   529  	if err != nil {
   530  		fmt.Fprintf(os.Stderr, "%v\n", err)
   531  		return "", false
   532  	}
   533  	return debugname, true
   534  }
   535  
   536  func debugCmd(cmd *cobra.Command, args []string) {
   537  	status := func() int {
   538  		dlvArgs, targetArgs := splitArgs(cmd, args)
   539  		debugname, ok := buildBinary(cmd, dlvArgs, false)
   540  		if !ok {
   541  			return 1
   542  		}
   543  		defer gobuild.Remove(debugname)
   544  		processArgs := append([]string{debugname}, targetArgs...)
   545  		return execute(0, processArgs, conf, "", debugger.ExecutingGeneratedFile, dlvArgs, buildFlags)
   546  	}()
   547  	os.Exit(status)
   548  }
   549  
   550  func traceCmd(cmd *cobra.Command, args []string) {
   551  	status := func() int {
   552  		err := logflags.Setup(log, logOutput, logDest)
   553  		defer logflags.Close()
   554  		if err != nil {
   555  			fmt.Fprintf(os.Stderr, "%v\n", err)
   556  			return 1
   557  		}
   558  		if loadConfErr != nil {
   559  			logflags.DebuggerLogger().Errorf("%v", loadConfErr)
   560  		}
   561  
   562  		if headless {
   563  			fmt.Fprintf(os.Stderr, "Warning: headless mode not supported with trace\n")
   564  		}
   565  		if acceptMulti {
   566  			fmt.Fprintf(os.Stderr, "Warning: accept multiclient mode not supported with trace")
   567  		}
   568  
   569  		var regexp string
   570  		var processArgs []string
   571  
   572  		dlvArgs, targetArgs := splitArgs(cmd, args)
   573  		var dlvArgsLen = len(dlvArgs)
   574  		switch dlvArgsLen {
   575  		case 0:
   576  			fmt.Fprintf(os.Stderr, "you must supply a regexp for functions to trace\n")
   577  			return 1
   578  		case 1:
   579  			regexp = args[0]
   580  			dlvArgs = dlvArgs[0:0]
   581  		default:
   582  			regexp = dlvArgs[dlvArgsLen-1]
   583  			dlvArgs = dlvArgs[:dlvArgsLen-1]
   584  		}
   585  
   586  		var debugname string
   587  		if traceAttachPid == 0 {
   588  			if dlvArgsLen >= 2 && traceExecFile != "" {
   589  				fmt.Fprintln(os.Stderr, "Cannot specify package when using exec.")
   590  				return 1
   591  			}
   592  
   593  			debugname = traceExecFile
   594  			if traceExecFile == "" {
   595  				debugexe, ok := buildBinary(cmd, dlvArgs, traceTestBinary)
   596  				if !ok {
   597  					return 1
   598  				}
   599  				debugname = debugexe
   600  				defer gobuild.Remove(debugname)
   601  			}
   602  
   603  			processArgs = append([]string{debugname}, targetArgs...)
   604  		}
   605  
   606  		// Make a local in-memory connection that client and server use to communicate
   607  		listener, clientConn := service.ListenerPipe()
   608  		defer listener.Close()
   609  
   610  		if workingDir == "" {
   611  			workingDir = "."
   612  		}
   613  
   614  		// Create and start a debug server
   615  		server := rpccommon.NewServer(&service.Config{
   616  			Listener:    listener,
   617  			ProcessArgs: processArgs,
   618  			APIVersion:  2,
   619  			Debugger: debugger.Config{
   620  				AttachPid:      traceAttachPid,
   621  				WorkingDir:     workingDir,
   622  				Backend:        backend,
   623  				CheckGoVersion: checkGoVersion,
   624  			},
   625  		})
   626  		if err := server.Run(); err != nil {
   627  			fmt.Fprintln(os.Stderr, err)
   628  			return 1
   629  		}
   630  		client := rpc2.NewClientFromConn(clientConn)
   631  		funcs, err := client.ListFunctions(regexp)
   632  		if err != nil {
   633  			fmt.Fprintln(os.Stderr, err)
   634  			return 1
   635  		}
   636  		for i := range funcs {
   637  			if traceUseEBPF {
   638  				err := client.CreateEBPFTracepoint(funcs[i])
   639  				if err != nil {
   640  					fmt.Fprintf(os.Stderr, "unable to set tracepoint on function %s: %#v\n", funcs[i], err)
   641  				}
   642  			} else {
   643  				// Fall back to breakpoint based tracing if we get an error.
   644  				_, err = client.CreateBreakpoint(&api.Breakpoint{
   645  					FunctionName: funcs[i],
   646  					Tracepoint:   true,
   647  					Line:         -1,
   648  					Stacktrace:   traceStackDepth,
   649  					LoadArgs:     &terminal.ShortLoadConfig,
   650  				})
   651  				if err != nil && !isBreakpointExistsErr(err) {
   652  					fmt.Fprintf(os.Stderr, "unable to set tracepoint on function %s: %#v\n", funcs[i], err)
   653  				}
   654  				addrs, err := client.FunctionReturnLocations(funcs[i])
   655  				if err != nil {
   656  					fmt.Fprintf(os.Stderr, "unable to set tracepoint on function %s: %#v\n", funcs[i], err)
   657  				}
   658  				for i := range addrs {
   659  					_, err = client.CreateBreakpoint(&api.Breakpoint{
   660  						Addr:        addrs[i],
   661  						TraceReturn: true,
   662  						Stacktrace:  traceStackDepth,
   663  						Line:        -1,
   664  						LoadArgs:    &terminal.ShortLoadConfig,
   665  					})
   666  					if err != nil && !isBreakpointExistsErr(err) {
   667  						fmt.Fprintf(os.Stderr, "unable to set tracepoint on function %s: %#v\n", funcs[i], err)
   668  					}
   669  				}
   670  			}
   671  		}
   672  		cmds := terminal.DebugCommands(client)
   673  		t := terminal.New(client, nil)
   674  		t.RedirectTo(os.Stderr)
   675  		defer t.Close()
   676  		if traceUseEBPF {
   677  			done := make(chan struct{})
   678  			defer close(done)
   679  			go func() {
   680  				gFnEntrySeen := map[int]struct{}{}
   681  				for {
   682  					select {
   683  					case <-done:
   684  						return
   685  					default:
   686  						tracepoints, err := client.GetBufferedTracepoints()
   687  						if err != nil {
   688  							panic(err)
   689  						}
   690  						for _, t := range tracepoints {
   691  							var params strings.Builder
   692  							for _, p := range t.InputParams {
   693  								if params.Len() > 0 {
   694  									params.WriteString(", ")
   695  								}
   696  								if p.Kind == reflect.String {
   697  									params.WriteString(fmt.Sprintf("%q", p.Value))
   698  								} else {
   699  									params.WriteString(p.Value)
   700  								}
   701  							}
   702  							_, seen := gFnEntrySeen[t.GoroutineID]
   703  							if seen {
   704  								for _, p := range t.ReturnParams {
   705  									fmt.Fprintf(os.Stderr, "=> %#v\n", p.Value)
   706  								}
   707  								delete(gFnEntrySeen, t.GoroutineID)
   708  							} else {
   709  								gFnEntrySeen[t.GoroutineID] = struct{}{}
   710  								fmt.Fprintf(os.Stderr, "> (%d) %s(%s)\n", t.GoroutineID, t.FunctionName, params.String())
   711  							}
   712  						}
   713  					}
   714  				}
   715  			}()
   716  		}
   717  		cmds.Call("continue", t)
   718  		return 0
   719  	}()
   720  	os.Exit(status)
   721  }
   722  
   723  func isBreakpointExistsErr(err error) bool {
   724  	return strings.Contains(err.Error(), "Breakpoint exists")
   725  }
   726  
   727  func testCmd(cmd *cobra.Command, args []string) {
   728  	status := func() int {
   729  		dlvArgs, targetArgs := splitArgs(cmd, args)
   730  		debugname, ok := buildBinary(cmd, dlvArgs, true)
   731  		if !ok {
   732  			return 1
   733  		}
   734  		defer gobuild.Remove(debugname)
   735  		processArgs := append([]string{debugname}, targetArgs...)
   736  
   737  		if workingDir == "" {
   738  			if len(dlvArgs) == 1 {
   739  				workingDir = getPackageDir(dlvArgs[0])
   740  			} else {
   741  				workingDir = "."
   742  			}
   743  		}
   744  
   745  		return execute(0, processArgs, conf, "", debugger.ExecutingGeneratedTest, dlvArgs, buildFlags)
   746  	}()
   747  	os.Exit(status)
   748  }
   749  
   750  func getPackageDir(pkg string) string {
   751  	out, err := exec.Command("go", "list", "--json", pkg).CombinedOutput()
   752  	if err != nil {
   753  		return "."
   754  	}
   755  	type listOut struct {
   756  		Dir string `json:"Dir"`
   757  	}
   758  	var listout listOut
   759  	err = json.Unmarshal(out, &listout)
   760  	if err != nil {
   761  		return "."
   762  	}
   763  	return listout.Dir
   764  }
   765  
   766  func attachCmd(cmd *cobra.Command, args []string) {
   767  	pid, err := strconv.Atoi(args[0])
   768  	if err != nil {
   769  		fmt.Fprintf(os.Stderr, "Invalid pid: %s\n", args[0])
   770  		os.Exit(1)
   771  	}
   772  	os.Exit(execute(pid, args[1:], conf, "", debugger.ExecutingOther, args, buildFlags))
   773  }
   774  
   775  func coreCmd(cmd *cobra.Command, args []string) {
   776  	os.Exit(execute(0, []string{args[0]}, conf, args[1], debugger.ExecutingOther, args, buildFlags))
   777  }
   778  
   779  func connectCmd(cmd *cobra.Command, args []string) {
   780  	if err := logflags.Setup(log, logOutput, logDest); err != nil {
   781  		fmt.Fprintf(os.Stderr, "%v\n", err)
   782  		os.Exit(1)
   783  		return
   784  	}
   785  	defer logflags.Close()
   786  	if loadConfErr != nil {
   787  		logflags.DebuggerLogger().Errorf("%v", loadConfErr)
   788  	}
   789  	addr := args[0]
   790  	if addr == "" {
   791  		fmt.Fprint(os.Stderr, "An empty address was provided. You must provide an address as the first argument.\n")
   792  		os.Exit(1)
   793  	}
   794  	os.Exit(connect(addr, nil, conf, debugger.ExecutingOther))
   795  }
   796  
   797  // waitForDisconnectSignal is a blocking function that waits for either
   798  // a SIGINT (Ctrl-C) or SIGTERM (kill -15) OS signal or for disconnectChan
   799  // to be closed by the server when the client disconnects.
   800  // Note that in headless mode, the debugged process is foregrounded
   801  // (to have control of the tty for debugging interactive programs),
   802  // so SIGINT gets sent to the debuggee and not to delve.
   803  func waitForDisconnectSignal(disconnectChan chan struct{}) {
   804  	ch := make(chan os.Signal, 1)
   805  	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
   806  	if runtime.GOOS == "windows" {
   807  		// On windows Ctrl-C sent to inferior process is delivered
   808  		// as SIGINT to delve. Ignore it instead of stopping the server
   809  		// in order to be able to debug signal handlers.
   810  		go func() {
   811  			for range ch {
   812  			}
   813  		}()
   814  		<-disconnectChan
   815  	} else {
   816  		select {
   817  		case <-ch:
   818  		case <-disconnectChan:
   819  		}
   820  	}
   821  }
   822  
   823  func splitArgs(cmd *cobra.Command, args []string) ([]string, []string) {
   824  	if cmd.ArgsLenAtDash() >= 0 {
   825  		return args[:cmd.ArgsLenAtDash()], args[cmd.ArgsLenAtDash():]
   826  	}
   827  	return args, []string{}
   828  }
   829  
   830  func connect(addr string, clientConn net.Conn, conf *config.Config, kind debugger.ExecuteKind) int {
   831  	// Create and start a terminal - attach to running instance
   832  	var client *rpc2.RPCClient
   833  	if clientConn != nil {
   834  		client = rpc2.NewClientFromConn(clientConn)
   835  	} else {
   836  		client = rpc2.NewClient(addr)
   837  	}
   838  	if client.IsMulticlient() {
   839  		state, _ := client.GetStateNonBlocking()
   840  		// The error return of GetState will usually be the ErrProcessExited,
   841  		// which we don't care about. If there are other errors they will show up
   842  		// later, here we are only concerned about stopping a running target so
   843  		// that we can initialize our connection.
   844  		if state != nil && state.Running {
   845  			_, err := client.Halt()
   846  			if err != nil {
   847  				fmt.Fprintf(os.Stderr, "could not halt: %v", err)
   848  				return 1
   849  			}
   850  		}
   851  	}
   852  	term := terminal.New(client, conf)
   853  	term.InitFile = initFile
   854  	status, err := term.Run()
   855  	if err != nil {
   856  		fmt.Println(err)
   857  	}
   858  	return status
   859  }
   860  
   861  func execute(attachPid int, processArgs []string, conf *config.Config, coreFile string, kind debugger.ExecuteKind, dlvArgs []string, buildFlags string) int {
   862  	if err := logflags.Setup(log, logOutput, logDest); err != nil {
   863  		fmt.Fprintf(os.Stderr, "%v\n", err)
   864  		return 1
   865  	}
   866  	defer logflags.Close()
   867  	if loadConfErr != nil {
   868  		logflags.DebuggerLogger().Errorf("%v", loadConfErr)
   869  	}
   870  
   871  	if headless && (initFile != "") {
   872  		fmt.Fprint(os.Stderr, "Warning: init file ignored with --headless\n")
   873  	}
   874  	if continueOnStart {
   875  		if !headless {
   876  			fmt.Fprint(os.Stderr, "Error: --continue only works with --headless; use an init file\n")
   877  			return 1
   878  		}
   879  		if !acceptMulti {
   880  			fmt.Fprint(os.Stderr, "Error: --continue requires --accept-multiclient\n")
   881  			return 1
   882  		}
   883  	}
   884  
   885  	if !headless && acceptMulti {
   886  		fmt.Fprint(os.Stderr, "Warning accept-multi: ignored\n")
   887  		// acceptMulti won't work in normal (non-headless) mode because we always
   888  		// call server.Stop after the terminal client exits.
   889  		acceptMulti = false
   890  	}
   891  
   892  	if !headless && !allowNonTerminalInteractive {
   893  		for _, f := range []struct {
   894  			name string
   895  			file *os.File
   896  		}{{"Stdin", os.Stdin}, {"Stdout", os.Stdout}, {"Stderr", os.Stderr}} {
   897  			if f.file == nil {
   898  				continue
   899  			}
   900  			if !isatty.IsTerminal(f.file.Fd()) {
   901  				fmt.Fprintf(os.Stderr, "%s is not a terminal, use '-r' to specify redirects for the target process or --allow-non-terminal-interactive=true if you really want to specify a redirect for Delve\n", f.name)
   902  				return 1
   903  			}
   904  		}
   905  	}
   906  
   907  	if len(redirects) > 0 && tty != "" {
   908  		fmt.Fprintf(os.Stderr, "Can not use -r and --tty together\n")
   909  		return 1
   910  	}
   911  
   912  	redirects, err := parseRedirects(redirects)
   913  	if err != nil {
   914  		fmt.Fprintf(os.Stderr, "%v\n", err)
   915  		return 1
   916  	}
   917  
   918  	var listener net.Listener
   919  	var clientConn net.Conn
   920  
   921  	// Make a TCP listener
   922  	if headless {
   923  		listener, err = net.Listen("tcp", addr)
   924  	} else {
   925  		listener, clientConn = service.ListenerPipe()
   926  	}
   927  	if err != nil {
   928  		fmt.Printf("couldn't start listener: %s\n", err)
   929  		return 1
   930  	}
   931  	defer listener.Close()
   932  
   933  	var server service.Server
   934  
   935  	disconnectChan := make(chan struct{})
   936  
   937  	if workingDir == "" {
   938  		workingDir = "."
   939  	}
   940  
   941  	// Create and start a debugger server
   942  	switch apiVersion {
   943  	case 1, 2:
   944  		server = rpccommon.NewServer(&service.Config{
   945  			Listener:           listener,
   946  			ProcessArgs:        processArgs,
   947  			AcceptMulti:        acceptMulti,
   948  			APIVersion:         apiVersion,
   949  			CheckLocalConnUser: checkLocalConnUser,
   950  			DisconnectChan:     disconnectChan,
   951  			Debugger: debugger.Config{
   952  				AttachPid:            attachPid,
   953  				WorkingDir:           workingDir,
   954  				Backend:              backend,
   955  				CoreFile:             coreFile,
   956  				Foreground:           headless && tty == "",
   957  				Packages:             dlvArgs,
   958  				BuildFlags:           buildFlags,
   959  				ExecuteKind:          kind,
   960  				DebugInfoDirectories: conf.DebugInfoDirectories,
   961  				CheckGoVersion:       checkGoVersion,
   962  				TTY:                  tty,
   963  				Redirects:            redirects,
   964  				DisableASLR:          disableASLR,
   965  			},
   966  		})
   967  	default:
   968  		fmt.Printf("Unknown API version: %d\n", apiVersion)
   969  		return 1
   970  	}
   971  
   972  	if err := server.Run(); err != nil {
   973  		if err == api.ErrNotExecutable {
   974  			switch kind {
   975  			case debugger.ExecutingGeneratedFile:
   976  				fmt.Fprintln(os.Stderr, "Can not debug non-main package")
   977  				return 1
   978  			case debugger.ExecutingExistingFile:
   979  				fmt.Fprintf(os.Stderr, "%s is not executable\n", processArgs[0])
   980  				return 1
   981  			default:
   982  				// fallthrough
   983  			}
   984  		}
   985  		fmt.Fprintln(os.Stderr, err)
   986  		return 1
   987  	}
   988  
   989  	var status int
   990  	if headless {
   991  		if continueOnStart {
   992  			client := rpc2.NewClient(listener.Addr().String())
   993  			client.Disconnect(true) // true = continue after disconnect
   994  		}
   995  		waitForDisconnectSignal(disconnectChan)
   996  		err = server.Stop()
   997  		if err != nil {
   998  			fmt.Println(err)
   999  		}
  1000  
  1001  		return status
  1002  	}
  1003  
  1004  	return connect(listener.Addr().String(), clientConn, conf, kind)
  1005  }
  1006  
  1007  func parseRedirects(redirects []string) ([3]string, error) {
  1008  	r := [3]string{}
  1009  	names := [3]string{"stdin", "stdout", "stderr"}
  1010  	for _, redirect := range redirects {
  1011  		idx := 0
  1012  		for i, name := range names {
  1013  			pfx := name + ":"
  1014  			if strings.HasPrefix(redirect, pfx) {
  1015  				idx = i
  1016  				redirect = redirect[len(pfx):]
  1017  				break
  1018  			}
  1019  		}
  1020  		if r[idx] != "" {
  1021  			return r, fmt.Errorf("redirect error: %s redirected twice", names[idx])
  1022  		}
  1023  		r[idx] = redirect
  1024  	}
  1025  	return r, nil
  1026  }