commands.go 30.9 KB
Newer Older
1 2 3
package cmds

import (
4
	"encoding/json"
5 6 7 8 9 10 11
	"errors"
	"fmt"
	"net"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
12
	"reflect"
13 14
	"runtime"
	"strconv"
D
Derek Parker 已提交
15
	"strings"
16 17
	"syscall"

18
	"github.com/go-delve/delve/pkg/config"
19
	"github.com/go-delve/delve/pkg/gobuild"
20 21 22 23 24 25
	"github.com/go-delve/delve/pkg/goversion"
	"github.com/go-delve/delve/pkg/logflags"
	"github.com/go-delve/delve/pkg/terminal"
	"github.com/go-delve/delve/pkg/version"
	"github.com/go-delve/delve/service"
	"github.com/go-delve/delve/service/api"
26
	"github.com/go-delve/delve/service/dap"
27
	"github.com/go-delve/delve/service/debugger"
28 29
	"github.com/go-delve/delve/service/rpc2"
	"github.com/go-delve/delve/service/rpccommon"
30
	"github.com/mattn/go-isatty"
31 32 33 34
	"github.com/spf13/cobra"
)

var (
D
Derek Parker 已提交
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
	// log is whether to log debug statements.
	log bool
	// logOutput is a comma separated list of components that should produce debug output.
	logOutput string
	// logDest is the file path or file descriptor where logs should go.
	logDest string
	// headless is whether to run without terminal.
	headless bool
	// continueOnStart is whether to continue the process on startup
	continueOnStart bool
	// apiVersion is the requested API version while running headless
	apiVersion int
	// acceptMulti allows multiple clients to connect to the same server
	acceptMulti bool
	// addr is the debugging server listen address.
	addr string
	// initFile is the path to initialization file.
	initFile string
	// buildFlags is the flags passed during compiler invocation.
	buildFlags string
	// workingDir is the working directory for running the program.
	workingDir string
	// checkLocalConnUser is true if the debugger should check that local
58
	// connections come from the same user that started the headless server
D
Derek Parker 已提交
59
	checkLocalConnUser bool
60 61
	// tty is used to provide an alternate TTY for the program you wish to debug.
	tty string
62 63
	// disableASLR is used to disable ASLR
	disableASLR bool
64

D
Derek Parker 已提交
65 66
	// backend selection
	backend string
67

D
Derek Parker 已提交
68
	// checkGoVersion is true if the debugger should check the version of Go
69 70
	// used to compile the executable and refuse to work on incompatible
	// versions.
D
Derek Parker 已提交
71
	checkGoVersion bool
72

D
Derek Parker 已提交
73 74
	// rootCommand is the root of the command tree.
	rootCommand *cobra.Command
75 76

	traceAttachPid  int
77 78
	traceExecFile   string
	traceTestBinary bool
79
	traceStackDepth int
80
	traceUseEBPF    bool
81

82 83 84 85 86
	// redirect specifications for target process
	redirects []string

	allowNonTerminalInteractive bool

87 88 89 90 91 92 93 94 95
	conf *config.Config
)

const dlvCommandLongDesc = `Delve is a source level debugger for Go programs.

Delve enables you to interact with your program by controlling the execution of the process,
evaluating variables, and providing information of thread / goroutine state, CPU register state and more.

The goal of this tool is to provide a simple yet powerful interface for debugging Go programs.
96 97 98 99

Pass flags to the program you are debugging using ` + "`--`" + `, for example:

` + "`dlv exec ./hello -- server --config conf/config.toml`"
100 101

// New returns an initialized command tree.
102
func New(docCall bool) *cobra.Command {
103 104 105 106
	// Config setup and load.
	conf = config.LoadConfig()
	buildFlagsDefault := ""
	if runtime.GOOS == "windows" {
107
		ver, _ := goversion.Installed()
D
Derek Parker 已提交
108
		if ver.Major > 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 9, Rev: -1}) {
109 110 111
			// Work-around for https://github.com/golang/go/issues/13154
			buildFlagsDefault = "-ldflags='-linkmode internal'"
		}
112 113 114
	}

	// Main dlv root command.
D
Derek Parker 已提交
115
	rootCommand = &cobra.Command{
116 117 118 119 120
		Use:   "dlv",
		Short: "Delve is a debugger for the Go programming language.",
		Long:  dlvCommandLongDesc,
	}

D
Derek Parker 已提交
121
	rootCommand.PersistentFlags().StringVarP(&addr, "listen", "l", "127.0.0.1:0", "Debugging server listen address.")
122

D
Derek Parker 已提交
123 124 125
	rootCommand.PersistentFlags().BoolVarP(&log, "log", "", false, "Enable debugging server logging.")
	rootCommand.PersistentFlags().StringVarP(&logOutput, "log-output", "", "", `Comma separated list of components that should produce debug output (see 'dlv help log')`)
	rootCommand.PersistentFlags().StringVarP(&logDest, "log-dest", "", "", "Writes logs to the specified file or file descriptor (see 'dlv help log').")
126

D
Derek Parker 已提交
127 128
	rootCommand.PersistentFlags().BoolVarP(&headless, "headless", "", false, "Run debug server only, in headless mode.")
	rootCommand.PersistentFlags().BoolVarP(&acceptMulti, "accept-multiclient", "", false, "Allows a headless server to accept multiple client connections.")
129
	rootCommand.PersistentFlags().IntVar(&apiVersion, "api-version", 1, "Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md.")
D
Derek Parker 已提交
130
	rootCommand.PersistentFlags().StringVar(&initFile, "init", "", "Init file, executed by the terminal client.")
131
	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\"")
132
	rootCommand.PersistentFlags().StringVar(&workingDir, "wd", "", "Working directory for running the program.")
D
Derek Parker 已提交
133 134 135
	rootCommand.PersistentFlags().BoolVarP(&checkGoVersion, "check-go-version", "", true, "Checks that the version of Go in use is compatible with Delve.")
	rootCommand.PersistentFlags().BoolVarP(&checkLocalConnUser, "only-same-user", "", true, "Only connections from the same user that started this instance of Delve are allowed to connect.")
	rootCommand.PersistentFlags().StringVar(&backend, "backend", "default", `Backend selection (see 'dlv help backend').`)
136 137
	rootCommand.PersistentFlags().StringArrayVarP(&redirects, "redirect", "r", []string{}, "Specifies redirect rules for target process (see 'dlv help redirect')")
	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")
138
	rootCommand.PersistentFlags().BoolVar(&disableASLR, "disable-aslr", false, "Disables address space randomization")
139

140 141
	// 'attach' subcommand.
	attachCommand := &cobra.Command{
142
		Use:   "attach pid [executable]",
143
		Short: "Attach to running process and begin debugging.",
144 145 146 147 148 149
		Long: `Attach to an already running process and begin debugging it.

This command will cause Delve to take control of an already running process, and
begin a new debug session.  When exiting the debug session you will have the
option to let the process continue or kill it.
`,
150 151 152 153 154
		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
			if len(args) == 0 {
				return errors.New("you must provide a PID")
			}
			return nil
155
		},
156
		Run: attachCmd,
157
	}
158
	attachCommand.Flags().BoolVar(&continueOnStart, "continue", false, "Continue the debugged process on start.")
D
Derek Parker 已提交
159
	rootCommand.AddCommand(attachCommand)
160

161 162 163 164
	// 'connect' subcommand.
	connectCommand := &cobra.Command{
		Use:   "connect addr",
		Short: "Connect to a headless debug server.",
165
		Long:  "Connect to a running headless debug server.",
166 167 168 169 170
		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
			if len(args) == 0 {
				return errors.New("you must provide an address as the first argument")
			}
			return nil
171
		},
172
		Run: connectCmd,
173
	}
D
Derek Parker 已提交
174
	rootCommand.AddCommand(connectCommand)
175

176 177 178
	// 'dap' subcommand.
	dapCommand := &cobra.Command{
		Use:   "dap",
179 180
		Short: "[EXPERIMENTAL] Starts a headless TCP server communicating via Debug Adaptor Protocol (DAP).",
		Long: `[EXPERIMENTAL] Starts a headless TCP server communicating via Debug Adaptor Protocol (DAP).
181

182
The server is always headless and requires a DAP client like vscode to connect and request a binary
P
polinasok 已提交
183 184 185 186
to be launched or process to be attached to. The following modes are supported:
- launch + exec (executes precompiled binary, like 'dlv exec')
- launch + debug (builds and launches, like 'dlv debug')
- launch + test (builds and tests, like 'dlv test')
187 188
- launch + replay (replays an rr trace, like 'dlv replay')
- launch + core (replays a core dump file, like 'dlv core')
P
polinasok 已提交
189
- attach + local (attaches to a running process, like 'dlv attach')
P
polinasok 已提交
190 191 192
The server does not yet accept multiple client connections (--accept-multiclient).
While --continue is not supported, stopOnEntry launch/attach attribute can be used to control if
execution is resumed at the start of the debug session.`,
193 194
		Run: dapCmd,
	}
195
	// TODO(polina): support --tty when dlv dap allows to launch a program from command-line
D
Derek Parker 已提交
196
	rootCommand.AddCommand(dapCommand)
197

198 199 200
	// 'debug' subcommand.
	debugCommand := &cobra.Command{
		Use:   "debug [package]",
201
		Short: "Compile and begin debugging main package in current directory, or the package specified.",
202 203 204 205 206 207
		Long: `Compiles your program with optimizations disabled, starts and attaches to it.

By default, with no arguments, Delve will compile the 'main' package in the
current directory, and begin to debug it. Alternatively you can specify a
package name and Delve will compile that package instead, and begin a new debug
session.`,
208 209
		Run: debugCmd,
	}
210
	debugCommand.Flags().String("output", "./__debug_bin", "Output path for the binary.")
D
Derek Parker 已提交
211
	debugCommand.Flags().BoolVar(&continueOnStart, "continue", false, "Continue the debugged process on start.")
212
	debugCommand.Flags().StringVar(&tty, "tty", "", "TTY to use for the target program")
D
Derek Parker 已提交
213
	rootCommand.AddCommand(debugCommand)
214 215 216

	// 'exec' subcommand.
	execCommand := &cobra.Command{
217
		Use:   "exec <path/to/binary>",
218 219 220 221 222 223
		Short: "Execute a precompiled binary, and begin a debug session.",
		Long: `Execute a precompiled binary and begin a debug session.

This command will cause Delve to exec the binary and immediately attach to it to
begin a new debug session. Please note that if the binary was not compiled with
optimizations disabled, it may be difficult to properly debug it. Please
224 225
consider compiling debugging binaries with -gcflags="all=-N -l" on Go 1.10
or later, -gcflags="-N -l" on earlier versions of Go.`,
226 227 228 229 230 231 232
		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
			if len(args) == 0 {
				return errors.New("you must provide a path to a binary")
			}
			return nil
		},
		Run: func(cmd *cobra.Command, args []string) {
233
			os.Exit(execute(0, args, conf, "", debugger.ExecutingExistingFile, args, buildFlags))
234 235
		},
	}
236
	execCommand.Flags().StringVar(&tty, "tty", "", "TTY to use for the target program")
D
Derek Parker 已提交
237 238
	execCommand.Flags().BoolVar(&continueOnStart, "continue", false, "Continue the debugged process on start.")
	rootCommand.AddCommand(execCommand)
239

240 241 242 243 244 245 246 247
	// Deprecated 'run' subcommand.
	runCommand := &cobra.Command{
		Use:   "run",
		Short: "Deprecated command. Use 'debug' instead.",
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Println("This command is deprecated, please use 'debug' instead.")
			os.Exit(0)
		},
248
	}
D
Derek Parker 已提交
249
	rootCommand.AddCommand(runCommand)
250 251 252 253 254

	// 'test' subcommand.
	testCommand := &cobra.Command{
		Use:   "test [package]",
		Short: "Compile test binary and begin debugging program.",
255 256 257 258 259
		Long: `Compiles a test binary with optimizations disabled and begins a new debug session.

The test command allows you to begin a new debug session in the context of your
unit tests. By default Delve will debug the tests in the current directory.
Alternatively you can specify a package name, and Delve will debug the tests in
260 261 262 263 264
that package instead. Double-dashes ` + "`--`" + ` can be used to pass arguments to the test program:

dlv test [package] -- -test.v -other-argument

See also: 'go help testflag'.`,
265
		Run: testCmd,
266
	}
267
	testCommand.Flags().String("output", "debug.test", "Output path for the binary.")
D
Derek Parker 已提交
268
	rootCommand.AddCommand(testCommand)
269

270 271 272 273
	// 'trace' subcommand.
	traceCommand := &cobra.Command{
		Use:   "trace [package] regexp",
		Short: "Compile and begin tracing program.",
274 275 276 277 278
		Long: `Trace program execution.

The trace sub command will set a tracepoint on every function matching the
provided regular expression and output information when tracepoint is hit.  This
is useful if you do not want to begin an entire debug session, but merely want
D
Derek Parker 已提交
279 280 281 282
to know what functions your process is executing.

The output of the trace sub command is printed to stderr, so if you would like to
only see the output of the trace operations you can redirect stdout.`,
283
		Run: traceCmd,
284
	}
285
	traceCommand.Flags().IntVarP(&traceAttachPid, "pid", "p", 0, "Pid to attach to.")
286 287
	traceCommand.Flags().StringVarP(&traceExecFile, "exec", "e", "", "Binary file to exec and trace.")
	traceCommand.Flags().BoolVarP(&traceTestBinary, "test", "t", false, "Trace a test binary.")
288 289
	traceCommand.Flags().BoolVarP(&traceUseEBPF, "ebpf", "", false, "Trace using eBPF (experimental).")
	traceCommand.Flags().IntVarP(&traceStackDepth, "stack", "s", 0, "Show stack trace with given depth. (Ignored with -ebpf)")
290
	traceCommand.Flags().String("output", "debug", "Output path for the binary.")
D
Derek Parker 已提交
291
	rootCommand.AddCommand(traceCommand)
292

293 294 295
	coreCommand := &cobra.Command{
		Use:   "core <executable> <core>",
		Short: "Examine a core dump.",
296
		Long: `Examine a core dump (only supports linux and windows core dumps).
297

298 299
The core command will open the specified core file and the associated
executable and let you examine the state of the process when the
300 301
core dump was taken.

302
Currently supports linux/amd64 and linux/arm64 core files, windows/amd64 minidumps and core files generated by Delve's 'dump' command.`,
303 304 305 306 307 308 309 310
		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
			if len(args) != 2 {
				return errors.New("you must provide a core file and an executable")
			}
			return nil
		},
		Run: coreCmd,
	}
D
Derek Parker 已提交
311
	rootCommand.AddCommand(coreCommand)
312

313
	// 'version' subcommand.
314
	var versionVerbose = false
315 316 317 318 319
	versionCommand := &cobra.Command{
		Use:   "version",
		Short: "Prints version.",
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Printf("Delve Debugger\n%s\n", version.DelveVersion)
320 321 322
			if versionVerbose {
				fmt.Printf("Build Details: %s\n", version.BuildInfo())
			}
323 324
		},
	}
325
	versionCommand.Flags().BoolVarP(&versionVerbose, "verbose", "v", false, "print verbose version info")
D
Derek Parker 已提交
326
	rootCommand.AddCommand(versionCommand)
327

328
	if path, _ := exec.LookPath("rr"); path != "" || docCall {
329 330 331 332
		replayCommand := &cobra.Command{
			Use:   "replay [trace directory]",
			Short: "Replays a rr trace.",
			Long: `Replays a rr trace.
333

334 335 336 337 338 339 340 341 342 343
The replay command will open a trace generated by mozilla rr. Mozilla rr must be installed:
https://github.com/mozilla/rr
			`,
			PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
				if len(args) == 0 {
					return errors.New("you must provide a path to a binary")
				}
				return nil
			},
			Run: func(cmd *cobra.Command, args []string) {
D
Derek Parker 已提交
344
				backend = "rr"
345
				os.Exit(execute(0, []string{}, conf, args[0], debugger.ExecutingOther, args, buildFlags))
346 347
			},
		}
D
Derek Parker 已提交
348
		rootCommand.AddCommand(replayCommand)
349 350
	}

D
Derek Parker 已提交
351
	rootCommand.AddCommand(&cobra.Command{
352 353 354 355 356 357 358 359 360 361 362 363
		Use:   "backend",
		Short: "Help about the --backend flag.",
		Long: `The --backend flag specifies which backend should be used, possible values
are:

	default		Uses lldb on macOS, native everywhere else.
	native		Native backend.
	lldb		Uses lldb-server or debugserver.
	rr		Uses mozilla rr (https://github.com/mozilla/rr).

`})

D
Derek Parker 已提交
364
	rootCommand.AddCommand(&cobra.Command{
365 366 367 368 369 370 371 372 373 374 375 376 377 378
		Use:   "log",
		Short: "Help about logging flags.",
		Long: `Logging can be enabled by specifying the --log flag and using the
--log-output flag to select which components should produce logs.

The argument of --log-output must be a comma separated list of component
names selected from this list:


	debugger	Log debugger commands
	gdbwire		Log connection to gdbserial backend
	lldbout		Copy output from debugserver/lldb to standard output
	debuglineerr	Log recoverable errors reading .debug_line
	rpc		Log all RPC messages
379
	dap		Log all DAP messages
380 381 382 383 384 385 386
	fncall		Log function call protocol
	minidump	Log minidump loading

Additionally --log-dest can be used to specify where the logs should be
written. 
If the argument is a number it will be interpreted as a file descriptor,
otherwise as a file path.
387 388
This option will also redirect the "server listening at" message in headless
and dap modes.
389

390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
`,
	})

	rootCommand.AddCommand(&cobra.Command{
		Use:   "redirect",
		Short: "Help about file redirection.",
		Long: `The standard file descriptors of the target process can be controlled using the '-r' and '--tty' arguments. 

The --tty argument allows redirecting all standard descriptors to a terminal, specified as an argument to --tty.

The syntax for '-r' argument is:

		-r [source:]destination

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.

File redirects can also be changed using the 'restart' command.
407 408 409
`,
	})

D
Derek Parker 已提交
410
	rootCommand.DisableAutoGenTag = true
411

D
Derek Parker 已提交
412
	return rootCommand
413 414
}

415 416
func dapCmd(cmd *cobra.Command, args []string) {
	status := func() int {
D
Derek Parker 已提交
417
		if err := logflags.Setup(log, logOutput, logDest); err != nil {
418 419 420 421 422
			fmt.Fprintf(os.Stderr, "%v\n", err)
			return 1
		}
		defer logflags.Close()

423
		if cmd.Flag("headless").Changed {
P
polinasok 已提交
424
			fmt.Fprintf(os.Stderr, "Warning: dap mode is always headless\n")
425
		}
D
Derek Parker 已提交
426
		if acceptMulti {
P
polinasok 已提交
427
			fmt.Fprintf(os.Stderr, "Warning: accept-multiclient mode not supported with dap\n")
428
		}
D
Derek Parker 已提交
429
		if initFile != "" {
430 431
			fmt.Fprint(os.Stderr, "Warning: init file ignored with dap\n")
		}
D
Derek Parker 已提交
432
		if continueOnStart {
433 434
			fmt.Fprintf(os.Stderr, "Warning: continue ignored with dap; specify via launch/attach request instead\n")
		}
435 436 437
		if backend != "default" {
			fmt.Fprintf(os.Stderr, "Warning: backend ignored with dap; specify via launch/attach request instead\n")
		}
D
Derek Parker 已提交
438
		if buildFlags != "" {
439 440
			fmt.Fprintf(os.Stderr, "Warning: build flags ignored with dap; specify via launch/attach request instead\n")
		}
D
Derek Parker 已提交
441
		if workingDir != "" {
442
			fmt.Fprintf(os.Stderr, "Warning: working directory ignored with dap: specify via launch request instead\n")
443 444 445 446 447 448 449 450 451
		}
		dlvArgs, targetArgs := splitArgs(cmd, args)
		if len(dlvArgs) > 0 {
			fmt.Fprintf(os.Stderr, "Warning: debug arguments ignored with dap; specify via launch/attach request instead\n")
		}
		if len(targetArgs) > 0 {
			fmt.Fprintf(os.Stderr, "Warning: program flags ignored with dap; specify via launch/attach request instead\n")
		}

D
Derek Parker 已提交
452
		listener, err := net.Listen("tcp", addr)
453 454 455 456 457 458
		if err != nil {
			fmt.Printf("couldn't start listener: %s\n", err)
			return 1
		}
		disconnectChan := make(chan struct{})
		server := dap.NewServer(&service.Config{
459 460 461 462
			Listener:       listener,
			DisconnectChan: disconnectChan,
			Debugger: debugger.Config{
				Backend:              backend,
463
				Foreground:           true, // server always runs without terminal client
464 465 466
				DebugInfoDirectories: conf.DebugInfoDirectories,
				CheckGoVersion:       checkGoVersion,
			},
467
			CheckLocalConnUser: checkLocalConnUser,
468 469 470 471 472 473 474 475 476 477
		})
		defer server.Stop()

		server.Run()
		waitForDisconnectSignal(disconnectChan)
		return 0
	}()
	os.Exit(status)
}

478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
func buildBinary(cmd *cobra.Command, args []string, isTest bool) (string, bool) {
	debugname, err := filepath.Abs(cmd.Flag("output").Value.String())
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return "", false
	}

	if isTest {
		err = gobuild.GoTestBuild(debugname, args, buildFlags)
	} else {
		err = gobuild.GoBuild(debugname, args, buildFlags)
	}
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return "", false
	}
	return debugname, true
}

497 498 499
func debugCmd(cmd *cobra.Command, args []string) {
	status := func() int {
		dlvArgs, targetArgs := splitArgs(cmd, args)
500 501
		debugname, ok := buildBinary(cmd, dlvArgs, false)
		if !ok {
502 503
			return 1
		}
504
		defer gobuild.Remove(debugname)
505
		processArgs := append([]string{debugname}, targetArgs...)
506
		return execute(0, processArgs, conf, "", debugger.ExecutingGeneratedFile, dlvArgs, buildFlags)
507 508 509 510 511 512
	}()
	os.Exit(status)
}

func traceCmd(cmd *cobra.Command, args []string) {
	status := func() int {
D
Derek Parker 已提交
513
		err := logflags.Setup(log, logOutput, logDest)
514
		defer logflags.Close()
515
		if err != nil {
516 517 518
			fmt.Fprintf(os.Stderr, "%v\n", err)
			return 1
		}
519

D
Derek Parker 已提交
520
		if headless {
521 522
			fmt.Fprintf(os.Stderr, "Warning: headless mode not supported with trace\n")
		}
D
Derek Parker 已提交
523
		if acceptMulti {
524 525 526
			fmt.Fprintf(os.Stderr, "Warning: accept multiclient mode not supported with trace")
		}

527 528 529 530
		var regexp string
		var processArgs []string

		dlvArgs, targetArgs := splitArgs(cmd, args)
531 532 533 534 535 536 537 538
		var dlvArgsLen = len(dlvArgs)
		if dlvArgsLen == 1 {
			regexp = args[0]
			dlvArgs = dlvArgs[0:0]
		} else if dlvArgsLen >= 2 {
			regexp = dlvArgs[dlvArgsLen-1]
			dlvArgs = dlvArgs[:dlvArgsLen-1]
		}
539

540
		var debugname string
541
		if traceAttachPid == 0 {
542 543 544
			if dlvArgsLen >= 2 && traceExecFile != "" {
				fmt.Fprintln(os.Stderr, "Cannot specify package when using exec.")
				return 1
545
			}
546

547
			debugname = traceExecFile
548
			if traceExecFile == "" {
549 550
				debugexe, ok := buildBinary(cmd, dlvArgs, traceTestBinary)
				if !ok {
551 552
					return 1
				}
553
				debugname = debugexe
554
				defer gobuild.Remove(debugname)
555 556
			}

557
			processArgs = append([]string{debugname}, targetArgs...)
558
		}
559 560 561

		// Make a local in-memory connection that client and server use to communicate
		listener, clientConn := service.ListenerPipe()
562 563
		defer listener.Close()

564 565 566 567
		if workingDir == "" {
			workingDir = "."
		}

568
		// Create and start a debug server
A
aarzilli 已提交
569
		server := rpccommon.NewServer(&service.Config{
570 571 572 573 574 575 576 577 578
			Listener:    listener,
			ProcessArgs: processArgs,
			APIVersion:  2,
			Debugger: debugger.Config{
				AttachPid:      traceAttachPid,
				WorkingDir:     workingDir,
				Backend:        backend,
				CheckGoVersion: checkGoVersion,
			},
D
Derek Parker 已提交
579
		})
580 581 582 583
		if err := server.Run(); err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 1
		}
584
		client := rpc2.NewClientFromConn(clientConn)
585 586 587 588 589 590
		funcs, err := client.ListFunctions(regexp)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 1
		}
		for i := range funcs {
591 592 593 594 595 596 597 598
			if traceUseEBPF {
				err := client.CreateEBPFTracepoint(funcs[i])
				if err != nil {
					fmt.Fprintln(os.Stderr, err)
					return 1
				}
			} else {
				// Fall back to breakpoint based tracing if we get an error.
D
Derek Parker 已提交
599
				_, err = client.CreateBreakpoint(&api.Breakpoint{
600 601 602 603 604
					FunctionName: funcs[i],
					Tracepoint:   true,
					Line:         -1,
					Stacktrace:   traceStackDepth,
					LoadArgs:     &terminal.ShortLoadConfig,
D
Derek Parker 已提交
605
				})
D
Derek Parker 已提交
606
				if err != nil && !isBreakpointExistsErr(err) {
D
Derek Parker 已提交
607 608 609
					fmt.Fprintln(os.Stderr, err)
					return 1
				}
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
				addrs, err := client.FunctionReturnLocations(funcs[i])
				if err != nil {
					fmt.Fprintln(os.Stderr, err)
					return 1
				}
				for i := range addrs {
					_, err = client.CreateBreakpoint(&api.Breakpoint{
						Addr:        addrs[i],
						TraceReturn: true,
						Stacktrace:  traceStackDepth,
						Line:        -1,
						LoadArgs:    &terminal.ShortLoadConfig,
					})
					if err != nil && !isBreakpointExistsErr(err) {
						fmt.Fprintln(os.Stderr, err)
						return 1
					}
				}
D
Derek Parker 已提交
628
			}
629 630 631 632
		}
		cmds := terminal.DebugCommands(client)
		t := terminal.New(client, nil)
		defer t.Close()
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663
		if traceUseEBPF {
			done := make(chan struct{})
			defer close(done)
			go func() {
				for {
					select {
					case <-done:
						return
					default:
						tracepoints, err := client.GetBufferedTracepoints()
						if err != nil {
							panic(err)
						}
						for _, t := range tracepoints {
							var params strings.Builder
							for _, p := range t.InputParams {
								if params.Len() > 0 {
									params.WriteString(", ")
								}
								if p.Kind == reflect.String {
									params.WriteString(fmt.Sprintf("%q", p.Value))
								} else {
									params.WriteString(p.Value)
								}
							}
							fmt.Printf("%s:%d %s(%s)\n", t.File, t.Line, t.FunctionName, params.String())
						}
					}
				}
			}()
		}
D
Derek Parker 已提交
664
		cmds.Call("continue", t)
665 666 667 668 669
		return 0
	}()
	os.Exit(status)
}

D
Derek Parker 已提交
670 671 672 673
func isBreakpointExistsErr(err error) bool {
	return strings.Contains(err.Error(), "Breakpoint exists")
}

674 675 676
func testCmd(cmd *cobra.Command, args []string) {
	status := func() int {
		dlvArgs, targetArgs := splitArgs(cmd, args)
677 678
		debugname, ok := buildBinary(cmd, dlvArgs, true)
		if !ok {
679 680
			return 1
		}
681
		defer gobuild.Remove(debugname)
682
		processArgs := append([]string{debugname}, targetArgs...)
683

684 685 686 687 688 689 690 691
		if workingDir == "" {
			if len(dlvArgs) == 1 {
				workingDir = getPackageDir(dlvArgs[0])
			} else {
				workingDir = "."
			}
		}

692
		return execute(0, processArgs, conf, "", debugger.ExecutingGeneratedTest, dlvArgs, buildFlags)
693 694 695 696
	}()
	os.Exit(status)
}

697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712
func getPackageDir(pkg string) string {
	out, err := exec.Command("go", "list", "--json", pkg).CombinedOutput()
	if err != nil {
		return "."
	}
	type listOut struct {
		Dir string `json:"Dir"`
	}
	var listout listOut
	err = json.Unmarshal(out, &listout)
	if err != nil {
		return "."
	}
	return listout.Dir
}

713 714 715 716 717 718
func attachCmd(cmd *cobra.Command, args []string) {
	pid, err := strconv.Atoi(args[0])
	if err != nil {
		fmt.Fprintf(os.Stderr, "Invalid pid: %s\n", args[0])
		os.Exit(1)
	}
719
	os.Exit(execute(pid, args[1:], conf, "", debugger.ExecutingOther, args, buildFlags))
720 721 722
}

func coreCmd(cmd *cobra.Command, args []string) {
723
	os.Exit(execute(0, []string{args[0]}, conf, args[1], debugger.ExecutingOther, args, buildFlags))
724 725 726 727 728
}

func connectCmd(cmd *cobra.Command, args []string) {
	addr := args[0]
	if addr == "" {
729
		fmt.Fprint(os.Stderr, "An empty address was provided. You must provide an address as the first argument.\n")
730 731
		os.Exit(1)
	}
732
	os.Exit(connect(addr, nil, conf, debugger.ExecutingOther))
733 734
}

735
// waitForDisconnectSignal is a blocking function that waits for either
736 737
// a SIGINT (Ctrl-C) or SIGTERM (kill -15) OS signal or for disconnectChan
// to be closed by the server when the client disconnects.
738 739 740 741 742
// Note that in headless mode, the debugged process is foregrounded
// (to have control of the tty for debugging interactive programs),
// so SIGINT gets sent to the debuggee and not to delve.
func waitForDisconnectSignal(disconnectChan chan struct{}) {
	ch := make(chan os.Signal, 1)
743
	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
744 745 746 747 748
	if runtime.GOOS == "windows" {
		// On windows Ctrl-C sent to inferior process is delivered
		// as SIGINT to delve. Ignore it instead of stopping the server
		// in order to be able to debug signal handlers.
		go func() {
749
			for range ch {
750 751
			}
		}()
752
		<-disconnectChan
753 754 755 756 757 758 759 760
	} else {
		select {
		case <-ch:
		case <-disconnectChan:
		}
	}
}

761 762 763 764 765 766 767
func splitArgs(cmd *cobra.Command, args []string) ([]string, []string) {
	if cmd.ArgsLenAtDash() >= 0 {
		return args[:cmd.ArgsLenAtDash()], args[cmd.ArgsLenAtDash():]
	}
	return args, []string{}
}

768
func connect(addr string, clientConn net.Conn, conf *config.Config, kind debugger.ExecuteKind) int {
769
	// Create and start a terminal - attach to running instance
770 771 772 773 774 775
	var client *rpc2.RPCClient
	if clientConn != nil {
		client = rpc2.NewClientFromConn(clientConn)
	} else {
		client = rpc2.NewClient(addr)
	}
776 777 778 779 780 781 782 783 784 785 786 787 788 789
	if client.IsMulticlient() {
		state, _ := client.GetStateNonBlocking()
		// The error return of GetState will usually be the ErrProcessExited,
		// which we don't care about. If there are other errors they will show up
		// later, here we are only concerned about stopping a running target so
		// that we can initialize our connection.
		if state != nil && state.Running {
			_, err := client.Halt()
			if err != nil {
				fmt.Fprintf(os.Stderr, "could not halt: %v", err)
				return 1
			}
		}
	}
790
	term := terminal.New(client, conf)
D
Derek Parker 已提交
791
	term.InitFile = initFile
792 793 794 795 796 797 798
	status, err := term.Run()
	if err != nil {
		fmt.Println(err)
	}
	return status
}

799
func execute(attachPid int, processArgs []string, conf *config.Config, coreFile string, kind debugger.ExecuteKind, dlvArgs []string, buildFlags string) int {
D
Derek Parker 已提交
800
	if err := logflags.Setup(log, logOutput, logDest); err != nil {
801 802 803
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return 1
	}
804
	defer logflags.Close()
805

D
Derek Parker 已提交
806
	if headless && (initFile != "") {
807 808
		fmt.Fprint(os.Stderr, "Warning: init file ignored with --headless\n")
	}
D
Derek Parker 已提交
809 810
	if continueOnStart {
		if !headless {
811 812 813
			fmt.Fprint(os.Stderr, "Error: --continue only works with --headless; use an init file\n")
			return 1
		}
D
Derek Parker 已提交
814
		if !acceptMulti {
815 816 817
			fmt.Fprint(os.Stderr, "Error: --continue requires --accept-multiclient\n")
			return 1
		}
818 819
	}

D
Derek Parker 已提交
820
	if !headless && acceptMulti {
821
		fmt.Fprint(os.Stderr, "Warning accept-multi: ignored\n")
D
Derek Parker 已提交
822
		// acceptMulti won't work in normal (non-headless) mode because we always
823
		// call server.Stop after the terminal client exits.
D
Derek Parker 已提交
824
		acceptMulti = false
825 826
	}

827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852
	if !headless && !allowNonTerminalInteractive {
		for _, f := range []struct {
			name string
			file *os.File
		}{{"Stdin", os.Stdin}, {"Stdout", os.Stdout}, {"Stderr", os.Stderr}} {
			if f.file == nil {
				continue
			}
			if !isatty.IsTerminal(f.file.Fd()) {
				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)
				return 1
			}
		}
	}

	if len(redirects) > 0 && tty != "" {
		fmt.Fprintf(os.Stderr, "Can not use -r and --tty together\n")
		return 1
	}

	redirects, err := parseRedirects(redirects)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return 1
	}

853 854 855 856
	var listener net.Listener
	var clientConn net.Conn

	// Make a TCP listener
D
Derek Parker 已提交
857 858
	if headless {
		listener, err = net.Listen("tcp", addr)
859 860 861 862 863 864 865 866 867
	} else {
		listener, clientConn = service.ListenerPipe()
	}
	if err != nil {
		fmt.Printf("couldn't start listener: %s\n", err)
		return 1
	}
	defer listener.Close()

868
	var server service.Server
869

870 871
	disconnectChan := make(chan struct{})

872 873 874 875
	if workingDir == "" {
		workingDir = "."
	}

876
	// Create and start a debugger server
D
Derek Parker 已提交
877
	switch apiVersion {
A
aarzilli 已提交
878 879
	case 1, 2:
		server = rpccommon.NewServer(&service.Config{
880 881 882 883 884 885 886 887 888 889 890 891
			Listener:           listener,
			ProcessArgs:        processArgs,
			AcceptMulti:        acceptMulti,
			APIVersion:         apiVersion,
			CheckLocalConnUser: checkLocalConnUser,
			DisconnectChan:     disconnectChan,
			Debugger: debugger.Config{
				AttachPid:            attachPid,
				WorkingDir:           workingDir,
				Backend:              backend,
				CoreFile:             coreFile,
				Foreground:           headless && tty == "",
892 893 894
				Packages:             dlvArgs,
				BuildFlags:           buildFlags,
				ExecuteKind:          kind,
895 896 897
				DebugInfoDirectories: conf.DebugInfoDirectories,
				CheckGoVersion:       checkGoVersion,
				TTY:                  tty,
898
				Redirects:            redirects,
899
				DisableASLR:          disableASLR,
900
			},
D
Derek Parker 已提交
901
		})
902
	default:
D
Derek Parker 已提交
903
		fmt.Printf("Unknown API version: %d\n", apiVersion)
904 905 906
		return 1
	}

907
	if err := server.Run(); err != nil {
908
		if err == api.ErrNotExecutable {
909
			switch kind {
910
			case debugger.ExecutingGeneratedFile:
911 912
				fmt.Fprintln(os.Stderr, "Can not debug non-main package")
				return 1
913
			case debugger.ExecutingExistingFile:
914 915 916 917 918 919
				fmt.Fprintf(os.Stderr, "%s is not executable\n", processArgs[0])
				return 1
			default:
				// fallthrough
			}
		}
920 921 922 923 924
		fmt.Fprintln(os.Stderr, err)
		return 1
	}

	var status int
D
Derek Parker 已提交
925 926
	if headless {
		if continueOnStart {
M
MinJae Kwon 已提交
927
			client := rpc2.NewClient(listener.Addr().String())
928 929
			client.Disconnect(true) // true = continue after disconnect
		}
930
		waitForDisconnectSignal(disconnectChan)
931
		err = server.Stop()
932 933
		if err != nil {
			fmt.Println(err)
934
		}
935

936
		return status
937 938
	}

939
	return connect(listener.Addr().String(), clientConn, conf, kind)
940
}
941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961

func parseRedirects(redirects []string) ([3]string, error) {
	r := [3]string{}
	names := [3]string{"stdin", "stdout", "stderr"}
	for _, redirect := range redirects {
		idx := 0
		for i, name := range names {
			pfx := name + ":"
			if strings.HasPrefix(redirect, pfx) {
				idx = i
				redirect = redirect[len(pfx):]
				break
			}
		}
		if r[idx] != "" {
			return r, fmt.Errorf("redirect error: %s redirected twice", names[idx])
		}
		r[idx] = redirect
	}
	return r, nil
}