commands.go 31.0 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.")
133
	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.")
D
Derek Parker 已提交
134 135
	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
183
to be launched or process to be attached to. The following modes can be specified via client's launch config:
P
polinasok 已提交
184 185 186
- 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')
190 191
Program and output binary paths will be interpreted relative to dlv's working directory.

P
polinasok 已提交
192 193 194
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.`,
195 196
		Run: dapCmd,
	}
197
	// TODO(polina): support --tty when dlv dap allows to launch a program from command-line
D
Derek Parker 已提交
198
	rootCommand.AddCommand(dapCommand)
199

200 201 202
	// 'debug' subcommand.
	debugCommand := &cobra.Command{
		Use:   "debug [package]",
203
		Short: "Compile and begin debugging main package in current directory, or the package specified.",
204 205 206 207 208 209
		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.`,
210 211
		Run: debugCmd,
	}
212
	debugCommand.Flags().String("output", "./__debug_bin", "Output path for the binary.")
D
Derek Parker 已提交
213
	debugCommand.Flags().BoolVar(&continueOnStart, "continue", false, "Continue the debugged process on start.")
214
	debugCommand.Flags().StringVar(&tty, "tty", "", "TTY to use for the target program")
D
Derek Parker 已提交
215
	rootCommand.AddCommand(debugCommand)
216 217 218

	// 'exec' subcommand.
	execCommand := &cobra.Command{
219
		Use:   "exec <path/to/binary>",
220 221 222 223 224 225
		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
226 227
consider compiling debugging binaries with -gcflags="all=-N -l" on Go 1.10
or later, -gcflags="-N -l" on earlier versions of Go.`,
228 229 230 231 232 233 234
		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) {
235
			os.Exit(execute(0, args, conf, "", debugger.ExecutingExistingFile, args, buildFlags))
236 237
		},
	}
238
	execCommand.Flags().StringVar(&tty, "tty", "", "TTY to use for the target program")
D
Derek Parker 已提交
239 240
	execCommand.Flags().BoolVar(&continueOnStart, "continue", false, "Continue the debugged process on start.")
	rootCommand.AddCommand(execCommand)
241

242 243 244 245 246 247 248 249
	// 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)
		},
250
	}
D
Derek Parker 已提交
251
	rootCommand.AddCommand(runCommand)
252 253 254 255 256

	// 'test' subcommand.
	testCommand := &cobra.Command{
		Use:   "test [package]",
		Short: "Compile test binary and begin debugging program.",
257 258 259 260 261
		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
262 263 264 265 266
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'.`,
267
		Run: testCmd,
268
	}
269
	testCommand.Flags().String("output", "debug.test", "Output path for the binary.")
D
Derek Parker 已提交
270
	rootCommand.AddCommand(testCommand)
271

272 273 274 275
	// 'trace' subcommand.
	traceCommand := &cobra.Command{
		Use:   "trace [package] regexp",
		Short: "Compile and begin tracing program.",
276 277 278 279 280
		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 已提交
281 282 283 284
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.`,
285
		Run: traceCmd,
286
	}
287
	traceCommand.Flags().IntVarP(&traceAttachPid, "pid", "p", 0, "Pid to attach to.")
288 289
	traceCommand.Flags().StringVarP(&traceExecFile, "exec", "e", "", "Binary file to exec and trace.")
	traceCommand.Flags().BoolVarP(&traceTestBinary, "test", "t", false, "Trace a test binary.")
290 291
	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)")
292
	traceCommand.Flags().String("output", "debug", "Output path for the binary.")
D
Derek Parker 已提交
293
	rootCommand.AddCommand(traceCommand)
294

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

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

304
Currently supports linux/amd64 and linux/arm64 core files, windows/amd64 minidumps and core files generated by Delve's 'dump' command.`,
305 306 307 308 309 310 311 312
		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 已提交
313
	rootCommand.AddCommand(coreCommand)
314

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

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

336 337 338 339 340 341 342 343 344 345
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 已提交
346
				backend = "rr"
347
				os.Exit(execute(0, []string{}, conf, args[0], debugger.ExecutingOther, args, buildFlags))
348 349
			},
		}
D
Derek Parker 已提交
350
		rootCommand.AddCommand(replayCommand)
351 352
	}

D
Derek Parker 已提交
353
	rootCommand.AddCommand(&cobra.Command{
354 355 356 357 358 359 360 361 362 363 364 365
		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 已提交
366
	rootCommand.AddCommand(&cobra.Command{
367 368 369 370 371 372 373 374 375 376 377 378 379 380
		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
381
	dap		Log all DAP messages
382 383 384 385 386 387 388
	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.
389 390
This option will also redirect the "server listening at" message in headless
and dap modes.
391

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

	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.
409 410 411
`,
	})

D
Derek Parker 已提交
412
	rootCommand.DisableAutoGenTag = true
413

D
Derek Parker 已提交
414
	return rootCommand
415 416
}

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

425
		if cmd.Flag("headless").Changed {
P
polinasok 已提交
426
			fmt.Fprintf(os.Stderr, "Warning: dap mode is always headless\n")
427
		}
D
Derek Parker 已提交
428
		if acceptMulti {
P
polinasok 已提交
429
			fmt.Fprintf(os.Stderr, "Warning: accept-multiclient mode not supported with dap\n")
430
		}
D
Derek Parker 已提交
431
		if initFile != "" {
432 433
			fmt.Fprint(os.Stderr, "Warning: init file ignored with dap\n")
		}
D
Derek Parker 已提交
434
		if continueOnStart {
435 436
			fmt.Fprintf(os.Stderr, "Warning: continue ignored with dap; specify via launch/attach request instead\n")
		}
437 438 439
		if backend != "default" {
			fmt.Fprintf(os.Stderr, "Warning: backend ignored with dap; specify via launch/attach request instead\n")
		}
D
Derek Parker 已提交
440
		if buildFlags != "" {
441 442
			fmt.Fprintf(os.Stderr, "Warning: build flags ignored with dap; specify via launch/attach request instead\n")
		}
D
Derek Parker 已提交
443
		if workingDir != "" {
444
			fmt.Fprintf(os.Stderr, "Warning: working directory ignored with dap: specify via launch request instead\n")
445 446 447 448 449 450 451 452 453
		}
		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 已提交
454
		listener, err := net.Listen("tcp", addr)
455 456 457 458 459 460
		if err != nil {
			fmt.Printf("couldn't start listener: %s\n", err)
			return 1
		}
		disconnectChan := make(chan struct{})
		server := dap.NewServer(&service.Config{
461 462 463 464
			Listener:       listener,
			DisconnectChan: disconnectChan,
			Debugger: debugger.Config{
				Backend:              backend,
465
				Foreground:           true, // server always runs without terminal client
466 467 468
				DebugInfoDirectories: conf.DebugInfoDirectories,
				CheckGoVersion:       checkGoVersion,
			},
469
			CheckLocalConnUser: checkLocalConnUser,
470 471 472 473 474 475 476 477 478 479
		})
		defer server.Stop()

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

480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
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
}

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

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

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

529 530 531 532
		var regexp string
		var processArgs []string

		dlvArgs, targetArgs := splitArgs(cmd, args)
533 534 535 536 537 538 539 540
		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]
		}
541

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

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

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

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

566 567 568 569
		if workingDir == "" {
			workingDir = "."
		}

570
		// Create and start a debug server
A
aarzilli 已提交
571
		server := rpccommon.NewServer(&service.Config{
572 573 574 575 576 577 578 579 580
			Listener:    listener,
			ProcessArgs: processArgs,
			APIVersion:  2,
			Debugger: debugger.Config{
				AttachPid:      traceAttachPid,
				WorkingDir:     workingDir,
				Backend:        backend,
				CheckGoVersion: checkGoVersion,
			},
D
Derek Parker 已提交
581
		})
582 583 584 585
		if err := server.Run(); err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 1
		}
586
		client := rpc2.NewClientFromConn(clientConn)
587 588 589 590 591 592
		funcs, err := client.ListFunctions(regexp)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 1
		}
		for i := range funcs {
593 594 595 596 597 598 599 600
			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 已提交
601
				_, err = client.CreateBreakpoint(&api.Breakpoint{
602 603 604 605 606
					FunctionName: funcs[i],
					Tracepoint:   true,
					Line:         -1,
					Stacktrace:   traceStackDepth,
					LoadArgs:     &terminal.ShortLoadConfig,
D
Derek Parker 已提交
607
				})
D
Derek Parker 已提交
608
				if err != nil && !isBreakpointExistsErr(err) {
D
Derek Parker 已提交
609 610 611
					fmt.Fprintln(os.Stderr, err)
					return 1
				}
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629
				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 已提交
630
			}
631 632 633 634
		}
		cmds := terminal.DebugCommands(client)
		t := terminal.New(client, nil)
		defer t.Close()
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
		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)
								}
							}
660
							fmt.Fprintf(os.Stderr, "> (%d) %s(%s)\n", t.GoroutineID, t.FunctionName, params.String())
661 662 663 664 665
						}
					}
				}
			}()
		}
D
Derek Parker 已提交
666
		cmds.Call("continue", t)
667 668 669 670 671
		return 0
	}()
	os.Exit(status)
}

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

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

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

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

699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
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
}

715 716 717 718 719 720
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)
	}
721
	os.Exit(execute(pid, args[1:], conf, "", debugger.ExecutingOther, args, buildFlags))
722 723 724
}

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

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

737
// waitForDisconnectSignal is a blocking function that waits for either
738 739
// a SIGINT (Ctrl-C) or SIGTERM (kill -15) OS signal or for disconnectChan
// to be closed by the server when the client disconnects.
740 741 742 743 744
// 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)
745
	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
746 747 748 749 750
	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() {
751
			for range ch {
752 753
			}
		}()
754
		<-disconnectChan
755 756 757 758 759 760 761 762
	} else {
		select {
		case <-ch:
		case <-disconnectChan:
		}
	}
}

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

770
func connect(addr string, clientConn net.Conn, conf *config.Config, kind debugger.ExecuteKind) int {
771
	// Create and start a terminal - attach to running instance
772 773 774 775 776 777
	var client *rpc2.RPCClient
	if clientConn != nil {
		client = rpc2.NewClientFromConn(clientConn)
	} else {
		client = rpc2.NewClient(addr)
	}
778 779 780 781 782 783 784 785 786 787 788 789 790 791
	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
			}
		}
	}
792
	term := terminal.New(client, conf)
D
Derek Parker 已提交
793
	term.InitFile = initFile
794 795 796 797 798 799 800
	status, err := term.Run()
	if err != nil {
		fmt.Println(err)
	}
	return status
}

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

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

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

855 856 857 858
	var listener net.Listener
	var clientConn net.Conn

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

870
	var server service.Server
871

872 873
	disconnectChan := make(chan struct{})

874 875 876 877
	if workingDir == "" {
		workingDir = "."
	}

878
	// Create and start a debugger server
D
Derek Parker 已提交
879
	switch apiVersion {
A
aarzilli 已提交
880 881
	case 1, 2:
		server = rpccommon.NewServer(&service.Config{
882 883 884 885 886 887 888 889 890 891 892 893
			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 == "",
894 895 896
				Packages:             dlvArgs,
				BuildFlags:           buildFlags,
				ExecuteKind:          kind,
897 898 899
				DebugInfoDirectories: conf.DebugInfoDirectories,
				CheckGoVersion:       checkGoVersion,
				TTY:                  tty,
900
				Redirects:            redirects,
901
				DisableASLR:          disableASLR,
902
			},
D
Derek Parker 已提交
903
		})
904
	default:
D
Derek Parker 已提交
905
		fmt.Printf("Unknown API version: %d\n", apiVersion)
906 907 908
		return 1
	}

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

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

938
		return status
939 940
	}

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

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
}