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

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

17
	"github.com/go-delve/delve/pkg/config"
18
	"github.com/go-delve/delve/pkg/gobuild"
19 20 21 22 23 24
	"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"
25
	"github.com/go-delve/delve/service/dap"
26
	"github.com/go-delve/delve/service/debugger"
27 28
	"github.com/go-delve/delve/service/rpc2"
	"github.com/go-delve/delve/service/rpccommon"
29
	"github.com/mattn/go-isatty"
30 31 32 33
	"github.com/spf13/cobra"
)

var (
D
Derek Parker 已提交
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
	// 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
57
	// connections come from the same user that started the headless server
D
Derek Parker 已提交
58
	checkLocalConnUser bool
59 60
	// tty is used to provide an alternate TTY for the program you wish to debug.
	tty string
61 62
	// disableASLR is used to disable ASLR
	disableASLR bool
63

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

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

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

	traceAttachPid  int
76 77
	traceExecFile   string
	traceTestBinary bool
78 79
	traceStackDepth int

80 81 82 83 84
	// redirect specifications for target process
	redirects []string

	allowNonTerminalInteractive bool

85 86 87 88 89 90 91 92 93
	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.
94 95 96 97

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

` + "`dlv exec ./hello -- server --config conf/config.toml`"
98 99

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

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

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

D
Derek Parker 已提交
121 122 123
	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').")
124

D
Derek Parker 已提交
125 126
	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.")
127
	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 已提交
128
	rootCommand.PersistentFlags().StringVar(&initFile, "init", "", "Init file, executed by the terminal client.")
129
	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\"")
130
	rootCommand.PersistentFlags().StringVar(&workingDir, "wd", "", "Working directory for running the program.")
D
Derek Parker 已提交
131 132 133
	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').`)
134 135
	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")
136
	rootCommand.PersistentFlags().BoolVar(&disableASLR, "disable-aslr", false, "Disables address space randomization")
137

138 139
	// 'attach' subcommand.
	attachCommand := &cobra.Command{
140
		Use:   "attach pid [executable]",
141
		Short: "Attach to running process and begin debugging.",
142 143 144 145 146 147
		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.
`,
148 149 150 151 152
		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
			if len(args) == 0 {
				return errors.New("you must provide a PID")
			}
			return nil
153
		},
154
		Run: attachCmd,
155
	}
156
	attachCommand.Flags().BoolVar(&continueOnStart, "continue", false, "Continue the debugged process on start.")
D
Derek Parker 已提交
157
	rootCommand.AddCommand(attachCommand)
158

159 160 161 162
	// 'connect' subcommand.
	connectCommand := &cobra.Command{
		Use:   "connect addr",
		Short: "Connect to a headless debug server.",
163
		Long:  "Connect to a running headless debug server.",
164 165 166 167 168
		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
169
		},
170
		Run: connectCmd,
171
	}
D
Derek Parker 已提交
172
	rootCommand.AddCommand(connectCommand)
173

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

The server supports debugging of a precompiled binary akin to 'dlv exec' via a launch request.
It does not yet support support specification of program arguments.
It does not yet support launch requests with 'debug' and 'test' modes that require compilation.
It does not yet support attach requests to debug a running process like with 'dlv attach'.
It does not yet support asynchronous request-response communication.
The server does not accept multiple client connections.`,
		Run: dapCmd,
	}
D
Derek Parker 已提交
188
	rootCommand.AddCommand(dapCommand)
189

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

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

232 233 234 235 236 237 238 239
	// 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)
		},
240
	}
D
Derek Parker 已提交
241
	rootCommand.AddCommand(runCommand)
242 243 244 245 246

	// 'test' subcommand.
	testCommand := &cobra.Command{
		Use:   "test [package]",
		Short: "Compile test binary and begin debugging program.",
247 248 249 250 251 252 253
		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
that package instead.`,
		Run: testCmd,
254
	}
255
	testCommand.Flags().String("output", "debug.test", "Output path for the binary.")
D
Derek Parker 已提交
256
	rootCommand.AddCommand(testCommand)
257

258 259 260 261
	// 'trace' subcommand.
	traceCommand := &cobra.Command{
		Use:   "trace [package] regexp",
		Short: "Compile and begin tracing program.",
262 263 264 265 266
		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 已提交
267 268 269 270
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.`,
271
		Run: traceCmd,
272
	}
273
	traceCommand.Flags().IntVarP(&traceAttachPid, "pid", "p", 0, "Pid to attach to.")
274 275
	traceCommand.Flags().StringVarP(&traceExecFile, "exec", "e", "", "Binary file to exec and trace.")
	traceCommand.Flags().BoolVarP(&traceTestBinary, "test", "t", false, "Trace a test binary.")
276
	traceCommand.Flags().IntVarP(&traceStackDepth, "stack", "s", 0, "Show stack trace with given depth.")
277
	traceCommand.Flags().String("output", "debug", "Output path for the binary.")
D
Derek Parker 已提交
278
	rootCommand.AddCommand(traceCommand)
279

280 281 282
	coreCommand := &cobra.Command{
		Use:   "core <executable> <core>",
		Short: "Examine a core dump.",
283
		Long: `Examine a core dump (only supports linux and windows core dumps).
284

285 286
The core command will open the specified core file and the associated
executable and let you examine the state of the process when the
287 288
core dump was taken.

289
Currently supports linux/amd64 and linux/arm64 core files and windows/amd64 minidumps.`,
290 291 292 293 294 295 296 297
		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 已提交
298
	rootCommand.AddCommand(coreCommand)
299

300 301 302 303 304 305
	// 'version' subcommand.
	versionCommand := &cobra.Command{
		Use:   "version",
		Short: "Prints version.",
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Printf("Delve Debugger\n%s\n", version.DelveVersion)
306 307
		},
	}
D
Derek Parker 已提交
308
	rootCommand.AddCommand(versionCommand)
309

310
	if path, _ := exec.LookPath("rr"); path != "" || docCall {
311 312 313 314
		replayCommand := &cobra.Command{
			Use:   "replay [trace directory]",
			Short: "Replays a rr trace.",
			Long: `Replays a rr trace.
315

316 317 318 319 320 321 322 323 324 325
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 已提交
326
				backend = "rr"
327
				os.Exit(execute(0, []string{}, conf, args[0], debugger.ExecutingOther, args, buildFlags))
328 329
			},
		}
D
Derek Parker 已提交
330
		rootCommand.AddCommand(replayCommand)
331 332
	}

D
Derek Parker 已提交
333
	rootCommand.AddCommand(&cobra.Command{
334 335 336 337 338 339 340 341 342 343 344 345
		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 已提交
346
	rootCommand.AddCommand(&cobra.Command{
347 348 349 350 351 352 353 354 355 356 357 358 359 360
		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
361
	dap		Log all DAP messages
362 363 364 365 366 367 368
	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.
369 370
This option will also redirect the "server listening at" message in headless
and dap modes.
371

372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
`,
	})

	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.
389 390 391
`,
	})

D
Derek Parker 已提交
392
	rootCommand.DisableAutoGenTag = true
393

D
Derek Parker 已提交
394
	return rootCommand
395 396
}

397 398
func dapCmd(cmd *cobra.Command, args []string) {
	status := func() int {
D
Derek Parker 已提交
399
		if err := logflags.Setup(log, logOutput, logDest); err != nil {
400 401 402 403 404
			fmt.Fprintf(os.Stderr, "%v\n", err)
			return 1
		}
		defer logflags.Close()

D
Derek Parker 已提交
405
		if headless {
406 407
			fmt.Fprintf(os.Stderr, "Warning: headless mode not supported with dap\n")
		}
D
Derek Parker 已提交
408
		if acceptMulti {
409 410
			fmt.Fprintf(os.Stderr, "Warning: accept multiclient mode not supported with dap\n")
		}
D
Derek Parker 已提交
411
		if initFile != "" {
412 413
			fmt.Fprint(os.Stderr, "Warning: init file ignored with dap\n")
		}
D
Derek Parker 已提交
414
		if continueOnStart {
415 416
			fmt.Fprintf(os.Stderr, "Warning: continue ignored with dap; specify via launch/attach request instead\n")
		}
D
Derek Parker 已提交
417
		if buildFlags != "" {
418 419
			fmt.Fprintf(os.Stderr, "Warning: build flags ignored with dap; specify via launch/attach request instead\n")
		}
D
Derek Parker 已提交
420
		if workingDir != "" {
421 422 423 424 425 426 427 428 429 430
			fmt.Fprintf(os.Stderr, "Warning: working directory ignored with dap; launch requests must specify full program path\n")
		}
		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 已提交
431
		listener, err := net.Listen("tcp", addr)
432 433 434 435 436 437
		if err != nil {
			fmt.Printf("couldn't start listener: %s\n", err)
			return 1
		}
		disconnectChan := make(chan struct{})
		server := dap.NewServer(&service.Config{
438 439 440 441 442 443 444 445 446
			Listener:       listener,
			DisconnectChan: disconnectChan,
			Debugger: debugger.Config{
				Backend:              backend,
				Foreground:           headless && tty == "",
				DebugInfoDirectories: conf.DebugInfoDirectories,
				CheckGoVersion:       checkGoVersion,
				TTY:                  tty,
			},
447 448 449 450 451 452 453 454 455 456
		})
		defer server.Stop()

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

457 458
func debugCmd(cmd *cobra.Command, args []string) {
	status := func() int {
459 460 461 462 463 464
		debugname, err := filepath.Abs(cmd.Flag("output").Value.String())
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			return 1
		}

465
		dlvArgs, targetArgs := splitArgs(cmd, args)
D
Derek Parker 已提交
466
		err = gobuild.GoBuild(debugname, dlvArgs, buildFlags)
467 468 469 470
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			return 1
		}
471
		defer gobuild.Remove(debugname)
472
		processArgs := append([]string{debugname}, targetArgs...)
473
		return execute(0, processArgs, conf, "", debugger.ExecutingGeneratedFile, dlvArgs, buildFlags)
474 475 476 477 478 479
	}()
	os.Exit(status)
}

func traceCmd(cmd *cobra.Command, args []string) {
	status := func() int {
D
Derek Parker 已提交
480
		err := logflags.Setup(log, logOutput, logDest)
481
		defer logflags.Close()
482
		if err != nil {
483 484 485
			fmt.Fprintf(os.Stderr, "%v\n", err)
			return 1
		}
486

D
Derek Parker 已提交
487
		if headless {
488 489
			fmt.Fprintf(os.Stderr, "Warning: headless mode not supported with trace\n")
		}
D
Derek Parker 已提交
490
		if acceptMulti {
491 492 493
			fmt.Fprintf(os.Stderr, "Warning: accept multiclient mode not supported with trace")
		}

494 495 496 497
		var regexp string
		var processArgs []string

		dlvArgs, targetArgs := splitArgs(cmd, args)
498 499 500 501 502 503 504 505
		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]
		}
506 507

		if traceAttachPid == 0 {
508 509 510
			if dlvArgsLen >= 2 && traceExecFile != "" {
				fmt.Fprintln(os.Stderr, "Cannot specify package when using exec.")
				return 1
511
			}
512 513 514 515 516 517 518 519 520

			debugname := traceExecFile
			if traceExecFile == "" {
				debugname, err = filepath.Abs(cmd.Flag("output").Value.String())
				if err != nil {
					fmt.Fprintf(os.Stderr, "%v\n", err)
					return 1
				}
				if traceTestBinary {
D
Derek Parker 已提交
521
					if err := gobuild.GoTestBuild(debugname, dlvArgs, buildFlags); err != nil {
522 523 524 525
						fmt.Fprintf(os.Stderr, "%v\n", err)
						return 1
					}
				} else {
D
Derek Parker 已提交
526
					if err := gobuild.GoBuild(debugname, dlvArgs, buildFlags); err != nil {
527 528 529 530
						fmt.Fprintf(os.Stderr, "%v\n", err)
						return 1
					}
				}
531
				defer gobuild.Remove(debugname)
532 533
			}

534
			processArgs = append([]string{debugname}, targetArgs...)
535
		}
536 537 538

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

541 542 543 544
		if workingDir == "" {
			workingDir = "."
		}

545
		// Create and start a debug server
A
aarzilli 已提交
546
		server := rpccommon.NewServer(&service.Config{
547 548 549 550 551 552 553 554 555
			Listener:    listener,
			ProcessArgs: processArgs,
			APIVersion:  2,
			Debugger: debugger.Config{
				AttachPid:      traceAttachPid,
				WorkingDir:     workingDir,
				Backend:        backend,
				CheckGoVersion: checkGoVersion,
			},
D
Derek Parker 已提交
556
		})
557 558 559 560
		if err := server.Run(); err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 1
		}
561
		client := rpc2.NewClientFromConn(clientConn)
562 563 564 565 566 567
		funcs, err := client.ListFunctions(regexp)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 1
		}
		for i := range funcs {
D
Derek Parker 已提交
568 569 570 571 572 573 574
			_, err = client.CreateBreakpoint(&api.Breakpoint{
				FunctionName: funcs[i],
				Tracepoint:   true,
				Line:         -1,
				Stacktrace:   traceStackDepth,
				LoadArgs:     &terminal.ShortLoadConfig,
			})
D
Derek Parker 已提交
575
			if err != nil && !isBreakpointExistsErr(err) {
576 577 578
				fmt.Fprintln(os.Stderr, err)
				return 1
			}
D
Derek Parker 已提交
579 580 581 582 583 584 585 586 587
			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,
D
Derek Parker 已提交
588
					Stacktrace:  traceStackDepth,
D
Derek Parker 已提交
589 590 591
					Line:        -1,
					LoadArgs:    &terminal.ShortLoadConfig,
				})
D
Derek Parker 已提交
592
				if err != nil && !isBreakpointExistsErr(err) {
D
Derek Parker 已提交
593 594 595 596
					fmt.Fprintln(os.Stderr, err)
					return 1
				}
			}
597 598 599 600
		}
		cmds := terminal.DebugCommands(client)
		t := terminal.New(client, nil)
		defer t.Close()
D
Derek Parker 已提交
601
		cmds.Call("continue", t)
602 603 604 605 606
		return 0
	}()
	os.Exit(status)
}

D
Derek Parker 已提交
607 608 609 610
func isBreakpointExistsErr(err error) bool {
	return strings.Contains(err.Error(), "Breakpoint exists")
}

611 612
func testCmd(cmd *cobra.Command, args []string) {
	status := func() int {
613 614 615 616 617 618
		debugname, err := filepath.Abs(cmd.Flag("output").Value.String())
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			return 1
		}

619
		dlvArgs, targetArgs := splitArgs(cmd, args)
D
Derek Parker 已提交
620
		err = gobuild.GoTestBuild(debugname, dlvArgs, buildFlags)
621
		if err != nil {
622
			fmt.Fprintf(os.Stderr, "%v\n", err)
623 624
			return 1
		}
625
		defer gobuild.Remove(debugname)
626
		processArgs := append([]string{debugname}, targetArgs...)
627

628 629 630 631 632 633 634 635
		if workingDir == "" {
			if len(dlvArgs) == 1 {
				workingDir = getPackageDir(dlvArgs[0])
			} else {
				workingDir = "."
			}
		}

636
		return execute(0, processArgs, conf, "", debugger.ExecutingGeneratedTest, dlvArgs, buildFlags)
637 638 639 640
	}()
	os.Exit(status)
}

641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656
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
}

657 658 659 660 661 662
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)
	}
663
	os.Exit(execute(pid, args[1:], conf, "", debugger.ExecutingOther, args, buildFlags))
664 665 666
}

func coreCmd(cmd *cobra.Command, args []string) {
667
	os.Exit(execute(0, []string{args[0]}, conf, args[1], debugger.ExecutingOther, args, buildFlags))
668 669 670 671 672
}

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

679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
// waitForDisconnectSignal is a blocking function that waits for either
// a SIGINT (Ctrl-C) signal from the OS or for disconnectChan to be closed
// by the server when the client disconnects.
// 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)
	signal.Notify(ch, syscall.SIGINT)
	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() {
			for {
				select {
				case <-ch:
				}
			}
		}()
		select {
		case <-disconnectChan:
		}
	} else {
		select {
		case <-ch:
		case <-disconnectChan:
		}
	}
}

710 711 712 713 714 715 716
func splitArgs(cmd *cobra.Command, args []string) ([]string, []string) {
	if cmd.ArgsLenAtDash() >= 0 {
		return args[:cmd.ArgsLenAtDash()], args[cmd.ArgsLenAtDash():]
	}
	return args, []string{}
}

717
func connect(addr string, clientConn net.Conn, conf *config.Config, kind debugger.ExecuteKind) int {
718
	// Create and start a terminal - attach to running instance
719 720 721 722 723 724
	var client *rpc2.RPCClient
	if clientConn != nil {
		client = rpc2.NewClientFromConn(clientConn)
	} else {
		client = rpc2.NewClient(addr)
	}
725 726 727 728 729 730 731 732 733 734 735 736 737 738
	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
			}
		}
	}
739
	term := terminal.New(client, conf)
D
Derek Parker 已提交
740
	term.InitFile = initFile
741 742 743 744 745 746 747
	status, err := term.Run()
	if err != nil {
		fmt.Println(err)
	}
	return status
}

748
func execute(attachPid int, processArgs []string, conf *config.Config, coreFile string, kind debugger.ExecuteKind, dlvArgs []string, buildFlags string) int {
D
Derek Parker 已提交
749
	if err := logflags.Setup(log, logOutput, logDest); err != nil {
750 751 752
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return 1
	}
753
	defer logflags.Close()
754

D
Derek Parker 已提交
755
	if headless && (initFile != "") {
756 757
		fmt.Fprint(os.Stderr, "Warning: init file ignored with --headless\n")
	}
D
Derek Parker 已提交
758 759
	if continueOnStart {
		if !headless {
760 761 762
			fmt.Fprint(os.Stderr, "Error: --continue only works with --headless; use an init file\n")
			return 1
		}
D
Derek Parker 已提交
763
		if !acceptMulti {
764 765 766
			fmt.Fprint(os.Stderr, "Error: --continue requires --accept-multiclient\n")
			return 1
		}
767 768
	}

D
Derek Parker 已提交
769
	if !headless && acceptMulti {
770
		fmt.Fprint(os.Stderr, "Warning accept-multi: ignored\n")
D
Derek Parker 已提交
771
		// acceptMulti won't work in normal (non-headless) mode because we always
772
		// call server.Stop after the terminal client exits.
D
Derek Parker 已提交
773
		acceptMulti = false
774 775
	}

776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801
	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
	}

802 803 804 805
	var listener net.Listener
	var clientConn net.Conn

	// Make a TCP listener
D
Derek Parker 已提交
806 807
	if headless {
		listener, err = net.Listen("tcp", addr)
808 809 810 811 812 813 814 815 816
	} else {
		listener, clientConn = service.ListenerPipe()
	}
	if err != nil {
		fmt.Printf("couldn't start listener: %s\n", err)
		return 1
	}
	defer listener.Close()

817
	var server service.Server
818

819 820
	disconnectChan := make(chan struct{})

821 822 823 824
	if workingDir == "" {
		workingDir = "."
	}

825
	// Create and start a debugger server
D
Derek Parker 已提交
826
	switch apiVersion {
A
aarzilli 已提交
827 828
	case 1, 2:
		server = rpccommon.NewServer(&service.Config{
829 830 831 832 833 834 835 836 837 838 839 840
			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 == "",
841 842 843
				Packages:             dlvArgs,
				BuildFlags:           buildFlags,
				ExecuteKind:          kind,
844 845 846
				DebugInfoDirectories: conf.DebugInfoDirectories,
				CheckGoVersion:       checkGoVersion,
				TTY:                  tty,
847
				Redirects:            redirects,
848
				DisableASLR:          disableASLR,
849
			},
D
Derek Parker 已提交
850
		})
851
	default:
D
Derek Parker 已提交
852
		fmt.Printf("Unknown API version: %d\n", apiVersion)
853 854 855
		return 1
	}

856
	if err := server.Run(); err != nil {
857
		if err == api.ErrNotExecutable {
858
			switch kind {
859
			case debugger.ExecutingGeneratedFile:
860 861
				fmt.Fprintln(os.Stderr, "Can not debug non-main package")
				return 1
862
			case debugger.ExecutingExistingFile:
863 864 865 866 867 868
				fmt.Fprintf(os.Stderr, "%s is not executable\n", processArgs[0])
				return 1
			default:
				// fallthrough
			}
		}
869 870 871 872 873
		fmt.Fprintln(os.Stderr, err)
		return 1
	}

	var status int
D
Derek Parker 已提交
874 875
	if headless {
		if continueOnStart {
M
MinJae Kwon 已提交
876
			client := rpc2.NewClient(listener.Addr().String())
877 878
			client.Disconnect(true) // true = continue after disconnect
		}
879
		waitForDisconnectSignal(disconnectChan)
880
		err = server.Stop()
881 882
		if err != nil {
			fmt.Println(err)
883
		}
884

885
		return status
886 887
	}

888
	return connect(listener.Addr().String(), clientConn, conf, kind)
889
}
890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910

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
}